feat: added download agenda as a pdf #823
feat: added download agenda as a pdf #823harshhgithub wants to merge 34 commits intoasyncapi:masterfrom
Conversation
✅ Deploy Preview for peaceful-ramanujan-288045 ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
@AceTheCreator @thulieblack can you please review , |
|
@ashmit-coder @TenzDelek your review as well |
TenzDelek
left a comment
There was a problem hiding this comment.
Overall looks fine, but I am concern about the code consistency within the repo if we add this. we generally use tailwind through out our codebase but this one doesn't seems to support it. I would research more on the possible alternatives packages or walk through that support those features.
let waits for others opinion as well. I might be wrong here 😄
|
@TenzDelek Thanks a lot for the feedback! 🙏 I looked into alternatives like react-pdf-tailwind and @react-pdf/styled-components, but they’re still limited or experimental for production-level use. That’s why I went with StyleSheet.create() here — it’s the officially supported approach for PDFs and scoped only within this component, so it won’t affect any Tailwind-styled parts of the app. |
|
@TenzDelek @AceTheCreator pls review |
AceTheCreator
left a comment
There was a problem hiding this comment.
Left you some reviews :)
components/Agenda/DownloadAgenda.tsx
Outdated
| </View> | ||
| <View style={styles.speakerCol}> | ||
| {Array.isArray(item.speaker) | ||
| ? item.speaker.map((id: any, j: React.Key | null | undefined) => { |
There was a problem hiding this comment.
The speaker ID can't be a type of any. It has to be a type of number
components/Agenda/DownloadAgenda.tsx
Outdated
| </View> | ||
| <View style={styles.speakerCol}> | ||
| {Array.isArray(item.speaker) | ||
| ? item.speaker.map((id: any, j: React.Key | null | undefined) => { |
There was a problem hiding this comment.
j: React.Key | null | undefined
This is a redundant line since the speaker id can also be used as a key, they're unique numbers :)
components/Agenda/DownloadAgenda.tsx
Outdated
| }) | ||
| : (() => { | ||
| const sp = city.speakers.find( | ||
| (s: any) => s.id === item.speaker |
There was a problem hiding this comment.
Please, avoid using any when a type exists. The speaker object has an interface; could you use the appropriate type construct?
components/Agenda/DownloadAgenda.tsx
Outdated
| ); | ||
| }; | ||
|
|
||
| export const PdfViewer = ({ city }: { city: any }) => ( |
There was a problem hiding this comment.
{ city: any }
City has an interface too :)
components/Agenda/DownloadAgenda.tsx
Outdated
| </div> | ||
| ); | ||
|
|
||
| export const PdfDownloadButton = ({ city }: { city: any }) => ( |
There was a problem hiding this comment.
{ city: any }
Use the appropriate city type instead of 'any'.
components/Agenda/DownloadAgenda.tsx
Outdated
| className="w-full md:w-[200px] px-10 py-3" | ||
| disabled={loading} | ||
| > | ||
| {loading ? 'Preparing PDF…' : 'Download PDF'} |
There was a problem hiding this comment.
I think we shouldn't use the word 'Download PDF' instead use 'Download Agenda'
|
@AceTheCreator i have made the requested changes , thankyou for your valuable feedback and reviews |
AceTheCreator
left a comment
There was a problem hiding this comment.
Left you a final review!
components/Agenda/DownloadAgenda.tsx
Outdated
| className="w-full md:w-[200px] px-10 py-3" | ||
| disabled={loading} | ||
| > | ||
| {loading ? 'Preparing Agenda…' : 'Download Agenda'} |
There was a problem hiding this comment.
The loading text is breaking onto a new line, and I think it’s caused by the button’s [200px] width.
|
@AceTheCreator fixed it . |
AceTheCreator
left a comment
There was a problem hiding this comment.
@harshhgithub one final thing i'm thinking of is, do we really want the download agenda button visible for past events?
cc @thulieblack
We could hide it for events marked as “completed” to keep the UI cleaner. |
|
Yes, if you can hide it once event is finished that would be great |
|
@thulieblack @AceTheCreator i have made the “Download Agenda” button hidden for past events and verified that the button appears for future event dates . |
AceTheCreator
left a comment
There was a problem hiding this comment.
Left you some review :)
components/Agenda/agenda.tsx
Outdated
| const isPastEvent = (dateString: string): boolean => { | ||
| if (dateString.includes('-')) { | ||
| const parts = dateString.split('-').map(p => p.trim()); | ||
| const endDateString = parts[parts.length - 1]; | ||
| const endDate = new Date(endDateString); | ||
| return endDate < new Date(); | ||
| } | ||
| return new Date(dateString) < new Date(); | ||
| }; | ||
|
|
There was a problem hiding this comment.
I think we should add a helper function to check whether an event is in the past, so it can be reused throughout the application. Right now, a new check is created each time it’s needed (for example, the tickets section also checks for past events).
package.json
Outdated
| "@googleapis/sheets": "^7.0.0", | ||
| "axios": "^1.12.0", | ||
| "@react-pdf/renderer": "^4.3.1", | ||
| "axios": "^1.8.2", |
There was a problem hiding this comment.
Sorry, where's axios being used in your implementation?
There was a problem hiding this comment.
yes axios isn’t used in this implementation.
It was unintentionally left in package.json and can be removed.
|
@AceTheCreator @thulieblack please have a look |
|
@AceTheCreator if i remove the axios it causes checks failing , axios was updated when i run npm run build |
AceTheCreator
left a comment
There was a problem hiding this comment.
Left you an additional suggestion
utils/isPastEvent.ts
Outdated
| export const isPastEvent = (dateString: string): boolean => { | ||
| const today = new Date(); | ||
| today.setHours(0, 0, 0, 0); | ||
|
|
||
| if (dateString.includes('-')) { | ||
| const parts = dateString.split('-').map((p) => p.trim()); | ||
| const endDateString = parts[parts.length - 1]; | ||
| const endDate = new Date(endDateString); | ||
| if (isNaN(endDate.getTime())) { | ||
| return false; | ||
| } | ||
| endDate.setHours(0, 0, 0, 0); | ||
| return endDate < today; | ||
| } | ||
| const parsedDate = new Date(dateString); | ||
| if (isNaN(parsedDate.getTime())) { | ||
| return false; | ||
| } | ||
| parsedDate.setHours(0, 0, 0, 0); | ||
| return parsedDate < today; | ||
| }; |
There was a problem hiding this comment.
@harshhgithub I noticed we already have a utility function called status that checks whether an event is ONGOING, UPCOMING, or ENDED. I think it would be better to reuse that instead of creating a new helper function. What do you think?
There was a problem hiding this comment.
@harshhgithub I noticed we already have a utility function called status that checks whether an event is ONGOING, UPCOMING, or ENDED. I think it would be better to reuse that instead of creating a new helper function. What do you think?
One thing I noticed though is that the current implementation doesn’t fully handle date ranges like 2 - 3 July, 2025. Splitting on - results in an invalid start date (new Date("2")), which can cause runtime issues during build/deploy.
I’m happy to refactor getEventStatus to safely parse ranged dates (derive proper start/end dates) and then reuse it instead of introducing isPastEvent. Let me know if that approach works for you.
There was a problem hiding this comment.
Yeah, i'd appreciate it if you extend the getEventStatus method. That'd be all for me
There was a problem hiding this comment.
@AceTheCreator @thulieblack I extended getEventStatus to handle date ranges correctly, reusing isPastEvent to keep the logic consistent across the codebase.
AceTheCreator
left a comment
There was a problem hiding this comment.
I think it will also good for you to include a unit test that can be used to validate these utility functions 🙏🏽
| if (isNaN(startDate.getTime())) { | ||
| return ConferenceStatus.ENDED; | ||
| } | ||
| } |
There was a problem hiding this comment.
I notice a bug here... For range dates like "9 - 11 December, 2026", parts[0] is just "9". V8 silently parses new Date("9") as Sept 1, 2001 instead of returning Invalid Date, so the isNaN guard is skipped and future events incorrectly return ONGOING.
Fix: when parts[0] is just a number, borrow the month/year from the end part to form a valid start date (e.g. "9" → "9 December, 2026").
There was a problem hiding this comment.
@AceTheCreator I’ve updated getEventStatus to normalize numeric start days by borrowing the month/year from the end date (e.g. "9 - 11 December, 2026"), which avoids the V8 parsing issue.
I’ve also added a small unit test alongside utils/status.test.ts
Resolves #553
This PR introduces a fully functional Agenda PDF feature for conference city pages.
Users can now view the agenda directly in the browser and download it as a PDF with consistent styling.
The feature enhances user experience by providing a readable, professional, and portable version of the conference agenda.
Here are some screenshots-
Example of the Exported PDF's-
Munich-Agenda.pdf
Lagos-Agenda (1).pdf
London-Agenda.4.pdf