feat: implement event joining flow with dummy payment page, including route configuration, Redux state management, and UI updates for event detail page
This commit is contained in:
@@ -66,7 +66,8 @@ import PrivacyAgreement from './pages/PrivacyAgreement';
|
||||
import EventDetail from './pages/EventDetail';
|
||||
import MemberProfile from './pages/MemberProfile';
|
||||
import OrderDetail from './pages/OrderDetail';
|
||||
import DummyPayPage from './pages/DummyPayPage';
|
||||
import DummyPayPage from './pages/DummyEventPayPage';
|
||||
import DummyEventPayPage from './pages/DummyEventPayPage';
|
||||
|
||||
setupIonicReact();
|
||||
|
||||
@@ -142,6 +143,8 @@ const IonicApp: React.FC<IonicAppProps> = ({ darkMode, schedule, setIsLoggedIn,
|
||||
|
||||
<Route exact={true} path="/helloworld" component={Helloworld} />
|
||||
|
||||
<Route exact={true} path={PATHS.DUMMY_EVENT_PAY_PAGE} component={DummyEventPayPage} />
|
||||
|
||||
<Route
|
||||
path="/logout"
|
||||
render={() => {
|
||||
|
@@ -31,6 +31,8 @@ const PATHS = {
|
||||
PARTY_USER_SIGN_IN: '/partyUserlogin',
|
||||
PARTY_USER_SIGN_UP: '/partyUserSignUp',
|
||||
|
||||
DUMMY_EVENT_PAY_PAGE: '/DummyEventPayPage',
|
||||
|
||||
//
|
||||
TABS_DEBUG: '/tabs/debug',
|
||||
|
||||
|
@@ -11,6 +11,7 @@ const constants = {
|
||||
// Used to construct all API request URLs
|
||||
API_ENDPOINT,
|
||||
SIGN_IN: `${API_ENDPOINT}/api/party-user-auth/sign-in`,
|
||||
PARTY_USER_JOIN_EVENT: `${API_ENDPOINT}/api/event/partyUserJoinEvent`,
|
||||
};
|
||||
|
||||
if (!constants.API_ENDPOINT) {
|
||||
|
14
03_source/mobile/src/data/dummy/dummy.actions.ts
Normal file
14
03_source/mobile/src/data/dummy/dummy.actions.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// import { setIsLoggedInData } from '../dataApi';
|
||||
import { ActionType } from '../../util/types';
|
||||
|
||||
export const setEventIdToJoin = (eventId: string) => {
|
||||
// await setIsLoggedInData(loggedIn);
|
||||
|
||||
return {
|
||||
type: 'set-dummy-event-id-to-join',
|
||||
eventId,
|
||||
} as const;
|
||||
};
|
||||
|
||||
export type DummyActions = ActionType<typeof setEventIdToJoin>;
|
||||
// | ActionType<typeof checkUserSession>
|
13
03_source/mobile/src/data/dummy/dummy.reducer.ts
Normal file
13
03_source/mobile/src/data/dummy/dummy.reducer.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { DummyActions as DummyActions } from './dummy.actions';
|
||||
import { DummyState as DummyState } from './dummy.state';
|
||||
|
||||
export function dummyReducer(state: DummyState, action: DummyActions): DummyState {
|
||||
switch (action.type) {
|
||||
case 'set-dummy-event-id-to-join':
|
||||
console.log('reducer called');
|
||||
|
||||
return { ...state, eventIdToJoin: action.eventId };
|
||||
default:
|
||||
return { ...state };
|
||||
}
|
||||
}
|
3
03_source/mobile/src/data/dummy/dummy.state.ts
Normal file
3
03_source/mobile/src/data/dummy/dummy.state.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface DummyState {
|
||||
eventIdToJoin?: string;
|
||||
}
|
@@ -211,3 +211,5 @@ export const mapCenter = (state: AppState) => {
|
||||
|
||||
export const getPartyUserUsername = (state: AppState) => state.user.username;
|
||||
export const getPartyUserState = (state: AppState) => state.user;
|
||||
|
||||
export const getEventIdToJoin = (state: AppState) => state.dummy.eventIdToJoin;
|
||||
|
@@ -15,6 +15,7 @@ import { locationsReducer } from './locations/locations.reducer';
|
||||
|
||||
// Additional feature reducers
|
||||
import { orderReducer } from './sessions/orders.reducer';
|
||||
import { dummyReducer } from './dummy/dummy.reducer';
|
||||
|
||||
export const initialState: AppState = {
|
||||
data: {
|
||||
@@ -40,10 +41,14 @@ export const initialState: AppState = {
|
||||
loading: false,
|
||||
//
|
||||
isSessionValid: false,
|
||||
//
|
||||
},
|
||||
locations: {
|
||||
locations: [],
|
||||
},
|
||||
dummy: {
|
||||
eventIdToJoin: '',
|
||||
},
|
||||
};
|
||||
|
||||
export const reducers = combineReducers({
|
||||
@@ -52,6 +57,7 @@ export const reducers = combineReducers({
|
||||
locations: locationsReducer,
|
||||
//
|
||||
order: orderReducer,
|
||||
dummy: dummyReducer,
|
||||
});
|
||||
|
||||
export type AppState = ReturnType<typeof reducers>;
|
||||
|
131
03_source/mobile/src/pages/DummyEventPayPage/index.tsx
Normal file
131
03_source/mobile/src/pages/DummyEventPayPage/index.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
// REQ0041/home_discover_event_tab
|
||||
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
useIonRouter,
|
||||
IonToast,
|
||||
} from '@ionic/react';
|
||||
import { chevronBackOutline, menuOutline } from 'ionicons/icons';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import './style.scss';
|
||||
import PATHS from '../../PATHS';
|
||||
import axios from 'axios';
|
||||
import { UserState } from '../../data/user/user.state';
|
||||
import { connect } from '../../data/connect';
|
||||
|
||||
import * as selectors from '../../data/selectors';
|
||||
import constants from '../../constants';
|
||||
|
||||
interface OwnProps {}
|
||||
|
||||
interface StateProps {
|
||||
isLoggedin: boolean;
|
||||
//
|
||||
partyUserState: UserState;
|
||||
//
|
||||
joinEventId: string;
|
||||
}
|
||||
|
||||
interface DispatchProps {}
|
||||
|
||||
interface PageProps extends OwnProps, StateProps, DispatchProps {}
|
||||
|
||||
const DummyPayPage: React.FC<PageProps> = ({
|
||||
isLoggedin,
|
||||
partyUserState,
|
||||
//
|
||||
joinEventId,
|
||||
}) => {
|
||||
const router = useIonRouter();
|
||||
|
||||
// if (!isLoggedin) return <NotLoggedIn />;
|
||||
|
||||
async function handlePayClick() {
|
||||
try {
|
||||
await axios.post(constants.PARTY_USER_JOIN_EVENT, {
|
||||
data: {
|
||||
eventItemId: joinEventId,
|
||||
email: partyUserState.meta?.email,
|
||||
},
|
||||
});
|
||||
|
||||
router.goBack();
|
||||
|
||||
setShowJoinOKToast(true);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancelClick() {
|
||||
router.goBack();
|
||||
}
|
||||
|
||||
const [showJoinOKToast, setShowJoinOKToast] = useState(false);
|
||||
|
||||
return (
|
||||
<IonPage id="speaker-list">
|
||||
<IonHeader translucent={true} className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
{/* <IonMenuButton /> */}
|
||||
<IonButton shape="round">
|
||||
<IonIcon slot="icon-only" icon={chevronBackOutline}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
<IonTitle>Dummy pay event page</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen={true}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: '1rem',
|
||||
textAlign: 'center',
|
||||
|
||||
padding: '3rem',
|
||||
}}
|
||||
>
|
||||
<div>This is a dummy page to emulate payment gateway work</div>
|
||||
|
||||
<div>
|
||||
<div>pay for event</div>
|
||||
<pre style={{ backgroundColor: 'RGB(0,0,0, 0.1)' }}>{JSON.stringify(joinEventId)}</pre>
|
||||
</div>
|
||||
|
||||
<IonButton onClick={handlePayClick}>Pay</IonButton>
|
||||
<IonButton onClick={handleCancelClick}>Cancel</IonButton>
|
||||
</div>
|
||||
|
||||
<IonToast
|
||||
isOpen={showJoinOKToast}
|
||||
message="ok, event paid, thank you..."
|
||||
duration={2000}
|
||||
// onDidDismiss={() => setShowJoinOKToast(false)}
|
||||
/>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect<OwnProps, StateProps, DispatchProps>({
|
||||
mapStateToProps: (state) => ({
|
||||
isLoggedin: state.user.isLoggedin,
|
||||
//
|
||||
joinEventId: selectors.getEventIdToJoin(state),
|
||||
//
|
||||
partyUserState: selectors.getPartyUserState(state),
|
||||
}),
|
||||
component: DummyPayPage,
|
||||
});
|
@@ -1,37 +0,0 @@
|
||||
// REQ0041/home_discover_event_tab
|
||||
|
||||
import {
|
||||
IonPage,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonButtons,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
} from '@ionic/react';
|
||||
import { menuOutline } from 'ionicons/icons';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import './style.scss';
|
||||
|
||||
const DummyPayPage: React.FC = () => {
|
||||
return (
|
||||
<IonPage id="speaker-list">
|
||||
<IonHeader translucent={true} className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="end">
|
||||
{/* <IonMenuButton /> */}
|
||||
<IonButton shape="round" id="events-open-modal" expand="block">
|
||||
<IonIcon slot="icon-only" icon={menuOutline}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
<IonTitle>Discover Events</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen={true}>DummyPayPage</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default DummyPayPage;
|
@@ -13,48 +13,27 @@ import {
|
||||
IonContent,
|
||||
IonPage,
|
||||
IonButtons,
|
||||
IonMenuButton,
|
||||
IonButton,
|
||||
IonIcon,
|
||||
IonDatetime,
|
||||
IonSelectOption,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonSelect,
|
||||
IonPopover,
|
||||
IonText,
|
||||
IonFooter,
|
||||
useIonRouter,
|
||||
IonAvatar,
|
||||
} from '@ionic/react';
|
||||
import './style.scss';
|
||||
import {
|
||||
accessibility,
|
||||
accessibilityOutline,
|
||||
chevronBackOutline,
|
||||
ellipsisHorizontal,
|
||||
ellipsisVertical,
|
||||
heart,
|
||||
locationOutline,
|
||||
locationSharp,
|
||||
logoIonic,
|
||||
man,
|
||||
manOutline,
|
||||
people,
|
||||
peopleOutline,
|
||||
timer,
|
||||
timerOutline,
|
||||
timerSharp,
|
||||
wallet,
|
||||
walletOutline,
|
||||
walletSharp,
|
||||
woman,
|
||||
womanOutline,
|
||||
} from 'ionicons/icons';
|
||||
import AboutPopover from '../../components/AboutPopover';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
import { TestContent } from './TestContent';
|
||||
import { Helloworld } from '../../api/Helloworld';
|
||||
import { getEventById } from '../../api/getEventById';
|
||||
import { connect } from '../../data/connect';
|
||||
@@ -62,6 +41,9 @@ import * as selectors from '../../data/selectors';
|
||||
import { Event } from '../../models/Event';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import AvatarRow from './AvatarRow';
|
||||
import { setPartyUserMeta } from '../../data/user/user.actions';
|
||||
import { setEventIdToJoin } from '../../data/dummy/dummy.actions';
|
||||
import PATHS from '../../PATHS';
|
||||
|
||||
const leftShift: number = -25;
|
||||
|
||||
@@ -71,7 +53,10 @@ interface OwnProps extends RouteComponentProps {
|
||||
|
||||
interface StateProps {}
|
||||
|
||||
interface DispatchProps {}
|
||||
interface DispatchProps {
|
||||
setPartyUserMeta: typeof setPartyUserMeta;
|
||||
setEventIdToJoin: typeof setEventIdToJoin;
|
||||
}
|
||||
|
||||
interface PageProps extends OwnProps, StateProps, DispatchProps {}
|
||||
|
||||
@@ -90,7 +75,7 @@ const showJoinedMembers = (joinMembers: Record<string, any>[]) => {
|
||||
);
|
||||
};
|
||||
|
||||
const EventDetail: React.FC<PageProps> = ({ event_detail }) => {
|
||||
const EventDetail: React.FC<PageProps> = ({ event_detail, setEventIdToJoin }) => {
|
||||
const router = useIonRouter();
|
||||
|
||||
const [showPopover, setShowPopover] = useState(false);
|
||||
@@ -136,6 +121,13 @@ const EventDetail: React.FC<PageProps> = ({ event_detail }) => {
|
||||
router.goBack();
|
||||
}
|
||||
|
||||
function handleJoinClick() {
|
||||
if (event_detail && event_detail?.id) {
|
||||
setEventIdToJoin(event_detail.id);
|
||||
router.push(PATHS.DUMMY_EVENT_PAY_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!event_detail) return <>loading</>;
|
||||
|
||||
return (
|
||||
@@ -268,7 +260,7 @@ const EventDetail: React.FC<PageProps> = ({ event_detail }) => {
|
||||
margin: '1rem',
|
||||
}}
|
||||
>
|
||||
<IonButton expand="full" shape="round">
|
||||
<IonButton expand="full" shape="round" onClick={handleJoinClick}>
|
||||
Join
|
||||
</IonButton>
|
||||
</div>
|
||||
@@ -286,8 +278,10 @@ const EventDetail: React.FC<PageProps> = ({ event_detail }) => {
|
||||
};
|
||||
|
||||
export default connect({
|
||||
mapDispatchToProps: {
|
||||
setEventIdToJoin,
|
||||
},
|
||||
mapStateToProps: (state, ownProps) => {
|
||||
console.log({ t1: selectors.getEvents(state) });
|
||||
return {
|
||||
event_detail: selectors.getEvent(state, ownProps),
|
||||
};
|
||||
|
@@ -14,7 +14,7 @@ import { menuOutline } from 'ionicons/icons';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import './style.scss';
|
||||
|
||||
const Helloworld: React.FC = () => {
|
||||
const Helloworld: React.FC = ({}) => {
|
||||
return (
|
||||
<IonPage id="speaker-list">
|
||||
<IonHeader translucent={true} className="ion-no-border">
|
||||
|
Reference in New Issue
Block a user