mirror of
https://github.com/myfatemi04/wheelshare-frontend.git
synced 2025-04-09 22:00:16 -04:00
add event signups with notes
This commit is contained in:
parent
69342ffe8e
commit
449308b2d6
|
@ -1,7 +1,7 @@
|
|||
import { useContext, useMemo } from 'react';
|
||||
import { Location } from '../../lib/estimateoptimalpath';
|
||||
import getDistance from '../../lib/getdistance';
|
||||
import { IEventSignupComplete } from '../types';
|
||||
import { IEventSignupWithLocation } from '../types';
|
||||
import useOptimalPath from '../useOptimalPath';
|
||||
import { CarpoolContext } from './Carpool';
|
||||
import useSignups from './useSignups';
|
||||
|
@ -21,7 +21,7 @@ export default function CarpoolRouteEstimator() {
|
|||
() =>
|
||||
signups.filter(
|
||||
(signup) => signup.latitude !== null
|
||||
) as IEventSignupComplete[],
|
||||
) as IEventSignupWithLocation[],
|
||||
[signups]
|
||||
);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
useSendCarpoolRequest,
|
||||
} from '../../state/Notifications/NotificationsHooks';
|
||||
import { useMe } from '../hooks';
|
||||
import { IEvent, IEventSignupComplete } from '../types';
|
||||
import { IEvent, IEventSignupWithLocation } from '../types';
|
||||
import useOptimalPath from '../useOptimalPath';
|
||||
import EventContext from './EventContext';
|
||||
import { useCurrentEventSignup } from './EventHooks';
|
||||
|
@ -33,7 +33,7 @@ function useMemberLocations(members: IEvent['carpools'][0]['members']) {
|
|||
longitude: signup.longitude,
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as IEventSignupComplete[],
|
||||
.filter(Boolean) as IEventSignupWithLocation[],
|
||||
[members, signups]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { green, lightgrey } from '../../lib/colors';
|
||||
import getPlaceDetails from '../../lib/getPlaceDetails';
|
||||
import { addOrUpdateEventSignup, removeEventSignup } from '../api';
|
||||
import { useMe } from '../hooks';
|
||||
import UIButton from '../UI/UIButton';
|
||||
import UIPlacesAutocomplete from '../UI/UIPlacesAutocomplete';
|
||||
import UITextInput from '../UI/UITextInput';
|
||||
import EventCarpools from './EventCarpools';
|
||||
import { useMutableEvent } from './EventHooks';
|
||||
import EventSignups from './EventSignups';
|
||||
|
@ -14,6 +15,9 @@ export default function EventInterestForm() {
|
|||
const me = useMe() || { id: 0, name: '' };
|
||||
const placeIdRef = useRef<string | null>(null);
|
||||
const canDriveRef = useRef(false);
|
||||
const [note, setNote] = useState('');
|
||||
const [noteSaved, setNoteSaved] = useState(true);
|
||||
const noteUpdateTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
{
|
||||
const signup = event.signups[me.id];
|
||||
|
@ -22,34 +26,48 @@ export default function EventInterestForm() {
|
|||
placeIdRef.current = signup?.placeId ?? null;
|
||||
canDriveRef.current = signup?.canDrive ?? false;
|
||||
}, [signup?.canDrive, signup?.placeId]);
|
||||
|
||||
useEffect(() => {
|
||||
setNote(signup?.note || '');
|
||||
}, [signup?.note]);
|
||||
}
|
||||
|
||||
const updateSignup = useCallback(async () => {
|
||||
const placeId = placeIdRef.current;
|
||||
const canDrive = canDriveRef.current;
|
||||
const updateSignup = useCallback(
|
||||
async (note: string) => {
|
||||
const placeId = placeIdRef.current;
|
||||
const canDrive = canDriveRef.current;
|
||||
|
||||
await addOrUpdateEventSignup(event.id, placeIdRef.current, canDrive);
|
||||
|
||||
if (placeId) {
|
||||
const details = await getPlaceDetails(placeId);
|
||||
|
||||
event.signups[me.id] = {
|
||||
user: { id: me.id, name: me.name },
|
||||
placeId,
|
||||
...details,
|
||||
await addOrUpdateEventSignup(
|
||||
event.id,
|
||||
placeIdRef.current,
|
||||
canDrive,
|
||||
};
|
||||
} else {
|
||||
event.signups[me.id] = {
|
||||
user: { id: me.id, name: me.name },
|
||||
placeId: null,
|
||||
latitude: null,
|
||||
longitude: null,
|
||||
formattedAddress: null,
|
||||
canDrive,
|
||||
};
|
||||
}
|
||||
}, [event.id, event.signups, me.id, me.name]);
|
||||
note
|
||||
);
|
||||
|
||||
if (placeId) {
|
||||
const details = await getPlaceDetails(placeId);
|
||||
|
||||
event.signups[me.id] = {
|
||||
user: { id: me.id, name: me.name },
|
||||
placeId,
|
||||
...details,
|
||||
canDrive,
|
||||
note,
|
||||
};
|
||||
} else {
|
||||
event.signups[me.id] = {
|
||||
user: { id: me.id, name: me.name },
|
||||
placeId: null,
|
||||
latitude: null,
|
||||
longitude: null,
|
||||
formattedAddress: null,
|
||||
canDrive,
|
||||
note,
|
||||
};
|
||||
}
|
||||
},
|
||||
[event.id, event.signups, me.id, me.name]
|
||||
);
|
||||
|
||||
const removeSignup = useCallback(async () => {
|
||||
await removeEventSignup(event.id);
|
||||
|
@ -59,6 +77,20 @@ export default function EventInterestForm() {
|
|||
}
|
||||
}, [event.id, event.signups, me.id]);
|
||||
|
||||
const updateNote = useCallback(
|
||||
(newNote: string) => {
|
||||
setNote(newNote);
|
||||
setNoteSaved(false);
|
||||
if (noteUpdateTimerRef.current) {
|
||||
clearTimeout(noteUpdateTimerRef.current);
|
||||
}
|
||||
noteUpdateTimerRef.current = setTimeout(() => {
|
||||
updateSignup(newNote).then(() => setNoteSaved(true));
|
||||
}, 1000);
|
||||
},
|
||||
[updateSignup]
|
||||
);
|
||||
|
||||
const interested = !!event.signups[me.id];
|
||||
|
||||
const canDrive = !!event.signups[me.id]?.canDrive;
|
||||
|
@ -72,7 +104,7 @@ export default function EventInterestForm() {
|
|||
? () => removeSignup()
|
||||
: () => {
|
||||
placeIdRef.current = null;
|
||||
updateSignup();
|
||||
updateSignup(note);
|
||||
}
|
||||
}
|
||||
style={{
|
||||
|
@ -91,7 +123,7 @@ export default function EventInterestForm() {
|
|||
<UIButton
|
||||
onClick={() => {
|
||||
canDriveRef.current = !canDriveRef.current;
|
||||
updateSignup();
|
||||
updateSignup(note);
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: canDrive ? green : lightgrey,
|
||||
|
@ -105,7 +137,7 @@ export default function EventInterestForm() {
|
|||
placeholder="Pickup and dropoff location"
|
||||
onSelected={(_address, placeId) => {
|
||||
placeIdRef.current = placeId;
|
||||
updateSignup();
|
||||
updateSignup(note);
|
||||
}}
|
||||
style={
|
||||
event.signups[me.id]?.placeId != null
|
||||
|
@ -115,6 +147,15 @@ export default function EventInterestForm() {
|
|||
placeId={event.signups[me.id]?.placeId}
|
||||
/>
|
||||
<br />
|
||||
<span style={{ fontSize: '0.875em' }}>
|
||||
Note (e.g. "Monday, Tuesday, Wednesday")
|
||||
</span>
|
||||
<UITextInput
|
||||
value={note}
|
||||
onChangeText={updateNote}
|
||||
style={noteSaved ? { border: '2px solid ' + green } : {}}
|
||||
/>
|
||||
<br />
|
||||
<EventCarpools />
|
||||
{event.signups !== null && <EventSignups />}
|
||||
</>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { CSSProperties, useCallback } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { CSSProperties, ForwardedRef, useCallback } from 'react';
|
||||
|
||||
const baseStyle = {
|
||||
marginTop: '0.5em',
|
||||
|
@ -9,23 +10,27 @@ const baseStyle = {
|
|||
border: '0px',
|
||||
};
|
||||
|
||||
export default function UITextInput({
|
||||
value,
|
||||
disabled = false,
|
||||
onChangeText,
|
||||
style,
|
||||
}: {
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
onChangeText: (text: string) => void;
|
||||
style?: CSSProperties;
|
||||
}) {
|
||||
function UITextInput(
|
||||
{
|
||||
value,
|
||||
disabled = false,
|
||||
onChangeText,
|
||||
style,
|
||||
}: {
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
onChangeText?: (text: string) => void;
|
||||
style?: CSSProperties;
|
||||
},
|
||||
ref: ForwardedRef<HTMLInputElement>
|
||||
) {
|
||||
const onChange = useCallback(
|
||||
(e) => onChangeText(e.target.value),
|
||||
(e) => onChangeText?.(e.target.value),
|
||||
[onChangeText]
|
||||
);
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
style={style ? { ...baseStyle, ...style } : baseStyle}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
|
@ -33,3 +38,5 @@ export default function UITextInput({
|
|||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default forwardRef(UITextInput);
|
||||
|
|
|
@ -75,9 +75,10 @@ export async function getEventSignups(
|
|||
export async function addOrUpdateEventSignup(
|
||||
eventId: number,
|
||||
placeId: string | null,
|
||||
canDrive: boolean
|
||||
canDrive: boolean,
|
||||
note: string
|
||||
) {
|
||||
await post(`/events/${eventId}/signup`, { placeId, canDrive });
|
||||
await post(`/events/${eventId}/signup`, { placeId, canDrive, note });
|
||||
}
|
||||
|
||||
export async function removeEventSignup(eventId: number) {
|
||||
|
|
|
@ -92,24 +92,23 @@ export type IEvent = {
|
|||
longitude: number;
|
||||
};
|
||||
|
||||
export type IEventSignupComplete = {
|
||||
export type IEventSignupBase = {
|
||||
user: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
canDrive: boolean;
|
||||
note: string;
|
||||
};
|
||||
|
||||
export type IEventSignupWithLocation = IEventSignupBase & {
|
||||
placeId: string;
|
||||
formattedAddress: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
|
||||
export type IEventSignupIncomplete = {
|
||||
user: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
canDrive: boolean;
|
||||
export type IEventSignupWithoutLocation = IEventSignupBase & {
|
||||
placeId: null;
|
||||
formattedAddress: null;
|
||||
latitude: null;
|
||||
|
@ -120,7 +119,9 @@ export type IEventSignupIncomplete = {
|
|||
* Model EventSignup
|
||||
*/
|
||||
|
||||
export type IEventSignup = IEventSignupComplete | IEventSignupIncomplete;
|
||||
export type IEventSignup =
|
||||
| IEventSignupWithLocation
|
||||
| IEventSignupWithoutLocation;
|
||||
|
||||
export type IInvitation = {
|
||||
user: {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useDebugValue, useMemo } from 'react';
|
||||
import estimateOptimalPath, { Path } from '../lib/estimateoptimalpath';
|
||||
import { ICarpool, IEventSignupComplete } from './types';
|
||||
import { ICarpool, IEventSignupWithLocation } from './types';
|
||||
|
||||
export default function useOptimalPath(
|
||||
members: IEventSignupComplete[],
|
||||
members: IEventSignupWithLocation[],
|
||||
destination: ICarpool['event']
|
||||
) {
|
||||
const path = useMemo(() => {
|
||||
|
@ -38,7 +38,7 @@ export default function useOptimalPath(
|
|||
}
|
||||
|
||||
return prev;
|
||||
}, null! as { path: Path<IEventSignupComplete, ICarpool['event']>; distance: number });
|
||||
}, null! as { path: Path<IEventSignupWithLocation, ICarpool['event']>; distance: number });
|
||||
|
||||
return path;
|
||||
}, [destination, members]);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ICarpool, IEventSignupComplete } from '../components/types';
|
||||
import { ICarpool, IEventSignupWithLocation } from '../components/types';
|
||||
import getDistance from './getdistance';
|
||||
|
||||
export type Location = {
|
||||
|
@ -13,9 +13,9 @@ export type Path<M extends Location, D extends Location> = {
|
|||
};
|
||||
|
||||
export default function estimateOptimalPath(
|
||||
path: Path<IEventSignupComplete, ICarpool['event']>
|
||||
path: Path<IEventSignupWithLocation, ICarpool['event']>
|
||||
): {
|
||||
path: Path<IEventSignupComplete, ICarpool['event']>;
|
||||
path: Path<IEventSignupWithLocation, ICarpool['event']>;
|
||||
distance: number;
|
||||
} {
|
||||
const { from, to, waypoints } = path;
|
||||
|
@ -49,7 +49,7 @@ export default function estimateOptimalPath(
|
|||
path: {
|
||||
from,
|
||||
to,
|
||||
waypoints: newWaypoints as IEventSignupComplete[],
|
||||
waypoints: newWaypoints as IEventSignupWithLocation[],
|
||||
},
|
||||
distance: getDistance(from, ...sequence, to),
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user