Robust Tab Visibility & Gating
Objective
Ensure that tabs not explicitly enabled in enabledTabs are completely hidden from every navigation surface in the UI and are entirely unreachable — whether through normal navigation, direct URL entry, browser history, or stale deep links. A disabled tab must leave no visible trace in the interface and must never cause a layout break or unexpected visual shift if accessed directly.
Technical Implementation
Target File
app/(landing)/hackathons/[slug]/page.tsx
Logic Mapping
Navigation Filtering — hackathonTabs useMemo
- Refactor the
hackathonTabs useMemo to filter the tabs array against currentHackathon.enabledTabs before returning
- Only tabs whose identifier exists in the
enabledTabs array should be included in the memoized result — the filtered array becomes the single source of truth for all navigation rendering
- The filter must be data-driven and dynamic — if an organizer enables or disables a tab mid-session, the memoized value should recompute and all navigation surfaces should update accordingly without a hard page reload
- The memo's dependency array must include
currentHackathon.enabledTabs to ensure it recalculates when the configuration changes
Component Guard — Tab Content Section
- In the "Tab Content" section of the page, wrap the rendering of each tab component — including
HackathonResources, AnnouncementsTab, SubmissionTab, and any others — in a conditional check against currentHackathon.enabledTabs
- A tab component must not render at all if its identifier is not present in
enabledTabs — not even partially, not even as a loading skeleton
- This guard operates as a secondary layer of protection beneath the navigation filtering, ensuring that even if a user reaches a disabled tab via a direct URL or stale link, the component is never instantiated
Deep Linking Gating
- If the
activeTab resolved from the URL query parameter does not exist in the filtered hackathonTabs array, immediately call setActiveTab('overview') to default to the overview tab
- This must be applied before any tab content renders — the default should be set early enough that the user never sees a flash of an incorrect or empty tab state
- If the
overview tab is itself disabled (an edge case), handle this gracefully rather than triggering an infinite loop — surface an appropriate fallback rather than repeatedly resetting the active tab
Component Consistency
Mobile Navigation
- The mobile bottom bar and/or mobile drawer menu must apply the same
enabledTabs filtering as the desktop navigation — disabled tabs must not appear in any mobile navigation surface
- Mobile and desktop navigation must remain fully in sync — a tab hidden on desktop must also be hidden on mobile at all times
- Both surfaces should derive their visible tabs from the same filtered
hackathonTabs memoized value to guarantee consistency and avoid duplicating the filter logic
⚠️ Caution
This is a production environment — not a sandbox.
- Code must be performant, accessible, and clean
- No dummy data — all gating logic must operate on real
enabledTabs values from currentHackathon
- AI-generated code will be scrutinized; poorly structured or "hallucinated" code will result in immediate issue closure
- Follow the existing design system: shadcn/ui, Tailwind, Framer Motion
Testing & Verification
Automated Tests
npm run lint # Ensure code quality
npm run build # Verify no breaking changes in routing or types
Manual Verification
Robust Tab Visibility & Gating
Objective
Ensure that tabs not explicitly enabled in
enabledTabsare completely hidden from every navigation surface in the UI and are entirely unreachable — whether through normal navigation, direct URL entry, browser history, or stale deep links. A disabled tab must leave no visible trace in the interface and must never cause a layout break or unexpected visual shift if accessed directly.Technical Implementation
Target File
app/(landing)/hackathons/[slug]/page.tsxLogic Mapping
Navigation Filtering —
hackathonTabsuseMemohackathonTabsuseMemoto filter thetabsarray againstcurrentHackathon.enabledTabsbefore returningenabledTabsarray should be included in the memoized result — the filtered array becomes the single source of truth for all navigation renderingcurrentHackathon.enabledTabsto ensure it recalculates when the configuration changesComponent Guard — Tab Content Section
HackathonResources,AnnouncementsTab,SubmissionTab, and any others — in a conditional check againstcurrentHackathon.enabledTabsenabledTabs— not even partially, not even as a loading skeletonDeep Linking Gating
activeTabresolved from the URL query parameter does not exist in the filteredhackathonTabsarray, immediately callsetActiveTab('overview')to default to the overview taboverviewtab is itself disabled (an edge case), handle this gracefully rather than triggering an infinite loop — surface an appropriate fallback rather than repeatedly resetting the active tabComponent Consistency
Mobile Navigation
enabledTabsfiltering as the desktop navigation — disabled tabs must not appear in any mobile navigation surfacehackathonTabsmemoized value to guarantee consistency and avoid duplicating the filter logicenabledTabsvalues fromcurrentHackathonTesting & Verification
Automated Tests
Manual Verification
hackathonTabsuseMemocorrectly filters out tabs not present inenabledTabsand that the filtered result drives all navigation renderingcurrentHackathon.enabledTabsis included in the memo's dependency array — confirm tabs update dynamically when the configuration changesHackathonResources,AnnouncementsTab,SubmissionTab, and other tab components do not render at all when their identifier is absent fromenabledTabssetActiveTab('overview')is called and the overview tab is shown without any flash of the disabled tab's content'overview'occurs before any tab content renders — no partial render of the disabled tab should be visible'overview'default correctly'overview'itself is disabled — confirm no infinite loop occurs and an appropriate fallback is shown