"feat: update data APIs to fetch orders and events via fetch instead of axios, add Order and Event models, update selectors and reducers, add EventDetail page with joined members display"

This commit is contained in:
louiscklaw
2025-06-03 17:04:11 +08:00
parent 24920fb313
commit 7610d80005
18 changed files with 867 additions and 246 deletions

View File

@@ -26,14 +26,31 @@ import {
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';
@@ -44,28 +61,46 @@ import { connect } from '../../data/connect';
import * as selectors from '../../data/selectors';
import { Event } from '../../models/Event';
import { RouteComponentProps } from 'react-router';
import AvatarRow from './AvatarRow';
const leftShift: number = -25;
interface OwnProps extends RouteComponentProps {
event?: Event;
event_detail?: Event;
}
interface StateProps {}
interface DispatchProps {}
interface SpeakerDetailProps extends OwnProps, StateProps, DispatchProps {}
interface EventDetailProps extends OwnProps, StateProps, DispatchProps {}
interface AboutProps {}
const showJoinedMembers = (joinMembers: Record<string, any>[]) => {
const avatars = joinMembers.map((jm) => jm.avatar);
console.log({ joinMembers });
return (
<>
<AvatarRow avatars={avatars} />
<IonButton style={{ '--padding-start': '20px', '--padding-end': '20px' }} shape="round">
More
</IonButton>
</>
);
};
const EventDetail: React.FC<EventDetailProps> = ({ event_detail }) => {
const router = useIonRouter();
const EventDetail: React.FC<SpeakerDetailProps> = () => {
const [showPopover, setShowPopover] = useState(false);
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
const [location, setLocation] = useState<
'madison' | 'austin' | 'chicago' | 'seattle'
>('madison');
const [conferenceDate, setConferenceDate] = useState(
'2047-05-17T00:00:00-05:00'
);
const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison');
const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00');
const [totalJoinMembers, setTotalJoinMembers] = useState<number>(0);
const [maleMembers, setMaleMembers] = useState<number>(0);
const [femaleMembers, setFemaleMembers] = useState<number>(0);
const selectOptions = {
header: 'Select a Location',
@@ -80,6 +115,14 @@ const EventDetail: React.FC<SpeakerDetailProps> = () => {
return format(parseISO(date), dateFormat);
}
useEffect(() => {
if (event_detail) {
setTotalJoinMembers(event_detail.joinMembers.length);
setMaleMembers(event_detail.joinMembers.filter((m) => m.sex == 'M').length);
setFemaleMembers(event_detail.joinMembers.filter((m) => m.sex == 'F').length);
}
}, [event_detail]);
const [eventDetail, setEventDetail] = useState<Event | null>(null);
useEffect(() => {
Helloworld();
@@ -89,15 +132,14 @@ const EventDetail: React.FC<SpeakerDetailProps> = () => {
});
}, []);
const router = useIonRouter();
function handleBackOnClick() {
router.goBack();
}
if (!eventDetail) return <>loading</>;
if (!event_detail) return <>loading</>;
return (
<IonPage id="about-page">
<IonPage id="event-detail-page">
<IonContent>
<IonHeader className="ion-no-border">
<IonToolbar>
@@ -109,63 +151,110 @@ const EventDetail: React.FC<SpeakerDetailProps> = () => {
</IonButtons>
<IonButtons slot="end">
<IonButton onClick={presentPopover}>
<IonIcon
slot="icon-only"
ios={ellipsisHorizontal}
md={ellipsisVertical}
></IonIcon>
<IonIcon slot="icon-only" ios={ellipsisHorizontal} md={ellipsisVertical}></IonIcon>
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<div className="about-header">
<div className="about-image madison" style={{ opacity: 1 }}></div>
</div>
<div>{eventDetail.avatar}</div>
<div>
<div>{format(new Date(eventDetail.eventDate), 'yyyy-MM-dd')}</div>
<h1>{eventDetail.title}</h1>
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<div>members place holder</div>
<IonButton shape="round">More</IonButton>
</div>
<div
style={{
marginBottom: '1rem',
paddingTop: '1rem',
borderBottom: '1px solid black',
}}
className="about-image madison"
style={{ opacity: 1, backgroundImage: `url(${event_detail.avatar[0]})` }}
></div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<div style={{ display: 'flex', gap: '1rem' }}>
<div>{eventDetail.currency}</div>
<div>{eventDetail.price}</div>
per person
<div style={{ paddingLeft: '0.5rem', paddingRight: '0.5rem' }}>
<div>
<div style={{ paddingTop: '0.25rem', color: '#007AFF' }}>
{format(new Date(event_detail.eventDate), 'EEE, dd MMM yyyy, hh:mm a')}
</div>
<div style={{ paddingTop: '0.25rem', fontSize: '1.8rem', fontWeight: '500' }}>
{event_detail.title}
</div>
<div
style={{
display: 'flex',
gap: '1rem',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
{event_detail.joinMembers && event_detail.joinMembers.length > 0 ? (
showJoinedMembers(event_detail.joinMembers)
) : (
<>join fast !</>
)}
</div>
</div>
</div>
<div
style={{
marginBottom: '1rem',
paddingTop: '1rem',
borderBottom: '1px solid gray',
}}
></div>
<div
style={{
paddingLeft: '0.5rem',
paddingRight: '0.5rem',
display: 'flex',
flexDirection: 'column',
gap: '1rem',
}}
>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<IonIcon icon={walletSharp} style={{ fontSize: '1.5rem' }}></IonIcon>
<div style={{ display: 'flex', gap: '0.15rem', alignItems: 'center' }}>
<div style={{ fontWeight: 'bold' }}>{event_detail.currency}</div>
<div style={{ fontWeight: 'bold' }}>{event_detail.price}</div>
per person
</div>
</div>
<div>{eventDetail.duration_m}</div>
<div style={{ display: 'flex', gap: '1rem' }}>
<div>{eventDetail.ageBottom}</div>
<div>{eventDetail.ageTop}</div>
<div>years old</div>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<IonIcon icon={timerSharp} style={{ fontSize: '1.5rem' }}></IonIcon>
<div style={{ display: 'flex', gap: '0.15rem', alignItems: 'center' }}>
{event_detail.duration_m}
<div>mins</div>
</div>
</div>
<div>{eventDetail.location}</div>
<div style={{ display: 'flex', gap: '1rem' }}>
<IonIcon icon={logoIonic}></IonIcon>
<div>40</div>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<IonIcon icon={people} style={{ fontSize: '1.5rem' }}></IonIcon>
<div style={{ display: 'flex', gap: '0.15rem', alignItems: 'center' }}>
<div>{event_detail.ageBottom}</div>~<div>{event_detail.ageTop}</div>
<div>years old</div>
</div>
</div>
<IonIcon icon={logoIonic}></IonIcon>
<div>20</div>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<IonIcon icon={locationSharp} style={{ fontSize: '1.5rem' }}></IonIcon>
{event_detail.location}
</div>
<IonIcon icon={logoIonic}></IonIcon>
<div>20</div>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<IonIcon
icon={accessibility}
style={{ fontSize: '1.5rem', color: 'rgb(139, 44, 245)' }}
></IonIcon>
<div>{totalJoinMembers}</div>
<IonIcon
icon={man}
style={{ fontSize: '1.5rem', color: 'rgb(67, 110, 205)' }}
></IonIcon>
<div>{maleMembers}</div>
<IonIcon
icon={woman}
style={{ fontSize: '1.5rem', color: 'rgb(235, 50, 35)' }}
></IonIcon>
<div>{femaleMembers}</div>
</div>
</div>
</IonContent>
@@ -197,8 +286,11 @@ const EventDetail: React.FC<SpeakerDetailProps> = () => {
};
export default connect({
mapStateToProps: (state, ownProps) => ({
event: selectors.getEvent(state, ownProps),
}),
mapStateToProps: (state, ownProps) => {
console.log({ t1: selectors.getEvents(state) });
return {
event_detail: selectors.getEvent(state, ownProps),
};
},
component: EventDetail,
});