Skip to content

Commit

Permalink
Merge pull request sap-labs-france#559 from sap-labs-france/select_no…
Browse files Browse the repository at this point in the history
…_car

Allow to select no car
  • Loading branch information
LucasBrazi06 authored Dec 9, 2021
2 parents 2319022 + 970d5d3 commit bd74480
Show file tree
Hide file tree
Showing 13 changed files with 83 additions and 42 deletions.
1 change: 1 addition & 0 deletions src/I18n/languages/cz.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
"car": "Car",
"selectCar": "Select a car",
"selectCars": "Select cars",
"clearCar": "Clear car",
"noCarMessageTitle": "Indicate what car you are using to get better recharges",
"addCarTitle": "Add a car",
"addCarButton": "Add car",
Expand Down
1 change: 1 addition & 0 deletions src/I18n/languages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
"car": "Car",
"selectCar": "Select a car",
"selectCars": "Select cars",
"clearCar": "Clear car",
"noCarMessageTitle": "Indicate what car you are using to get better recharges",
"addCarTitle": "Add a car",
"addCarButton": "Add car",
Expand Down
1 change: 1 addition & 0 deletions src/I18n/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
"car": "Car",
"selectCar": "Select a car",
"selectCars": "Select cars",
"clearCar": "Clear car",
"noCarMessageTitle": "Indicate what car you are using to get better recharges",
"addCarTitle": "Add a car",
"addCarButton": "Add car",
Expand Down
1 change: 1 addition & 0 deletions src/I18n/languages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
"car": "Vehículo",
"selectCar": "Select a car",
"selectCars": "Select cars",
"clearCar": "Clear car",
"noCarMessageTitle": "Indicate what car you are using to get better recharges",
"addCarTitle": "Add a car",
"addCarButton": "Add car",
Expand Down
1 change: 1 addition & 0 deletions src/I18n/languages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
"car": "Voiture",
"selectCar": "Sélectionnez une voiture",
"selectCars": "Sélectionnez des voitures",
"clearCar": "Effacer voiture",
"noCarMessageTitle": "Indiquez la voiture utilisée pour bénéficier de meilleures recharges",
"addCarTitle": "Ajouter une voiture",
"addCarButton": "Ajouter",
Expand Down
1 change: 1 addition & 0 deletions src/I18n/languages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
"car": "Car",
"selectCar": "Select a car",
"selectCars": "Select cars",
"clearCar": "Clear car",
"noCarMessageTitle": "Indicate what car you are using to get better recharges",
"addCarTitle": "Ajouter une voiture",
"addCarButton": "Ajouter",
Expand Down
1 change: 1 addition & 0 deletions src/I18n/languages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
"car": "Car",
"selectCar": "Select a car",
"selectCars": "Select cars",
"clearCar": "Clear car",
"noCarMessageTitle": "Indicate what car you are using to get better recharges",
"addCarTitle": "Ajouter une voiture",
"addCarButton": "Ajouter",
Expand Down
58 changes: 35 additions & 23 deletions src/components/modal/ModalSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,27 @@ import computeListItemCommonStyle from '../list/ListItemCommonStyle';
export interface Props<T> extends BaseProps {
defaultItem?: T;
buildItemName?: (item: T) => string;
// Disable the opening with specific styles
disabled?: boolean;
label?: string;
// Render list item
renderItem?: (item?: T) => React.ReactElement;
// Render a generic item when the items list is empty (only if renderItemPlaceholder is null)
renderNoItem?: () => React.ReactElement;
// Render a generic item when nothing is selected
renderItemPlaceholder?: () => React.ReactElement;
// Whether or not to display the button to clear the input
clearable?: boolean;
selectionMode: ItemSelectionMode;
onItemsSelected: (selectedItems: T[]) => void;
defaultItemLoading?: boolean;
// Can open the modal
openable?: boolean;
itemsEquals?: (a: T, b: T) => boolean;
}
interface State<T> {
isVisible: boolean;
noneSelected: boolean;
selectedItems: T[];
selectedItemsCount: number;
}
Expand All @@ -42,12 +51,15 @@ export default class ModalSelect<T extends ListItem> extends React.Component<Pro
this.state = {
isVisible: false,
selectedItems: [],
selectedItemsCount: 0
selectedItemsCount: 0,
noneSelected: false
};
}

public clearInput() {
this.setState({ selectedItems: [] });

public resetInput(noneSelected: boolean = false): void {
this.itemsListRef?.current?.clearSelectedItems();
this.setState({noneSelected, selectedItems: [], selectedItemsCount: 0, isVisible: false}, () => this.props.onItemsSelected([]));
}

public render() {
Expand All @@ -57,17 +69,15 @@ export default class ModalSelect<T extends ListItem> extends React.Component<Pro
const itemsList = React.Children.only(this.props.children);
return (
<View style={style.container}>
<View style={style.buttonContainer}>
{label && <Text style={style.label}>{label}</Text>}
{this.renderButton(style)}
</View>
{label && <Text style={style.label}>{label}</Text>}
{this.renderButton(style)}
<Modal
propagateSwipe={true}
supportedOrientations={['portrait', 'landscape']}
style={style.modal}
isVisible={isVisible}
swipeDirection={'down'}
animationInTiming={1000}
animationInTiming={800}
onSwipeComplete={() => this.setState({ isVisible: false })}
// Modal component registers the given method for hardwareBackPress event and unregisters it when the modal inner content unmounts.
// Inner component list also unsubscribe on unmount, allowing the last subscriber to choose back implementation.
Expand All @@ -91,7 +101,7 @@ export default class ModalSelect<T extends ListItem> extends React.Component<Pro
</View>
{selectionMode === ItemSelectionMode.MULTI && (
<View style={style.bottomButtonContainer}>
<Button style={style.modalButton} block light onPress={() => this.clearSelection()}>
<Button style={style.modalButton} block light onPress={() => this.resetInput()}>
<Text style={style.buttonText}>{I18n.t('general.reset')}</Text>
</Button>
<Button
Expand All @@ -110,32 +120,28 @@ export default class ModalSelect<T extends ListItem> extends React.Component<Pro
);
}

private clearSelection(): void {
this.itemsListRef?.current?.clearSelectedItems();
this.setState(this.state);
}

private validateSelection(): void {
const { onItemsSelected } = this.props;
const selectedItems = this.itemsListRef?.current?.state.selectedItems;
if (!Utils.isEmptyArray(selectedItems)) {
this.setState({ isVisible: false }, () => onItemsSelected(selectedItems));
this.setState({ isVisible: false }, () => onItemsSelected?.(selectedItems));
}
}

private onItemSelected(selectedItems: T[]): void {
const { selectionMode, onItemsSelected } = this.props;
if (selectionMode === ItemSelectionMode.MULTI) {
this.setState({ selectedItems });
this.setState({ selectedItems, noneSelected: false });
} else if (selectionMode === ItemSelectionMode.SINGLE && !Utils.isEmptyArray(selectedItems)) {
this.setState({ selectedItems, isVisible: false }, () => onItemsSelected(selectedItems));
this.setState({ selectedItems, isVisible: false, noneSelected: false }, () => onItemsSelected?.(selectedItems));
}
}

private renderButton(style: any) {
const { defaultItemLoading, defaultItem, renderNoItem, renderItem, renderItemPlaceholder, openable, disabled } = this.props;
const { selectedItems } = this.state;
const { defaultItemLoading, renderNoItem, renderItem, renderItemPlaceholder, openable, disabled, clearable, defaultItem } = this.props;
const { selectedItems, noneSelected } = this.state;
const listItemCommonStyle = computeListItemCommonStyle();

const commonColors = Utils.getCurrentCommonColor();
if (defaultItemLoading) {
return (
Expand All @@ -144,22 +150,28 @@ export default class ModalSelect<T extends ListItem> extends React.Component<Pro
</View>
);
}
if (defaultItem || selectedItems[0]) {
if ((selectedItems[0] || defaultItem)) {
return (
<TouchableOpacity
disabled={disabled || !openable}
onPress={() => this.setState({ isVisible: true })}
style={[style.itemContainer, disabled && style.buttonDisabled]}>
{renderItem?.(selectedItems.length > 0 ? selectedItems[0] : defaultItem)}
{clearable && (
<TouchableOpacity style={style.clearContainer} onPress={() => this.resetInput(true)}>
<Text style={{textAlign: 'right', color: commonColors.primary}}>{I18n.t('cars.clearCar')}</Text>
</TouchableOpacity>
)}
{renderItem?.(selectedItems[0] ?? defaultItem)}
</TouchableOpacity>
);
} else if (renderItemPlaceholder) {
} if (renderItemPlaceholder && (noneSelected || !renderNoItem)) {
return (
<TouchableOpacity onPress={() => this.setState({ isVisible: true })} style={style.itemContainer}>
{renderItemPlaceholder?.()}
</TouchableOpacity>
);
} else {
}
else {
return <View style={style.itemContainer}>{renderNoItem?.()}</View>;
}
}
Expand Down
9 changes: 3 additions & 6 deletions src/components/modal/ModalStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ export default function computeStyleSheet(): StyleSheet.NamedStyles<any> {
container: {
width: '100%'
},
button: {
width: '95%',
alignSelf: 'center',
height: '40@s',
backgroundColor: commonColor.buttonBg
},
buttonDisabled: {
opacity: 0.5
},
Expand Down Expand Up @@ -120,6 +114,9 @@ export default function computeStyleSheet(): StyleSheet.NamedStyles<any> {
marginLeft: '5%',
marginBottom: '6@s',
textAlign: 'left'
},
clearContainer: {
width: '100%'
}
});
const portraitStyles = {};
Expand Down
5 changes: 3 additions & 2 deletions src/components/user/UserComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import computeListItemCommonStyle from '../list/ListItemCommonStyle';
export interface Props extends BaseProps {
user: User;
selected?: boolean;
outlinedInactive?: boolean;
}

interface State {}
Expand All @@ -37,13 +38,13 @@ export default class UserComponent extends React.Component<Props, State> {
const style = computeStyleSheet();
const listItemCommonStyle = computeListItemCommonStyle();
const chipStyle = computeChipStyleSheet();
const { user, navigation, selected } = this.props;
const { user, navigation, selected, outlinedInactive } = this.props;
const userFullName = Utils.buildUserName(user);
const userRole = user ? user.role : '';
const userStatus = user ? user.status : '';
const statusStyle = this.computeStatusStyle(userStatus, chipStyle);
return (
<View style={[listItemCommonStyle.container, style.container]}>
<View style={[listItemCommonStyle.container, style.container, outlinedInactive && userStatus !== UserStatus.ACTIVE && listItemCommonStyle.outlinedError]}>
<View style={style.userContent}>
<View style={style.avatarContainer}>
{selected ? <UserAvatar isSelected={true} navigation={navigation} /> : <UserAvatar user={user} navigation={navigation} />}
Expand Down
2 changes: 1 addition & 1 deletion src/screens/cars/AddCar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export default class AddCar extends BaseScreen<Props, State> {
buttonStyle={modalCommonStyles.primaryButton}
onPress={this.addCar.bind(this)}
loading={addCarPending}
title={I18n.t('cars.addCarButton').toUpperCase()}
title={I18n.t('cars.addCarButton')}
/>
</ScrollView>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export interface State {
showNoBadgeErrorMessage?: boolean;
showBadgeInactiveErrorMessage?: boolean;
showBillingErrorMessage?: boolean;
userInactiveError?: boolean;
transactionPending?: boolean;
didPreparing?: boolean;
showAdviceMessage?: boolean;
Expand Down Expand Up @@ -111,6 +112,7 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
showNoBadgeErrorMessage: false,
showBadgeInactiveErrorMessage: false,
showBillingErrorMessage: false,
userInactiveError: false,
transactionPending: false,
showAdviceMessage: false,
didPreparing: false,
Expand Down Expand Up @@ -288,6 +290,7 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
let transaction = null;
let showNoBadgeErrorMessage: boolean;
let showBadgeInactiveErrorMessage: boolean;
let userInactiveError: boolean;
let showBillingErrorMessage: boolean;
let showStartTransactionDialog: boolean;
let showAdviceMessage = false;
Expand All @@ -304,6 +307,7 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
const transactionStillPending = this.isTransactionStillPending(connector);
if (transactionStillPending) {
buttonDisabled = true;
userInactiveError = true;
} else {
// if the transaction is no longer pending, reset the flags
this.setState({ transactionPending: false, didPreparing: false });
Expand All @@ -324,6 +328,11 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
transaction = await this.getTransaction(connector.currentTransactionID);
}
const { selectedUser, selectedTag } = this.state;
// Check selected user is active
if (selectedUser?.status !== UserStatus.ACTIVE) {
buttonDisabled = true;
userInactiveError = true;
}
// Get the default tag and car of the selected user (only to get errors codes)
const userDefaultTagCar = await this.getUserDefaultTagAndCar(selectedUser);
// If error codes, disabled the button
Expand Down Expand Up @@ -373,6 +382,7 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
showBillingErrorMessage,
showNoBadgeErrorMessage,
showBadgeInactiveErrorMessage,
userInactiveError,
showAdviceMessage,
buttonDisabled,
chargingStation,
Expand Down Expand Up @@ -948,12 +958,12 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
}

private renderAccordion(style: any) {
const { showChargingSettings, showBillingErrorMessage, showNoBadgeErrorMessage, showBadgeInactiveErrorMessage } = this.state;
const { showChargingSettings, showBillingErrorMessage, showNoBadgeErrorMessage, showBadgeInactiveErrorMessage, userInactiveError } = this.state;
return (
<TouchableOpacity onPress={() => this.setState({ showChargingSettings: !showChargingSettings })} style={style.accordion}>
<Text style={style.accordionText}>
{I18n.t('transactions.chargingSettings')}
{(showBillingErrorMessage || showNoBadgeErrorMessage || showBadgeInactiveErrorMessage) && (
{(showBillingErrorMessage || showNoBadgeErrorMessage || showBadgeInactiveErrorMessage || userInactiveError) && (
<Text style={style.errorAsterisque}>*</Text>
)}
</Text>
Expand Down Expand Up @@ -1048,7 +1058,7 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
<ModalSelect<User>
disabled={disabled}
openable={isAdmin}
renderItem={() => <UserComponent user={selectedUser} navigation={navigation} />}
renderItem={() => <UserComponent outlinedInactive={true} user={selectedUser} navigation={navigation} />}
defaultItem={selectedUser}
onItemsSelected={this.onUserSelected.bind(this)}
navigation={navigation}
Expand All @@ -1071,9 +1081,11 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
disabled={disabled}
openable={true}
renderNoItem={this.renderNoCar.bind(this)}
clearable={true}
renderItem={() => <CarComponent car={selectedCar} navigation={navigation} />}
ref={this.carModalRef}
defaultItem={selectedCar}
renderItemPlaceholder={this.renderCarPlaceholder.bind(this)}
defaultItemLoading={tagCarLoading}
onItemsSelected={(selectedCars: Car[]) => this.setState({ selectedCar: selectedCars?.[0] })}
navigation={navigation}
Expand All @@ -1084,6 +1096,19 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
);
}

private renderCarPlaceholder() {
const listItemCommonStyle = computeListItemCommonStyle();
const style = computeStyleSheet();
return (
<View style={[listItemCommonStyle.container, style.noItemContainer, style.noCarContainer]}>
<Icon style={style.noCarIcon} type={'MaterialCommunityIcons'} name={'car'} />
<View style={style.column}>
<Text style={style.messageText}>{I18n.t('cars.noCarMessageTitle')}</Text>
</View>
</View>
);
}

private renderNoCar() {
const listItemCommonStyle = computeListItemCommonStyle();
const style = computeStyleSheet();
Expand Down Expand Up @@ -1118,6 +1143,7 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
disabled={disabled}
openable={true}
renderNoItem={this.renderNoTag.bind(this)}
itemsEquals={(a, b) => a?.visualID === b?.visualID}
ref={this.tagModalRef}
defaultItem={selectedTag}
defaultItemLoading={tagCarLoading}
Expand Down Expand Up @@ -1168,8 +1194,8 @@ export default class ChargingStationConnectorDetails extends BaseAutoRefreshScre
this.setState({ tagCarLoading: true });
try {
const userDefaultTagCar = await this.getUserDefaultTagAndCar(selectedUser);
this.carModalRef.current?.clearInput();
this.tagModalRef.current?.clearInput();
this.carModalRef.current?.resetInput();
this.tagModalRef.current?.resetInput();
// Temporary workaround to ensure that the default property is set (server-side changes are to be done)
if (userDefaultTagCar?.tag) {
userDefaultTagCar.tag.default = true;
Expand Down
Loading

0 comments on commit bd74480

Please sign in to comment.