Skip to content

Commit

Permalink
Merge pull request #2292 from zetkin/release-241025
Browse files Browse the repository at this point in the history
241025 Release
  • Loading branch information
richardolsson authored Oct 25, 2024
2 parents c241c3b + 028f5bc commit 7ab2f66
Show file tree
Hide file tree
Showing 50 changed files with 1,467 additions and 105 deletions.
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
ZETKIN_APP_DOMAIN=http://www.dev.zetkin.org
ZETKIN_GEN2_ORGANIZE_URL=http://organize.dev.zetkin.org

ZETKIN_PRIVACY_POLICY_LINK=https://zetkin.org/privacy

Expand Down
12 changes: 9 additions & 3 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,23 @@ export const decorators = [
<Story />
</ThemeProvider>
),
(story) => {
(Story) => {
const store = createStore();
const router = useRouter();
const env = new Environment(store, new MockApiClient(), router);
return (
<ReduxProvider store={store}>
<EnvProvider env={env}>{story()}</EnvProvider>
<EnvProvider env={env}>
<Story />
</EnvProvider>
</ReduxProvider>
);
},
(story) => <I18nProvider>{story()}</I18nProvider>,
(Story) => (
<I18nProvider>
<Story />
</I18nProvider>
),
];

export const parameters = {
Expand Down
2 changes: 2 additions & 0 deletions src/core/env/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ type EnvVars = {
FEAT_AREAS?: string | null;
MUIX_LICENSE_KEY: string | null;
ZETKIN_APP_DOMAIN: string | null;
ZETKIN_GEN2_ORGANIZE_URL?: string | null;
};

export default class Environment {
Expand All @@ -20,6 +21,7 @@ export default class Environment {
FEAT_AREAS: null,
MUIX_LICENSE_KEY: null,
ZETKIN_APP_DOMAIN: null,
ZETKIN_GEN2_ORGANIZE_URL: null,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { FC } from 'react';
import { Box, Divider, Typography } from '@mui/material';

import messageIds from 'features/import/l10n/messageIds';
import { EnumColumn } from 'features/import/utils/types';
import { UIDataColumn } from 'features/import/hooks/useUIDataColumn';
import { useNumericRouteParams } from 'core/hooks';
import { Msg, useMessages } from 'core/i18n';
import useCustomFields from 'features/profile/hooks/useCustomFields';
import useEnumMapping from 'features/import/hooks/useEnumMapping';
import EnumConfigRow from './EnumConfigRow';

interface EnumConfigProps {
uiDataColumn: UIDataColumn<EnumColumn>;
}

const EnumConfig: FC<EnumConfigProps> = ({ uiDataColumn }) => {
const { orgId } = useNumericRouteParams();
const messages = useMessages(messageIds);
const { data: fields } = useCustomFields(orgId);
const { deselectOption, getSelectedOption, selectOption } = useEnumMapping(
uiDataColumn.originalColumn,
uiDataColumn.columnIndex
);

if (!fields || !fields.length) {
return null;
}
const field = fields.find(
(field) => field.slug === uiDataColumn.originalColumn.field
);
const options = field?.enum_choices;
if (!options || !options.length) {
return null;
}

return (
<Box
display="flex"
flexDirection="column"
overflow="hidden"
padding={2}
sx={{ overflowY: 'auto' }}
>
<Box alignItems="baseline" display="flex" justifyContent="space-between">
<Typography sx={{ paddingBottom: 2 }} variant="h5">
<Msg id={messageIds.configuration.configure.enum.header} />
</Typography>
</Box>

<Box alignItems="center" display="flex" paddingY={2}>
<Box width="50%">
<Typography variant="body2">
{uiDataColumn.title.toLocaleUpperCase()}
</Typography>
</Box>
<Box width="50%">
<Typography variant="body2">{field.title}</Typography>
</Box>
</Box>
{uiDataColumn.uniqueValues.map((uniqueValue, index) => (
<Box key={index}>
{index != 0 && <Divider sx={{ marginY: 1 }} />}
<EnumConfigRow
numRows={uiDataColumn.numRowsByUniqueValue[uniqueValue]}
onDeselectOption={() => deselectOption(uniqueValue)}
onSelectOption={(key) => selectOption(key, uniqueValue)}
options={options}
selectedOption={getSelectedOption(uniqueValue)}
title={field.title}
value={uniqueValue.toString()}
/>
</Box>
))}
{uiDataColumn.numberOfEmptyRows > 0 && (
<>
<Divider sx={{ marginY: 1 }} />
<EnumConfigRow
italic
numRows={uiDataColumn.numberOfEmptyRows}
onDeselectOption={() => deselectOption(null)}
onSelectOption={(key) => selectOption(key, null)}
options={options}
selectedOption={getSelectedOption(null)}
title={field.title}
value={messages.configuration.configure.tags.empty()}
/>
</>
)}
</Box>
);
};

export default EnumConfig;
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { ArrowForward, Delete } from '@mui/icons-material';
import {
Box,
Button,
FormControl,
IconButton,
InputLabel,
MenuItem,
Select,
Typography,
} from '@mui/material';
import { FC, useState } from 'react';

import messageIds from 'features/import/l10n/messageIds';
import { EnumChoice } from 'utils/types/zetkin';
import { Msg } from 'core/i18n';

interface EnumConfigRowProps {
italic?: boolean;
numRows: number;
onSelectOption: (key: string) => void;
onDeselectOption: () => void;
options: EnumChoice[];
selectedOption: string | null;
value: string;
title: string;
}

const EnumConfigRow: FC<EnumConfigRowProps> = ({
italic,
numRows,
onSelectOption,
onDeselectOption,
options,
selectedOption,
value,
title,
}) => {
const [mapping, setMapping] = useState(false);

const showSelect = mapping || selectedOption;

return (
<Box display="flex" flexDirection="column">
<Box display="flex">
<Box
alignItems="flex-start"
display="flex"
justifyContent="space-between"
paddingTop={1}
width="50%"
>
<Box display="flex" sx={{ wordBreak: 'break-all' }} width="100%">
<Typography fontStyle={italic ? 'italic' : ''}>{value}</Typography>
</Box>
<ArrowForward color="secondary" sx={{ marginRight: 1 }} />
</Box>
<Box
alignItems="flex-start"
display="flex"
paddingRight={1}
width="50%"
>
{!showSelect && (
<Button onClick={() => setMapping(true)}>
<Msg
id={
messageIds.configuration.configure.ids
.showOrganizationSelectButton
}
/>
</Button>
)}
{showSelect && (
<>
<FormControl fullWidth size="small">
<InputLabel>{title}</InputLabel>
<Select
label={title}
onChange={(event) => {
if (typeof event.target.value == 'string') {
onSelectOption(event.target.value);
}
}}
value={selectedOption || ''}
>
{options.map((option) => (
<MenuItem key={option.key} value={option.key}>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>
<IconButton
onClick={() => {
onDeselectOption();
setMapping(false);
}}
>
<Delete color="secondary" />
</IconButton>
</>
)}
</Box>
</Box>
<Typography color="secondary">
<Msg
id={messageIds.configuration.configure.enum.numberOfRows}
values={{ numRows }}
/>
</Typography>
</Box>
);
};

export default EnumConfigRow;
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import TagConfig from './TagConfig';
import {
ColumnKind,
DateColumn,
EnumColumn,
IDFieldColumn,
OrgColumn,
TagColumn,
} from 'features/import/utils/types';
import useUIDataColumn, {
UIDataColumn,
} from 'features/import/hooks/useUIDataColumn';
import EnumConfig from './EnumConfig';

interface ConfigurationProps {
columnIndexBeingConfigured: number;
Expand Down Expand Up @@ -50,6 +52,9 @@ const Configuration: FC<ConfigurationProps> = ({
{uiDataColumn && uiDataColumn.originalColumn.kind == ColumnKind.DATE && (
<DateConfig uiDataColumn={uiDataColumn as UIDataColumn<DateColumn>} />
)}
{uiDataColumn && uiDataColumn.originalColumn.kind == ColumnKind.ENUM && (
<EnumConfig uiDataColumn={uiDataColumn as UIDataColumn<EnumColumn>} />
)}
</Box>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ const FieldSelect: FC<FieldSelectProps> = ({
return `date:${column.originalColumn.field}`;
}

if (column.originalColumn.kind == ColumnKind.ENUM) {
return `enum:${column.originalColumn.field}`;
}

if (column.originalColumn.kind != ColumnKind.UNKNOWN) {
return column.originalColumn.kind.toString();
}
Expand Down Expand Up @@ -100,6 +104,14 @@ const FieldSelect: FC<FieldSelectProps> = ({
selected: true,
});
onConfigureStart();
} else if (event.target.value.startsWith('enum')) {
onChange({
field: event.target.value.slice(5),
kind: ColumnKind.ENUM,
mapping: [],
selected: true,
});
onConfigureStart();
}
}}
sx={{ opacity: column.originalColumn.selected ? '' : '50%' }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const isConfigurableColumn = (column: Column): column is ConfigurableColumn => {
ColumnKind.ORGANIZATION,
ColumnKind.TAG,
ColumnKind.DATE,
ColumnKind.ENUM,
].includes(column.kind);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import messageIds from 'features/import/l10n/messageIds';
import PreviewGrid from './PreviewGrid';
import { useMessages } from 'core/i18n';
import { CellData, ColumnKind, Sheet } from 'features/import/utils/types';
import useColumn from 'features/import/hooks/useColumn';
import { useNumericRouteParams } from 'core/hooks';
import useCustomFields from 'features/profile/hooks/useCustomFields';

interface EnumPreviewProps {
currentSheet: Sheet;
fieldKey: string;
fields: Record<string, CellData> | undefined;
}

const EnumPreview = ({ currentSheet, fieldKey, fields }: EnumPreviewProps) => {
const { orgId } = useNumericRouteParams();
const messages = useMessages(messageIds);
const { fieldOptions } = useColumn(orgId);
const customFields = useCustomFields(orgId).data ?? [];

const hasMapped = currentSheet.columns.some(
(column) => column.kind === ColumnKind.ENUM && column.mapping.length > 0
);

let columnHeader = '';
fieldOptions.flat().forEach((columnOp) => {
if (columnOp.value === `enum:${fieldKey}`) {
columnHeader = columnOp.label;
}
});

const field = customFields.find((f) => f.slug === fieldKey);
const enumChoices = field?.enum_choices || [];

const enumKey = fields?.[fieldKey];

const option = enumChoices.find((o) => o.key === enumKey);

return (
<PreviewGrid
columnHeader={columnHeader}
emptyLabel={
hasMapped && !option ? messages.configuration.preview.noValue() : ''
}
rowValue={hasMapped && option ? option.label : null}
unmappedRow={!hasMapped}
/>
);
};

export default EnumPreview;
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useNumericRouteParams } from 'core/hooks';
import usePersonPreview from 'features/import/hooks/usePersonPreview';
import useSheets from 'features/import/hooks/useSheets';
import { ColumnKind, Sheet } from 'features/import/utils/types';
import EnumPreview from './EnumPreview';

const Preview = () => {
const theme = useTheme();
Expand Down Expand Up @@ -149,6 +150,17 @@ const Preview = () => {
/>
);
}

if (column.kind === ColumnKind.ENUM) {
return (
<EnumPreview
key={columnIdx}
currentSheet={currentSheet}
fieldKey={column.field}
fields={fields}
/>
);
}
}
})}
{tagColumnSelected && (
Expand Down
Loading

0 comments on commit 7ab2f66

Please sign in to comment.