Compare commits

..

No commits in common. "70b1b36852e6b9c0fb0607176f5e405756aa290c" and "b17572da4b1f8b49f60d436f5b3b9b82d6a86440" have entirely different histories.

4 changed files with 31 additions and 49 deletions

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import useSchedule from '../hooks/useSchedule.js'; import useSchedule from '../hooks/useSchedule.js';
import { getSpeakerName, isTrackHidden } from './utils.js'; import { getSpeakerName, isTrackHidden } from './utils.js';
import { Fragment, useState } from 'react'; import { Fragment } from 'react';
import useScheduleTable from '../hooks/useScheduleTable.js'; import useScheduleTable from '../hooks/useScheduleTable.js';
import Event from './Event.jsx'; import Event from './Event.jsx';
import defaultSpeaker from '../assets/default-speaker.png'; import defaultSpeaker from '../assets/default-speaker.png';
@ -17,7 +17,6 @@ export default function Schedule({
const { const {
speakers, speakers,
tracks, tracks,
eventTypes,
halls, halls,
events, events,
slots, slots,
@ -26,32 +25,25 @@ export default function Schedule({
isComplete, isComplete,
} = useSchedule(conferenceId); } = useSchedule(conferenceId);
const [eventTypeId, setEventTypeId] = useState(0);
const { const {
header, header,
rows, rows,
} = useScheduleTable({ } = useScheduleTable({
eventTypeId, tracks,
halls, halls,
events, events,
slots, slots,
}); });
return (<> return (<>
{isComplete && <select value={eventTypeId} onChange={e => setEventTypeId(parseInt(e.target.value, 10))}>
<option value={0}>All event types</option>
{eventTypes.map(eventType =>
<option key={eventType.id} value={eventType.id}>{eventType.name[lang]}</option>)}
</select>}
{isLoading && <progress value={loadingProgress}/>} {isLoading && <progress value={loadingProgress}/>}
{isComplete && <div className="schedule"> {isComplete && <div className="schedule">
<hr/> <hr />
<table> <table>
<thead> <thead>
<tr> <tr>
{header.map(hall => <th key={hall.id}>{hall.name[lang]}</th>)} {header.map(hall => <th key={hall.id}>{hall.name[lang]}</th>)}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{rows.map(row => <tr key={row.id}> {rows.map(row => <tr key={row.id}>
@ -69,7 +61,9 @@ export default function Schedule({
<div className="separator"/> <div className="separator"/>
<table> <table>
<tbody> <tbody>
{tracks.filter(track => !isTrackHidden(track)).map(track => <tr key={track.id}> {Object.values(tracks).filter(track =>
!isTrackHidden(track)
).map(track => <tr key={track.id}>
<td className={track.css_class}>{track.name[lang]}</td> <td className={track.css_class}>{track.name[lang]}</td>
</tr>)} </tr>)}
{Object.entries(langs).map(([langId, langName]) => <tr key={langId}> {Object.entries(langs).map(([langId, langName]) => <tr key={langId}>
@ -78,7 +72,7 @@ export default function Schedule({
</tbody> </tbody>
</table> </table>
<div className="separator" /> <div className="separator" />
{events.map(event => <section key={event.id} id={'event-'.concat(event.id)}> {Object.values(events).map(event => <section key={event.id} id={'event-'.concat(event.id)}>
<p> <p>
<strong>{event.title}</strong> <strong>{event.title}</strong>
{event.participant_users && !isTrackHidden(event.track) && <> {event.participant_users && !isTrackHidden(event.track) && <>
@ -97,13 +91,13 @@ export default function Schedule({
</section>)} </section>)}
{<> {<>
<div className="grid members"> <div className="grid members">
{speakers.map(speaker => <div key={speaker.id} className="col4 wmember"> {Object.values(speakers).map(speaker => <div key={speaker.id} className="col4 wmember">
<a href={'#'.concat(speaker.id)}> <a href={'#'.concat(speaker.id)}>
<img width="100" height="100" src={defaultSpeaker} alt={getSpeakerName(speaker)} /> <img width="100" height="100" src={defaultSpeaker} alt={getSpeakerName(speaker)} />
</a> </a>
</div>)} </div>)}
</div> </div>
{speakers.map(speaker => <Fragment key={speaker.id}> {Object.values(speakers).map(speaker => <Fragment key={speaker.id}>
<div className="speaker" id={'speaker-'.concat(speaker.id)}> <div className="speaker" id={'speaker-'.concat(speaker.id)}>
<img width="100" height="100" src={defaultSpeaker} alt={getSpeakerName(speaker)}/> <img width="100" height="100" src={defaultSpeaker} alt={getSpeakerName(speaker)}/>
<h3>{getSpeakerName(speaker)}</h3> <h3>{getSpeakerName(speaker)}</h3>

View File

@ -4,7 +4,7 @@ import useTracks from './useTracks.js';
import useEventTypes from './useEventTypes.js'; import useEventTypes from './useEventTypes.js';
import useHalls from './useHalls.js'; import useHalls from './useHalls.js';
import useSlots from './useSlots.js'; import useSlots from './useSlots.js';
import { calculateProgress, normalizeResponse } from '../utils.js'; import { addIdAndRelations, calculateProgress } from '../utils.js';
import { useMemo } from 'react'; import { useMemo } from 'react';
export default function useSchedule(conferenceId) { export default function useSchedule(conferenceId) {
@ -14,7 +14,7 @@ export default function useSchedule(conferenceId) {
isValidating: speakersValidating, isValidating: speakersValidating,
} = useSpeakers(conferenceId); } = useSpeakers(conferenceId);
const speakers = useMemo(() => normalizeResponse(speakersResponse), [speakersResponse]); const speakers = useMemo(() => addIdAndRelations(speakersResponse || []), [speakersResponse]);
const { const {
data: tracksResponse, data: tracksResponse,
@ -22,7 +22,7 @@ export default function useSchedule(conferenceId) {
isValidating: tracksValidating, isValidating: tracksValidating,
} = useTracks(conferenceId); } = useTracks(conferenceId);
const tracks = useMemo(() => normalizeResponse(tracksResponse), [tracksResponse]); const tracks = useMemo(() => addIdAndRelations(tracksResponse || []), [tracksResponse]);
const { const {
data: eventTypesResponse, data: eventTypesResponse,
@ -30,7 +30,7 @@ export default function useSchedule(conferenceId) {
isValidating: eventTypesValidating, isValidating: eventTypesValidating,
} = useEventTypes(conferenceId); } = useEventTypes(conferenceId);
const eventTypes = useMemo(() => normalizeResponse(eventTypesResponse), [eventTypesResponse]); const eventTypes = useMemo(() => addIdAndRelations(eventTypesResponse || []), [eventTypesResponse]);
const { const {
data: hallsResponse, data: hallsResponse,
@ -38,7 +38,7 @@ export default function useSchedule(conferenceId) {
isValidating: hallsValidating, isValidating: hallsValidating,
} = useHalls(conferenceId); } = useHalls(conferenceId);
const halls = useMemo(() => normalizeResponse(hallsResponse), [hallsResponse]); const halls = useMemo(() => addIdAndRelations(hallsResponse || []), [hallsResponse]);
const { const {
data: eventsResponse, data: eventsResponse,
@ -46,7 +46,7 @@ export default function useSchedule(conferenceId) {
isValidating: eventsValidating, isValidating: eventsValidating,
} = useEvents(conferenceId); } = useEvents(conferenceId);
const events = useMemo(() => normalizeResponse(eventsResponse, [ const events = useMemo(() => addIdAndRelations(eventsResponse || [], [
['event_type', eventTypes, 'event_type_id'], ['event_type', eventTypes, 'event_type_id'],
['track', tracks, 'track_id'], ['track', tracks, 'track_id'],
['participant_users', speakers, 'participant_user_ids'], ['participant_users', speakers, 'participant_user_ids'],
@ -58,7 +58,7 @@ export default function useSchedule(conferenceId) {
isValidating: slotsValidating, isValidating: slotsValidating,
} = useSlots(conferenceId); } = useSlots(conferenceId);
const slots = useMemo(() => normalizeResponse(slotsResponse, [ const slots = useMemo(() => addIdAndRelations(slotsResponse || [], [
['hall', halls, 'hall_id'], ['hall', halls, 'hall_id'],
['event', events, 'event_id'], ['event', events, 'event_id'],
]), [slotsResponse, halls, events]); ]), [slotsResponse, halls, events]);

View File

@ -1,17 +1,10 @@
import { useMemo } from 'react';
export default function useScheduleTable({ export default function useScheduleTable({
eventTypeId,
halls = {},
events = {}, events = {},
slots = {}, halls = {},
}) { }) {
const filteredEvents = useMemo(() => events.filter(event => eventTypeId > 0 ? event.event_type_id === eventTypeId : true), [eventTypeId, events]); const header = Object.values(halls);
const filteredEventIds = useMemo(() => filteredEvents.map(event => event.id), [filteredEvents]);
const filteredHallIds = useMemo(() => new Set(slots.filter(slot => filteredEventIds.includes(slot.event_id)).map(slot => slot.hall_id)), [filteredEventIds, slots]);
const header = useMemo(() => halls.filter(hall => filteredHallIds.has(hall.id)), [filteredHallIds, halls]);
const rows = filteredEvents.map(event => ({ const rows = Object.values(events).map(event => ({
id: event.id, id: event.id,
cells: [{ cells: [{
id: 1, id: 1,

View File

@ -42,19 +42,14 @@ export function calculateProgress(...elements) {
}; };
} }
export const normalizeResponse = (items = [], relations = []) => export const addIdAndRelations = (items, relations = []) =>
Object.entries(items).map(([id, item]) => Object.fromEntries(Object.entries(items).map(([id, item]) =>
({ ([id, {
id: parseInt(id, 10), id: parseInt(id, 10),
...item, ...item,
...Object.fromEntries(relations.map(([field, collection, idField]) => { ...Object.fromEntries(relations.map(([field, collection, idField]) => ([
const key = item[idField]; field,
const fn = Array.isArray(key) ? 'filter' : 'find'; Array.isArray(item[idField]) ? item[idField].map(id => collection[id]) : collection[item[idField]],
]))),
return [ }])
field, ));
collection[fn](item => item.id === key),
];
})),
})
);