From 5a3eee3524a7a0d676e769d46f2b4fa48894ef76 Mon Sep 17 00:00:00 2001 From: David Emulo <161654052+Davidemulo@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:02:25 +0100 Subject: [PATCH 01/12] Create hackathon-detail-design.md (#466) --- .../[slug]/hackathon-detail-design.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 app/(landing)/hackathons/[slug]/hackathon-detail-design.md diff --git a/app/(landing)/hackathons/[slug]/hackathon-detail-design.md b/app/(landing)/hackathons/[slug]/hackathon-detail-design.md new file mode 100644 index 00000000..cdb81ff1 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/hackathon-detail-design.md @@ -0,0 +1,30 @@ +Hackathon Detail Page Redesign +Issue: #414 + +Figma Design +https://www.figma.com/design/EMNGAQl1SGObXcsoa24krt/Boundless_Project-Details?node-id=0-1&t=A1ywRcn60Xyw0X6h-1 + +This design proposes a cleaner and more professional UI/UX for the hackathon detail page. + +Included in the Figma file: +- Desktop layout +- Mobile layout +- Banner / hero placement proposal +- Redesigned hero section +- Sticky sidebar card +- Tab navigation +- All tab layouts (overview, participants, resources, announcements, submissions, discussions, find team, winners) +- Loading state +- Hackathon not found state + +Banner Placement Proposal +The hackathon banner is placed as a full-width hero image at the top of the page, allowing it to visually represent the hackathon and improve page identity. + +The sidebar becomes a compact summary card with key information and actions. + +Design Goals +- Simpler UI and improved visual hierarchy +- Clear primary actions (Join, Submit, View Submission) +- Consistent spacing and typography +- Better mobile usability +- Professional and product-quality look From 6639c96dc2f6667dd95261cbf73dd8aea7781926 Mon Sep 17 00:00:00 2001 From: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:49:46 +0100 Subject: [PATCH 02/12] Production (#472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * Merge fixes to production (#467) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign * UI fixes (#458) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * Blog fix (#464) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * chore: add blog content * Blog fix (#465) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * chore: add blog content * fix: fix broken blog image and notification redirect * merge fork main to boundlessfi main (#468) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge to main (#469) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge (#470) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge fork to main (#471) * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * Create hackathon-detail-design.md (#466) --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: David Emulo <161654052+Davidemulo@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> Co-authored-by: David Emulo <161654052+Davidemulo@users.noreply.github.com> --- hooks/hackathon/use-participants.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/hooks/hackathon/use-participants.ts b/hooks/hackathon/use-participants.ts index 45e43122..8b448a6c 100644 --- a/hooks/hackathon/use-participants.ts +++ b/hooks/hackathon/use-participants.ts @@ -18,7 +18,6 @@ export function useParticipants() { useEffect(() => { if (hackathonId) { setIsLoading(true); - const fetchAllData = async () => { try { // Fetch teams From 3482a521e057b2b3d0241f46024517a78e8bb85f Mon Sep 17 00:00:00 2001 From: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:33:34 +0100 Subject: [PATCH 03/12] Production (#474) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * Merge fixes to production (#467) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign * UI fixes (#458) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * Blog fix (#464) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * chore: add blog content * Blog fix (#465) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * chore: add blog content * fix: fix broken blog image and notification redirect * merge fork main to boundlessfi main (#468) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge to main (#469) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge (#470) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge fork to main (#471) * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * Create hackathon-detail-design.md (#466) --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: David Emulo <161654052+Davidemulo@users.noreply.github.com> * merge David's Pr (#473) * Create hackathon-detail-design.md (#466) * Production (#472) * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * Merge fixes to production (#467) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign * UI fixes (#458) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * Blog fix (#464) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * chore: add blog content * Blog fix (#465) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * chore: add blog content * fix: fix broken blog image and notification redirect * merge fork main to boundlessfi main (#468) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge to main (#469) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge (#470) * fix: improve timeline input , ui improvement and fixes for participation tab * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * merge fork to main (#471) * merg to prod (#450) * fix: improve timeline input , ui improvement and fixes for participation tab (#442) * feat: integrate Sentry for error tracking and reporting - Added Sentry configuration for both server and edge environments to capture errors and performance metrics. - Updated error handling across various components to report errors to Sentry, enhancing monitoring capabilities. - Introduced a new MessagesProvider for managing in-app messaging and notifications. - Refactored the .env.example file to include Sentry DSN and related configurations for production environments. - Removed the deprecated connect-wallet component to streamline the codebase. * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * UI fixes (#445) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: handle potential null values in notification data - Updated notification handling to safely access properties using optional chaining. - Ensured that the notification data is checked for null values before accessing its properties in various components, improving robustness and preventing runtime errors. * feat: Enforce Submission Requirements (requireGithub, requireDemoVideo, requireOtherLinks) (#443) * feat: enforce submission requirements (requireGithub, requireDemoVideo, requireOtherLinks) - Add dynamic schema validation based on currentHackathon flags - Enforce requireDemoVideo at schema level with Zod refine - Add Step 2 validation for requireGithub, requireDemoVideo, requireOtherLinks - Add final validation pass in onSubmit before API call - Add dynamic asterisk (*) to required field labels - Add legend and helper text for required fields - Wrap links section in FormField for proper error display * UI fixes (#444) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * feat: prevent removal of required links and style required asterisks in red Made-with: Cursor --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: felipevega2x * feat(submissions): enforce team size limits (teamMin, teamMax) (#446) * feat(submissions): enforce team size limits (teamMin, teamMax) - Add validation in handleNext (Step 0) to block progression when team size is below teamMin, showing specific message with members needed - Guard handleAddInvitee function to prevent adding members when team reaches teamMax capacity - Add capacity indicator Badge showing "X / Y members" with visual states: orange when below minimum, yellow when at capacity - Disable "Add Member" button and inputs when team is full - Display helper text when team is below minimum requirement Closes #405 * fix(submissions): remove hardcoded team size fallbacks - Use undefined instead of hardcoded defaults (1, 10) - Add hasTeamLimits guard to only enforce when hackathon defines limits - Conditionally render Badge and helper text when limits are defined * refactor: enhance error handling and submission response structure - Introduced a utility function to standardize API error messages across submission operations. - Updated the create and update submission functions to handle optional chaining for response checks. - Improved the response structure to include messages for successful submissions. - Adjusted the CreateSubmissionRequest interface to remove the hackathonId field and made organizationId optional in the update submission request. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> * UI fixes (#451) (#453) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * ui-fixes (#455) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * merge to prod (#457) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> * merg to prod (#459) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> * merg to prod (#461) * UI fixes (#451) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * UI fixes (#454) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * feat(navbar): enhance navbar styles and layout - Added CSS variables for active navbar link states, improving visual feedback. - Refactored navbar layout for better alignment and responsiveness, including adjustments for mobile and desktop views. - Updated component structure to ensure consistent styling and behavior across different states. * feat(announcements): enhance announcement rendering and markdown support - Implemented a `stripMarkdown` function to convert Markdown content to plain text for better preview handling in the announcement page. - Created an `AnnouncementPreview` component to render announcements with Markdown support, improving content display in the announcements tab. - Updated the announcement editor to utilize a dynamic Markdown editor, enhancing the editing experience for users. * feat(newsletter): implement newsletter API integration with subscribe… (#452) * feat(newsletter): implement newsletter API integration with subscribe, confirm, unsubscribe, and preferences * Draft feat(newsletter): Imp some extra things * feat(newsletter): add API routes, client helpers, and UI pages * Docs: add comprehensive bug test report for organization features (#456) * Test: add comprehensive bug test report for organization features * docs: update bug test report for organization features and address critical issues * fix: update branch name in bug test report and upgrade dompurify to version 3.3.2 * fix: update the milestone submission flow for campaign --------- Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> * Create hackathon-detail-design.md (#466) --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: David Emulo <161654052+Davidemulo@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> Co-authored-by: David Emulo <161654052+Davidemulo@users.noreply.github.com> --------- Co-authored-by: David Emulo <161654052+Davidemulo@users.noreply.github.com> Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> --------- Co-authored-by: Collins Ikechukwu Co-authored-by: Matias Aguilar Co-authored-by: felipevega2x Co-authored-by: Josué Araya Marín <104031367+Josue19-08@users.noreply.github.com> Co-authored-by: Sandeep chauhan <92181599+ryzen-xp@users.noreply.github.com> Co-authored-by: Ekene Ngwudike <94962750+Ekene001@users.noreply.github.com> Co-authored-by: David Emulo <161654052+Davidemulo@users.noreply.github.com> From 560a0cb164be8929b80391d1bac9968c4ef0e436 Mon Sep 17 00:00:00 2001 From: Bioba <97415163+Agbeleshe@users.noreply.github.com> Date: Mon, 9 Mar 2026 23:50:58 +0100 Subject: [PATCH 04/12] feat: implement Wallet Not Ready modal and readiness checks (#462) * feat: implement Wallet Not Ready modal and readiness checks * fix: redesigned not wallet ready and global usage --------- Co-authored-by: Benjtalkshow --- components/escrow/FundEscrowButton.tsx | 436 +++++++++++--------- components/landing-page/navbar.tsx | 37 +- components/modals/fund-project/index.tsx | 62 +-- components/providers/wallet-provider.tsx | 16 + components/wallet/LandingWalletWrapper.tsx | 9 +- components/wallet/WalletNotReadyModal.tsx | 163 ++++++++ components/wallet/WalletTrigger.tsx | 3 +- features/projects/components/Initialize.tsx | 26 +- hooks/use-protected-action.ts | 6 + hooks/use-wallet-protection.ts | 43 +- hooks/use-wallet-readiness.ts | 63 +++ 11 files changed, 627 insertions(+), 237 deletions(-) create mode 100644 components/wallet/WalletNotReadyModal.tsx create mode 100644 hooks/use-wallet-readiness.ts diff --git a/components/escrow/FundEscrowButton.tsx b/components/escrow/FundEscrowButton.tsx index 54652343..1ea7f8d1 100644 --- a/components/escrow/FundEscrowButton.tsx +++ b/components/escrow/FundEscrowButton.tsx @@ -15,6 +15,10 @@ import { } from '@trustless-work/escrow'; import { toast } from 'sonner'; import { reportError } from '@/lib/error-reporting'; +import { useWalletProtection } from '@/hooks/use-wallet-protection'; +import WalletRequiredModal from '@/components/wallet/WalletRequiredModal'; +import WalletNotReadyModal from '@/components/wallet/WalletNotReadyModal'; +import { WalletSheet } from '@/components/wallet/WalletSheet'; // Extended type to include balance property that may exist at runtime // Using intersection type to avoid type conflicts with required balance property @@ -34,7 +38,6 @@ import { * Component to fund an existing multi-release escrow using Trustless Work */ export const FundEscrowButton = () => { - const { walletAddress } = useWalletContext(); const { contractId, escrow, updateEscrow } = useEscrowContext(); const { fundEscrow } = useFundEscrow(); const { sendTransaction } = useSendTransaction(); @@ -45,6 +48,22 @@ export const FundEscrowButton = () => { transactionHash?: string; } | null>(null); + // Wallet protection hook + const { + requireWallet, + showWalletModal, + showNotReadyModal, + notReadyReasons, + handleWalletConnected, + closeWalletModal, + closeNotReadyModal, + publicKey: walletAddress, + } = useWalletProtection({ + actionName: 'fund escrow', + }); + + const [isWalletDrawerOpen, setIsWalletDrawerOpen] = useState(false); + // Calculate total amount from all milestones const calculateTotalAmount = (): number => { if (!escrow || !escrow.milestones) { @@ -94,79 +113,86 @@ export const FundEscrowButton = () => { ); } - // Step 1: Prepare the payload according to FundEscrowPayload type - const payload: FundEscrowPayload = { - contractId: contractId, - signer: walletAddress, - amount: totalAmount, - }; - - // Log payload for debugging - // Step 2: Execute function from Trustless Work - const fundResponse: EscrowRequestResponse = await fundEscrow( - payload, - 'multi-release' as EscrowType - ); + const walletReady = await requireWallet(async () => { + // Step 1: Prepare the payload according to FundEscrowPayload type + const payload: FundEscrowPayload = { + contractId: contractId, + signer: walletAddress!, + amount: totalAmount, + }; - // Type guard: Check if response is successful - if ( - fundResponse.status !== ('SUCCESS' as Status) || - !fundResponse.unsignedTransaction - ) { - const errorMessage = - 'message' in fundResponse && typeof fundResponse.message === 'string' - ? fundResponse.message - : 'Failed to fund escrow'; - throw new Error(errorMessage); - } + // Step 2: Execute function from Trustless Work + const fundResponse: EscrowRequestResponse = await fundEscrow( + payload, + 'multi-release' as EscrowType + ); - const { unsignedTransaction } = fundResponse; + // Type guard: Check if response is successful + if ( + fundResponse.status !== ('SUCCESS' as Status) || + !fundResponse.unsignedTransaction + ) { + const errorMessage = + 'message' in fundResponse && + typeof fundResponse.message === 'string' + ? fundResponse.message + : 'Failed to fund escrow'; + throw new Error(errorMessage); + } - // Step 3: Sign transaction with wallet - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress, - }); + const { unsignedTransaction } = fundResponse; + + // Step 3: Sign transaction with wallet + const signedXdr = await signTransaction({ + unsignedTransaction, + address: walletAddress!, + }); + + // Step 4: Send transaction + const sendResponse = await sendTransaction(signedXdr); + + // Type guard: Check if response is successful + if ( + 'status' in sendResponse && + sendResponse.status !== ('SUCCESS' as Status) + ) { + const errorMessage = + 'message' in sendResponse && + typeof sendResponse.message === 'string' + ? sendResponse.message + : 'Failed to send transaction'; + throw new Error(errorMessage); + } - // Step 4: Send transaction - const sendResponse = await sendTransaction(signedXdr); + // Update escrow balance in context + if (escrow) { + const escrowWithBalance = escrow as MultiReleaseEscrowWithBalance; + const currentBalance = escrowWithBalance.balance || 0; + const updatedEscrow: MultiReleaseEscrowWithBalance = { + ...escrow, + balance: currentBalance + totalAmount, + }; + updateEscrow(updatedEscrow as MultiReleaseEscrow); + } - // Type guard: Check if response is successful - if ( - 'status' in sendResponse && - sendResponse.status !== ('SUCCESS' as Status) - ) { - const errorMessage = + // Display success status + const successMessage = 'message' in sendResponse && typeof sendResponse.message === 'string' ? sendResponse.message - : 'Failed to send transaction'; - throw new Error(errorMessage); - } + : 'Escrow funded successfully!'; - // Update escrow balance in context - if (escrow) { - // Balance may not be in the type, so we use type assertion - const escrowWithBalance = escrow as MultiReleaseEscrowWithBalance; - const currentBalance = escrowWithBalance.balance || 0; - const updatedEscrow: MultiReleaseEscrowWithBalance = { - ...escrow, - balance: currentBalance + totalAmount, - }; - updateEscrow(updatedEscrow as MultiReleaseEscrow); - } + setFundingStatus({ + success: true, + message: successMessage, + }); - // Display success status - const successMessage = - 'message' in sendResponse && typeof sendResponse.message === 'string' - ? sendResponse.message - : 'Escrow funded successfully!'; + toast.success('Escrow funded successfully!'); + }, totalAmount); - setFundingStatus({ - success: true, - message: successMessage, - }); - - toast.success('Escrow funded successfully!'); + if (!walletReady) { + setIsLoading(false); + return; + } } catch (err) { reportError(err, { context: 'escrow-fund' }); setFundingStatus({ @@ -187,147 +213,159 @@ export const FundEscrowButton = () => { const totalAmount = calculateTotalAmount(); - if (fundingStatus) { - return ( - - -
- {fundingStatus.success ? ( - - ) : ( - - )} - + {fundingStatus ? ( + + +
+ {fundingStatus.success ? ( + + ) : ( + + )} + + {fundingStatus.success + ? 'Escrow Funded Successfully!' + : 'Funding Failed'} + +
+ - {fundingStatus.success - ? 'Escrow Funded Successfully!' - : 'Funding Failed'} -
-
- - {fundingStatus.message} - -
- {fundingStatus.success && escrow && ( - -
-
- - Previous Balance: - - - {formatAmount( - ((escrow as MultiReleaseEscrowWithBalance).balance || 0) - - totalAmount - )} - -
-
- - Funded Amount: - - - +{formatAmount(totalAmount)} - -
-
- - New Balance: - - - {formatAmount( - (escrow as MultiReleaseEscrowWithBalance).balance || 0 - )} - + {fundingStatus.message} + + + {fundingStatus.success && escrow && ( + +
+
+ + Previous Balance: + + + {formatAmount( + ((escrow as MultiReleaseEscrowWithBalance).balance || 0) - + totalAmount + )} + +
+
+ + Funded Amount: + + + +{formatAmount(totalAmount)} + +
+
+ + New Balance: + + + {formatAmount( + (escrow as MultiReleaseEscrowWithBalance).balance || 0 + )} + +
+ +
+ )} + + ) : !contractId || !escrow ? ( +
+

+ Please initialize an escrow first before funding. +

+
+ ) : !escrow.milestones || escrow.milestones.length === 0 ? ( +
+

+ Error: Escrow initialized without milestones +

+

+ This escrow was initialized without milestones. Please initialize a + new escrow with milestones. +

+
+ ) : ( + <> +
+
+ + Total Funding Amount: + + + {formatAmount(totalAmount)} +
- - - )} - - ); - } - - if (!contractId || !escrow) { - return ( -
-

- Please initialize an escrow first before funding. -

-
- ); - } - - // Check if escrow has milestones - if (!escrow.milestones || escrow.milestones.length === 0) { - return ( -
-

- Error: Escrow initialized without milestones -

-

- This escrow was initialized without milestones. Please initialize a - new escrow with milestones. -

-
- ); - } +

+ This amount is the sum of all milestone amounts ( + {escrow.milestones.length} milestones) +

+
- return ( -
-
-
- - Total Funding Amount: - - - {formatAmount(totalAmount)} - -
-

- This amount is the sum of all milestone amounts ( - {escrow.milestones.length} milestones) -

-
- - + + + )} + + {/* Wallet Modals */} + + + setIsWalletDrawerOpen(true)} + actionName='fund escrow' + /> + +
); }; diff --git a/components/landing-page/navbar.tsx b/components/landing-page/navbar.tsx index 01fe37b4..930ff85f 100644 --- a/components/landing-page/navbar.tsx +++ b/components/landing-page/navbar.tsx @@ -33,6 +33,8 @@ import WalletRequiredModal from '@/components/wallet/WalletRequiredModal'; import { WalletTrigger } from '../wallet/WalletTrigger'; import { NotificationBell } from '../notifications/NotificationBell'; import CreateProjectModal from '@/features/projects/components/CreateProjectModal'; +import WalletNotReadyModal from '@/components/wallet/WalletNotReadyModal'; +import { useWalletContext } from '../providers/wallet-provider'; const BRAND_COLOR = '#a7f950'; const ACTIONS = { @@ -200,12 +202,16 @@ function AuthenticatedActions() { const { executeProtectedAction, showWalletModal, + showNotReadyModal, + notReadyReasons, closeWalletModal, + closeNotReadyModal, handleWalletConnected, } = useProtectedAction({ actionName: ACTIONS.CREATE_PROJECT, onSuccess: () => setCreateProjectModalOpen(true), }); + const { onOpenWallet } = useWalletContext(); return ( <> @@ -294,17 +300,31 @@ function AuthenticatedActions() { actionName={ACTIONS.CREATE_PROJECT} onWalletConnected={handleWalletConnected} /> + ); } function UnauthenticatedActions() { const [createProjectModalOpen, setCreateProjectModalOpen] = useState(false); - const { showWalletModal, closeWalletModal, handleWalletConnected } = - useProtectedAction({ - actionName: ACTIONS.CREATE_PROJECT, - onSuccess: () => setCreateProjectModalOpen(true), - }); + const { + showWalletModal, + showNotReadyModal, + notReadyReasons, + closeWalletModal, + closeNotReadyModal, + handleWalletConnected, + } = useProtectedAction({ + actionName: ACTIONS.CREATE_PROJECT, + onSuccess: () => setCreateProjectModalOpen(true), + }); + const { onOpenWallet } = useWalletContext(); return ( <> @@ -334,6 +354,13 @@ function UnauthenticatedActions() { actionName={ACTIONS.CREATE_PROJECT} onWalletConnected={handleWalletConnected} /> + ); } diff --git a/components/modals/fund-project/index.tsx b/components/modals/fund-project/index.tsx index e74f2b7b..763645c1 100644 --- a/components/modals/fund-project/index.tsx +++ b/components/modals/fund-project/index.tsx @@ -12,6 +12,9 @@ import { fundCrowdfundingProject } from '@/features/projects/api'; import { useWalletContext } from '@/components/providers/wallet-provider'; import { useWalletProtection } from '@/hooks/use-wallet-protection'; import { signTransaction } from '@/lib/config/wallet-kit'; +import WalletRequiredModal from '@/components/wallet/WalletRequiredModal'; +import WalletNotReadyModal from '@/components/wallet/WalletNotReadyModal'; +import { WalletSheet } from '@/components/wallet/WalletSheet'; import { useFundEscrow, useSendTransaction, @@ -82,9 +85,19 @@ const FundProject = ({ open, setOpen, project }: FundProjectProps) => { // Wallet hooks const { walletAddress } = useWalletContext(); - const { requireWallet } = useWalletProtection({ + const { + requireWallet, + showWalletModal, + showNotReadyModal, + notReadyReasons, + handleWalletConnected, + closeWalletModal, + closeNotReadyModal, + } = useWalletProtection({ actionName: 'fund project', }); + + const [isWalletDrawerOpen, setIsWalletDrawerOpen] = useState(false); // Form data state const [formData, setFormData] = useState({ amount: {}, @@ -292,29 +305,11 @@ const FundProject = ({ open, setOpen, project }: FundProjectProps) => { setError(null); const walletValid = await requireWallet(async () => { - if (!walletAddress) { - setError('Wallet address is required'); - setFlowStep('form'); - setIsLoading(false); - setIsSubmitting(false); - return; - } - - if (!project.contractId) { - setError( - 'This project does not have an escrow contract set up. Please contact the project creator or support if you believe this is an error.' - ); - setFlowStep('form'); - setIsLoading(false); - setIsSubmitting(false); - return; - } - try { // Step 1: Prepare the payload according to FundEscrowPayload type const payload: FundEscrowPayload = { - contractId: project.contractId, - signer: walletAddress, + contractId: project.contractId!, + signer: walletAddress!, amount: fundingAmount, }; @@ -342,7 +337,7 @@ const FundProject = ({ open, setOpen, project }: FundProjectProps) => { // Step 3: Sign transaction with wallet const signedXdr = await signTransaction({ unsignedTransaction, - address: walletAddress, + address: walletAddress!, }); // Extract transaction hash from signed XDR @@ -405,7 +400,7 @@ const FundProject = ({ open, setOpen, project }: FundProjectProps) => { setIsLoading(false); setIsSubmitting(false); } - }); + }, fundingAmount); if (!walletValid) { setFlowStep('form'); @@ -593,6 +588,27 @@ const FundProject = ({ open, setOpen, project }: FundProjectProps) => { isSubmitting={isSubmitting} /> )} + + {/* Wallet Modals */} + + + setIsWalletDrawerOpen(true)} + actionName='fund project' + /> + + ); }; diff --git a/components/providers/wallet-provider.tsx b/components/providers/wallet-provider.tsx index 46586a6a..fa1b918d 100644 --- a/components/providers/wallet-provider.tsx +++ b/components/providers/wallet-provider.tsx @@ -26,6 +26,9 @@ type WalletContextType = { syncWallet: () => Promise; getSupportedTrustlineAssets: () => Promise; addTrustline: (assetCode: string) => Promise; + isWalletOpen: boolean; + onOpenWallet: () => void; + onCloseWallet: () => void; }; const WalletContext = createContext(undefined); @@ -123,6 +126,16 @@ export const WalletProvider = ({ children }: { children: ReactNode }) => { const isLoading = isSessionLoading || walletLoading; const hasWalletFromSession = !!session?.user?.wallet?.address; + const [isWalletOpen, setIsWalletOpen] = useState(false); + + const onOpenWallet = useCallback(() => { + setIsWalletOpen(true); + }, []); + + const onCloseWallet = useCallback(() => { + setIsWalletOpen(false); + }, []); + return ( { syncWallet, getSupportedTrustlineAssets, addTrustline, + isWalletOpen, + onOpenWallet, + onCloseWallet, }} > {children} diff --git a/components/wallet/LandingWalletWrapper.tsx b/components/wallet/LandingWalletWrapper.tsx index 4b827829..8e37ecd6 100644 --- a/components/wallet/LandingWalletWrapper.tsx +++ b/components/wallet/LandingWalletWrapper.tsx @@ -4,11 +4,12 @@ import { useState } from 'react'; import { FamilyWalletButton } from './FamilyWalletButton'; import { FamilyWalletDrawer, DrawerView } from './FamilyWalletDrawer'; import { useAuthStatus } from '@/hooks/use-auth'; +import { useWalletContext } from '@/components/providers/wallet-provider'; export function LandingWalletWrapper() { - const [open, setOpen] = useState(false); const [drawerView, setDrawerView] = useState('main'); const { isAuthenticated, isLoading } = useAuthStatus(); + const { isWalletOpen, onOpenWallet, onCloseWallet } = useWalletContext(); if (isLoading || !isAuthenticated) { return null; @@ -19,13 +20,13 @@ export function LandingWalletWrapper() { { if (view) setDrawerView(view); - setOpen(true); + onOpenWallet(); }} /> ); diff --git a/components/wallet/WalletNotReadyModal.tsx b/components/wallet/WalletNotReadyModal.tsx new file mode 100644 index 00000000..ec1a3976 --- /dev/null +++ b/components/wallet/WalletNotReadyModal.tsx @@ -0,0 +1,163 @@ +import React from 'react'; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from '../ui/dialog'; +import { Button } from '../ui/button'; +import { + XIcon, + Wallet, + ExternalLink, + Activity, + Coins, + ShieldCheck, + AlertCircle, +} from 'lucide-react'; +import { WalletNotReadyReason } from '@/hooks/use-wallet-readiness'; + +interface WalletNotReadyModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; + reasons: WalletNotReadyReason[]; + onOpenWallet: () => void; + actionName: string; +} + +const WalletNotReadyModal: React.FC = ({ + open, + onOpenChange, + reasons, + onOpenWallet, + actionName, +}) => { + const handleOpenWallet = () => { + onOpenChange(false); + onOpenWallet(); + }; + + return ( + + + + + + + + +
+ {/* Header Icon with Glow */} +
+
+
+ +
+
+ + {/* Title & Description */} +
+ + Wallet Not Ready + + + A few quick steps are needed before you can{' '} + {actionName}. + +
+ + {/* Steps Section */} +
+ {reasons.includes('not_activated') && ( + } + title='Activate Stellar Account' + description="Your account isn't on-chain. Send at least 2 XLM to this address to activate it." + /> + )} + {reasons.includes('no_usdc_trustline') && ( + } + title='Add USDC Trustline' + description='Required to hold USDC on Stellar. Open your wallet to add the trustline.' + /> + )} + {reasons.includes('insufficient_xlm') && + !reasons.includes('not_activated') && ( + } + title='Low XLM Balance' + description='Transaction fees and reserve requirements need a small amount of XLM.' + /> + )} + {reasons.includes('insufficient_usdc') && ( + } + title='Insufficient USDC Balance' + description='You need more USDC in your wallet to complete this transaction.' + /> + )} +
+ + {/* Actions */} +
+ + +
+ + + Why is this required? + + +
+ +
+ ); +}; + +const StepItem = ({ + icon, + title, + description, +}: { + icon: React.ReactNode; + title: string; + description: string; +}) => ( +
+
+ {icon} +
+
+

{title}

+

{description}

+
+
+); + +export default WalletNotReadyModal; diff --git a/components/wallet/WalletTrigger.tsx b/components/wallet/WalletTrigger.tsx index db0eff67..31f39591 100644 --- a/components/wallet/WalletTrigger.tsx +++ b/components/wallet/WalletTrigger.tsx @@ -21,7 +21,8 @@ export function WalletTrigger({ className, drawerType = 'sheet', }: WalletTriggerProps) { - const { walletAddress, hasWalletFromSession, isLoading } = useWalletContext(); + const { walletAddress, hasWalletFromSession, isLoading, onOpenWallet } = + useWalletContext(); const [open, setOpen] = useState(false); // Wallet is managed by backend; no "Connect Wallet" flow. Show trigger only when diff --git a/features/projects/components/Initialize.tsx b/features/projects/components/Initialize.tsx index 5460ab3b..18a9c74b 100644 --- a/features/projects/components/Initialize.tsx +++ b/features/projects/components/Initialize.tsx @@ -13,6 +13,8 @@ import ProjectSubmissionSuccess from './ProjectSubmissionSuccess'; import Loading from '@/components/loading/Loading'; import { useWalletProtection } from '@/hooks/use-wallet-protection'; import WalletRequiredModal from '@/components/wallet/WalletRequiredModal'; +import WalletNotReadyModal from '@/components/wallet/WalletNotReadyModal'; +import { WalletSheet } from '@/components/wallet/WalletSheet'; type StepState = 'pending' | 'active' | 'completed'; @@ -47,12 +49,17 @@ const Initialize: React.FC = ({ onSuccess }) => { const { requireWallet, showWalletModal, + showNotReadyModal, + notReadyReasons, handleWalletConnected, closeWalletModal, + closeNotReadyModal, } = useWalletProtection({ actionName: 'initialize project', }); + const [isWalletDrawerOpen, setIsWalletDrawerOpen] = useState(false); + const localSteps: Step[] = [ { title: 'Submit your project Details', @@ -97,7 +104,7 @@ const Initialize: React.FC = ({ onSuccess }) => { const submitInit = async () => { if (!formData) return; - requireWallet(async () => { + await requireWallet(async () => { try { setIsSubmitting(true); toast.loading('Initializing project...'); @@ -203,6 +210,21 @@ const Initialize: React.FC = ({ onSuccess }) => { actionName='initialize project' onWalletConnected={handleWalletConnected} /> + + {/* Wallet Not Ready Modal */} + setIsWalletDrawerOpen(true)} + actionName='initialize project' + /> + + {/* Wallet Sheet */} + ); }; @@ -237,7 +259,7 @@ const MilestonesPhase = ({ -
- - {/* Stats Cards Grid */} -
- {/* Profile Card */} - -
- - -
- -
- Profile -
-
- -
- - - - {session.user.name?.charAt(0) || - session.user.email.charAt(0)} - - -
-

- {session.user.name || 'No name'} -

-

- {session.user.email} -

-
-
-
-
- - {/* Account Details Card */} - -
- - -
- -
- Account Details -
-
- -
- User ID - - {session.user.id} - -
-
- Email - - {session.user.email} - -
-
-
- - {/* Status Card */} - -
- - -
- -
- Status & Verification -
-
- -
- Email Status -
- {session.user.emailVerified ? ( - <> - - - Verified - - - ) : ( - - Unverified - - )} -
-
-
- Account Status -
-
- - Active - -
-
- {(session.user as { lastLoginMethod?: string | null }) - ?.lastLoginMethod && ( -
- - Last Login Method - - - {(() => { - const method = ( - session.user as { lastLoginMethod?: string | null } - ).lastLoginMethod; - return method === 'google' - ? 'Google' - : method === 'email' - ? 'Email' - : method || 'N/A'; - })()} - -
- )} -
-
-
- - {/* Welcome Card */} -
- -
-
- - - Welcome to Boundless - - - Your platform for crowdfunding and grants - - - -

- This is your dashboard where you can manage your projects, view - contributions, and access all the features of the platform. The - authentication system is now working properly! -

-
-
-
-
- - ); -} diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx deleted file mode 100644 index 69a6c55d..00000000 --- a/app/dashboard/page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { DashboardContent } from '@/components/dashboard-content'; -import React from 'react'; - -export default function Page() { - return ; -} diff --git a/components/dashboard-content.tsx b/components/dashboard-content.tsx index a8dcd2c5..2aba3177 100644 --- a/components/dashboard-content.tsx +++ b/components/dashboard-content.tsx @@ -7,7 +7,7 @@ import { SectionCards } from '@/components/section-cards'; import { SiteHeader } from '@/components/site-header'; import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar'; import { useAuthStatus } from '@/hooks/use-auth'; -import data from '../app/dashboard/data.json'; +import data from '../data/data.json'; import React, { useState } from 'react'; import { FamilyWalletButton } from '@/components/wallet/FamilyWalletButton'; import { diff --git a/components/hackathons/hackathonBanner.tsx b/components/hackathons/hackathonBanner.tsx index f08d27b0..d18a9212 100644 --- a/components/hackathons/hackathonBanner.tsx +++ b/components/hackathons/hackathonBanner.tsx @@ -30,6 +30,7 @@ interface HackathonBannerProps { onLeaveClick?: () => void; isLeaving?: boolean; participantType?: 'INDIVIDUAL' | 'TEAM' | 'TEAM_OR_INDIVIDUAL'; + submissionDeadlineExtendedAt?: string | null; } export function HackathonBanner({ @@ -46,6 +47,7 @@ export function HackathonBanner({ registrationDeadline, isLeaving, participantType, + submissionDeadlineExtendedAt, onJoinClick, onSubmitClick, onViewSubmissionClick, @@ -252,6 +254,11 @@ export function HackathonBanner({ {status === 'ongoing' ? 'Ends In' : 'Starts In'} + {status === 'ongoing' && submissionDeadlineExtendedAt && ( + + Extended + + )} void; onLeaveClick?: () => void; participantType?: 'INDIVIDUAL' | 'TEAM' | 'TEAM_OR_INDIVIDUAL'; + submissionDeadlineExtendedAt?: string | null; } export function HackathonStickyCard(props: HackathonStickyCardProps) { @@ -54,6 +55,7 @@ export function HackathonStickyCard(props: HackathonStickyCardProps) { onLeaveClick, isLeaving, participantType, + submissionDeadlineExtendedAt, } = props; const { status } = useHackathonStatus(startDate, deadline); @@ -162,6 +164,11 @@ export function HackathonStickyCard(props: HackathonStickyCardProps) { Deadline {formatDateWithFallback(deadline)} + {submissionDeadlineExtendedAt && ( + + Extended + + )} )} diff --git a/app/dashboard/data.json b/data/data.json similarity index 100% rename from app/dashboard/data.json rename to data/data.json diff --git a/lib/api/hackathon.ts b/lib/api/hackathon.ts index b450807e..b747804f 100644 --- a/lib/api/hackathon.ts +++ b/lib/api/hackathon.ts @@ -55,7 +55,6 @@ export const getHackathon = async ( slug: string ): Promise => { const res = await api.get(`/hackathons/s/${slug}`); - return res.data as GetHackathonResponse; }; diff --git a/lib/api/hackathons.ts b/lib/api/hackathons.ts index dc6e1ecc..3b63befe 100644 --- a/lib/api/hackathons.ts +++ b/lib/api/hackathons.ts @@ -347,6 +347,7 @@ export type Hackathon = { startDate: string; // ISO date submissionDeadline: string; // ISO date + submissionDeadlineExtendedAt?: string | null; registrationDeadline: string; // ISO date judgingDeadline?: string; // ISO date From b2249e3e63d13b4e4d0f202bbe528c56388306fb Mon Sep 17 00:00:00 2001 From: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Date: Tue, 10 Mar 2026 00:42:57 +0100 Subject: [PATCH 06/12] merge into main (#476) * fix: delete /dashboard * fix: implement extend deadline and remove dummy /dashboard From 2f7335f5edd7f481314e191b312f010621f3a7b0 Mon Sep 17 00:00:00 2001 From: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Date: Tue, 10 Mar 2026 00:43:44 +0100 Subject: [PATCH 07/12] merge into main (#477) * fix: delete /dashboard * fix: implement extend deadline and remove dummy /dashboard From 12a5ccda0830949a1d1941a0f0f0924363f2a9a0 Mon Sep 17 00:00:00 2001 From: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Date: Tue, 10 Mar 2026 01:08:15 +0100 Subject: [PATCH 08/12] UI fixes (#478) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * fix: my project dashbaord count and extend hackathon deadline * fix: my project dashbaord count and extend hackathon deadline --- app/me/layout.tsx | 25 +++++++++++++++++++++++++ components/app-sidebar.tsx | 18 +++++------------- components/landing-page/navbar.tsx | 2 +- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/app/me/layout.tsx b/app/me/layout.tsx index 0560658f..85dabcb1 100644 --- a/app/me/layout.tsx +++ b/app/me/layout.tsx @@ -23,9 +23,14 @@ interface MeLayoutProfile { image?: string; joinedHackathons?: ProfileItemWithId[]; hackathonSubmissionsAsParticipant?: ProfileItemWithId[]; + projects?: ProfileItemWithId[]; }; image?: string; hackathonSubmissionsAsParticipant?: ProfileItemWithId[]; + projects?: ProfileItemWithId[]; + stats?: { + projectsCreated?: number; + }; } const getId = (item: ProfileItemWithId): string | undefined => @@ -69,6 +74,25 @@ const MeLayout = ({ children }: MeLayoutProps): React.ReactElement => { }).length; }, [typedProfile]); + const projectsCount = useMemo(() => { + if (!typedProfile) return 0; + // stats.projectsCreated is often the most direct count + if (typeof typedProfile.stats?.projectsCreated === 'number') { + return typedProfile.stats.projectsCreated; + } + // Fallback to array lengths + const fromUser = typedProfile.user?.projects ?? []; + const fromProfile = typedProfile.projects ?? []; + const merged = [...fromUser, ...fromProfile]; + const seen = new Set(); + return merged.filter(p => { + const id = getId(p); + if (!id || seen.has(id)) return false; + seen.add(id); + return true; + }).length; + }, [typedProfile]); + // Only show full-screen spinner on first load, not on background refetches if (isLoading && !hasLoadedOnce.current) { return ( @@ -92,6 +116,7 @@ const MeLayout = ({ children }: MeLayoutProps): React.ReactElement => { counts={{ participating: hackathonsCount, submissions: submissionsCount, + projects: projectsCount, }} variant='inset' /> diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx index ec78d978..c4703ddb 100644 --- a/components/app-sidebar.tsx +++ b/components/app-sidebar.tsx @@ -29,11 +29,13 @@ import { import Image from 'next/image'; import Link from 'next/link'; import { useNotificationStore } from '@/lib/stores/notification-store'; +import { Logo } from './landing-page/navbar'; const getNavigationData = (counts?: { participating?: number; unreadNotifications?: number; submissions?: number; + projects?: number; }) => ({ main: [ { @@ -57,7 +59,7 @@ const getNavigationData = (counts?: { title: 'My Projects', url: '/me/projects', icon: IconFolder, - badge: '3', + badge: (counts?.projects ?? 0) > 0 ? String(counts?.projects) : undefined, }, { title: 'Create Project', @@ -132,7 +134,7 @@ export function AppSidebar({ ...props }: { user: UserData; - counts?: { participating?: number; submissions?: number }; + counts?: { participating?: number; submissions?: number; projects?: number }; } & React.ComponentProps) { const unreadNotifications = useNotificationStore(state => state.unreadCount); @@ -164,17 +166,7 @@ export function AppSidebar({ size='lg' className='group hover:bg-sidebar-accent/0 transition-all duration-200' > - -
- Boundless Logo -
- + diff --git a/components/landing-page/navbar.tsx b/components/landing-page/navbar.tsx index 930ff85f..10cddc32 100644 --- a/components/landing-page/navbar.tsx +++ b/components/landing-page/navbar.tsx @@ -124,7 +124,7 @@ export function Navbar() { ); } -function Logo() { +export function Logo() { return ( Date: Tue, 10 Mar 2026 13:35:34 +0100 Subject: [PATCH 09/12] UI fixes (#479) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * fix: my project dashbaord count and extend hackathon deadline * fix: my project dashbaord count and extend hackathon deadline * fix: fix auto validate wallet address and user nav * fix: fix notification badge * fix: fix coderabbit corrections --- app/me/notifications/page.tsx | 4 -- components/app-sidebar.tsx | 13 +++-- components/nav-user.tsx | 63 +++++++++++++++++------ components/wallet/FamilyWalletDrawer.tsx | 65 ++++++++++++++++-------- hooks/useNotifications.ts | 22 +++++++- 5 files changed, 123 insertions(+), 44 deletions(-) diff --git a/app/me/notifications/page.tsx b/app/me/notifications/page.tsx index 8b7e58fa..59a71019 100644 --- a/app/me/notifications/page.tsx +++ b/app/me/notifications/page.tsx @@ -104,10 +104,6 @@ export default function NotificationsPage() { const { setUnreadCount, clearUnreadCount } = useNotificationStore(); - useEffect(() => { - setUnreadCount(unreadCount); - }, [unreadCount, setUnreadCount]); - useNotificationPolling(notificationsHook, { interval: 30000, enabled: true, diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx index c4703ddb..d4b3ac94 100644 --- a/components/app-sidebar.tsx +++ b/components/app-sidebar.tsx @@ -27,9 +27,10 @@ import { SidebarMenuItem, } from '@/components/ui/sidebar'; import Image from 'next/image'; -import Link from 'next/link'; import { useNotificationStore } from '@/lib/stores/notification-store'; import { Logo } from './landing-page/navbar'; +import { useNotifications } from '@/hooks/useNotifications'; +import { authClient } from '@/lib/auth-client'; const getNavigationData = (counts?: { participating?: number; @@ -115,8 +116,8 @@ const getNavigationData = (counts?: { url: '/me/notifications', icon: IconBell, badge: - counts?.unreadNotifications && counts.unreadNotifications > 0 - ? counts.unreadNotifications.toString() + (counts?.unreadNotifications ?? 0) > 0 + ? String(counts?.unreadNotifications) : undefined, }, ], @@ -136,6 +137,12 @@ export function AppSidebar({ user: UserData; counts?: { participating?: number; submissions?: number; projects?: number }; } & React.ComponentProps) { + const { data: session } = authClient.useSession(); + const userId = session?.user?.id; + + // Initialize notifications hook to ensure it fetches globally and syncs with store + useNotifications({ enabled: !!userId }); + const unreadNotifications = useNotificationStore(state => state.unreadCount); const navigationData = React.useMemo( diff --git a/components/nav-user.tsx b/components/nav-user.tsx index edcd65d0..df269f80 100644 --- a/components/nav-user.tsx +++ b/components/nav-user.tsx @@ -26,6 +26,9 @@ import { useSidebar, } from '@/components/ui/sidebar'; import { Badge } from './ui/badge'; +import Link from 'next/link'; +import { useAuthActions } from '@/hooks/use-auth'; +import { useNotificationStore } from '@/lib/stores/notification-store'; export interface NavUserProps { user: { @@ -37,6 +40,14 @@ export interface NavUserProps { export const NavUser = ({ user }: NavUserProps): React.ReactElement => { const { isMobile } = useSidebar(); + const { logout } = useAuthActions(); + const { unreadCount: unreadNotifications, clearUnreadCount } = + useNotificationStore(); + + const handleLogout = () => { + logout(); + clearUnreadCount(); + }; return ( @@ -68,7 +79,7 @@ export const NavUser = ({ user }: NavUserProps): React.ReactElement => { {
- + {user.name} - + {user.email}
@@ -96,22 +107,44 @@ export const NavUser = ({ user }: NavUserProps): React.ReactElement => { - - - Account Settings + + + + Account Settings + - - - Billing + + + + Billing + - - - Notifications - 3 + + + + Notifications + {unreadNotifications > 0 && ( + + {unreadNotifications} + + )} + - - + + Log out diff --git a/components/wallet/FamilyWalletDrawer.tsx b/components/wallet/FamilyWalletDrawer.tsx index e9e7cb67..9f49efac 100644 --- a/components/wallet/FamilyWalletDrawer.tsx +++ b/components/wallet/FamilyWalletDrawer.tsx @@ -212,6 +212,10 @@ export function FamilyWalletDrawer({ setValidateResult('idle'); try { const result = await validateSendDestination(dest, currency); + + // Protect against stale responses if the user has changed the destination + if (sendDestination.trim() !== dest) return; + if (result.valid) { setValidateResult('valid'); setValidateError(''); @@ -222,15 +226,45 @@ export function FamilyWalletDrawer({ ); } } catch (err: unknown) { + // Protect against stale responses + if (sendDestination.trim() !== dest) return; + const { message, details } = getErrorDisplay(err); setValidateResult('invalid'); setValidateError(message); setValidateErrorDetails(details); } finally { - setValidateLoading(false); + // We still want to clear loading if it's the latest call + if (sendDestination.trim() === dest) { + setValidateLoading(false); + } } }, [sendDestination, sendCurrency, getErrorDisplay]); + // Auto-validate destination address + useEffect(() => { + const trimmedDest = sendDestination.trim(); + + // Reset state if empty + if (!trimmedDest) { + setValidateResult('idle'); + setValidateError(''); + return; + } + + // Immediate trigger if 56 chars + if (trimmedDest.length === 56) { + handleValidateDestination(); + return; + } + + const timer = setTimeout(() => { + handleValidateDestination(); + }, 500); + + return () => clearTimeout(timer); + }, [sendDestination, sendCurrency, handleValidateDestination]); + const handleSendSubmit = useCallback(async () => { const dest = sendDestination.trim(); const currency = sendCurrency || 'XLM'; @@ -833,37 +867,26 @@ export function FamilyWalletDrawer({ -
+
{ setSendDestination(e.target.value); - setValidateResult('idle'); - setValidateError(''); }} - className='font-mono text-sm' + className='pr-10 font-mono text-sm' /> - + ) : validateResult === 'invalid' && + sendDestination.trim().length >= 56 ? ( + + ) : null} +
{validateResult === 'invalid' && validateError && ( diff --git a/hooks/useNotifications.ts b/hooks/useNotifications.ts index 8493ad1f..953a504a 100644 --- a/hooks/useNotifications.ts +++ b/hooks/useNotifications.ts @@ -3,11 +3,13 @@ import { useSocket } from './useSocket'; import { getNotifications } from '@/lib/api/notifications'; import { Notification } from '@/types/notifications'; import { reportError } from '@/lib/error-reporting'; +import { useNotificationStore } from '@/lib/stores/notification-store'; interface UseNotificationsOptions { page?: number; limit?: number; autoFetch?: boolean; + enabled?: boolean; } export interface UseNotificationsReturn { @@ -32,11 +34,17 @@ export function useNotifications( // Handle overloaded arguments const userId = typeof input === 'string' ? input : undefined; const options = typeof input === 'object' ? input : {}; - const { page: initialPage = 1, limit = 10, autoFetch = true } = options; + const { + page: initialPage = 1, + limit = 10, + autoFetch = true, + enabled = true, + } = options; const { socket, isConnected } = useSocket({ namespace: '/notifications', userId, + autoConnect: enabled && autoFetch, }); const [notifications, setNotifications] = useState([]); @@ -45,6 +53,16 @@ export function useNotifications( const [error, setError] = useState(null); const [total, setTotal] = useState(0); const [currentPage, setCurrentPage] = useState(initialPage); + const [hasFetched, setHasFetched] = useState(false); + + const { setUnreadCount: setGlobalUnreadCount } = useNotificationStore(); + + // Sync with global store + useEffect(() => { + if (hasFetched) { + setGlobalUnreadCount(unreadCount); + } + }, [unreadCount, setGlobalUnreadCount, hasFetched]); // Merge server list with current state: dedupe by id, preserve optimistic read state (short rollback path) const mergeNotifications = useCallback( @@ -79,6 +97,7 @@ export function useNotifications( mergeNotifications(prev, response.notifications) ); setTotal(response.total || 0); + setHasFetched(true); } } catch (err) { reportError(err, { context: 'notifications-fetch' }); @@ -142,6 +161,7 @@ export function useNotifications( // Listen for unread count updates const handleUnreadCount = (data: { count: number }) => { setUnreadCount(data.count); + setHasFetched(true); }; // Listen for notification updates From e1c216c9da8f5d7d48fee315773c1c9f5c87a2be Mon Sep 17 00:00:00 2001 From: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Date: Tue, 10 Mar 2026 22:51:41 +0100 Subject: [PATCH 10/12] UI fixes (#480) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * fix: my project dashbaord count and extend hackathon deadline * fix: my project dashbaord count and extend hackathon deadline * fix: fix auto validate wallet address and user nav * fix: fix notification badge * fix: fix coderabbit corrections * fix: fix pagination in organization participants page --- .../[hackathonId]/participants/page.tsx | 29 +++++++++-------- hooks/use-hackathons.ts | 32 +++++++++++-------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/app/(landing)/organizations/[id]/hackathons/[hackathonId]/participants/page.tsx b/app/(landing)/organizations/[id]/hackathons/[hackathonId]/participants/page.tsx index 832add8f..b5a17ef1 100644 --- a/app/(landing)/organizations/[id]/hackathons/[hackathonId]/participants/page.tsx +++ b/app/(landing)/organizations/[id]/hackathons/[hackathonId]/participants/page.tsx @@ -53,6 +53,7 @@ const ParticipantsPage: React.FC = () => { const hackathonId = params.hackathonId as string; const [view, setView] = useState<'table' | 'grid'>('table'); + const [pageSize, setPageSize] = useState(PAGE_SIZE); const [filters, setFilters] = useState({ search: '', status: 'all', @@ -63,9 +64,9 @@ const ParticipantsPage: React.FC = () => { () => ({ organizationId, autoFetch: false, - pageSize: PAGE_SIZE, // Grid looks better with multiples of 3/4 + pageSize, // Use dynamic page size }), - [organizationId] + [organizationId, pageSize] ); const { @@ -110,7 +111,7 @@ const ParticipantsPage: React.FC = () => { fetchParticipants( actualHackathonId, 1, - PAGE_SIZE, + pageSize, mapFiltersToParams(filters, debouncedSearch) ); } @@ -120,6 +121,7 @@ const ParticipantsPage: React.FC = () => { debouncedSearch, filters.status, filters.type, + pageSize, ]); // Statistics @@ -161,12 +163,12 @@ const ParticipantsPage: React.FC = () => { }, [organizationId, actualHackathonId]); // Handlers - const handlePageChange = (page: number) => { + const handlePageChange = (page: number, limit?: number) => { if (actualHackathonId) { fetchParticipants( actualHackathonId, page, - PAGE_SIZE, + limit ?? pageSize, mapFiltersToParams(filters, debouncedSearch) ); } @@ -220,7 +222,7 @@ const ParticipantsPage: React.FC = () => { fetchParticipants( actualHackathonId, participantsPagination.currentPage, - PAGE_SIZE, + pageSize, mapFiltersToParams(filters, debouncedSearch) ); } @@ -241,16 +243,17 @@ const ParticipantsPage: React.FC = () => { }, onPaginationChange: updater => { if (typeof updater === 'function') { - const newState = ( - updater as (old: { pageIndex: number; pageSize: number }) => { - pageIndex: number; - pageSize: number; - } - )({ + const newState = updater({ pageIndex: participantsPagination.currentPage - 1, pageSize: participantsPagination.itemsPerPage, }); - handlePageChange(newState.pageIndex + 1); + + if (newState.pageSize !== participantsPagination.itemsPerPage) { + setPageSize(newState.pageSize); + handlePageChange(1, newState.pageSize); + } else { + handlePageChange(newState.pageIndex + 1); + } } }, }); diff --git a/hooks/use-hackathons.ts b/hooks/use-hackathons.ts index 9679b970..4b56eff7 100644 --- a/hooks/use-hackathons.ts +++ b/hooks/use-hackathons.ts @@ -228,17 +228,19 @@ export function useHackathons( organizationId, // Add organization filter }); + const pagination = (response.data?.pagination || + response.meta?.pagination) as any; setHackathons(response.data?.hackathons || []); setHackathonsPagination({ - currentPage: response.data?.pagination.page || 1, - totalPages: response.data?.pagination.totalPages || 1, - totalItems: response.data?.pagination.total || 0, - itemsPerPage: response.data?.pagination.limit || pageSize, - hasNext: response.data?.pagination.hasNext || false, - hasPrev: response.data?.pagination.hasPrev || false, + currentPage: pagination?.page || 1, + totalPages: pagination?.totalPages || 1, + totalItems: pagination?.total || 0, + itemsPerPage: pagination?.limit || pageSize, + hasNext: !!pagination?.hasNext, + hasPrev: !!pagination?.hasPrev, }); // Update ref immediately - hackathonsPageRef.current = response.data?.pagination.page || 1; + hackathonsPageRef.current = pagination?.page || 1; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to fetch hackathons'; @@ -575,16 +577,18 @@ export function useHackathons( filters ?? initialParticipantFilters ); + const pagination = (response.data?.pagination || + response.meta?.pagination) as any; setParticipants(response.data?.participants || []); setParticipantsPagination({ - currentPage: response.data?.pagination.page || 1, - totalPages: response.data?.pagination.totalPages || 1, - totalItems: response.data?.pagination.total || 0, - itemsPerPage: response.data?.pagination.limit || pageSize, - hasNext: response.data?.pagination.hasNext || false, - hasPrev: response.data?.pagination.hasPrev || false, + currentPage: pagination?.page || 1, + totalPages: pagination?.totalPages || 1, + totalItems: pagination?.total || 0, + itemsPerPage: pagination?.limit || pageSize, + hasNext: !!pagination?.hasNext, + hasPrev: !!pagination?.hasPrev, }); - participantsPageRef.current = response.data?.pagination.page || 1; + participantsPageRef.current = pagination?.page || 1; } catch (error) { const errorMessage = error instanceof Error From 746f040515ee5af14b61ea906fcbf08c86b06330 Mon Sep 17 00:00:00 2001 From: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com> Date: Wed, 11 Mar 2026 02:24:57 +0100 Subject: [PATCH 11/12] UI fixes (#483) * fix: improve timeline input , ui improvement and fixes for participation tab * fix: implement 2fa for email and password login * fix: fix conflict * fix: fix submission form * fix: fix hackathon submission and participant page * fix: fix hackathon submission and participant page * fix: fix auto refresh ib submission page * fix: hackathon submission fixes * fix: fix coderabbit corrections * fix: fix coderabbit corrections * chore: write boundless on x challenge blog * fix: remove blog * fix: my project dashbaord count and extend hackathon deadline * fix: my project dashbaord count and extend hackathon deadline * fix: fix auto validate wallet address and user nav * fix: fix notification badge * fix: fix coderabbit corrections * fix: fix pagination in organization participants page From 4fa41f9e189e3112043f8b0410aaef4ca1597069 Mon Sep 17 00:00:00 2001 From: Collins Ikechukwu Date: Sat, 14 Mar 2026 01:17:53 +0100 Subject: [PATCH 12/12] Feat/new hackathon layout (#486) * feat: Implement comprehensive hackathon detail page with new header, sidebar, and tabbed content sections including teams, announcements, winners, participants, and resources. * fix: update coderabit * feat: Enhance hackathon team invitation flow, recruitment post management, and dynamic tab visibility. * feat: Integrate AuthModalProvider for authentication handling and enhance messaging features across hackathon components * refactor: Update UI components to use primary color scheme for consistency across landing pages and hackathon sections --- app/(landing)/blog/[slug]/not-found.tsx | 5 +- .../code-of-conduct/CodeOfConductContent.tsx | 10 +- .../disclaimer/DisclaimerContent.tsx | 8 +- .../hackathons/[slug]/HackathonPageClient.tsx | 114 ++- .../announcements/[announcementId]/page.tsx | 9 +- .../hackathons/[slug]/components/Banner.tsx | 18 + .../components/header/ActionButtons.tsx | 140 ++++ .../[slug]/components/header/Logo.tsx | 20 + .../[slug]/components/header/TitleAndInfo.tsx | 145 ++++ .../[slug]/components/header/index.tsx | 34 + .../components/sidebar/FollowAndMessage.tsx | 163 ++++ .../components/sidebar/PoolAndAction.tsx | 282 +++++++ .../[slug]/components/sidebar/index.tsx | 16 + .../[slug]/components/tabs/Lists.tsx | 52 ++ .../tabs/contents/AnnouncementsTab.tsx | 15 + .../components/tabs/contents/Discussions.tsx | 31 + .../components/tabs/contents/FindTeam.tsx | 290 +++++++ .../components/tabs/contents/Overview.tsx | 274 ++++++ .../components/tabs/contents/Participants.tsx | 271 ++++++ .../components/tabs/contents/ResourcesTab.tsx | 14 + .../components/tabs/contents/Submissions.tsx | 248 ++++++ .../components/tabs/contents/Winners.tsx | 98 +++ .../announcements/announcementCard.tsx | 123 +++ .../tabs/contents/announcements/header.tsx | 46 ++ .../tabs/contents/announcements/index.tsx | 96 +++ .../[slug]/components/tabs/contents/index.tsx | 0 .../contents/participants/ParticipantCard.tsx | 95 +++ .../tabs/contents/resources/ResourceCard.tsx | 107 +++ .../tabs/contents/resources/header.tsx | 44 + .../tabs/contents/resources/index.tsx | 175 ++++ .../contents/submissions/SubmissionCard.tsx | 93 +++ .../tabs/contents/teams/MyTeamView.tsx | 393 +++++++++ .../tabs/contents/teams/TeamCard.tsx | 131 +++ .../contents/winners/GeneralWinnerCard.tsx | 33 + .../tabs/contents/winners/MainStageHeader.tsx | 14 + .../contents/winners/PodiumWinnerCard.tsx | 53 ++ .../tabs/contents/winners/TopWinnerCard.tsx | 102 +++ .../[slug]/components/tabs/index.tsx | 222 +++++ app/(landing)/hackathons/[slug]/page.tsx | 18 +- .../hackathons/[slug]/submit/page.tsx | 16 +- app/(landing)/hackathons/layout.tsx | 2 +- .../newsletter/confirm/error/page.tsx | 2 +- app/(landing)/newsletter/confirmed/page.tsx | 2 +- .../newsletter/unsubscribe/error/page.tsx | 2 +- .../newsletter/unsubscribed/page.tsx | 2 +- .../org/[slug]/org-profile-client.tsx | 10 +- app/(landing)/privacy/PrivacyContent.tsx | 8 +- app/(landing)/privacy/TermsContent.tsx | 8 +- .../[slug]/edit/components/TeamSection.tsx | 4 +- app/me/hackathons/submissions/page.tsx | 10 +- .../submissions/submission-components.tsx | 8 +- app/me/notifications/page.tsx | 4 +- app/me/settings/SettingsContent.tsx | 6 +- app/providers.tsx | 45 +- components/ComingSoon.tsx | 4 +- components/ProjectCard.tsx | 2 +- components/auth/AuthModalProvider.tsx | 110 +++ components/auth/LoginForm.tsx | 10 +- components/auth/LoginWrapper.tsx | 15 +- components/auth/SignupForm.tsx | 4 +- components/auth/TwoFactorVerify.tsx | 10 +- components/avatars/BasicAvatar.tsx | 39 + components/avatars/GroupAvatar.tsx | 41 + components/bounties/BountyComments.tsx | 4 +- components/common/SharePopover.tsx | 111 +++ components/common/share.tsx | 3 + .../crowdfunding/campaign-comments-tab.tsx | 4 +- components/hackathons/ExtendedBadge.tsx | 34 + components/hackathons/HackathonComments.tsx | 6 +- components/hackathons/HackathonsPageHero.tsx | 2 +- components/hackathons/discussion/comment.tsx | 4 +- components/hackathons/hackathonBanner.tsx | 22 +- components/hackathons/hackathonNavTabs.tsx | 8 +- components/hackathons/hackathonStickyCard.tsx | 18 +- .../overview/RegisterHackathonModal.tsx | 2 +- .../hackathons/overview/hackathonPrizes.tsx | 22 +- .../hackathons/overview/hackathonTimeline.tsx | 62 +- .../hackathons/overview/joinHackathon.tsx | 6 +- .../participants/hackathonParticipant.tsx | 4 +- .../participants/participantAvatar.tsx | 4 +- .../participants/participantFilter.tsx | 2 +- .../hackathons/participants/profileCard.tsx | 4 +- .../submissions/SubmissionDetailModal.tsx | 10 +- .../hackathons/submissions/SubmissionForm.tsx | 39 +- .../hackathons/submissions/submissionCard.tsx | 14 +- .../submissions/submissionCard2.tsx | 6 +- .../hackathons/submissions/submissionTab.tsx | 6 +- .../team-formation/ContactTeamModal.tsx | 154 ++++ .../team-formation/CreateTeamPostModal.tsx | 702 ++++++++-------- .../team-formation/InviteUserModal.tsx | 2 +- .../team-formation/MyInvitationsList.tsx | 14 +- .../team-formation/TeamDetailsSheet.tsx | 87 +- .../team-formation/TeamFormationTab.tsx | 38 +- .../team-formation/TeamInvitationsList.tsx | 4 +- .../TeamRecruitmentPostCard.tsx | 59 +- components/landing-page/Explore.tsx | 4 +- components/landing-page/Hero2.tsx | 2 +- components/landing-page/blog/BlogCard.tsx | 12 +- components/landing-page/blog/BlogGrid.tsx | 18 +- .../landing-page/blog/BlogPostDetails.tsx | 8 +- .../landing-page/blog/BlogSectionClient.tsx | 2 +- .../landing-page/blog/StreamingBlogGrid.tsx | 18 +- components/landing-page/navbar.tsx | 4 +- components/layout/sidebar.tsx | 6 +- components/messages/MessagesProvider.tsx | 56 +- components/messages/MessagesTrigger.tsx | 7 +- components/messages/TetherMessage.tsx | 780 ++++++++++++++++++ components/modals/Success.tsx | 2 +- components/nav-user.tsx | 2 +- components/overview/Newsletter.tsx | 4 +- components/profile/update/SecurityTab.tsx | 10 +- components/profile/update/TwoFactorTab.tsx | 34 +- .../comment-section/project-comments.tsx | 4 +- components/project-details/project-about.tsx | 14 +- .../project-details/project-details.tsx | 12 +- components/project-details/project-layout.tsx | 10 +- .../project-sidebar/ProjectSidebarActions.tsx | 4 +- .../project-sidebar/ProjectSidebarHeader.tsx | 2 +- components/testimonials/TestimonialCard.tsx | 6 +- components/ui/avatar.tsx | 66 +- components/ui/popover-cult.tsx | 364 ++++++++ components/ui/separator.tsx | 2 +- components/ui/side-panel.tsx | 128 +++ components/ui/tabs.tsx | 43 +- components/waitlist/WaitlistForm.tsx | 4 +- .../components/CreateProjectModal/Contact.tsx | 12 +- .../CreateProjectModal/SuccessScreen.tsx | 2 +- .../components/MilestoneSubmissionModal.tsx | 4 +- .../components/MilestoneSubmissionSuccess.tsx | 6 +- .../projects/components/ProjectPageHero.tsx | 2 +- hooks/hackathon/use-hackathon-queries.ts | 525 ++++++++++++ hooks/hackathon/use-hackathon-status.ts | 24 +- hooks/hackathon/use-hackathon-submissions.ts | 12 +- hooks/hackathon/use-team-posts.ts | 97 +-- hooks/use-follow.ts | 12 +- hooks/use-require-auth-for-action.ts | 45 + lib/api/hackathon.ts | 6 +- lib/api/hackathons.ts | 25 +- lib/api/hackathons/core.ts | 2 +- lib/api/hackathons/teams.ts | 490 ++++++----- lib/providers/hackathonProvider.tsx | 566 +++---------- package-lock.json | 519 +++++++++++- package.json | 5 +- types/follow.ts | 8 +- types/hackathon/participant.ts | 9 +- types/messages.ts | 1 + 146 files changed, 8182 insertions(+), 1641 deletions(-) create mode 100644 app/(landing)/hackathons/[slug]/components/Banner.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/header/Logo.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/header/TitleAndInfo.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/header/index.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/sidebar/FollowAndMessage.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/sidebar/PoolAndAction.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/sidebar/index.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/Lists.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/AnnouncementsTab.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/Discussions.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/FindTeam.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/Overview.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/ResourcesTab.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/Submissions.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/Winners.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/announcementCard.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/header.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/index.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/index.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/participants/ParticipantCard.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/resources/ResourceCard.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/resources/header.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/resources/index.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/teams/MyTeamView.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/teams/TeamCard.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/winners/MainStageHeader.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx create mode 100644 app/(landing)/hackathons/[slug]/components/tabs/index.tsx create mode 100644 components/auth/AuthModalProvider.tsx create mode 100644 components/avatars/BasicAvatar.tsx create mode 100644 components/avatars/GroupAvatar.tsx create mode 100644 components/common/SharePopover.tsx create mode 100644 components/common/share.tsx create mode 100644 components/hackathons/ExtendedBadge.tsx create mode 100644 components/hackathons/team-formation/ContactTeamModal.tsx create mode 100644 components/messages/TetherMessage.tsx create mode 100644 components/ui/popover-cult.tsx create mode 100644 components/ui/side-panel.tsx create mode 100644 hooks/hackathon/use-hackathon-queries.ts create mode 100644 hooks/use-require-auth-for-action.ts diff --git a/app/(landing)/blog/[slug]/not-found.tsx b/app/(landing)/blog/[slug]/not-found.tsx index 0dd90ca8..b21c2cf5 100644 --- a/app/(landing)/blog/[slug]/not-found.tsx +++ b/app/(landing)/blog/[slug]/not-found.tsx @@ -28,10 +28,7 @@ export default function BlogNotFound() { -
@@ -170,7 +170,6 @@ export default function AnnouncementDetailPage() {
- {/* Content */}
{markdownLoading ? ( @@ -191,7 +190,7 @@ export default function AnnouncementDetailPage() { This announcement was published by the hackathon organizers.

window.close()} + onClick={() => router.push(`/hackathons/${slug}?tab=announcements`)} variant='outline' size='sm' > diff --git a/app/(landing)/hackathons/[slug]/components/Banner.tsx b/app/(landing)/hackathons/[slug]/components/Banner.tsx new file mode 100644 index 00000000..62c2cfe7 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/Banner.tsx @@ -0,0 +1,18 @@ +import Image from 'next/image'; +import React from 'react'; + +const Banner = ({ banner, title }: { banner: string; title?: string }) => { + return ( +
+ {`${title +
+ ); +}; + +export default Banner; diff --git a/app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx b/app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx new file mode 100644 index 00000000..e422e1f6 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx @@ -0,0 +1,140 @@ +'use client'; + +import { useParams, useRouter } from 'next/navigation'; +import { BoundlessButton } from '@/components/buttons'; +import { IconUsers, IconUserPlus, IconLogout } from '@tabler/icons-react'; +import { + useHackathon, + useMyTeam, + useJoinHackathon, + useLeaveHackathon, +} from '@/hooks/hackathon/use-hackathon-queries'; +import { useHackathonData } from '@/lib/providers/hackathonProvider'; +import { useOptionalAuth } from '@/hooks/use-auth'; +import type { Participant } from '@/types/hackathon'; +import SharePopover from '@/components/common/SharePopover'; +import { toast } from 'sonner'; +import { useRequireAuthForAction } from '@/hooks/use-require-auth-for-action'; +import { useHackathonStatus } from '@/hooks/hackathon/use-hackathon-status'; + +const ActionButtons = () => { + const { slug } = useParams<{ slug: string }>(); + const router = useRouter(); + const { user } = useOptionalAuth(); + const { currentHackathon: hackathon, refreshCurrentHackathon } = + useHackathonData(); + const { data: myTeam } = useMyTeam(slug); + const { withAuth } = useRequireAuthForAction(); + + const joinMutation = useJoinHackathon(slug); + const leaveMutation = useLeaveHackathon(slug); + + const { status: hackathonStatus } = useHackathonStatus( + hackathon?.startDate, + hackathon?.submissionDeadline, + hackathon?.status + ); + + const isParticipant = user + ? !!hackathon?.isParticipant || + (hackathon?.participants || []).some( + (p: Participant) => p.userId === user.id + ) + : false; + + const handleJoin = withAuth(async () => { + try { + await joinMutation.mutateAsync(); + await refreshCurrentHackathon(); + toast.success('Successfully joined the hackathon!'); + } catch (error: any) { + toast.error(error?.message || 'Failed to join hackathon'); + } + }); + + const handleLeave = withAuth(async () => { + try { + await leaveMutation.mutateAsync(); + await refreshCurrentHackathon(); + toast.success('You have left the hackathon'); + } catch (error: any) { + toast.error(error?.message || 'Failed to leave hackathon'); + } + }); + + const handleTabChange = (tab: string) => { + const searchParams = new URLSearchParams(window.location.search); + searchParams.set('tab', tab); + router.push(`?${searchParams.toString()}`); + + const tabsElement = document.getElementById('hackathon-tabs'); + if (tabsElement) { + tabsElement.scrollIntoView({ behavior: 'smooth' }); + } + }; + + const isRegistrationClosed = + hackathon?.registrationOpen === false || + (hackathon?.registrationDeadline && + new Date(hackathon.registrationDeadline) < new Date()) || + ['JUDGING', 'COMPLETED', 'ARCHIVED', 'CANCELLED'].includes( + hackathon?.status || '' + ); + + const isIndividualOnly = hackathon?.participantType === 'INDIVIDUAL'; + + return ( +
+ {!isParticipant ? ( + } + onClick={handleJoin} + loading={joinMutation.isPending} + disabled={isRegistrationClosed} + > + {isRegistrationClosed ? 'REGISTRATION CLOSED' : 'JOIN HACKATHON'} + + ) : ( +
+ + REGISTERED + + + + +
+ )} + +
+ {!isIndividualOnly && ( + handleTabChange('team-formation')} + icon={} + > + {myTeam ? 'MY TEAM' : 'FIND TEAM'} + + )} + + +
+
+ ); +}; + +export default ActionButtons; diff --git a/app/(landing)/hackathons/[slug]/components/header/Logo.tsx b/app/(landing)/hackathons/[slug]/components/header/Logo.tsx new file mode 100644 index 00000000..3297c84e --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/header/Logo.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import Image from 'next/image'; + +const Logo = ({ logo, title }: { logo: string; title: string }) => { + return ( +
+ {logo ? ( + {title + ) : null} +
+ ); +}; + +export default Logo; diff --git a/app/(landing)/hackathons/[slug]/components/header/TitleAndInfo.tsx b/app/(landing)/hackathons/[slug]/components/header/TitleAndInfo.tsx new file mode 100644 index 00000000..63eeda75 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/header/TitleAndInfo.tsx @@ -0,0 +1,145 @@ +import { Zap, Globe2Icon } from 'lucide-react'; +import { + Avatar, + AvatarFallback, + AvatarGroup, + AvatarGroupCount, + AvatarImage, +} from '@/components/ui/avatar'; +import { Separator } from '@/components/ui/separator'; +import type { Hackathon, Participant } from '@/lib/api/hackathons'; +import { ExtendedBadge } from '@/components/hackathons/ExtendedBadge'; + +type HackathonStatus = Hackathon['status']; + +function getStatusLabel(status: HackathonStatus) { + switch (status) { + case 'ACTIVE': + return 'Submissions Open'; + case 'JUDGING': + return 'Judging'; + case 'UPCOMING': + return 'Upcoming'; + case 'COMPLETED': + return 'Completed'; + case 'CANCELLED': + return 'Cancelled'; + case 'ARCHIVED': + return 'Archived'; + default: + return 'Draft'; + } +} + +function isActiveStatus(status: HackathonStatus) { + return status === 'ACTIVE'; +} + +interface TitleAndInfoProps { + title?: string; + status?: HackathonStatus; + participantType?: 'INDIVIDUAL' | 'TEAM' | 'TEAM_OR_INDIVIDUAL'; + participantCount?: number; + venueType?: 'VIRTUAL' | 'PHYSICAL'; + participants?: Participant[]; + submissionDeadline?: string; + submissionDeadlineOriginal?: string; +} + +const TitleAndInfo = ({ + title = 'Boundless Global Hackathon', + status = 'UPCOMING', + participantType = 'INDIVIDUAL', + participantCount = 0, + venueType = 'VIRTUAL', + participants = [], + submissionDeadline, + submissionDeadlineOriginal, +}: TitleAndInfoProps) => { + const statusLabel = getStatusLabel(status); + const isActive = isActiveStatus(status); + const venueLabel = venueType === 'PHYSICAL' ? 'Physical' : 'Virtual'; + const typeLabel = + participantType === 'TEAM' + ? 'Team' + : participantType === 'TEAM_OR_INDIVIDUAL' + ? 'Team / Individual' + : 'Individual'; + + const displayParticipants = participants.slice(0, 5); + + return ( +
+

+ {title} +

+ +
+ + + {statusLabel} + + + +
+ +
+ + + + {typeLabel} + + +
+ +
+ + + + {venueLabel} + + + {participantCount > 0 && ( + <> +
+ +
+
+ + {displayParticipants.map((participant, i) => ( + + + + {participant.user.profile.name?.[0]?.toUpperCase() || 'U'} + + + ))} + {participantCount > 5 && ( + + +{(participantCount - 5).toLocaleString()} + + )} + +
+ + )} +
+
+ ); +}; + +export default TitleAndInfo; diff --git a/app/(landing)/hackathons/[slug]/components/header/index.tsx b/app/(landing)/hackathons/[slug]/components/header/index.tsx new file mode 100644 index 00000000..92785515 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/header/index.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import Logo from './Logo'; +import TitleAndInfo from './TitleAndInfo'; +import ActionButtons from './ActionButtons'; +import type { Hackathon } from '@/lib/api/hackathons'; + +interface HeaderProps { + hackathon: Hackathon; +} + +const Header = ({ hackathon }: HeaderProps) => { + return ( +
+
+ + +
+
+ +
+
+ ); +}; + +export default Header; diff --git a/app/(landing)/hackathons/[slug]/components/sidebar/FollowAndMessage.tsx b/app/(landing)/hackathons/[slug]/components/sidebar/FollowAndMessage.tsx new file mode 100644 index 00000000..f4ab07cb --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/sidebar/FollowAndMessage.tsx @@ -0,0 +1,163 @@ +'use client'; + +import React from 'react'; +import { useParams } from 'next/navigation'; +import Image from 'next/image'; +import { MessageSquare, UserPlus, Check, AlertCircle } from 'lucide-react'; +import { BoundlessButton } from '@/components/buttons/BoundlessButton'; +import { useHackathonData } from '@/lib/providers/hackathonProvider'; +import { useFollow } from '@/hooks/use-follow'; +import { Skeleton } from '@/components/ui/skeleton'; +import { toast } from 'sonner'; + +export default function FollowAndMessage() { + const { currentHackathon: hackathon } = useHackathonData(); + const hackathonLoading = !hackathon; + const hackathonError = false; // Handled by parent + + const org = hackathon?.organization; + const orgName = org?.name ?? 'Organization'; + const orgLogo = org?.logo ?? ''; + const orgHandle = org?.name + ? `@${org.name.toLowerCase().replace(/\s+/g, '_')}` + : '@organization'; + + const { + isFollowing, + isLoading: followLoading, + toggleFollow, + } = useFollow('ORGANIZATION', org?.id || '', false); + + const handleToggleFollow = async () => { + const nextState = !isFollowing; + try { + await toggleFollow(); + toast.success( + nextState ? `Following ${orgName}` : `Unfollowed ${orgName}` + ); + } catch (err: unknown) { + const errorMessage = + err instanceof Error ? err.message : 'Failed to update follow status'; + toast.error(errorMessage); + } + }; + + if (hackathonLoading) { + return ( +
+ +
+ +
+ + +
+
+
+ + +
+
+ ); + } + + if (hackathonError || !org?.id) { + return ( +
+ +

+ Organizer information unavailable +

+
+ ); + } + + return ( +
+
+

+ Organizer +

+
+ +
+
+
+ {orgLogo ? ( + {orgName} + ) : ( + + + {[0, 60, 120, 180, 240, 300].map((angle, i) => { + const rad = (angle * Math.PI) / 180; + const x2 = 28 + 14 * Math.cos(rad); + const y2 = 28 + 14 * Math.sin(rad); + return ( + + + + + ); + })} + + )} +
+
+

{orgName}

+

{orgHandle}

+
+
+ +
+ + ) : ( + + ) + } + iconPosition='left' + onClick={handleToggleFollow} + disabled={followLoading} + className={ + isFollowing + ? 'border-primary/40 bg-primary/10 text-primary cursor-default opacity-80' + : '' + } + > + {isFollowing ? 'Following' : 'Follow'} + + } + iconPosition='left' + > + Message + +
+
+
+ ); +} diff --git a/app/(landing)/hackathons/[slug]/components/sidebar/PoolAndAction.tsx b/app/(landing)/hackathons/[slug]/components/sidebar/PoolAndAction.tsx new file mode 100644 index 00000000..f45082d0 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/sidebar/PoolAndAction.tsx @@ -0,0 +1,282 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { + Clock, + Briefcase, + ChevronRight, + Flower, + AlertCircle, +} from 'lucide-react'; +import { useHackathonData } from '@/lib/providers/hackathonProvider'; +import { useOptionalAuth } from '@/hooks/use-auth'; +import { useRequireAuthForAction } from '@/hooks/use-require-auth-for-action'; +import { Badge } from '@/components/ui/badge'; +import { Skeleton } from '@/components/ui/skeleton'; +import Image from 'next/image'; +import { BoundlessButton } from '@/components/buttons'; +import { cn } from '@/lib/utils'; +import { ExtendedBadge } from '@/components/hackathons/ExtendedBadge'; +import { Participant } from '@/lib/api/hackathons'; + +function useCountdown(deadline?: string) { + const [timeLeft, setTimeLeft] = useState({ d: 0, h: 0, m: 0, s: 0 }); + const [isMounted, setIsMounted] = useState(false); + + useEffect(() => { + setIsMounted(true); + }, []); + + useEffect(() => { + if (!deadline || !isMounted) return; + + const tick = () => { + const diff = new Date(deadline).getTime() - Date.now(); + if (diff <= 0) { + setTimeLeft({ d: 0, h: 0, m: 0, s: 0 }); + return; + } + const d = Math.floor(diff / 86400000); + const h = Math.floor((diff % 86400000) / 3600000); + const m = Math.floor((diff % 3600000) / 60000); + const s = Math.floor((diff % 60000) / 1000); + setTimeLeft({ d, h, m, s }); + }; + + tick(); + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, [deadline, isMounted]); + + return timeLeft; +} + +// ─── Component ──────────────────────────────────────────────────────────────── + +export default function PoolAndAction() { + const params = useParams(); + const router = useRouter(); + const { user } = useOptionalAuth(); + const slug = params.slug as string; + + const { + currentHackathon: hackathon, + submissions, + error, + loading, + } = useHackathonData(); + const hackathonError = error; + const isDataLoading = loading || !hackathon; + const participants = hackathon?.participants || []; + const deadline = hackathon?.submissionDeadline; + const timeLeft = useCountdown(deadline); + + const totalPool = + hackathon?.prizeTiers.reduce( + (acc, t) => acc + Number(t.prizeAmount || 0), + 0 + ) ?? 0; + + const now = new Date(); + const isSubmissionClosed = + ['COMPLETED', 'JUDGING', 'ARCHIVED', 'CANCELLED'].includes( + hackathon?.status || '' + ) || (deadline ? now > new Date(deadline) : false); + + const isLive = hackathon?.status === 'ACTIVE' && !isSubmissionClosed; + const isEnded = + ['COMPLETED', 'JUDGING', 'ARCHIVED', 'CANCELLED'].includes( + hackathon?.status || '' + ) || (deadline ? now > new Date(deadline) : false); + const currency = hackathon?.prizeTiers[0]?.currency ?? 'USDC'; + const categories = hackathon?.categories ?? []; + + const isParticipant = user + ? participants.some((p: Participant) => p.userId === user.id) + : false; + + const { withAuth } = useRequireAuthForAction(); + + const handleSubmit = withAuth(() => { + if (isButtonDisabled) return; + router.push(`/hackathons/${slug}/submit`); + }); + + if (isDataLoading) { + return ( +
+
+ + +
+
+ + +
+
+ + +
+
+ ); + } + + if (hackathonError) { + return ( +
+ +

+ Failed to load hackathon +

+

+ {typeof hackathonError === 'string' + ? hackathonError + : 'Please refresh the page and try again.'} +

+
+ ); + } + + const getButtonText = () => { + if (hackathon?.status === 'ARCHIVED') return 'Hackathon Archived'; + if (hackathon?.status === 'CANCELLED') return 'Hackathon Cancelled'; + if (isEnded) return 'Submissions Closed'; + if (!isParticipant) return 'Register to Submit'; + return 'Submit Now'; + }; + + const isButtonDisabled = + isEnded || + !isParticipant || + ['ARCHIVED', 'CANCELLED'].includes(hackathon?.status || ''); + + return ( +
+
+ + + + {isLive ? 'Live' : (hackathon?.status ?? 'Upcoming')} + + + {categories.slice(0, 3).map(cat => ( + + {cat} + + ))} +
+ +
+
+
+ +
+
+

+ Total Prize Pool +

+

+ {totalPool.toLocaleString()}{' '} + {currency} +

+
+
+ + {hackathon && hackathon.prizeTiers.length > 0 && ( +
+
+
+ {hackathon.prizeTiers.map((tier, i) => ( +
+ +
+

+ {tier.name ?? + `${i + 1}${['st', 'nd', 'rd'][i] ?? 'th'} Place`} +

+

+ {Number(tier.prizeAmount ?? 0).toLocaleString()}{' '} + + {tier.currency ?? currency} + +

+
+
+ ))} +
+
+ )} + +
+
+

+ Ends In +

+
+ + + {String(timeLeft.d).padStart(2, '0')}d :{' '} + {String(timeLeft.h).padStart(2, '0')}h :{' '} + {String(timeLeft.m).padStart(2, '0')}m :{' '} + {String(timeLeft.s).padStart(2, '0')}s + +
+
+
+
+

+ Submissions +

+
+ + + {hackathon?._count.submissions ?? 0} + +
+
+
+ + + ) + } + > + {getButtonText()} + +
+
+ ); +} diff --git a/app/(landing)/hackathons/[slug]/components/sidebar/index.tsx b/app/(landing)/hackathons/[slug]/components/sidebar/index.tsx new file mode 100644 index 00000000..d99bc44e --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/sidebar/index.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import PoolAndAction from './PoolAndAction'; +import FollowAndMessage from './FollowAndMessage'; + +const Sidebar = () => { + return ( +
+
+ +
+ +
+ ); +}; + +export default Sidebar; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/Lists.tsx b/app/(landing)/hackathons/[slug]/components/tabs/Lists.tsx new file mode 100644 index 00000000..2aa30023 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/Lists.tsx @@ -0,0 +1,52 @@ +'use client'; + +import { TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { LucideIcon } from 'lucide-react'; + +interface TabItem { + id: string; + label: string; + badge?: number; + icon?: LucideIcon; +} + +interface ListsProps { + tabs: TabItem[]; +} + +export default function Lists({ tabs }: ListsProps) { + return ( + // Outer wrapper handles the bottom border + horizontal scroll on mobile +
+ + {tabs.map(({ id, label, badge, icon: Icon }) => ( + + {Icon && } + {label} + {badge !== undefined && badge > 0 && ( + + {badge} + + )} + + ))} + +
+ ); +} diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/AnnouncementsTab.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/AnnouncementsTab.tsx new file mode 100644 index 00000000..95c0a6b7 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/AnnouncementsTab.tsx @@ -0,0 +1,15 @@ +'use client'; + +import { TabsContent } from '@/components/ui/tabs'; + +import AnnouncementsIndex from './announcements'; + +const Announcements = () => { + return ( + + + + ); +}; + +export default Announcements; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/Discussions.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/Discussions.tsx new file mode 100644 index 00000000..28038761 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/Discussions.tsx @@ -0,0 +1,31 @@ +'use client'; + +import React from 'react'; +import { useParams } from 'next/navigation'; +import { useHackathon } from '@/hooks/hackathon/use-hackathon-queries'; +import { TabsContent } from '@/components/ui/tabs'; +import { HackathonDiscussions } from '@/components/hackathons/discussion/comment'; + +const HackathonDiscussionsTab = () => { + const { slug } = useParams<{ slug: string }>(); + const { data: hackathon } = useHackathon(slug); + + if (!hackathon) return null; + + return ( + +
+

Discussions

+

+ Join the conversation, ask questions, and share updates. +

+
+ +
+ ); +}; + +export default HackathonDiscussionsTab; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/FindTeam.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/FindTeam.tsx new file mode 100644 index 00000000..fb10e1fe --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/FindTeam.tsx @@ -0,0 +1,290 @@ +'use client'; + +import React, { useCallback, useState } from 'react'; +import { useParams } from 'next/navigation'; +import { + Search, + ChevronDown, + Plus, + Sparkles, + Loader2, + Info, +} from 'lucide-react'; +import { TabsContent } from '@/components/ui/tabs'; +import { + useHackathon, + useMyTeam, + useHackathonTeams, +} from '@/hooks/hackathon/use-hackathon-queries'; +import TeamCard from './teams/TeamCard'; +import MyTeamView from './teams/MyTeamView'; +import { BoundlessButton } from '@/components/buttons/BoundlessButton'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { CreateTeamPostModal } from '@/components/hackathons/team-formation/CreateTeamPostModal'; +import { ContactTeamModal } from '@/components/hackathons/team-formation/ContactTeamModal'; +import { Team } from '@/lib/api/hackathons/teams'; +import { useMessages } from '@/components/messages/MessagesProvider'; +import { createConversation } from '@/lib/api/messages'; +import { toast } from 'sonner'; +import type { ApiError } from '@/lib/api/api'; + +const isApiError = (e: unknown): e is ApiError => + e !== null && + typeof e === 'object' && + 'message' in e && + typeof (e as ApiError).message === 'string'; + +const FindTeam = () => { + const { slug } = useParams<{ slug: string }>(); + const { data: hackathon } = useHackathon(slug); + const { data: myTeam, isLoading: isMyTeamLoading } = useMyTeam(slug); + const { openMessages } = useMessages(); + + const handleMessageLeader = useCallback( + async (team: Team, trigger: HTMLElement) => { + const leaderId = team.leader?.id; + if (!leaderId) return; + try { + const { conversation } = await createConversation(leaderId); + openMessages({ conversationId: conversation.id, trigger }); + } catch (err) { + const msg = isApiError(err) + ? err.message + : 'Failed to start conversation'; + toast.error(msg); + } + }, + [openMessages] + ); + + const [searchQuery, setSearchQuery] = useState(''); + const [categoryFilter, setCategoryFilter] = useState('All Categories'); + const [roleFilter, setRoleFilter] = useState('Role'); + const [page, setPage] = useState(1); + const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + const [selectedTeam, setSelectedTeam] = useState(null); + const [isContactModalOpen, setIsContactModalOpen] = useState(false); + + const { data: teamsResponse, isLoading: isTeamsLoading } = useHackathonTeams( + slug, + { + page, + limit: 12, + search: searchQuery, + category: + categoryFilter !== 'All Categories' ? categoryFilter : undefined, + role: roleFilter !== 'Role' ? roleFilter : undefined, + openOnly: true, + }, + !!hackathon?.id && hackathon.participantType !== 'INDIVIDUAL' + ); + + const teams = teamsResponse?.data?.teams || []; + + const handleJoin = (team: Team) => { + setSelectedTeam(team); + setIsContactModalOpen(true); + }; + + if (!hackathon) return null; + + const isIndividualOnly = hackathon.participantType === 'INDIVIDUAL'; + + if (myTeam) { + return ( + + + + ); + } + + return ( + + {isIndividualOnly ? ( +
+
+ +
+
+

+ Individual Participants Only +

+

+ This hackathon is for individual builders only. Team formation is + not allowed for this event. +

+
+
+ ) : ( + <> +
+
+

Open Teams

+

+ Find builders to collaborate with on your project. +

+
+ {!myTeam && ( + } + iconPosition='left' + className='h-11 rounded-xl px-6 font-bold' + onClick={() => setIsCreateModalOpen(true)} + > + Create Team + + )} +
+ +
+
+ + { + setSearchQuery(e.target.value); + setPage(1); + }} + className='focus:border-primary/20 h-12 w-full rounded-xl border border-white/5 bg-[#141517]/50 pr-4 pl-12 text-sm text-white placeholder-gray-500 transition-all outline-none' + /> +
+ +
+ + + + + + { + setCategoryFilter('All Categories'); + setPage(1); + }} + > + All Categories + + {hackathon.categories?.map(cat => ( + { + setCategoryFilter(cat); + setPage(1); + }} + > + {cat} + + ))} + + + + + + + + + { + setRoleFilter('Role'); + setPage(1); + }} + > + All Roles + + {[ + 'Frontend', + 'Backend', + 'Smart Contract', + 'UI/UX', + 'Rust', + ].map(role => ( + { + setRoleFilter(role); + setPage(1); + }} + > + {role} + + ))} + + +
+
+ + {isTeamsLoading || isMyTeamLoading ? ( +
+ +

+ Loading Teams... +

+
+ ) : teams.length > 0 ? ( +
+ {teams.map(team => ( + handleJoin(team)} + onMessageLeader={handleMessageLeader} + /> + ))} +
+ ) : ( +
+
+ +
+
+

+ No Teams Found +

+

+ Be the first to start a revolution! Create a team and invite + builders to join your journey. +

+
+
+ )} + + + + + + )} +
+ ); +}; + +export default FindTeam; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/Overview.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/Overview.tsx new file mode 100644 index 00000000..c1861629 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/Overview.tsx @@ -0,0 +1,274 @@ +'use client'; + +import { useParams } from 'next/navigation'; +import { useHackathon } from '@/hooks/hackathon/use-hackathon-queries'; +import { TabsContent } from '@/components/ui/tabs'; +import { Info, Target, Clock, Trophy, ChevronRight } from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { cn } from '@/lib/utils'; + +import { useMarkdown } from '@/hooks/use-markdown'; + +const Overview = () => { + const { slug } = useParams<{ slug: string }>(); + const { data: hackathon } = useHackathon(slug); + + const { styledContent, loading: markdownLoading } = useMarkdown( + hackathon?.description || '', + { + breaks: true, + gfm: true, + } + ); + + if (!hackathon) return null; + + const now = new Date(); + + interface RawTimelineItem { + label: string; + date?: string; + description?: string; + } + + interface TimelineItem { + label: string; + date: string; + description?: string; + } + + const rawTimelineItems: RawTimelineItem[] = [ + { + label: 'Registration Opens', + date: hackathon.createdAt, + description: 'Sign up and start brainstorming your project.', + }, + { + label: 'Registration Deadline', + date: hackathon.registrationDeadline, + description: 'Last chance to join and form your team.', + }, + { + label: 'Hackathon Starts', + date: hackathon.startDate, + description: 'The hacking phase begins! Start building.', + }, + { + label: 'Submission Deadline', + date: hackathon.submissionDeadline, + description: 'Final project submission and demo video due.', + }, + { + label: 'Judging Ends', + date: hackathon.judgingDeadline, + description: 'Winners will be announced soon after.', + }, + ]; + + const timelineItems: TimelineItem[] = rawTimelineItems.filter( + (item): item is TimelineItem => !!item.date + ); + + // Determine current active milestone + const getStatus = (itemDate: string, index: number) => { + const d = new Date(itemDate); + const nextItem = timelineItems[index + 1]; + const nextD = nextItem ? new Date(nextItem.date) : null; + + if (now > d && (!nextD || now < nextD)) { + return 'active'; + } + if (now > d) { + return 'completed'; + } + return 'upcoming'; + }; + + return ( + +
+

Overview

+

+ Everything you need to know about this hackathon. +

+
+ + {/* About Section */} +
+
+

About the Hackathon

+
+
+ {markdownLoading ? ( +
+
+ Loading description... +
+ ) : ( + styledContent + )} +
+
+ + {/* Tracks & Focus Areas +
+
+ +

Tracks & Focus Areas

+
+
+ {(hackathon.categories.length > 0 + ? hackathon.categories + : ['DeFi 2.0', 'Infrastructure', 'Tooling', 'Public Goods'] + ).map((track, i) => ( +
+

{track}

+

+ Focus on {track.toLowerCase()} innovations, scalability, and + user-centric decentralized applications. +

+
+ ))} +
+
*/} + + {/* Timeline Section */} +
+
+

Timeline

+
+
+ {/* Vertical Line */} +
+ + {timelineItems.map((item, i) => { + const status = getStatus(item.date, i); + const formattedDate = new Date(item.date).toLocaleDateString( + 'en-US', + { + month: 'long', + day: 'numeric', + year: 'numeric', + } + ); + + return ( +
+
+ {status === 'active' && ( +
+ )} +
+ +
+

+ {item.label} + {status === 'active' && ' (Current)'} +

+

+ {formattedDate} +

+ {item.description && ( +

+ {item.description} +

+ )} +
+
+ ); + })} +
+
+ + {/* Prizes Section */} +
+
+

Prizes

+
+
+ {hackathon.prizeTiers.map((tier, i) => ( +
+ {i === 0 && ( + + Top Tier + + )} + +
+ +
+ +

+ {tier.name || + (i === 0 ? '1st Place' : i === 1 ? '2nd Place' : '3rd Place')} +

+
+ + {Number(tier.prizeAmount).toLocaleString()} + + + {tier.currency || 'USDC'} + +
+ + {tier.description && ( +

+ {tier.description} +

+ )} +
+ ))} +
+
+
+ ); +}; + +export default Overview; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx new file mode 100644 index 00000000..e935ee23 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/Participants.tsx @@ -0,0 +1,271 @@ +'use client'; + +import React, { useCallback, useState, useMemo } from 'react'; +import { useParams } from 'next/navigation'; +import { + useHackathon, + useHackathonParticipants, +} from '@/hooks/hackathon/use-hackathon-queries'; +import { TabsContent } from '@/components/ui/tabs'; +import { ChevronDown, Search, Filter, Loader2 } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import ParticipantCard from './participants/ParticipantCard'; +import { Participant } from '@/types/hackathon/participant'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { useMessages } from '@/components/messages/MessagesProvider'; +import { createConversation } from '@/lib/api/messages'; +import { toast } from 'sonner'; +import type { ApiError } from '@/lib/api/api'; + +const isApiError = (e: unknown): e is ApiError => + e !== null && + typeof e === 'object' && + 'message' in e && + typeof (e as ApiError).message === 'string'; + +const Participants = () => { + const { slug } = useParams<{ slug: string }>(); + const { data: hackathon } = useHackathon(slug); + const { openMessages } = useMessages(); + + const handleMessageParticipant = useCallback( + async (userId: string, trigger: HTMLElement) => { + try { + const { conversation } = await createConversation(userId); + openMessages({ conversationId: conversation.id, trigger }); + } catch (err) { + const msg = isApiError(err) + ? err.message + : 'Failed to start conversation'; + toast.error(msg); + } + }, + [openMessages] + ); + + // State for filtering and pagination + const [statusFilter, setStatusFilter] = useState< + 'all' | 'submitted' | 'in_progress' + >('all'); + const [skillFilter, setSkillFilter] = useState('all'); + const [page, setPage] = useState(1); + const [accumulatedParticipants, setAccumulatedParticipants] = useState< + Participant[] + >([]); + const limit = 12; + + const { + data: participantsData, + isLoading, + isFetching, + } = useHackathonParticipants(slug, { + page, + limit, + status: statusFilter === 'all' ? undefined : statusFilter, + skill: skillFilter === 'all' ? undefined : skillFilter, + }); + + // Reset page and list when filters change + React.useEffect(() => { + setPage(1); + setAccumulatedParticipants([]); + }, [statusFilter, skillFilter]); + + // Accumulate participants as they are fetched + React.useEffect(() => { + if (participantsData?.participants) { + if (page === 1) { + setAccumulatedParticipants(participantsData.participants); + } else { + setAccumulatedParticipants(prev => [ + ...prev, + ...participantsData.participants, + ]); + } + } + }, [participantsData?.participants, page]); + + // Dynamically derive unique skills from participants + const availableSkills = useMemo(() => { + const skillsSet = new Set(); + // Try to get skills from all participants fetchable (if we had them all) + // For now, we derive from what we have in the current view or mock if empty + accumulatedParticipants.forEach((p: Participant) => { + p.user.profile.skills?.forEach((s: string) => { + skillsSet.add(s); + }); + }); + + if (skillsSet.size === 0) { + return [ + 'Solidity', + 'Rust', + 'React', + 'TypeScript', + 'Python', + 'Go', + 'Design', + ]; + } + return Array.from(skillsSet).sort(); + }, [accumulatedParticipants]); + + if (!hackathon) return null; + + const totalBuilders = participantsData?.pagination?.total || 0; + const hasNextPage = participantsData?.pagination?.hasNext || false; + + const handleLoadMore = () => { + if (hasNextPage) { + setPage(prev => prev + 1); + } + }; + + return ( + + {/* Header with Count and Filters */} +
+
+

Participants

+

+ + {totalBuilders.toLocaleString()} + {' '} + builders competing in {hackathon.name} +

+
+ +
+ {/* Skills Filter */} + + + + + + setSkillFilter('all')}> + All Skills + + {availableSkills.map(skill => ( + setSkillFilter(skill)} + > + {skill} + + ))} + + + + {/* Status Filter */} + + + + + + setStatusFilter('all')}> + All Statuses + + setStatusFilter('submitted')}> + Submitted + + setStatusFilter('in_progress')}> + In Progress + + + +
+
+ + {/* Grid of Participant Cards */} +
+ {isLoading && page === 1 ? ( + Array.from({ length: 6 }).map((_, i) => ( +
+ )) + ) : accumulatedParticipants.length > 0 ? ( + accumulatedParticipants.map(p => ( + + handleMessageParticipant(p.userId ?? p.user.id, trigger) + } + /> + )) + ) : ( +
+ +

No builders found

+

+ Try adjusting your filters to find more participants. +

+
+ )} +
+ + {/* Load More Button */} + {hasNextPage && ( +
+ +
+ )} + + ); +}; + +export default Participants; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/ResourcesTab.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/ResourcesTab.tsx new file mode 100644 index 00000000..aec05d6e --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/ResourcesTab.tsx @@ -0,0 +1,14 @@ +'use client'; + +import { TabsContent } from '@/components/ui/tabs'; +import { ResourcesList } from './resources/index'; + +const ResourcesTab = () => { + return ( + + + + ); +}; + +export default ResourcesTab; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/Submissions.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/Submissions.tsx new file mode 100644 index 00000000..19a8f37a --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/Submissions.tsx @@ -0,0 +1,248 @@ +'use client'; + +import React, { useState, useMemo } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { + useHackathon, + useExploreSubmissions, +} from '@/hooks/hackathon/use-hackathon-queries'; +import { ExploreSubmissionsResponse } from '@/lib/api/hackathons'; +import { TabsContent } from '@/components/ui/tabs'; +import { + Search, + ChevronDown, + ChevronLeft, + ChevronRight, + Loader2, + Sparkles, +} from 'lucide-react'; +import SubmissionCard from './submissions/SubmissionCard'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { cn } from '@/lib/utils'; + +const Submissions = () => { + const { slug } = useParams<{ slug: string }>(); + const router = useRouter(); + const { data: hackathon } = useHackathon(slug); + + // State for filtering and pagination + const [searchQuery, setSearchQuery] = useState(''); + const [trackFilter, setTrackFilter] = useState('All Projects'); + const [statusFilter, setStatusFilter] = useState('Status'); + const [page, setPage] = useState(1); + const limit = 12; + + const { data: submissionsData, isLoading } = useExploreSubmissions( + hackathon?.id || '', + { + page, + limit, + search: searchQuery, + category: trackFilter === 'All Projects' ? undefined : trackFilter, + status: statusFilter === 'Status' ? undefined : statusFilter, + }, + !!hackathon?.id + ); + + const submissions = submissionsData?.submissions || []; + const pagination = submissionsData?.pagination || { + total: 0, + totalPages: 0, + hasNext: false, + hasPrev: false, + }; + + if (!hackathon) return null; + + const tracks = ['All Projects', ...(hackathon.categories || [])]; + + const handlePageChange = (newPage: number) => { + setPage(newPage); + // Scroll to top of tab content if needed + }; + + return ( + +
+

Explore Submissions

+

+ Browse projects submitted by our community of builders. +

+
+ + {/* Header with Search and Filters */} +
+ {/* Search Bar */} +
+ + { + setSearchQuery(e.target.value); + setPage(1); // Reset to page 1 on search + }} + className='focus:border-primary/20 h-12 w-full rounded-xl border border-white/5 bg-[#141517] pr-4 pl-12 text-sm text-white placeholder-gray-500 transition-all outline-none focus:bg-[#1a1b1e]' + /> +
+ + {/* Filters */} +
+ + + + + + {tracks.map(track => ( + { + setTrackFilter(track); + setPage(1); + }} + className='cursor-pointer hover:bg-white/5' + > + {track} + + ))} + + + + + + + + + {['Status', 'Submitted', 'Shortlisted'].map(status => ( + { + setStatusFilter(status); + setPage(1); + }} + className='cursor-pointer hover:bg-white/5' + > + {status} + + ))} + + +
+
+ + {/* Grid of Submission Cards */} + {isLoading ? ( +
+ +

+ Loading Submissions... +

+
+ ) : submissions.length > 0 ? ( + <> +
+ {submissions.map((sub: ExploreSubmissionsResponse) => ( + + ))} +
+ + {/* Pagination */} + {pagination.totalPages > 1 && ( +
+ + + {Array.from({ length: Math.min(pagination.totalPages, 5) }).map( + (_, i) => { + // Simplistic pagination display logic + const pageNum = i + 1; + const isActive = pageNum === page; + return ( + + ); + } + )} + + {pagination.totalPages > 5 && ( + ... + )} + + {pagination.totalPages > 5 && ( + + )} + + +
+ )} + + ) : ( +
+
+ +
+
+

+ No Submissions Found +

+

+ Try adjusting your search or filters to find projects. +

+
+
+ )} +
+ ); +}; + +export default Submissions; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/Winners.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/Winners.tsx new file mode 100644 index 00000000..ca81fd16 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/Winners.tsx @@ -0,0 +1,98 @@ +'use client'; + +import { TabsContent } from '@/components/ui/tabs'; +import { useHackathonData } from '@/lib/providers/hackathonProvider'; +import { MainStageHeader } from './winners/MainStageHeader'; +import { TopWinnerCard } from './winners/TopWinnerCard'; +import { PodiumWinnerCard } from './winners/PodiumWinnerCard'; +import { GeneralWinnerCard } from './winners/GeneralWinnerCard'; +import { Trophy } from 'lucide-react'; + +const Winners = () => { + const { currentHackathon, winners, submissions } = useHackathonData(); + + if (!winners || winners.length === 0) { + return ( + +
+ +

+ Winners Coming Soon +

+

+ The judging phase is still in progress. Check back soon for the + results. +

+
+
+ ); + } + + // Sort winners by rank + const sortedWinners = [...winners].sort((a, b) => a.rank - b.rank); + + const rank1 = sortedWinners.find(w => w.rank === 1); + const podium = sortedWinners.filter(w => w.rank === 2 || w.rank === 3); + const others = sortedWinners.filter(w => w.rank > 3); + + // Helper to find submission for a winner + const getSubmission = (submissionId: string) => { + return submissions.find(s => s._id === submissionId); + }; + + return ( + +
+ + +
+ {rank1 && ( + + )} + + {podium.length > 0 && ( +
+ {podium.map(winner => ( + + ))} +
+ )} + + {others.length > 0 && ( +
+
+
+ + Honorable Mentions + +
+
+ +
+ {others.map(winner => ( + + ))} +
+
+ )} +
+
+ + ); +}; + +export default Winners; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/announcementCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/announcementCard.tsx new file mode 100644 index 00000000..0942b8d1 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/announcementCard.tsx @@ -0,0 +1,123 @@ +'use client'; + +import { format } from 'date-fns'; +import { ChevronRight, Pin } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { HackathonAnnouncement } from '@/lib/api/types'; +import BasicAvatar from '@/components/avatars/BasicAvatar'; +import { Button } from '@/components/ui/button'; +import { useParams, useRouter } from 'next/navigation'; + +interface AnnouncementCardProps { + announcement: HackathonAnnouncement; +} + +export function AnnouncementCard({ announcement }: AnnouncementCardProps) { + const { slug } = useParams<{ slug: string }>(); + const router = useRouter(); + + const handleNavigate = () => { + router.push(`/hackathons/${slug}/announcements/${announcement.id}`); + }; + + // Determine a category label based on content keywords since API doesn't have a direct category field + const getCategoryLabel = (title: string, content: string) => { + const text = (title + ' ' + content).toLowerCase(); + if ( + text.includes('deadline') || + text.includes('requirement') || + text.includes('extension') + ) + return { + label: 'IMPORTANT', + className: 'bg-red-500/10 text-red-500 border-red-500/20', + }; + if ( + text.includes('api') || + text.includes('technical') || + text.includes('endpoint') || + text.includes('dev') + ) + return { + label: 'TECHNICAL', + className: 'bg-lime-500/10 text-lime-500 border-lime-500/20', + }; + if ( + text.includes('mixer') || + text.includes('social') || + text.includes('community') || + text.includes('event') + ) + return { + label: 'EVENT', + className: 'bg-blue-500/10 text-blue-500 border-blue-500/20', + }; + return { + label: 'UPDATE', + className: 'bg-gray-500/10 text-gray-500 border-gray-500/20', + }; + }; + + const category = getCategoryLabel(announcement.title, announcement.content); + + return ( +
+
+
+ + {category.label} + + + {format(new Date(announcement.createdAt), 'MMM d, yyyy')} •{' '} + {format(new Date(announcement.createdAt), 'h:mm aa')} + +
+ {announcement.isPinned && ( + + )} +
+ +
+

+ {announcement.title} +

+

+ {announcement.content} +

+
+ +
+
+ +
+ + +
+
+ ); +} diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/header.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/header.tsx new file mode 100644 index 00000000..624f9625 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/header.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { cn } from '@/lib/utils'; + +interface HeaderProps { + activeFilter: string; + onFilterChange: (filter: string) => void; +} + +const filters = [ + { label: 'All', id: 'all' }, + { label: 'Technical', id: 'technical' }, + { label: 'Logistics', id: 'logistics' }, + { label: 'Socials', id: 'socials' }, +]; + +export function AnnouncementsHeader({ + activeFilter, + onFilterChange, +}: HeaderProps) { + return ( +
+
+

Announcements

+

Stay updated with the latest news.

+
+ +
+ {filters.map(filter => ( + + ))} +
+
+ ); +} diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/index.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/index.tsx new file mode 100644 index 00000000..f98cad03 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/announcements/index.tsx @@ -0,0 +1,96 @@ +'use client'; + +import { useState, useMemo } from 'react'; +import { useParams } from 'next/navigation'; +import { useHackathonAnnouncements } from '@/hooks/hackathon/use-hackathon-queries'; +import { AnnouncementsHeader } from './header'; +import { AnnouncementCard } from './announcementCard'; +import { Megaphone } from 'lucide-react'; +import { Skeleton } from '@/components/ui/skeleton'; + +export default function AnnouncementsIndex() { + const { slug } = useParams<{ slug: string }>(); + const { data: announcements, isLoading } = useHackathonAnnouncements(slug); + const [activeFilter, setActiveFilter] = useState('all'); + + const filteredAnnouncements = useMemo(() => { + if (!announcements) return []; + if (activeFilter === 'all') return announcements; + + return announcements.filter(announcement => { + const text = ( + announcement.title + + ' ' + + announcement.content + ).toLowerCase(); + if (activeFilter === 'technical') + return ( + text.includes('api') || + text.includes('technical') || + text.includes('endpoint') + ); + if (activeFilter === 'logistics') + return ( + text.includes('deadline') || + text.includes('requirement') || + text.includes('extension') + ); + if (activeFilter === 'socials') + return ( + text.includes('mixer') || + text.includes('social') || + text.includes('community') + ); + return true; + }); + }, [announcements, activeFilter]); + + return ( +
+ + +
+ {isLoading ? ( + Array.from({ length: 3 }).map((_, i) => ( +
+
+ + +
+ +
+ + +
+
+ )) + ) : filteredAnnouncements.length > 0 ? ( + filteredAnnouncements.map(announcement => ( + + )) + ) : ( +
+
+ +
+

+ No announcements found +

+

+ We couldn't find any announcements for this category. +

+
+ )} +
+
+ ); +} diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/index.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/index.tsx new file mode 100644 index 00000000..e69de29b diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/participants/ParticipantCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/participants/ParticipantCard.tsx new file mode 100644 index 00000000..b1b6be1b --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/participants/ParticipantCard.tsx @@ -0,0 +1,95 @@ +'use client'; + +import React from 'react'; +import BasicAvatar from '@/components/avatars/BasicAvatar'; +import { Badge } from '@/components/ui/badge'; +import { BoundlessButton } from '@/components/buttons'; +import { IconMessage } from '@tabler/icons-react'; +import { cn } from '@/lib/utils'; + +interface ParticipantCardProps { + name: string; + username: string; + image?: string; + submitted?: boolean; + skills?: string[]; + userId?: string; + onMessage?: (trigger: HTMLElement) => void; +} + +const ParticipantCard = ({ + name, + username, + image, + submitted, + skills = [], + userId, + onMessage, +}: ParticipantCardProps) => { + const visibleSkills = skills.slice(0, 3); + const hiddenSkillsCount = skills.length - visibleSkills.length; + + return ( +
+ ); +}; + +export default ParticipantCard; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/ResourceCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/ResourceCard.tsx new file mode 100644 index 00000000..0c91654e --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/ResourceCard.tsx @@ -0,0 +1,107 @@ +'use client'; + +import React from 'react'; +import { cn } from '@/lib/utils'; +import { + LucideIcon, + ArrowRight, + ExternalLink, + Settings, + Download, +} from 'lucide-react'; + +export interface ResourceCardProps { + title: string; + description: string; + icon: LucideIcon; + actionText: string; + actionHref?: string; + isComingSoon?: boolean; + type?: 'read' | 'repo' | 'download' | 'watch' | 'config'; +} + +export const ResourceCard = ({ + title, + description, + icon: Icon, + actionText, + actionHref, + isComingSoon = false, + type = 'read', +}: ResourceCardProps) => { + if (isComingSoon) { + return ( +
+
+ +
+
+

{title}

+

+ {description} +

+
+
+ ); + } + + const getActionIcon = () => { + switch (type) { + case 'read': + case 'repo': + return ( + + ); + case 'download': + return ; + case 'watch': + return ; + case 'config': + return ; + default: + return ; + } + }; + + const cardContent = ( + <> +
+
+ +
+
+

{title}

+

+ {description} +

+
+
+ +
+ {actionText} + {getActionIcon()} +
+ + ); + + const containerClassName = + 'group flex flex-col justify-between gap-8 rounded-3xl border border-white/5 bg-[#0D0E10] p-8 transition-all duration-300 hover:border-primary/30 hover:bg-[#141517]'; + + if (actionHref) { + return ( +
+ {cardContent} + + ); + } + + return
{cardContent}
; +}; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/header.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/header.tsx new file mode 100644 index 00000000..582a4e74 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/header.tsx @@ -0,0 +1,44 @@ +'use client'; + +import React from 'react'; +import { cn } from '@/lib/utils'; + +interface ResourceHeaderProps { + activeTab: string; + setActiveTab: (tab: string) => void; +} + +const tabs = ['All', 'Technical', 'Design', 'Media']; + +export const ResourceHeader = ({ + activeTab, + setActiveTab, +}: ResourceHeaderProps) => { + return ( +
+
+

Developer Resources

+

+ Everything you need to build your project on Boundless. +

+
+ +
+ {tabs.map(tab => ( + + ))} +
+
+ ); +}; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/index.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/index.tsx new file mode 100644 index 00000000..34a3e58b --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/resources/index.tsx @@ -0,0 +1,175 @@ +'use client'; + +import React, { useState } from 'react'; +import { + BookOpen, + Code2, + Palette, + Play, + Server, + Box, + ShieldCheck, + Layers, + Lock, + Calendar, +} from 'lucide-react'; +import { ResourceHeader } from './header'; +import { ResourceCard } from './ResourceCard'; +import { BoundlessButton } from '@/components/buttons/BoundlessButton'; +import { cn } from '@/lib/utils'; +import { useParams } from 'next/navigation'; +import { useHackathon } from '@/hooks/hackathon/use-hackathon-queries'; +import { Skeleton } from '@/components/ui/skeleton'; +import { type HackathonResourceItem } from '@/lib/api/hackathons'; +import { ResourceCardProps } from './ResourceCard'; + +interface MappedResource extends ResourceCardProps { + category: string; +} + +// Helper to map API resources to UI format +const mapApiResource = (resource: HackathonResourceItem): MappedResource => { + const content = ( + (resource.description || '') + + ' ' + + (resource.file?.name || '') + + ' ' + + (resource.link || '') + ).toLowerCase(); + + const isDoc = + content.includes('doc') || + content.includes('guide') || + content.includes('trustlesswork.com'); + const isSdk = + content.includes('sdk') || + content.includes('api') || + content.includes('git') || + content.includes('repo'); + const isDesign = + content.includes('design') || + content.includes('figma') || + content.includes('brand') || + content.includes('asset'); + const isMedia = + content.includes('video') || + content.includes('tutorial') || + content.includes('youtube') || + content.includes('play'); + + let icon = Box; + let category = 'All'; + let type: 'read' | 'repo' | 'download' | 'watch' | 'config' = 'read'; + let actionText = 'View Resource'; + + if (isDoc) { + icon = BookOpen; + category = 'Technical'; + actionText = 'Read Docs'; + type = 'read'; + } else if (isSdk) { + icon = Code2; + category = 'Technical'; + actionText = 'View Repository'; + type = 'repo'; + } else if (isDesign) { + icon = Palette; + category = 'Design'; + actionText = 'Download Kit'; + type = 'download'; + } else if (isMedia) { + icon = Play; + category = 'Media'; + actionText = 'Watch Now'; + type = 'watch'; + } + + // Deriving title + let title = resource.file?.name; + if (!title && resource.link) { + try { + const url = new URL(resource.link); + title = url.hostname.replace('www.', ''); + if (title.includes('docs.')) { + title = 'Documentation'; + } else if (title.includes('github.com')) { + title = 'GitHub Repository'; + } else if (title.includes('figma.com')) { + title = 'Design Assets'; + } else if (title.includes('youtube.com') || title.includes('youtu.be')) { + title = 'Video Tutorial'; + } + } catch { + title = 'External Resource'; + } + } + + if (!title && resource.description) { + title = resource.description.split('\n')[0].substring(0, 40); + } + + return { + title: title || 'Untitled Resource', + description: + resource.description || + `Access the ${title || 'resource'} via the link below.`, + icon, + actionText, + actionHref: resource.link || resource.file?.url, + type, + category, + }; +}; + +export const ResourcesList = () => { + const { slug } = useParams() as { slug: string }; + const { data: hackathon, isLoading } = useHackathon(slug); + const [activeTab, setActiveTab] = useState('All'); + + if (isLoading) { + return ( +
+ {[1, 2, 3].map(i => ( + + ))} +
+ ); + } + + const apiResources: MappedResource[] = + hackathon?.resources?.map(mapApiResource) || []; + + // Add "More Coming Soon" if there are fewer than 3 resources + if (apiResources.length < 3) { + apiResources.push({ + title: 'More Coming Soon', + description: 'New resources and tools are being added to help you build.', + icon: Box, + actionText: '', + actionHref: '#', + type: 'read', + category: 'All', + isComingSoon: true, + }); + } + + const filteredResources = apiResources.filter( + (r: MappedResource) => + activeTab === 'All' || r.category === activeTab || r.isComingSoon + ); + + return ( +
+ + +
+ {filteredResources.map((resource: MappedResource, idx: number) => ( + + ))} +
+
+ ); +}; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx new file mode 100644 index 00000000..25fc73ef --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx @@ -0,0 +1,93 @@ +import { LayoutGrid } from 'lucide-react'; +import GroupAvatar from '@/components/avatars/GroupAvatar'; +import BasicAvatar from '@/components/avatars/BasicAvatar'; +import { BoundlessButton } from '@/components/buttons/BoundlessButton'; +import type { ExploreSubmissionsResponse } from '@/lib/api/hackathons'; + +interface SubmissionCardProps { + submission: ExploreSubmissionsResponse; +} + +const SubmissionCard = ({ submission }: SubmissionCardProps) => { + const { + id, + projectName, + description, + category, + participationType = 'INDIVIDUAL', + teamName, + teamMembers = [], + participant, + logo, + } = submission; + + const isTeam = participationType?.toUpperCase() === 'TEAM'; + + const submitterName = isTeam + ? (teamName ?? teamMembers?.[0]?.name ?? 'Unnamed Team') + : (participant?.name ?? 'Anonymous'); + + const submitterAvatar = isTeam + ? (teamMembers?.[0]?.avatar ?? '') + : (participant?.image ?? ''); + + const projectUrl = `/projects/${id}?type=submission`; + + return ( +
+ {/* Project Icon/Logo */} +
+ +
+ + {/* Project Info */} +
+

+ {projectName} +

+

+ {description} +

+ + {/* Tags/Categories */} +
+ + {category} + + {submission.category && ( + + {submission.category} + + )} +
+
+ + {/* Footer: Avatars + View Button */} +
+
+ {isTeam ? ( + m.avatar ?? '')} /> + ) : ( + + )} +
+ + + + View Project + + +
+
+ ); +}; + +export default SubmissionCard; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/teams/MyTeamView.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/teams/MyTeamView.tsx new file mode 100644 index 00000000..0932327a --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/teams/MyTeamView.tsx @@ -0,0 +1,393 @@ +'use client'; + +import React, { useState } from 'react'; +import { + Users, + UserPlus, + Settings, + LogOut, + Crown, + ShieldCheck, + Briefcase, +} from 'lucide-react'; +import { Team, TeamMember } from '@/lib/api/hackathons/teams'; +import { + useLeaveTeam, + useInviteToTeam, + useInvitationActions, + useTransferLeadership, + useRefreshHackathon, +} from '@/hooks/hackathon/use-hackathon-queries'; +import { BoundlessButton } from '@/components/buttons/BoundlessButton'; +import BasicAvatar from '@/components/avatars/BasicAvatar'; +import { useOptionalAuth } from '@/hooks/use-auth'; +import { useRequireAuthForAction } from '@/hooks/use-require-auth-for-action'; +import { getUserProfileByUsername } from '@/lib/api/auth'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog'; +import { CreateTeamPostModal } from '@/components/hackathons/team-formation/CreateTeamPostModal'; + +interface MyTeamViewProps { + team: Team; + hackathonSlug: string; +} + +const MyTeamView = ({ team, hackathonSlug }: MyTeamViewProps) => { + const { user } = useOptionalAuth(); + const isLeader = team.leader.id === user?.id; + + const leaveMutation = useLeaveTeam(hackathonSlug); + const inviteMutation = useInviteToTeam(hackathonSlug); + const transferMutation = useTransferLeadership(hackathonSlug); + const refresh = useRefreshHackathon(hackathonSlug); + const { withAuth } = useRequireAuthForAction(); + + const [inviteIdentifier, setInviteIdentifier] = useState(''); + const [inviteMessage, setInviteMessage] = useState(''); + const [isVerifying, setIsVerifying] = useState(false); + const [verificationError, setVerificationError] = useState( + null + ); + const [isEditModalOpen, setIsEditModalOpen] = useState(false); + const [isLeaveDialogOpen, setIsLeaveDialogOpen] = useState(false); + const [isTransferDialogOpen, setIsTransferDialogOpen] = useState(false); + const [selectedMember, setSelectedMember] = useState<{ + id: string; + name: string; + } | null>(null); + + const handleLeave = withAuth(async () => { + await leaveMutation.mutateAsync(team.id); + setIsLeaveDialogOpen(false); + }); + + const handleInvite = async (e: React.FormEvent) => { + e.preventDefault(); + if (!inviteIdentifier) return; + + setIsVerifying(true); + setVerificationError(null); + + // 1. Verify user exists first + try { + const profile = await getUserProfileByUsername(inviteIdentifier); + if (!profile) { + setVerificationError('User not found. Please check the username.'); + setIsVerifying(false); + return; + } + } catch (err: any) { + setVerificationError( + err.response?.status === 404 + ? 'User not found. Please check the username.' + : 'Failed to verify user. Please try again.' + ); + setIsVerifying(false); + return; + } + + // 2. Send invitation + try { + await inviteMutation.mutateAsync({ + teamId: team.id, + inviteeIdentifier: inviteIdentifier, + message: inviteMessage, + }); + + // Only clear inputs and errors on successful invite + setInviteIdentifier(''); + setInviteMessage(''); + setVerificationError(null); + } catch (err: any) { + const errorMessage = + err.response?.data?.message || + err.message || + 'Failed to send invitation. Please try again.'; + setVerificationError(errorMessage); + } finally { + setIsVerifying(false); + } + }; + + const handleTransfer = async () => { + if (!selectedMember) return; + await transferMutation.mutateAsync({ + teamId: team.id, + newLeaderId: selectedMember.id, + }); + setIsTransferDialogOpen(false); + setSelectedMember(null); + }; + + return ( +
+ {/* Team Header */} +
+
+
+ {team.teamName.charAt(0).toUpperCase()} +
+
+

+ {team.teamName} +

+
+ + {team.memberCount} / {team.maxSize} Members + + + + {team.isOpen ? 'Open for Recruitment' : 'Closed'} + +
+

+ {team.description} +

+ + {/* Roles Needed inside Header */} + {team.lookingFor && team.lookingFor.length > 0 && ( +
+ {team.lookingFor.map((roleObj, idx) => ( +
+ {typeof roleObj === 'string' ? roleObj : roleObj.role} +
+ ))} +
+ )} +
+
+ +
+ {isLeader ? ( + setIsEditModalOpen(true)} + > + Edit Team + + ) : ( + setIsLeaveDialogOpen(true)} + loading={leaveMutation.isPending} + > + Leave Team + + )} +
+
+ +
+ {/* Invite Builders Section (Horizontal) */} + {isLeader && ( +
+
+
+
+ +

+ Invite Builders +

+
+
+
+ + { + setInviteIdentifier(e.target.value); + setVerificationError(null); + }} + className='focus:border-primary/50 w-full rounded-xl border border-white/10 bg-white/5 p-4 font-mono text-sm text-white placeholder-gray-500 transition-all outline-none' + /> + {verificationError && ( +

+ {verificationError} +

+ )} +
+
+ + setInviteMessage(e.target.value)} + className='focus:border-primary/50 w-full rounded-xl border border-white/10 bg-white/5 p-4 text-sm text-white placeholder-gray-500 transition-all outline-none' + /> +
+
+
+ + Send Invitation + +
+
+ )} + + {/* Member list - Full Width */} +
+
+

+ Team Members +

+
+ +
+ {/* Leader Card */} +
+
+
+ +
+
+
+ + Leader + +
+
+
+ {isLeader && ( + + )} +
+ + {/* Other Members */} + {Array.isArray(team.members) && + team.members + .filter( + (m): m is TeamMember => + typeof m !== 'string' && m.userId !== team.leader.id + ) + .map(member => ( +
+
+
+
+ +
+
+
+ {isLeader && ( + { + setSelectedMember({ + id: member.userId, + name: member.name, + }); + setIsTransferDialogOpen(true); + }} + loading={transferMutation.isPending} + > + Transfer Lead + + )} +
+ ))} +
+
+
+ + {/* Modals & Dialogs */} + + + + + + Leave Team + + Are you sure you want to leave this team? This action cannot be + undone. + + + + + Cancel + + + Leave Team + + + + + + + + + Transfer Leadership + + Are you sure you want to transfer leadership to{' '} + + {selectedMember?.name} + + ? You will lose leader permissions. + + + + + Cancel + + + Transfer + + + + +
+ ); +}; + +export default MyTeamView; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/teams/TeamCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/teams/TeamCard.tsx new file mode 100644 index 00000000..054fc6d7 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/teams/TeamCard.tsx @@ -0,0 +1,131 @@ +'use client'; + +import React from 'react'; +import { cn } from '@/lib/utils'; +import { Team, TeamMember } from '@/lib/api/hackathons/teams'; +import GroupAvatar from '@/components/avatars/GroupAvatar'; +import { BoundlessButton } from '@/components/buttons/BoundlessButton'; +import { MessageCircle } from 'lucide-react'; + +interface TeamCardProps { + team: Team; + onJoin?: (team: Team) => void; + onMessageLeader?: (team: Team, trigger: HTMLElement) => void; +} + +const TeamCard = ({ team, onJoin, onMessageLeader }: TeamCardProps) => { + const { + teamName, + description, + lookingFor = [], + memberCount, + maxSize, + members = [], + isOpen, + } = team; + + const status = isOpen ? 'ACTIVE' : 'CLOSED'; + const category = 'DEFI'; + + const isTeamMember = (member: string | TeamMember): member is TeamMember => { + return typeof member !== 'string'; + }; + + return ( +
+
+
+
+ {teamName.charAt(0).toUpperCase()} +
+
+

+ {teamName} +

+
+ + {category} + + + {status} + + + {memberCount}/{maxSize} BUILDERS + +
+
+
+ +
+ {onMessageLeader && team.leader?.id && ( + onMessageLeader(team, e.currentTarget)} + aria-label='Message team leader' + > + + Message + + )} + onJoin?.(team)} + disabled={!isOpen || memberCount >= maxSize} + > + Join Team + +
+
+ + {/* Description */} +

+ {description} +

+ + {/* Bottom Section */} +
+

+ ROLES NEEDED +

+
+
+ {lookingFor.slice(0, 3).map((role, idx) => ( + + {typeof role === 'string' ? role : role.role} + + ))} + {lookingFor.length > 3 && ( + + +{lookingFor.length - 3} + + )} + {lookingFor.length === 0 && ( + + Full Team + + )} +
+ (isTeamMember(m) ? (m.image ?? '') : ''))} + /> +
+
+
+ ); +}; + +export default TeamCard; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx new file mode 100644 index 00000000..4f70f751 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/GeneralWinnerCard.tsx @@ -0,0 +1,33 @@ +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { HackathonWinner } from '@/lib/api/hackathons'; +import { SubmissionCardProps } from '@/types/hackathon'; + +interface GeneralWinnerCardProps { + winner: HackathonWinner; + submission?: SubmissionCardProps; +} + +export const GeneralWinnerCard = ({ + winner, + submission, +}: GeneralWinnerCardProps) => { + return ( +
+
+
+ #{winner.rank} +
+
+

+ {winner.projectName} +

+ + {winner.teamName || winner.participants[0]?.username} + +
+
+ +
{winner.prize}
+
+ ); +}; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/MainStageHeader.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/MainStageHeader.tsx new file mode 100644 index 00000000..88e37a65 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/MainStageHeader.tsx @@ -0,0 +1,14 @@ +import { AwardIcon, Star } from 'lucide-react'; + +export const MainStageHeader = () => { + return ( +
+
+ +
+

+ Main Stage Winners +

+
+ ); +}; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx new file mode 100644 index 00000000..2471134c --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/PodiumWinnerCard.tsx @@ -0,0 +1,53 @@ +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { HackathonWinner } from '@/lib/api/hackathons'; +import { Trophy } from 'lucide-react'; +import { SubmissionCardProps } from '@/types/hackathon'; +import Image from 'next/image'; + +interface PodiumWinnerCardProps { + winner: HackathonWinner; + submission?: SubmissionCardProps; +} + +export const PodiumWinnerCard = ({ + winner, + submission, +}: PodiumWinnerCardProps) => { + return ( +
+
+
+ Rank #{winner.rank} +
+
+
+ +
+ {winner.prize} +
+
+ +

+ {winner.projectName} +

+ +

+ {submission?.description || 'No description provided for this project.'} +

+ +
+ + + + {winner.participants[0]?.username.slice(0, 2).toUpperCase()} + + +
+ + {winner.teamName || winner.participants[0]?.username} + +
+
+
+ ); +}; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx new file mode 100644 index 00000000..1f81e040 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/contents/winners/TopWinnerCard.tsx @@ -0,0 +1,102 @@ +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { HackathonWinner } from '@/lib/api/hackathons'; +import { Trophy } from 'lucide-react'; +import Image from 'next/image'; +import { SubmissionCardProps } from '@/types/hackathon'; +import BasicAvatar from '@/components/avatars/BasicAvatar'; + +interface TopWinnerCardProps { + winner: HackathonWinner; + submission?: SubmissionCardProps; +} + +export const TopWinnerCard = ({ winner, submission }: TopWinnerCardProps) => { + const bannerUrl = submission?.logo || '/images/default-project-banner.png'; // Fallback to logo or default + + return ( +
+
+ {/* Project Visual */} +
+ {submission?.logo ? ( + {winner.projectName} + ) : ( +
+ +
+ )} +
+ + {/* Project Info */} +
+
+
+
+ Rank #1 - GRAND PRIZE +
+

+ {winner.projectName} +

+
+ +
+
+ +
+
+ + {winner.prize} + + + USDC DISTRIBUTED + +
+
+
+ +

+ {submission?.description || + 'No description provided for this project.'} +

+ +
+
+ {winner.participants.map((participant, idx) => ( + + // + // + // + // {participant.username.slice(0, 2).toUpperCase()} + // + // + ))} +
+ {/*
+ + {winner.teamName ? 'Team members' : 'Participant'} + + + {winner.teamName + ? winner.teamName + : winner.participants[0]?.username} + +
*/} +
+
+
+
+ ); +}; diff --git a/app/(landing)/hackathons/[slug]/components/tabs/index.tsx b/app/(landing)/hackathons/[slug]/components/tabs/index.tsx new file mode 100644 index 00000000..2360f4e5 --- /dev/null +++ b/app/(landing)/hackathons/[slug]/components/tabs/index.tsx @@ -0,0 +1,222 @@ +'use client'; + +import { useSearchParams, useRouter, useParams } from 'next/navigation'; +import { Tabs } from '@/components/ui/tabs'; +import Lists from './Lists'; +import Overview from './contents/Overview'; +import Participants from './contents/Participants'; +import Submissions from './contents/Submissions'; +import Discussions from './contents/Discussions'; +import Announcements from './contents/AnnouncementsTab'; +import Winners from './contents/Winners'; +import ResourcesTab from './contents/ResourcesTab'; +import FindTeam from './contents/FindTeam'; +import { useEffect, useState, useMemo } from 'react'; +import { useHackathonData } from '@/lib/providers/hackathonProvider'; +import { useHackathonAnnouncements } from '@/hooks/hackathon/use-hackathon-queries'; +import { useCommentSystem } from '@/hooks/use-comment-system'; +import { CommentEntityType } from '@/types/comment'; +import { Megaphone } from 'lucide-react'; + +interface HackathonTabsProps { + sidebar?: React.ReactNode; +} + +const HackathonTabs = ({ sidebar }: HackathonTabsProps) => { + const router = useRouter(); + const searchParams = useSearchParams(); + const params = useParams(); + const slug = params?.slug as string; + + const { + currentHackathon, + winners, + submissions, + loading: generalLoading, + } = useHackathonData(); + const { data: announcements = [], isLoading: announcementsLoading } = + useHackathonAnnouncements(slug, !!slug); + + const { comments: discussionComments } = useCommentSystem({ + entityType: CommentEntityType.HACKATHON, + entityId: currentHackathon?.id || '', + page: 1, + limit: 1, + enabled: !!currentHackathon?.id, + }); + + const [activeTab, setActiveTab] = useState('overview'); + + const hackathonTabs = useMemo(() => { + if (!currentHackathon) return []; + const hasParticipants = currentHackathon._count?.participants > 0; + const hasResources = currentHackathon.resources?.length > 0; + const hasWinners = (winners && winners.length > 0) || generalLoading; + const hasAnnouncements = announcements.length > 0 || announcementsLoading; + + const participantType = currentHackathon.participantType; + const isTeamHackathon = + participantType === 'TEAM' || participantType === 'TEAM_OR_INDIVIDUAL'; + + const tabs = [ + { id: 'overview', label: 'Overview' }, + ...(hasParticipants + ? [ + { + id: 'participants', + label: 'Participants', + badge: currentHackathon._count.participants, + }, + ] + : []), + ...(hasResources + ? [ + { + id: 'resources', + label: 'Resources', + badge: currentHackathon.resources.length, + }, + ] + : []), + ...(hasAnnouncements + ? [ + { + id: 'announcements', + label: 'Announcements', + badge: announcements.length, + icon: Megaphone, + }, + ] + : []), + { + id: 'submissions', + label: 'Submissions', + badge: submissions.filter(p => p.status === 'Approved').length, + }, + { + id: 'discussions', + label: 'Discussions', + badge: discussionComments.pagination.totalItems || 0, + }, + ]; + + if (isTeamHackathon) { + tabs.push({ + id: 'team-formation', + label: 'Find Team', + }); + } + + if (hasWinners) { + tabs.push({ + id: 'winners', + label: 'Winners', + badge: winners.length, + }); + } + + const tabIdToEnabledKey: Record = { + 'team-formation': 'joinATeamTab', + winners: 'winnersTab', + resources: 'resourcesTab', + participants: 'participantsTab', + announcements: 'announcementsTab', + submissions: 'submissionTab', + discussions: 'discussionTab', + }; + + const enabledTabs = currentHackathon.enabledTabs; + + if (Array.isArray(enabledTabs)) { + const enabledSet = new Set(enabledTabs); + return tabs.filter(tab => { + if (tab.id === 'overview') return true; + const key = tabIdToEnabledKey[tab.id] || tab.id; + return enabledSet.has(key); + }); + } + + return tabs; + }, [ + currentHackathon, + winners, + submissions, + discussionComments.pagination.totalItems, + announcements, + announcementsLoading, + generalLoading, + ]); + + useEffect(() => { + if (!currentHackathon) return; + + const tabFromUrl = searchParams.get('tab'); + if (!tabFromUrl) { + setActiveTab('overview'); + return; + } + + if (hackathonTabs.some(tab => tab.id === tabFromUrl)) { + setActiveTab(tabFromUrl); + } else { + // If the tab is not in the list yet, check if it's because we're still loading data + const isKnownTabLoading = + (tabFromUrl === 'announcements' && announcementsLoading) || + (tabFromUrl === 'winners' && generalLoading); + + if (!isKnownTabLoading) { + setActiveTab('overview'); + const queryParams = new URLSearchParams(searchParams.toString()); + queryParams.set('tab', 'overview'); + router.replace(`?${queryParams.toString()}`, { scroll: false }); + } + } + }, [ + searchParams, + hackathonTabs, + router, + currentHackathon, + announcementsLoading, + generalLoading, + ]); + + const handleTabChange = (value: string) => { + setActiveTab(value); + const queryParams = new URLSearchParams(searchParams.toString()); + queryParams.set('tab', value); + router.push(`?${queryParams.toString()}`, { scroll: false }); + }; + + const isTabVisible = (tabId: string) => + hackathonTabs.some(t => t.id === tabId); + + return ( + + +
+
+
+ {isTabVisible('overview') && } + {isTabVisible('participants') && } + {isTabVisible('submissions') && } + {isTabVisible('announcements') && } + {isTabVisible('discussions') && } + {isTabVisible('winners') && } + {isTabVisible('resources') && } + {isTabVisible('team-formation') && } +
+
+ {sidebar} +
+
+
+
+ ); +}; + +export default HackathonTabs; diff --git a/app/(landing)/hackathons/[slug]/page.tsx b/app/(landing)/hackathons/[slug]/page.tsx index 6caeb1d7..f079775a 100644 --- a/app/(landing)/hackathons/[slug]/page.tsx +++ b/app/(landing)/hackathons/[slug]/page.tsx @@ -4,6 +4,10 @@ import { getHackathon } from '@/lib/api/hackathon'; import { generateHackathonMetadata } from '@/lib/metadata'; import { HackathonDataProvider } from '@/lib/providers/hackathonProvider'; import HackathonPageClient from './HackathonPageClient'; +import Banner from './components/Banner'; +import Header from './components/header'; +import HackathonTabs from './components/tabs'; +import Sidebar from './components/sidebar'; interface HackathonPageProps { params: Promise<{ slug: string }>; @@ -41,9 +45,19 @@ export default async function HackathonPage({ params }: HackathonPageProps) { notFound(); } + const hackathon = response.data; + return ( - - + +
+ + +
+
+ +
+ } /> +
); } catch { diff --git a/app/(landing)/hackathons/[slug]/submit/page.tsx b/app/(landing)/hackathons/[slug]/submit/page.tsx index f0721551..de2c2b5b 100644 --- a/app/(landing)/hackathons/[slug]/submit/page.tsx +++ b/app/(landing)/hackathons/[slug]/submit/page.tsx @@ -2,7 +2,7 @@ import { use, useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; -import { useHackathonData } from '@/lib/providers/hackathonProvider'; +import { useHackathon } from '@/hooks/hackathon/use-hackathon-queries'; import { useAuthStatus } from '@/hooks/use-auth'; import { useSubmission } from '@/hooks/hackathon/use-submission'; import { SubmissionFormContent } from '@/components/hackathons/submissions/SubmissionForm'; @@ -22,17 +22,9 @@ export default function SubmitProjectPage({ const resolvedParams = use(params); const hackathonSlug = resolvedParams.slug; - const { - currentHackathon, - loading: hackathonLoading, - setCurrentHackathon, - } = useHackathonData(); - - useEffect(() => { - if (hackathonSlug) { - setCurrentHackathon(hackathonSlug); - } - }, [hackathonSlug, setCurrentHackathon]); + // React Query fetches the hackathon — no manual setCurrentHackathon or useEffect needed. + const { data: currentHackathon, isLoading: hackathonLoading } = + useHackathon(hackathonSlug); const hackathonId = currentHackathon?.id || ''; const orgId = currentHackathon?.organizationId || undefined; diff --git a/app/(landing)/hackathons/layout.tsx b/app/(landing)/hackathons/layout.tsx index 68565993..e4970df6 100644 --- a/app/(landing)/hackathons/layout.tsx +++ b/app/(landing)/hackathons/layout.tsx @@ -17,7 +17,7 @@ export default function HackathonLayout({ return ( - + {children} diff --git a/app/(landing)/newsletter/confirm/error/page.tsx b/app/(landing)/newsletter/confirm/error/page.tsx index 84de7a0e..edeb961d 100644 --- a/app/(landing)/newsletter/confirm/error/page.tsx +++ b/app/(landing)/newsletter/confirm/error/page.tsx @@ -16,7 +16,7 @@ function Content() {

{msgs[p.get('reason') ?? ''] ?? 'An unexpected error occurred.'}

- + Back to home diff --git a/app/(landing)/newsletter/confirmed/page.tsx b/app/(landing)/newsletter/confirmed/page.tsx index 67718529..5af76033 100644 --- a/app/(landing)/newsletter/confirmed/page.tsx +++ b/app/(landing)/newsletter/confirmed/page.tsx @@ -6,7 +6,7 @@ export default function NewsletterConfirmedPage() {

Your subscription has been confirmed. Welcome aboard!

- + Back to home diff --git a/app/(landing)/newsletter/unsubscribe/error/page.tsx b/app/(landing)/newsletter/unsubscribe/error/page.tsx index b957980a..d9e7816a 100644 --- a/app/(landing)/newsletter/unsubscribe/error/page.tsx +++ b/app/(landing)/newsletter/unsubscribe/error/page.tsx @@ -15,7 +15,7 @@ function Content() {

{msgs[p.get('reason') ?? ''] ?? 'An unexpected error occurred.'}

- + Back to home diff --git a/app/(landing)/newsletter/unsubscribed/page.tsx b/app/(landing)/newsletter/unsubscribed/page.tsx index 0d758a3c..fafb3f83 100644 --- a/app/(landing)/newsletter/unsubscribed/page.tsx +++ b/app/(landing)/newsletter/unsubscribed/page.tsx @@ -6,7 +6,7 @@ export default function NewsletterUnsubscribedPage() {

You won't receive any more emails from us.

- + Back to home diff --git a/app/(landing)/org/[slug]/org-profile-client.tsx b/app/(landing)/org/[slug]/org-profile-client.tsx index e5706198..0761e2d3 100644 --- a/app/(landing)/org/[slug]/org-profile-client.tsx +++ b/app/(landing)/org/[slug]/org-profile-client.tsx @@ -63,8 +63,8 @@ export default function OrgProfileClient({ slug }: OrgProfileClientProps) { label: 'Hackathons', value: stats.totalHackathons, icon: Trophy, - color: 'text-[#a7f950]', - bgColor: 'bg-[#a7f950]/10', + color: 'text-primary', + bgColor: 'bg-primary/10', }, { label: 'Bounties', @@ -85,9 +85,9 @@ export default function OrgProfileClient({ slug }: OrgProfileClientProps) { return (
{/* Banner / Header */} -
-
-
+
+
+
diff --git a/app/(landing)/privacy/PrivacyContent.tsx b/app/(landing)/privacy/PrivacyContent.tsx index 0070705d..f0958e37 100644 --- a/app/(landing)/privacy/PrivacyContent.tsx +++ b/app/(landing)/privacy/PrivacyContent.tsx @@ -193,7 +193,7 @@ const PrivacyContent = () => { placeholder='Search keyword' value={searchQuery} onChange={e => setSearchQuery(e.target.value)} - className='w-full rounded-lg border border-[#2B2B2B] bg-[#101010] py-2.5 pr-4 pl-10 text-sm text-white placeholder:text-gray-500 focus:border-[#A7F950] focus:ring-1 focus:ring-[#A7F950] focus:outline-none' + className='focus:border-primary focus:ring-primary w-full rounded-lg border border-[#2B2B2B] bg-[#101010] py-2.5 pr-4 pl-10 text-sm text-white placeholder:text-gray-500 focus:ring-1 focus:outline-none' />
@@ -210,7 +210,7 @@ const PrivacyContent = () => { onClick={() => scrollToSection(item.id)} className={`block w-full rounded px-3 py-2 text-left text-sm transition-colors hover:bg-[#1a1a1a] ${ activeSection === item.id - ? 'bg-[#1a1a1a] text-[#A7F950]' + ? 'text-primary bg-[#1a1a1a]' : 'text-gray-300' }`} > @@ -841,14 +841,14 @@ const PrivacyContent = () => {
collins@boundlessfi.xyz https://boundlessfi.xyz diff --git a/app/(landing)/privacy/TermsContent.tsx b/app/(landing)/privacy/TermsContent.tsx index 71abd96c..ac2b6a18 100644 --- a/app/(landing)/privacy/TermsContent.tsx +++ b/app/(landing)/privacy/TermsContent.tsx @@ -192,7 +192,7 @@ const TermsContent = () => { placeholder='Search keyword' value={searchQuery} onChange={e => setSearchQuery(e.target.value)} - className='w-full rounded-lg border border-[#2B2B2B] bg-[#101010] py-2.5 pr-4 pl-10 text-sm text-white placeholder:text-gray-500 focus:border-[#A7F950] focus:ring-1 focus:ring-[#A7F950] focus:outline-none' + className='focus:border-primary focus:ring-primary w-full rounded-lg border border-[#2B2B2B] bg-[#101010] py-2.5 pr-4 pl-10 text-sm text-white placeholder:text-gray-500 focus:ring-1 focus:outline-none' />
@@ -209,7 +209,7 @@ const TermsContent = () => { onClick={() => scrollToSection(item.id)} className={`block w-full rounded px-3 py-2 text-left text-sm transition-colors hover:bg-[#1a1a1a] ${ activeSection === item.id - ? 'bg-[#1a1a1a] text-[#A7F950]' + ? 'text-primary bg-[#1a1a1a]' : 'text-gray-300' }`} > @@ -484,14 +484,14 @@ const TermsContent = () => {
collins@boundlessfi.xyz https://boundlessfi.xyz diff --git a/app/me/crowdfunding/[slug]/edit/components/TeamSection.tsx b/app/me/crowdfunding/[slug]/edit/components/TeamSection.tsx index b8369c36..48236482 100644 --- a/app/me/crowdfunding/[slug]/edit/components/TeamSection.tsx +++ b/app/me/crowdfunding/[slug]/edit/components/TeamSection.tsx @@ -120,8 +120,8 @@ const CollapsibleTeamMember = ({ className={cn( 'rounded-lg border px-3 py-2 text-sm transition-all', member.role === role.value - ? 'border-[#A7F950] bg-[#A7F95014] text-[#A7F950]' - : 'border-[#2B2B2B] bg-[#0A0A0A] text-white hover:border-[#A7F950]/50' + ? 'border-primary text-primary bg-[#A7F95014]' + : 'hover:border-primary/50 border-[#2B2B2B] bg-[#0A0A0A] text-white' )} > {role.label} diff --git a/app/me/hackathons/submissions/page.tsx b/app/me/hackathons/submissions/page.tsx index 767c32af..8d079f34 100644 --- a/app/me/hackathons/submissions/page.tsx +++ b/app/me/hackathons/submissions/page.tsx @@ -258,7 +258,7 @@ const SubmissionsPage: FC = () => { diff --git a/components/avatars/BasicAvatar.tsx b/components/avatars/BasicAvatar.tsx new file mode 100644 index 00000000..d827905a --- /dev/null +++ b/components/avatars/BasicAvatar.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'; +import { cn } from '@/lib/utils'; + +const BasicAvatar = ({ + name, + username, + image, + truncate = true, +}: { + name: string; + username: string; + image?: string; + truncate?: boolean; +}) => { + return ( +
+ + + {name.slice(0, 2).toUpperCase()} + +
+

+ {name} +

+

+ @{username} +

+
+
+ ); +}; + +export default BasicAvatar; diff --git a/components/avatars/GroupAvatar.tsx b/components/avatars/GroupAvatar.tsx new file mode 100644 index 00000000..006d2b2a --- /dev/null +++ b/components/avatars/GroupAvatar.tsx @@ -0,0 +1,41 @@ +import { + Avatar, + AvatarFallback, + AvatarGroup, + AvatarGroupCount, + AvatarImage, +} from '@/components/ui/avatar'; + +interface GroupAvatarProps { + members: string[]; +} + +const GroupAvatar = ({ members }: GroupAvatarProps) => { + const showCount = members.length > 3; + const maxVisible = showCount ? 3 : members.length; + const visibleMembers = members.slice(0, maxVisible); + const remainingCount = members.length - maxVisible; + + return ( + + {visibleMembers.map((member, index) => ( + + + + {member.slice(0, 2).toUpperCase()} + + + ))} + {remainingCount > 0 && ( + + +{remainingCount} + + )} + + ); +}; + +export default GroupAvatar; diff --git a/components/bounties/BountyComments.tsx b/components/bounties/BountyComments.tsx index 9907b171..652e4c81 100644 --- a/components/bounties/BountyComments.tsx +++ b/components/bounties/BountyComments.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useAuth } from '@/hooks/use-auth'; +import { useOptionalAuth } from '@/hooks/use-auth'; import { GenericCommentThread } from '@/components/comments/GenericCommentThread'; import { useCommentSystem } from '@/hooks/use-comment-system'; import { CommentEntityType } from '@/types/comment'; @@ -10,7 +10,7 @@ interface BountyCommentsProps { } export function BountyComments({ bountyId }: BountyCommentsProps) { - const { user } = useAuth(); + const { user } = useOptionalAuth(); // Initialize the comment system for this bounty const commentSystem = useCommentSystem({ diff --git a/components/common/SharePopover.tsx b/components/common/SharePopover.tsx new file mode 100644 index 00000000..b425ffca --- /dev/null +++ b/components/common/SharePopover.tsx @@ -0,0 +1,111 @@ +'use client'; + +import React from 'react'; +import { + PopoverRoot, + PopoverTrigger, + PopoverContent, + PopoverBody, + PopoverHeader, + PopoverButton, +} from '@/components/ui/popover-cult'; +import { + IconShare3, + IconCopy, + IconBrandTwitter, + IconBrandLinkedin, + IconMail, +} from '@tabler/icons-react'; +import { toast } from 'sonner'; + +interface SharePopoverProps { + title?: string; + url?: string; + className?: string; + trigger?: React.ReactNode; +} + +const SharePopover = ({ + title, + url, + className, + trigger, +}: SharePopoverProps) => { + const shareUrl = + url || (typeof window !== 'undefined' ? window.location.href : ''); + const shareTitle = title || 'Check out this hackathon on Boundless!'; + + const handleCopyLink = () => { + navigator.clipboard.writeText(shareUrl); + toast.success('Link copied to clipboard!'); + }; + + const handleTwitterShare = () => { + const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent( + shareTitle + )}&url=${encodeURIComponent(shareUrl)}`; + const newWindow = window.open(twitterUrl, '_blank', 'noopener,noreferrer'); + if (newWindow) newWindow.opener = null; + }; + + const handleLinkedinShare = () => { + const linkedinUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent( + shareUrl + )}`; + const newWindow = window.open(linkedinUrl, '_blank', 'noopener,noreferrer'); + if (newWindow) newWindow.opener = null; + }; + + const handleEmailShare = () => { + window.location.href = `mailto:?subject=${encodeURIComponent( + shareTitle + )}&body=${encodeURIComponent(shareUrl)}`; + }; + + return ( + + + {trigger || } + + + + + Share Hackathon + + + + + + Copy Link + + + + X (Twitter) + + + + LinkedIn + + + + Email + + + + + ); +}; + +export default SharePopover; diff --git a/components/common/share.tsx b/components/common/share.tsx new file mode 100644 index 00000000..07446e4b --- /dev/null +++ b/components/common/share.tsx @@ -0,0 +1,3 @@ +import SharePopover from './SharePopover'; + +export default SharePopover; diff --git a/components/crowdfunding/campaign-comments-tab.tsx b/components/crowdfunding/campaign-comments-tab.tsx index 80779a9f..75dab824 100644 --- a/components/crowdfunding/campaign-comments-tab.tsx +++ b/components/crowdfunding/campaign-comments-tab.tsx @@ -12,7 +12,7 @@ import { Comment as CommentType, ReportReason, } from '@/types/comment'; -import { useAuth } from '@/hooks/use-auth'; +import { useOptionalAuth } from '@/hooks/use-auth'; import { reportError } from '@/lib/error-reporting'; import { Loader2, MessageCircle } from 'lucide-react'; @@ -26,7 +26,7 @@ export function CampaignCommentsTab({ campaignId }: CampaignCommentsTabProps) { >('createdAt'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); - const { user } = useAuth(false); + const { user } = useOptionalAuth(); // Use the generic comment system const { diff --git a/components/hackathons/ExtendedBadge.tsx b/components/hackathons/ExtendedBadge.tsx new file mode 100644 index 00000000..6ce3e7f6 --- /dev/null +++ b/components/hackathons/ExtendedBadge.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { Badge } from '@/components/ui/badge'; +import { cn } from '@/lib/utils'; + +interface ExtendedBadgeProps { + submissionDeadline?: string; + submissionDeadlineOriginal?: string; + className?: string; +} + +export const ExtendedBadge = ({ + submissionDeadline, + submissionDeadlineOriginal, + className, +}: ExtendedBadgeProps) => { + const isExtended = + submissionDeadlineOriginal && + submissionDeadline && + new Date(submissionDeadline) > new Date(submissionDeadlineOriginal); + + if (!isExtended) return null; + + return ( + + Extended + + ); +}; diff --git a/components/hackathons/HackathonComments.tsx b/components/hackathons/HackathonComments.tsx index 62ea783e..b0d08eca 100644 --- a/components/hackathons/HackathonComments.tsx +++ b/components/hackathons/HackathonComments.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useAuth } from '@/hooks/use-auth'; +import { useOptionalAuth } from '@/hooks/use-auth'; import { GenericCommentThread } from '@/components/comments/GenericCommentThread'; import { useCommentSystem } from '@/hooks/use-comment-system'; import { CommentEntityType } from '@/types/comment'; @@ -10,9 +10,8 @@ interface HackathonCommentsProps { } export function HackathonComments({ hackathonId }: HackathonCommentsProps) { - const { user } = useAuth(); + const { user } = useOptionalAuth(); - // Initialize the comment system for this hackathon const commentSystem = useCommentSystem({ entityType: CommentEntityType.HACKATHON, entityId: hackathonId, @@ -21,7 +20,6 @@ export function HackathonComments({ hackathonId }: HackathonCommentsProps) { enabled: true, }); - // Current user info for the comment system const currentUser = user ? { id: user.id, diff --git a/components/hackathons/HackathonsPageHero.tsx b/components/hackathons/HackathonsPageHero.tsx index 3790f435..418db5a3 100644 --- a/components/hackathons/HackathonsPageHero.tsx +++ b/components/hackathons/HackathonsPageHero.tsx @@ -65,7 +65,7 @@ export default function HackathonsPageHero() { Start Exploring Hackathons diff --git a/components/hackathons/discussion/comment.tsx b/components/hackathons/discussion/comment.tsx index 0865cca7..d29855d3 100644 --- a/components/hackathons/discussion/comment.tsx +++ b/components/hackathons/discussion/comment.tsx @@ -156,7 +156,7 @@ export function HackathonDiscussions({ if (loading) return (
- + Loading discussions...
); @@ -169,7 +169,7 @@ export function HackathonDiscussions({

diff --git a/components/hackathons/hackathonBanner.tsx b/components/hackathons/hackathonBanner.tsx index d18a9212..b5623192 100644 --- a/components/hackathons/hackathonBanner.tsx +++ b/components/hackathons/hackathonBanner.tsx @@ -53,10 +53,12 @@ export function HackathonBanner({ onViewSubmissionClick, onFindTeamClick, onLeaveClick, + status: backendStatus, }: HackathonBannerProps) { const { status, timeRemaining, formatCountdown } = useHackathonStatus( startDate, - deadline + deadline, + backendStatus ); const { isAuthenticated } = useAuthStatus(); const router = useRouter(); @@ -172,7 +174,7 @@ export function HackathonBanner({ return ( ); diff --git a/components/hackathons/hackathonStickyCard.tsx b/components/hackathons/hackathonStickyCard.tsx index d2b3ed72..b2b1e897 100644 --- a/components/hackathons/hackathonStickyCard.tsx +++ b/components/hackathons/hackathonStickyCard.tsx @@ -35,6 +35,7 @@ interface HackathonStickyCardProps { onLeaveClick?: () => void; participantType?: 'INDIVIDUAL' | 'TEAM' | 'TEAM_OR_INDIVIDUAL'; submissionDeadlineExtendedAt?: string | null; + status?: string; } export function HackathonStickyCard(props: HackathonStickyCardProps) { @@ -56,9 +57,10 @@ export function HackathonStickyCard(props: HackathonStickyCardProps) { isLeaving, participantType, submissionDeadlineExtendedAt, + status: backendStatus, } = props; - const { status } = useHackathonStatus(startDate, deadline); + const { status } = useHackathonStatus(startDate, deadline, backendStatus); const { isAuthenticated } = useAuthStatus(); const router = useRouter(); const pathname = usePathname(); @@ -100,7 +102,7 @@ export function HackathonStickyCard(props: HackathonStickyCardProps) { return (
- + {imageUrl && (
{/* Prize Pool Section */} {totalPrizePool && ( -
+
- + Prize Pool
-
+
${totalPrizePool}
@@ -165,7 +167,7 @@ export function HackathonStickyCard(props: HackathonStickyCardProps) { {formatDateWithFallback(deadline)} {submissionDeadlineExtendedAt && ( - + Extended )} @@ -209,7 +211,7 @@ export function HackathonStickyCard(props: HackathonStickyCardProps) { onClick={ !isAuthenticated ? handleRedirectToAuth : onJoinClick } - className='w-full bg-[#a7f950] py-4 text-sm font-bold text-black hover:bg-[#8fd93f]' + className='bg-primary w-full py-4 text-sm font-bold text-black hover:bg-[#8fd93f]' > {getRegisterButtonText || 'Join'} @@ -238,7 +240,7 @@ export function HackathonStickyCard(props: HackathonStickyCardProps) { {status === 'ongoing' && isRegistered && !hasSubmitted && (