"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

@@ -0,0 +1,12 @@
import { IonIcon } from '@ionic/react';
import { woman } from 'ionicons/icons';
import React from 'react';
export function NumOfFemaleMemberJoin({ joinMembers }) {
return (
<>
<IonIcon icon={woman} style={{ fontSize: '1.1rem', color: 'rgb(235, 50, 35)' }}></IonIcon>
<div>{joinMembers.filter((jm) => jm.sex == 'F').length}</div>
</>
);
}

View File

@@ -0,0 +1,12 @@
import { IonIcon } from '@ionic/react';
import { man } from 'ionicons/icons';
import React from 'react';
export function NumOfMaleMemberJoin({ joinMembers }) {
return (
<>
<IonIcon icon={man} style={{ fontSize: '1.1rem', color: 'rgb(67, 110, 205)' }}></IonIcon>
<div>{joinMembers.filter((jm) => jm.sex == 'M').length}</div>
</>
);
}

View File

@@ -0,0 +1,15 @@
import { IonIcon } from '@ionic/react';
import { accessibility } from 'ionicons/icons';
import React from 'react';
export function NumOfMemberJoin({ joinMembers }) {
return (
<>
<IonIcon
icon={accessibility}
style={{ fontSize: '1.1rem', color: 'rgb(139, 44, 245)' }}
></IonIcon>
<div>{joinMembers.length}</div>
</>
);
}

View File

@@ -1,6 +1,6 @@
// REQ0041/home_discover_event_tab
import React, { useEffect, useRef, useState } from 'react';
import React, { useRef } from 'react';
import {
IonHeader,
IonToolbar,
@@ -8,54 +8,49 @@ import {
IonContent,
IonPage,
IonButtons,
IonMenuButton,
IonGrid,
IonRow,
IonCol,
useIonRouter,
IonButton,
IonIcon,
IonPopover,
IonAvatar,
IonImg,
IonItem,
IonLabel,
IonList,
IonModal,
IonSearchbar,
useIonModal,
IonInput,
IonRefresher,
IonRefresherContent,
RefresherEventDetail,
} from '@ionic/react';
import SpeakerItem from '../../components/SpeakerItem';
import { Speaker } from '../../models/Speaker';
import { Session } from '../../models/Schedule';
import { connect } from '../../data/connect';
import * as selectors from '../../data/selectors';
import '../SpeakerList.scss';
import { getEvents } from '../../api/getEvents';
import { format } from 'date-fns';
import { Event } from './types';
import { chevronDownCircleOutline, heart, menuOutline } from 'ionicons/icons';
import AboutPopover from '../../components/AboutPopover';
// import { Event } from './types';
import { chevronDownCircleOutline, menuOutline } from 'ionicons/icons';
import Loading from '../../components/Loading';
import { Event } from '../../models/Event';
//
import { NumOfMemberJoin } from './NumOfMemberJoin';
import { NumOfMaleMemberJoin } from './NumOfMaleMemberJoin';
import { NumOfFemaleMemberJoin } from './NumOfFemaleMemberJoin';
interface OwnProps {}
interface StateProps {
events: Event[];
fetchEventResult: any;
}
interface DispatchProps {}
interface SpeakerListProps extends OwnProps, StateProps, DispatchProps {}
const EventList: React.FC<SpeakerListProps> = ({ events }) => {
const EventList: React.FC<SpeakerListProps> = ({ fetchEventResult }) => {
const router = useIonRouter();
const modal = useRef<HTMLIonModalElement>(null);
const router = useIonRouter();
const {
result: { status },
data: { events },
} = fetchEventResult;
function handleShowPartyEventDetail(event_id: string) {
router.push(`/event_detail/${event_id}`);
@@ -68,6 +63,9 @@ const EventList: React.FC<SpeakerListProps> = ({ events }) => {
}, 2000);
}
if (status != 200)
return <>Error during fetching event list, check /events endpoint if working</>;
if (!events || events.length == 0) return <Loading />;
return (
@@ -84,9 +82,14 @@ const EventList: React.FC<SpeakerListProps> = ({ events }) => {
</IonToolbar>
</IonHeader>
<IonContent fullscreen={true}>
<IonContent className="ion-padding" fullscreen={true}>
<IonRefresher slot="fixed" onIonRefresh={handleRefresh}>
<IonRefresherContent pullingIcon={chevronDownCircleOutline} pullingText="Pull to refresh" refreshingSpinner="circles" refreshingText="Refreshing..."></IonRefresherContent>
<IonRefresherContent
pullingIcon={chevronDownCircleOutline}
pullingText="Pull to refresh"
refreshingSpinner="circles"
refreshingText="Refreshing..."
></IonRefresherContent>
</IonRefresher>
<IonHeader collapse="condense">
@@ -101,35 +104,43 @@ const EventList: React.FC<SpeakerListProps> = ({ events }) => {
<IonCol size="12" size-md="6" key={idx}>
<div
style={{
padding: '1rem',
border: '1px solid black',
border: '1px solid lightgrey',
borderRadius: '1rem',
}}
onClick={() => handleShowPartyEventDetail(event.id)}
>
<div
style={{
backgroundImage: `url("https://plus.unsplash.com/premium_photo-1683121126477-17ef068309bc")`,
backgroundImage: `url(${event.avatar[0]})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
height: '33vw',
//
borderRadius: '1rem 1rem 0 0',
}}
></div>
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '1rem',
//
marginTop: '1rem',
}}
>
<div>{format(new Date(event.eventDate), 'yyyy-MM-dd')}</div>
<div>{event.title}</div>
<div>{event.currency}</div>
<div>{event.price}</div>
<div>
{40} {20} {20}
<div style={{ marginTop: '1rem' }}>
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '0.5rem',
padding: '0.5rem',
paddingBottom: '1rem',
}}
>
{/* <div>{format(new Date(event.eve ntDate), 'yyyy-MM-dd')}</div> */}
<div style={{ color: 'rgb(0, 122, 255)' }}>
{format(new Date(event.eventDate), 'EEE, dd MMM yyyy, hh:mm a')}
</div>
<div style={{ fontSize: '1.2rem', fontWeight: 'bold' }}>{event.name}</div>
<div style={{ fontSize: '1.2rem', fontWeight: 'bold' }}>{event.price}</div>
<div style={{ display: 'flex', gap: '0.25rem', alignItems: 'center' }}>
<NumOfMemberJoin joinMembers={event.joinMembers} />
<NumOfMaleMemberJoin joinMembers={event.joinMembers} />
<NumOfFemaleMemberJoin joinMembers={event.joinMembers} />
</div>
</div>
</div>
</div>
@@ -140,7 +151,12 @@ const EventList: React.FC<SpeakerListProps> = ({ events }) => {
</IonContent>
{/* REQ0079/event-filter */}
<IonModal ref={modal} trigger="events-open-modal" initialBreakpoint={0.5} breakpoints={[0, 0.25, 0.5, 0.75]}>
<IonModal
ref={modal}
trigger="events-open-modal"
initialBreakpoint={0.5}
breakpoints={[0, 0.25, 0.5, 0.75]}
>
<IonContent className="ion-padding">
<div
style={{
@@ -203,8 +219,10 @@ const EventList: React.FC<SpeakerListProps> = ({ events }) => {
};
export default connect<OwnProps, StateProps, DispatchProps>({
mapStateToProps: (state) => ({
events: selectors.getEvents(state),
}),
mapStateToProps: (state) => {
return {
fetchEventResult: selectors.getEvents(state),
};
},
component: React.memo(EventList),
});

View File

@@ -1,4 +1,5 @@
export interface Event {
// OBSOLETED
export interface EventOBSOLETED {
eventDate: Date;
joinMembers: undefined;
title: string;