Compare commits
5 Commits
01f29b80c5
...
8f3fedcf07
Author | SHA1 | Date | |
---|---|---|---|
|
8f3fedcf07 | ||
|
b0c6569685 | ||
|
e2056c3d72 | ||
|
e52f045775 | ||
|
277e3a215b |
@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
19
src/Schedule/Cell/DateHeader.jsx
Normal file
19
src/Schedule/Cell/DateHeader.jsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { bg, enGB } from 'date-fns/locale';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
|
||||||
|
export default function DateHeader({
|
||||||
|
date,
|
||||||
|
lang,
|
||||||
|
}) {
|
||||||
|
const locale = lang === 'bg' ? bg : enGB;
|
||||||
|
|
||||||
|
return format(date, 'dd MMMM - EEEE', {
|
||||||
|
locale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DateHeader.propTypes = {
|
||||||
|
date: PropTypes.object.isRequired,
|
||||||
|
lang: PropTypes.string,
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
import { isTrackHidden } from './utils.js';
|
import { isTrackHidden } from '../utils.js';
|
||||||
import Speaker from './Speaker.jsx';
|
import Speaker from '../Speaker.jsx';
|
||||||
import FeedbackLink from './FeedbackLink.jsx';
|
import FeedbackLink from '../FeedbackLink.jsx';
|
||||||
|
|
||||||
export default function Event(event) {
|
export default function Event(event) {
|
||||||
return (<>
|
return (<>
|
8
src/Schedule/Cell/TimeSlot.jsx
Normal file
8
src/Schedule/Cell/TimeSlot.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { format } from 'date-fns';
|
||||||
|
|
||||||
|
export default function TimeSlot({
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
}) {
|
||||||
|
return format(start, 'HH:mm').concat(' - ').concat(format(end, 'HH:mm'));
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
export default function Day({
|
|
||||||
date,
|
|
||||||
}) {
|
|
||||||
// TODO: format with date-fns
|
|
||||||
return date.getDate().toString().concat('.').concat((date.getMonth() + 1).toString()).concat(' - ').concat(date.getDay());
|
|
||||||
}
|
|
||||||
|
|
||||||
Day.propTypes = {
|
|
||||||
date: PropTypes.object.isRequired,
|
|
||||||
};
|
|
@ -3,12 +3,13 @@ import useSchedule from '../hooks/useSchedule.js';
|
|||||||
import { isTrackHidden } from './utils.js';
|
import { isTrackHidden } from './utils.js';
|
||||||
import { Fragment, useState } from 'react';
|
import { Fragment, useState } from 'react';
|
||||||
import useScheduleTable from '../hooks/useScheduleTable.js';
|
import useScheduleTable from '../hooks/useScheduleTable.js';
|
||||||
import Event from './Event.jsx';
|
import Event from './Cell/Event.jsx';
|
||||||
import './Schedule.scss';
|
import './Schedule.scss';
|
||||||
import { langs } from './constants.js';
|
import { langs } from './constants.js';
|
||||||
import Speaker from './Speaker.jsx';
|
import Speaker from './Speaker.jsx';
|
||||||
import FeedbackLink from './FeedbackLink.jsx';
|
import FeedbackLink from './FeedbackLink.jsx';
|
||||||
import Day from './Day.jsx';
|
import DateHeader from './Cell/DateHeader.jsx';
|
||||||
|
import TimeSlot from './Cell/TimeSlot.jsx';
|
||||||
|
|
||||||
export default function Schedule({
|
export default function Schedule({
|
||||||
conferenceId,
|
conferenceId,
|
||||||
@ -56,7 +57,8 @@ export default function Schedule({
|
|||||||
<tbody>
|
<tbody>
|
||||||
{rows.map(row => <tr key={row.id}>
|
{rows.map(row => <tr key={row.id}>
|
||||||
{row.cells.map(cell => <td key={cell.id} {...cell.attributes}>
|
{row.cells.map(cell => <td key={cell.id} {...cell.attributes}>
|
||||||
{cell.day && <Day date={cell.day} />}
|
{cell.dateHeader && <DateHeader date={cell.dateHeader} lang={lang} />}
|
||||||
|
{cell.timeSlot && <TimeSlot {...cell.timeSlot} />}
|
||||||
{cell.event && <Event {...cell.event} />}
|
{cell.event && <Event {...cell.event} />}
|
||||||
</td>)}
|
</td>)}
|
||||||
</tr>)}
|
</tr>)}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { getMidnightTimestamp, isSameDay, sorter } from '../utils.js';
|
import { sorter, toMidnight } from '../utils.js';
|
||||||
import { langs } from '../Schedule/constants.js';
|
import { langs } from '../Schedule/constants.js';
|
||||||
|
import { getTime, isSameDay, toDate } from 'date-fns';
|
||||||
|
|
||||||
export default function useScheduleTable({
|
export default function useScheduleTable({
|
||||||
eventTypeId,
|
eventTypeId,
|
||||||
@ -12,9 +13,11 @@ export default function useScheduleTable({
|
|||||||
const filteredEvents = events.filter(event => eventTypeId > 0 ? event.event_type_id === eventTypeId : true);
|
const filteredEvents = events.filter(event => eventTypeId > 0 ? event.event_type_id === eventTypeId : true);
|
||||||
const filteredEventIds = filteredEvents.map(event => event.id);
|
const filteredEventIds = filteredEvents.map(event => event.id);
|
||||||
const filteredSlots = slots.sort(sorter('starts_at')).filter(slot => filteredEventIds.includes(slot.event_id));
|
const filteredSlots = slots.sort(sorter('starts_at')).filter(slot => filteredEventIds.includes(slot.event_id));
|
||||||
const days = Array.from(new Set(filteredSlots.map(slot =>
|
const days = Array.from(new Set(filteredSlots.map(slot => getTime(toMidnight(slot))))).map(ts => toDate(ts));
|
||||||
getMidnightTimestamp(slot.starts_at)
|
const microslots = Array.from(new Set(filteredSlots.flatMap(slot => [
|
||||||
))).map(ts => new Date(ts));
|
getTime(slot.starts_at),
|
||||||
|
getTime(slot.ends_at),
|
||||||
|
]))).sort().map(ts => toDate(ts));
|
||||||
const filteredHallIds = new Set(filteredSlots.map(slot => slot.hall_id));
|
const filteredHallIds = new Set(filteredSlots.map(slot => slot.hall_id));
|
||||||
const filteredHalls = halls.filter(hall => filteredHallIds.has(hall.id));
|
const filteredHalls = halls.filter(hall => filteredHallIds.has(hall.id));
|
||||||
const hallSlots = Object.fromEntries(filteredHalls.map(hall => [
|
const hallSlots = Object.fromEntries(filteredHalls.map(hall => [
|
||||||
@ -31,31 +34,47 @@ export default function useScheduleTable({
|
|||||||
},
|
},
|
||||||
...filteredHalls,
|
...filteredHalls,
|
||||||
];
|
];
|
||||||
const rows = days.flatMap(day => [{
|
|
||||||
id: 'header-'.concat(day.getTime().toString()),
|
const rows = microslots.flatMap((date, index, array) => {
|
||||||
|
const isFirst = index === 0;
|
||||||
|
const isLast = index === array.length - 1;
|
||||||
|
const nextDate = !isLast ? array[index + 1] : null;
|
||||||
|
const isFirstForTheDay = index > 0 && !isSameDay(date, array[index - 1]);
|
||||||
|
const isLastForTheDay = array?.[index + 1] && !isSameDay(date, array[index + 1]);
|
||||||
|
|
||||||
|
const showHeader = isFirst || isFirstForTheDay;
|
||||||
|
const showSlot = !isLast && !isLastForTheDay;
|
||||||
|
|
||||||
|
return [
|
||||||
|
...showHeader ? [{
|
||||||
|
id: 'header-'.concat(getTime(date).toString()),
|
||||||
cells: [{
|
cells: [{
|
||||||
id: 1,
|
id: 1,
|
||||||
attributes: {
|
attributes: {
|
||||||
colSpan: header.length,
|
colSpan: header.length,
|
||||||
},
|
},
|
||||||
day,
|
dateHeader: date,
|
||||||
}]
|
}],
|
||||||
},
|
}] : [],
|
||||||
...filteredSlots.filter(slot => isSameDay(slot.starts_at, day)).map(slot => ({
|
...showSlot ? [{
|
||||||
id: slot.id,
|
id: 'slot-'.concat(getTime(date).toString()),
|
||||||
cells: [{
|
cells: [{
|
||||||
id: 1,
|
id: 1,
|
||||||
day, // TODO replace with slot time
|
timeSlot: {
|
||||||
|
start: date,
|
||||||
|
end: nextDate,
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
id: 2,
|
id: 2,
|
||||||
attributes: {
|
// attributes: {
|
||||||
className: 'schedule-'.concat(slot.event.language).concat(' ').concat(slot.event.track?.css_class),
|
// className: 'schedule-'.concat(slot.event.language).concat(' ').concat(slot.event.track?.css_class),
|
||||||
colSpan: 2,
|
// colSpan: 2,
|
||||||
},
|
// },
|
||||||
event: slot.event,
|
// event: slot.event,
|
||||||
}],
|
}],
|
||||||
}))
|
}] : [],
|
||||||
]);
|
];
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
header,
|
header,
|
||||||
|
15
src/utils.js
15
src/utils.js
@ -1,3 +1,5 @@
|
|||||||
|
import { parseJSON, set } from 'date-fns';
|
||||||
|
|
||||||
export const sorter = field => (a, b) => {
|
export const sorter = field => (a, b) => {
|
||||||
const fieldA = a[field];
|
const fieldA = a[field];
|
||||||
const fieldB = b[field];
|
const fieldB = b[field];
|
||||||
@ -8,7 +10,7 @@ export const sorter = field => (a, b) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const parseDateFields = (item, dateFields) => Object.fromEntries(Object.entries(item).map(([key, value]) =>
|
export const parseDateFields = (item, dateFields) => Object.fromEntries(Object.entries(item).map(([key, value]) =>
|
||||||
[key, dateFields.includes(key) ? new Date(value) : value]
|
[key, dateFields.includes(key) ? parseJSON(value) : value]
|
||||||
));
|
));
|
||||||
|
|
||||||
export function calculateProgress(...elements) {
|
export function calculateProgress(...elements) {
|
||||||
@ -62,8 +64,9 @@ export const normalizeResponse = (items = [], relations = []) =>
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getMidnightTimestamp = date => (new Date(date.getTime())).setHours(0, 0, 0, 0);
|
export const toMidnight = date => set(date, {
|
||||||
|
hours: 0,
|
||||||
export function isSameDay(dateA, dateB) {
|
minutes: 0,
|
||||||
return getMidnightTimestamp(dateA) === getMidnightTimestamp(dateB);
|
seconds: 0,
|
||||||
}
|
milliseconds: 0,
|
||||||
|
});
|
||||||
|
@ -850,6 +850,11 @@ data-view-byte-offset@^1.0.0:
|
|||||||
es-errors "^1.3.0"
|
es-errors "^1.3.0"
|
||||||
is-data-view "^1.0.1"
|
is-data-view "^1.0.1"
|
||||||
|
|
||||||
|
date-fns@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14"
|
||||||
|
integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
|
||||||
|
|
||||||
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2:
|
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2:
|
||||||
version "4.3.7"
|
version "4.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user