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 EventDetail from './pages/EventDetail';
|
||||||
import MemberProfile from './pages/MemberProfile';
|
import MemberProfile from './pages/MemberProfile';
|
||||||
import OrderDetail from './pages/OrderDetail';
|
import OrderDetail from './pages/OrderDetail';
|
||||||
import DummyPayPage from './pages/DummyPayPage';
|
import DummyPayPage from './pages/DummyEventPayPage';
|
||||||
|
import DummyEventPayPage from './pages/DummyEventPayPage';
|
||||||
|
|
||||||
setupIonicReact();
|
setupIonicReact();
|
||||||
|
|
||||||
@@ -142,6 +143,8 @@ const IonicApp: React.FC<IonicAppProps> = ({ darkMode, schedule, setIsLoggedIn,
|
|||||||
|
|
||||||
<Route exact={true} path="/helloworld" component={Helloworld} />
|
<Route exact={true} path="/helloworld" component={Helloworld} />
|
||||||
|
|
||||||
|
<Route exact={true} path={PATHS.DUMMY_EVENT_PAY_PAGE} component={DummyEventPayPage} />
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/logout"
|
path="/logout"
|
||||||
render={() => {
|
render={() => {
|
||||||
|
@@ -31,6 +31,8 @@ const PATHS = {
|
|||||||
PARTY_USER_SIGN_IN: '/partyUserlogin',
|
PARTY_USER_SIGN_IN: '/partyUserlogin',
|
||||||
PARTY_USER_SIGN_UP: '/partyUserSignUp',
|
PARTY_USER_SIGN_UP: '/partyUserSignUp',
|
||||||
|
|
||||||
|
DUMMY_EVENT_PAY_PAGE: '/DummyEventPayPage',
|
||||||
|
|
||||||
//
|
//
|
||||||
TABS_DEBUG: '/tabs/debug',
|
TABS_DEBUG: '/tabs/debug',
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ const constants = {
|
|||||||
// Used to construct all API request URLs
|
// Used to construct all API request URLs
|
||||||
API_ENDPOINT,
|
API_ENDPOINT,
|
||||||
SIGN_IN: `${API_ENDPOINT}/api/party-user-auth/sign-in`,
|
SIGN_IN: `${API_ENDPOINT}/api/party-user-auth/sign-in`,
|
||||||
|
PARTY_USER_JOIN_EVENT: `${API_ENDPOINT}/api/event/partyUserJoinEvent`,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!constants.API_ENDPOINT) {
|
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 getPartyUserUsername = (state: AppState) => state.user.username;
|
||||||
export const getPartyUserState = (state: AppState) => state.user;
|
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
|
// Additional feature reducers
|
||||||
import { orderReducer } from './sessions/orders.reducer';
|
import { orderReducer } from './sessions/orders.reducer';
|
||||||
|
import { dummyReducer } from './dummy/dummy.reducer';
|
||||||
|
|
||||||
export const initialState: AppState = {
|
export const initialState: AppState = {
|
||||||
data: {
|
data: {
|
||||||
@@ -40,10 +41,14 @@ export const initialState: AppState = {
|
|||||||
loading: false,
|
loading: false,
|
||||||
//
|
//
|
||||||
isSessionValid: false,
|
isSessionValid: false,
|
||||||
|
//
|
||||||
},
|
},
|
||||||
locations: {
|
locations: {
|
||||||
locations: [],
|
locations: [],
|
||||||
},
|
},
|
||||||
|
dummy: {
|
||||||
|
eventIdToJoin: '',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reducers = combineReducers({
|
export const reducers = combineReducers({
|
||||||
@@ -52,6 +57,7 @@ export const reducers = combineReducers({
|
|||||||
locations: locationsReducer,
|
locations: locationsReducer,
|
||||||
//
|
//
|
||||||
order: orderReducer,
|
order: orderReducer,
|
||||||
|
dummy: dummyReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppState = ReturnType<typeof reducers>;
|
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,
|
IonContent,
|
||||||
IonPage,
|
IonPage,
|
||||||
IonButtons,
|
IonButtons,
|
||||||
IonMenuButton,
|
|
||||||
IonButton,
|
IonButton,
|
||||||
IonIcon,
|
IonIcon,
|
||||||
IonDatetime,
|
|
||||||
IonSelectOption,
|
|
||||||
IonList,
|
|
||||||
IonItem,
|
|
||||||
IonLabel,
|
|
||||||
IonSelect,
|
|
||||||
IonPopover,
|
IonPopover,
|
||||||
IonText,
|
|
||||||
IonFooter,
|
IonFooter,
|
||||||
useIonRouter,
|
useIonRouter,
|
||||||
IonAvatar,
|
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
import {
|
import {
|
||||||
accessibility,
|
accessibility,
|
||||||
accessibilityOutline,
|
|
||||||
chevronBackOutline,
|
chevronBackOutline,
|
||||||
ellipsisHorizontal,
|
ellipsisHorizontal,
|
||||||
ellipsisVertical,
|
ellipsisVertical,
|
||||||
heart,
|
|
||||||
locationOutline,
|
|
||||||
locationSharp,
|
locationSharp,
|
||||||
logoIonic,
|
|
||||||
man,
|
man,
|
||||||
manOutline,
|
|
||||||
people,
|
people,
|
||||||
peopleOutline,
|
|
||||||
timer,
|
|
||||||
timerOutline,
|
|
||||||
timerSharp,
|
timerSharp,
|
||||||
wallet,
|
|
||||||
walletOutline,
|
|
||||||
walletSharp,
|
walletSharp,
|
||||||
woman,
|
woman,
|
||||||
womanOutline,
|
|
||||||
} from 'ionicons/icons';
|
} from 'ionicons/icons';
|
||||||
import AboutPopover from '../../components/AboutPopover';
|
import AboutPopover from '../../components/AboutPopover';
|
||||||
import { format, parseISO } from 'date-fns';
|
import { format, parseISO } from 'date-fns';
|
||||||
import { TestContent } from './TestContent';
|
|
||||||
import { Helloworld } from '../../api/Helloworld';
|
import { Helloworld } from '../../api/Helloworld';
|
||||||
import { getEventById } from '../../api/getEventById';
|
import { getEventById } from '../../api/getEventById';
|
||||||
import { connect } from '../../data/connect';
|
import { connect } from '../../data/connect';
|
||||||
@@ -62,6 +41,9 @@ import * as selectors from '../../data/selectors';
|
|||||||
import { Event } from '../../models/Event';
|
import { Event } from '../../models/Event';
|
||||||
import { RouteComponentProps } from 'react-router';
|
import { RouteComponentProps } from 'react-router';
|
||||||
import AvatarRow from './AvatarRow';
|
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;
|
const leftShift: number = -25;
|
||||||
|
|
||||||
@@ -71,7 +53,10 @@ interface OwnProps extends RouteComponentProps {
|
|||||||
|
|
||||||
interface StateProps {}
|
interface StateProps {}
|
||||||
|
|
||||||
interface DispatchProps {}
|
interface DispatchProps {
|
||||||
|
setPartyUserMeta: typeof setPartyUserMeta;
|
||||||
|
setEventIdToJoin: typeof setEventIdToJoin;
|
||||||
|
}
|
||||||
|
|
||||||
interface PageProps extends OwnProps, StateProps, DispatchProps {}
|
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 router = useIonRouter();
|
||||||
|
|
||||||
const [showPopover, setShowPopover] = useState(false);
|
const [showPopover, setShowPopover] = useState(false);
|
||||||
@@ -136,6 +121,13 @@ const EventDetail: React.FC<PageProps> = ({ event_detail }) => {
|
|||||||
router.goBack();
|
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</>;
|
if (!event_detail) return <>loading</>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -268,7 +260,7 @@ const EventDetail: React.FC<PageProps> = ({ event_detail }) => {
|
|||||||
margin: '1rem',
|
margin: '1rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IonButton expand="full" shape="round">
|
<IonButton expand="full" shape="round" onClick={handleJoinClick}>
|
||||||
Join
|
Join
|
||||||
</IonButton>
|
</IonButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -286,8 +278,10 @@ const EventDetail: React.FC<PageProps> = ({ event_detail }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default connect({
|
export default connect({
|
||||||
|
mapDispatchToProps: {
|
||||||
|
setEventIdToJoin,
|
||||||
|
},
|
||||||
mapStateToProps: (state, ownProps) => {
|
mapStateToProps: (state, ownProps) => {
|
||||||
console.log({ t1: selectors.getEvents(state) });
|
|
||||||
return {
|
return {
|
||||||
event_detail: selectors.getEvent(state, ownProps),
|
event_detail: selectors.getEvent(state, ownProps),
|
||||||
};
|
};
|
||||||
|
@@ -14,7 +14,7 @@ import { menuOutline } from 'ionicons/icons';
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
const Helloworld: React.FC = () => {
|
const Helloworld: React.FC = ({}) => {
|
||||||
return (
|
return (
|
||||||
<IonPage id="speaker-list">
|
<IonPage id="speaker-list">
|
||||||
<IonHeader translucent={true} className="ion-no-border">
|
<IonHeader translucent={true} className="ion-no-border">
|
||||||
|
Reference in New Issue
Block a user