Skip to content

Commit ddd2260

Browse files
committed
feat(qr-code): integrate QR code generation for session tracks and talks
1 parent 32b498a commit ddd2260

File tree

7 files changed

+173
-1
lines changed

7 files changed

+173
-1
lines changed

package-lock.json

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"react-countdown": "^2.3.6",
3434
"react-dom": "^18.3.1",
3535
"react-icons": "^5.5.0",
36+
"react-qr-code": "^2.0.18",
3637
"react-query": "^3.39.2",
3738
"react-router-dom": "^7.6.2",
3839
"react-use": "^17.6.0",

src/App.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import { SessionFeedback2023 } from "./2023/SessionFeedback/SessionFeedback2023"
6464
import { AttendeeInformation2023 } from "./2023/Attendee/AttendeeInformation2023";
6565
import { Communities2023 } from "./2023/Communities/Communities2023";
6666
import { SpeakerInformation2023 } from "./2023/Speakers/SpeakerInformation2023";
67+
import QrCodeSection from "@views/QrCode/QrCodeSection";
6768

6869
//<editor-fold desc="2025">
6970
const Talks = lazy(() => import("./views/Talks/Talks"));
@@ -379,6 +380,14 @@ const App: FC<React.PropsWithChildren<unknown>> = () => {
379380
/>
380381
{/* </editor-fold> */}
381382
{/* <editor-fold desc="2024 edition> */}
383+
<Route
384+
path="/qr-code"
385+
element={
386+
<React.Suspense fallback={<Loading />}>
387+
<QrCodeSection />
388+
</React.Suspense>
389+
}
390+
/>
382391
<Route
383392
path={ROUTE_2024_HOME}
384393
element={

src/services/sessionsAdapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
QuestionAnswers,
55
Session,
66
SessionCategory,
7-
} from "../types/sessions";
7+
} from "@/types/sessions";
88

99
export const extractSessionTags = (
1010
questionAnswers: QuestionAnswers[],

src/types/sessions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export interface SessionCategory {
2424
}
2525

2626
export interface Session {
27+
readonly room: string;
28+
readonly roomId: number;
2729
readonly id: number;
2830
readonly title: string;
2931
readonly description: string;

src/views/QrCode/QrCode.tsx

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { ChangeEvent, FC, useMemo, useState } from "react";
2+
import QRCode from "react-qr-code";
3+
import { IGroup, Session } from "@/types/sessions";
4+
import { format, isWithinInterval, parseISO } from "date-fns";
5+
import { SectionWrapper } from "@components/SectionWrapper/SectionWrapper";
6+
import { Color } from "@styles/colors";
7+
8+
interface QrCodeProps {
9+
currentDateTime: Date;
10+
openFeedbackId: string;
11+
tracks?: Array<IGroup>;
12+
}
13+
14+
interface Track {
15+
name: string;
16+
id: number;
17+
}
18+
19+
const areTracksAvailable = (availableTracks: Set<Track>) =>
20+
availableTracks.size > 0;
21+
22+
const QrCode: FC<React.PropsWithChildren<QrCodeProps>> = ({
23+
currentDateTime,
24+
openFeedbackId,
25+
tracks,
26+
}) => {
27+
const availableTracks: Set<Track> = useMemo<Set<Track>>(() => {
28+
const list: Track[] =
29+
tracks
30+
?.flatMap((group) => group.sessions)
31+
.filter((session) =>
32+
isWithinInterval(currentDateTime, {
33+
start: session.startsAt,
34+
end: session.endsAt,
35+
}),
36+
)
37+
?.map((session) => ({
38+
name: session.room,
39+
id: session.roomId,
40+
}))
41+
.sort((a, b) => a.name.localeCompare(b.name)) ?? [];
42+
43+
return new Set(list);
44+
}, [tracks, currentDateTime]);
45+
46+
const [selectedTrack, setSelectedTrack] = useState<number>(
47+
Array.from(availableTracks)?.at(0)?.id ?? 0,
48+
);
49+
const availableTalks: Array<Session> | undefined = tracks
50+
?.map((track: IGroup) => track.sessions)
51+
.flat(1)
52+
.filter((talk) =>
53+
isWithinInterval(currentDateTime, {
54+
start: talk.startsAt,
55+
end: talk.endsAt,
56+
}),
57+
);
58+
59+
const [session, setSession] = useState<Session | undefined>(
60+
availableTalks?.at(0),
61+
);
62+
63+
const [qrValue, setQrValue] = useState(
64+
"https://openfeedback.io/TG4hBcL7iPtV2LecVdHu/2025-07-09/945091",
65+
);
66+
67+
const setQrCodeHandler = (e: ChangeEvent<HTMLSelectElement>) => {
68+
const qrCode: number = parseInt(e.target.value);
69+
e.preventDefault();
70+
setSelectedTrack(qrCode);
71+
setSession(availableTalks?.filter((talk) => talk.roomId == qrCode)[0]);
72+
if (session != undefined)
73+
setQrValue(
74+
`https://openfeedback.io/${openFeedbackId}/${format(parseISO(session.startsAt), "yyyy-MM-dd")}/${session.id}`,
75+
);
76+
};
77+
78+
return (
79+
<SectionWrapper color={Color.WHITE} marginTop={8}>
80+
<h1>OpenFeedback QR Code</h1>
81+
<div
82+
style={{
83+
width: "85vh",
84+
background: "white",
85+
display: "flex",
86+
flexDirection: "column",
87+
margin: "0 auto 200px",
88+
alignItems: "center",
89+
}}
90+
>
91+
<p>{currentDateTime.toLocaleString()}</p>
92+
{areTracksAvailable(availableTracks) && (
93+
<>
94+
<h2>Choose track</h2>
95+
<select onChange={setQrCodeHandler} value={selectedTrack}>
96+
{Array.from(availableTracks).map((option) => (
97+
<option key={option.id} value={option.id}>
98+
{option.name}
99+
</option>
100+
))}
101+
</select>
102+
</>
103+
)}
104+
<br />
105+
{session && (
106+
<p>
107+
<strong>
108+
{session.speakers.map((speaker) => speaker.name).join(", ")}
109+
</strong>{" "}
110+
{session.title}
111+
</p>
112+
)}
113+
<QRCode value={qrValue} size={512} />
114+
</div>
115+
</SectionWrapper>
116+
);
117+
};
118+
export default QrCode;

src/views/QrCode/QrCodeSection.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import QrCode from "@views/QrCode/QrCode";
2+
import { useMemo } from "react";
3+
import conferenceData from "@data/2025.json";
4+
import { useFetchTalks } from "@hooks/useFetchTalks";
5+
6+
const QrCodeSection = () => {
7+
const openFeedbackId = conferenceData.openFeedbackId;
8+
const talks = useFetchTalks();
9+
const currentDateTime: Date = useMemo(() => {
10+
return new Date(2025, 6, 18, 10, 35, 0);
11+
}, []);
12+
13+
return (
14+
<QrCode
15+
currentDateTime={currentDateTime}
16+
openFeedbackId={openFeedbackId}
17+
tracks={talks?.data}
18+
/>
19+
);
20+
};
21+
22+
export default QrCodeSection;

0 commit comments

Comments
 (0)