Skip to content
Closed
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
6 changes: 3 additions & 3 deletions src/components/ag-grid/station-ag-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { RmgStyle, SidePanelMode, StationInfo } from '../../constants/constants';
import { useTranslation } from 'react-i18next';
import { HStack } from '@chakra-ui/react';
import { setIsShareTrackEnabled, setSelectedStation, setSidePanelMode } from '../../redux/app/app-slice';
import { setIsShareTrackEnabled, setSelectedStations, setSidePanelMode } from '../../redux/app/app-slice';
import { getRowSpanForColine } from '../../redux/param/coline-action';
import GzmtrStationCode from './gzmtr-station-code';
import { MonoColour } from '@railmapgen/rmg-palette-resources';
Expand All @@ -31,7 +31,7 @@ interface StationAgGridProps {
type RowDataType = StationInfo & { id: string; rowSpan: [number, string | undefined] };

const rowSelection: RowSelectionOptions = {
mode: 'singleRow',
mode: 'multiRow',
checkboxes: false,
enableClickSelection: true,
};
Expand Down Expand Up @@ -168,7 +168,7 @@ export default function StationAgGrid(props: StationAgGridProps) {

if (selectedRowIds?.length) {
dispatch(setSidePanelMode(SidePanelMode.STATION));
dispatch(setSelectedStation(selectedRowIds[0]));
dispatch(setSelectedStations(selectedRowIds));
dispatch(setIsShareTrackEnabled(undefined));
}
}, []);
Expand Down
2 changes: 1 addition & 1 deletion src/components/modal/add-station-modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ describe('AddStationModal', () => {
expect(addedStations).toHaveLength(1);

// open side panel
expect(mockStore.getState().app.selectedStation).toBe(addedStations[0]);
expect(mockStore.getState().app.selectedStations[0]).toBe(addedStations[0]);
expect(mockStore.getState().app.sidePanelMode).toBe(SidePanelMode.STATION);
});
});
Expand Down
6 changes: 3 additions & 3 deletions src/components/modal/remove-confirm-modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('RemoveConfirmModal', () => {
const mockStore = createTestStore({
app: {
...realStore.app,
selectedStation: 'stn1',
selectedStations: ['stn1'],
},
param: {
...realStore.param,
Expand Down Expand Up @@ -99,7 +99,7 @@ describe('RemoveConfirmModal', () => {
const mockStore = createTestStore({
app: {
...realStore.app,
selectedStation: 'stn2',
selectedStations: ['stn2'],
},
param: {
...realStore.param,
Expand All @@ -115,6 +115,6 @@ describe('RemoveConfirmModal', () => {
// assertions
expect(mockStore.getState().param.stn_list).not.toHaveProperty('stn2'); // removal of station
expect(mockStore.getState().app.sidePanelMode).toBe(SidePanelMode.CLOSE); // close side panel
expect(mockStore.getState().app.selectedStation).toBe('linestart'); // reset station selection
expect(mockStore.getState().app.selectedStations[0]).toBe('linestart'); // reset station selection
});
});
2 changes: 1 addition & 1 deletion src/components/modal/remove-confirm-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function RemoveConfirmModal(props: RemoveConfirmModalProps) {
const { t } = useTranslation();

const dispatch = useRootDispatch();
const selectedStation = useRootSelector(state => state.app.selectedStation);
const selectedStation = useRootSelector(state => state.app.selectedStations[0]);

const [error, setError] = useState(false);

Expand Down
92 changes: 92 additions & 0 deletions src/components/side-panel/batch-station-edit-panel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Box, Button, HStack, Text, VStack } from '@chakra-ui/react';
import { RmgFields, RmgSidePanelFooter } from '@railmapgen/rmg-components';
import { useTranslation } from 'react-i18next';
import { Facilities, Services, TEMP } from '../../constants/constants';
import { useRootDispatch, useRootSelector } from '../../redux';
import { updateStationsProperty } from '../../redux/param/action';
import { checkStationCouldBeRemoved, removeStation } from '../../redux/param/remove-station-action';
import { setSelectedStations } from '../../redux/app/app-slice';

import { SidePanelMode } from '../../constants/constants';
import { setSidePanelMode } from '../../redux/app/app-slice';
import { useStationEditFields } from './station-side-panel/use-station-edit-fields';

export default function BatchStationEditPanel() {
const { t } = useTranslation();
const dispatch = useRootDispatch();

const selectedStations = useRootSelector(state => state.app.selectedStations);
const { style, stn_list, loop } = useRootSelector(state => state.param);

if (!selectedStations || selectedStations.length <= 1) return null;

const firstStation = stn_list[selectedStations[0]];
const commonServices = firstStation?.services || [];
const commonFacility = firstStation?.facility || '';
const commonOneLine = firstStation?.one_line || false;
const commonIntPadding = firstStation?.int_padding;
const commonCharacterSpacing = firstStation?.character_spacing;
const commonUnderConstruction = firstStation?.underConstruction;

const fields = useStationEditFields({
style,
loop,
values: {
services: commonServices,
facility: commonFacility,
one_line: commonOneLine,
int_padding: commonIntPadding,
character_spacing: commonCharacterSpacing,
underConstruction: commonUnderConstruction,
},
handlers: {
onServicesChange: (val: Services[]) => dispatch(updateStationsProperty(selectedStations, 'services', val)),
onFacilityChange: (val: string | number) =>
dispatch(updateStationsProperty(selectedStations, 'facility', val as Facilities)),
onOneLineChange: (val: boolean) => dispatch(updateStationsProperty(selectedStations, 'one_line', val)),
onIntPaddingChange: (val: number) => dispatch(updateStationsProperty(selectedStations, 'int_padding', val)),
onCharacterSpacingChange: (val: number) =>
dispatch(updateStationsProperty(selectedStations, 'character_spacing', val)),
onUnderConstructionChange: (val: boolean | TEMP) =>
dispatch(updateStationsProperty(selectedStations, 'underConstruction', val)),
},
});

const handleDelete = () => {
dispatch((dispatch, getState) => {
const currentSelected = getState().app.selectedStations;
currentSelected.forEach(id => {
if (getState().param.stn_list[id]) {
if (checkStationCouldBeRemoved(id)(dispatch, getState)) {
dispatch(removeStation(id));
}
}
});
dispatch(setSelectedStations([]));
dispatch(setSidePanelMode(SidePanelMode.CLOSE));
});
};

return (
<VStack spacing={4} align="stretch">
<Box p={4}>
<Text mb={4}>
{t('StationSidePanel.batch_selected', {
count: selectedStations.length,
defaultValue: `Selected ${selectedStations.length} stations`,
})}
</Text>
<RmgFields fields={fields} />
</Box>

<RmgSidePanelFooter>
<HStack justify="space-between">
<Button size="sm" variant="outline" onClick={handleDelete}>
{t('StationSidePanel.footer.remove')}
</Button>
</HStack>
</RmgSidePanelFooter>
</VStack>
);
}

15 changes: 11 additions & 4 deletions src/components/side-panel/side-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useRootSelector } from '../../redux';
import { closePaletteAppClip, onPaletteAppClipEmit, setSidePanelMode } from '../../redux/app/app-slice';
import { useDispatch } from 'react-redux';
import { SidePanelMode } from '../../constants/constants';
import BatchStationEditPanel from './batch-station-edit-panel';
import StationSidePanel from './station-side-panel/station-side-panel';
import StyleSidePanel from './style-side-panel/style-side-panel';
import { RmgMultiLineString, RmgSidePanel, RmgSidePanelHeader } from '@railmapgen/rmg-components';
Expand All @@ -17,14 +18,20 @@ export default function SidePanel() {
const { t } = useTranslation();
const dispatch = useDispatch();

const { sidePanelMode, selectedStation, paletteAppClipInput } = useRootSelector(state => state.app);
const { sidePanelMode, selectedStations, paletteAppClipInput } = useRootSelector(state => state.app);
const selectedStation = selectedStations[0] || 'linestart';
const name = useRootSelector(state => state.param.stn_list[selectedStation]?.localisedName);

const mode: Record<SidePanelMode, { header: ReactNode; body?: ReactNode; footer?: ReactNode }> = {
STATION: {
header: <RmgMultiLineString text={name?.zh + '/' + name?.en || ''} />,
body: <StationSidePanel />,
footer: <StationSidePanelFooter />,
header:
selectedStations && selectedStations.length > 1 ? (
t('StationSidePanel.batch_edit')
) : (
<RmgMultiLineString text={name?.zh + '/' + name?.en || ''} />
),
body: selectedStations && selectedStations.length > 1 ? <BatchStationEditPanel /> : <StationSidePanel />,
footer: selectedStations && selectedStations.length > 1 ? null : <StationSidePanelFooter />,
},
STYLE: { header: t('StyleSidePanel.header'), body: <StyleSidePanel /> },
BRANCH: { header: t('BranchSidePanel.header'), body: <BranchSidePanel /> },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function BranchSection() {
const { t } = useTranslation();
const dispatch = useRootDispatch();

const selectedStation = useRootSelector(state => state.app.selectedStation);
const selectedStation = useRootSelector(state => state.app.selectedStations[0]);
const stationList = useRootSelector(state => state.param.stn_list);
const { parents, children, branch } = stationList[selectedStation];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function InfoSection() {
const { t } = useTranslation();
const dispatch = useRootDispatch();

const selectedStation = useRootSelector(state => state.app.selectedStation);
const selectedStation = useRootSelector(state => state.app.selectedStations[0]);
console.log('InfoSection:: Rendering for', selectedStation);
const style = useRootSelector(state => state.param.style);
const { num, localisedName, localisedSecondaryName } = useRootSelector(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const testStationId = realStore.param.stn_list.linestart.children[0];
describe('InterchangeSection', () => {
it('Can render InterchangeCard with headings as expected', () => {
const mockStore = createTestStore({
app: { ...realStore.app, selectedStation: testStationId },
app: { ...realStore.app, selectedStations: [testStationId] },
param: {
...realStore.param,
style: RmgStyle.GZMTR,
Expand Down Expand Up @@ -42,7 +42,7 @@ describe('InterchangeSection', () => {

it('Can handle add interchange group as expected', () => {
const mockStore = createTestStore({
app: { ...realStore.app, selectedStation: testStationId },
app: { ...realStore.app, selectedStations: [testStationId] },
param: {
...realStore.param,
style: RmgStyle.GZMTR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function InterchangeSection() {
const { t } = useTranslation();
const dispatch = useRootDispatch();

const selectedStation = useRootSelector(state => state.app.selectedStation);
const selectedStation = useRootSelector(state => state.app.selectedStations[0]);
const { theme, style } = useRootSelector(state => state.param);
const { transfer } = useRootSelector(state => state.param.stn_list[selectedStation]);

Expand Down
Loading