Skip to content

[STU-154] Landing page UX overhaul — copy, icons, and benefits section#36

Merged
BAWES merged 8 commits into
mainfrom
feature/STU-154-landing-ux-improvements
May 21, 2026
Merged

[STU-154] Landing page UX overhaul — copy, icons, and benefits section#36
BAWES merged 8 commits into
mainfrom
feature/STU-154-landing-ux-improvements

Conversation

@BAWES
Copy link
Copy Markdown
Owner

@BAWES BAWES commented May 21, 2026

Summary

  • Improved hero copy with clearer value proposition and inclusive CTAs
  • Added role-specific emoji icons to portal grid cards for visual differentiation
  • Replaced technical search signals section with user-facing benefit cards
  • Removed dead CSS from an earlier landing page iteration

Context

Part of STU-154 landing page and login UX redesign. This cherry-picks the UX Designer's work that was completed in a worktree session.

Test plan

  • Landing page renders at desktop (1440x900) and mobile (390x844)
  • Portal cards show emoji icons
  • Benefits section renders correctly
  • Dark mode and light mode both work

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Landing page redesigned with updated hero messaging, revised calls-to-action, and new feature benefits section
    • Added visual icons to portal role options in the portal grid
  • Tests

    • Added smoke test suite for landing page covering desktop and mobile viewport compatibility

Review Change Stack

…benefits section

- Replace hero copy with clearer value proposition and inclusive CTAs
- Add role-specific emoji icons to portal grid cards for visual differentiation
- Replace technical search signals section with user-facing benefit cards
- Remove dead CSS from earlier landing page iteration (~96 lines)
- Add new CSS classes for portalIcon, landingBenefitsSection, and benefitGrid

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
studenthub-next Error Error May 21, 2026 9:12pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Caution

Review failed

Failed to post review comments

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR updates the landing page with new role-to-icon mappings and a benefits grid, refreshes hero copy and CTAs, adds smoke test coverage for desktop and mobile, and adjusts certificate schema validation to remove unused fields and convert certificate type to boolean.

Changes

Landing Page Content Refresh

Layer / File(s) Summary
Data structures: icons and benefits mappings
src/app/page.tsx
Introduces portalIcons role-to-emoji mapping and a structured benefits array containing marketing content titles and descriptions used by downstream portal and benefits sections.
Hero section content and CTAs
src/app/page.tsx
Updates hero mid-rail "find talent" headline/subheadline and replaces hero aside content with new actions, platform description, refreshed CTA links (Get started, Request demo), and updated landingHeroStats items.
Portal grid icon rendering
src/app/page.tsx
Adds a <span> element displaying the corresponding portalIcons emoji inside each portal grid link.
Benefits section replacement
src/app/page.tsx
Replaces the prior search-focused section with a new "Why StudentHub" benefits section that iterates over the benefits array to render a grid of 4 benefit cards with titles and descriptions.
Landing page smoke tests and assertions
e2e/smoke/landing.spec.ts, scripts/smoke-test.mjs
Adds comprehensive Playwright landing page smoke test suite verifying hero text, presence of landing CTAs, platform highlights strip, portal grid with exactly 5 links and 5 aria-hidden icons, benefits section with eyebrow and 4 ordered benefit cards, and public navigation. Includes mobile viewport test. Updates scripts/smoke-test.mjs to assert homepage HTTP 200 and presence of key landing text ("Every role gets its own workspace.", "Why StudentHub", "Get started").

Certificate Schema Validation

Layer / File(s) Summary
Certificate type schema transformation
src/modules/candidates/actions.ts
In addCandidateCertificate, removes certificate_title and certificate_issuer from the Zod schema and redefines certificate_type as a string enum ("true" or "false") with a transform that converts the validated value to a boolean.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • BAWES/studenthub-codex#27: Updates addCandidateCertificate in src/modules/candidates/actions.ts with overlapping changes to certificate validation schema definition.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main changes: landing page UX improvements with hero copy updates, role-specific icons, and a new benefits section.
Description check ✅ Passed The description includes summary, context, and test plan sections covering the key changes. However, it lacks the required template structure including explicit Type selection, full Checklist completion, and detailed Screenshots section.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/STU-154-landing-ux-improvements

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/app/page.tsx (1)

69-74: ⚡ Quick win

Fragile index-based logic for "PDF" vs "Live" determination.

The condition index === 1 hardcodes the assumption that "CV export" is always the second item. If the array order changes, this logic silently breaks. Consider using an explicit data structure or checking the item name.

♻️ Proposed refactor using explicit mapping
-{["Profile", "CV export", "Timesheet", "Payment"].map((item, index) => (
+{[
+  { label: "Profile", status: "Live" },
+  { label: "CV export", status: "PDF" },
+  { label: "Timesheet", status: "Live" },
+  { label: "Payment", status: "Live" },
+].map((item) => (
-  <div key={item}>
-    <span>{item}</span>
-    <strong>{index === 1 ? "PDF" : "Live"}</strong>
+  <div key={item.label}>
+    <span>{item.label}</span>
+    <strong>{item.status}</strong>
   </div>
 ))}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/page.tsx` around lines 69 - 74, The mapping over ["Profile", "CV
export", "Timesheet", "Payment"] uses fragile index-based logic (index === 1) to
decide "PDF" vs "Live"; update the rendering in the map so it derives the label
from the item itself (e.g., check the item string or use an explicit array of
objects like {name, mode}) instead of using index, and update the JSX in the map
callback (the inline map and the span/strong rendering) to use that property to
show "PDF" for CV export and "Live" for others.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/page.tsx`:
- Around line 9-15: The portalIcons map is currently typed as Record<string,
string>, causing unchecked indexed access against the specific portalRoles;
update types so keys are the exact union of roles and eliminate undefined on
portalIcons[role]. Derive a PortalRole from the existing portalRoles (e.g.,
using typeof portalRoles[number] or make portalRoles readonly with as const) and
then type portalIcons as Record<PortalRole, string> or recreate portalIcons
using the satisfies pattern so accesses like portalIcons[role] are typed as
string (refer to symbols portalRoles and portalIcons and the place where
portalIcons[role] is used).

---

Nitpick comments:
In `@src/app/page.tsx`:
- Around line 69-74: The mapping over ["Profile", "CV export", "Timesheet",
"Payment"] uses fragile index-based logic (index === 1) to decide "PDF" vs
"Live"; update the rendering in the map so it derives the label from the item
itself (e.g., check the item string or use an explicit array of objects like
{name, mode}) instead of using index, and update the JSX in the map callback
(the inline map and the span/strong rendering) to use that property to show
"PDF" for CV export and "Live" for others.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5cdd0556-5d99-473e-839c-740d6c08da92

📥 Commits

Reviewing files that changed from the base of the PR and between 11e0d1e and 1ba4f90.

📒 Files selected for processing (1)
  • src/app/page.tsx

Comment thread src/app/page.tsx
Comment on lines +9 to +15
const portalIcons: Record<string, string> = {
candidate: "🎓",
staff: "📋",
company: "🏭",
admin: "⚙️",
inspector: "🔍",
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Strengthen typing to align with portalRoles and satisfy strict mode.

portalIcons is typed as Record<string, string>, but the keys are exactly the five roles defined in portalRoles (line 36). With noUncheckedIndexedAccess enabled (per coding guidelines), accessing portalIcons[role] at line 108 will return string | undefined, requiring a runtime check or type assertion.

Define a more precise type to ensure compile-time safety and eliminate the need for runtime guards.

🔒 Proposed fix to align types with portalRoles
+type PortalRole = typeof portalRoles[number];
+
-const portalIcons: Record<string, string> = {
+const portalIcons: Record<PortalRole, string> = {
   candidate: "🎓",
   staff: "📋",
   company: "🏭",
   admin: "⚙️",
   inspector: "🔍",
 };

Alternatively, use satisfies for inference while retaining safety:

-const portalIcons: Record<string, string> = {
+const portalIcons = {
   candidate: "🎓",
   staff: "📋",
   company: "🏭",
   admin: "⚙️",
   inspector: "🔍",
-};
+} satisfies Record<PortalRole, string>;

As per coding guidelines: "Strict mode enabled — no implicit any, no unchecked index access."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const portalIcons: Record<string, string> = {
candidate: "🎓",
staff: "📋",
company: "🏭",
admin: "⚙️",
inspector: "🔍",
};
type PortalRole = typeof portalRoles[number];
const portalIcons: Record<PortalRole, string> = {
candidate: "🎓",
staff: "📋",
company: "🏭",
admin: "⚙️",
inspector: "🔍",
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/page.tsx` around lines 9 - 15, The portalIcons map is currently typed
as Record<string, string>, causing unchecked indexed access against the specific
portalRoles; update types so keys are the exact union of roles and eliminate
undefined on portalIcons[role]. Derive a PortalRole from the existing
portalRoles (e.g., using typeof portalRoles[number] or make portalRoles readonly
with as const) and then type portalIcons as Record<PortalRole, string> or
recreate portalIcons using the satisfies pattern so accesses like
portalIcons[role] are typed as string (refer to symbols portalRoles and
portalIcons and the place where portalIcons[role] is used).

@BAWES
Copy link
Copy Markdown
Owner Author

BAWES commented May 21, 2026

DevRel Review

Status: Approved — solid landing page improvements

Clean, single-file change to src/app/page.tsx. The new copy is more concrete and benefit-driven:

  • Portal icons add visual differentiation
  • "Why StudentHub" section with specific benefits replaces the vague "search signals"
  • Copy is sharper: "find talent", "Send CVs" instead of placeholder "jaafar"
  • Stats section is more specific: "5 role-specific portals" vs generic statements

This directly addresses GitHub issue #28 (landing page UX overhaul).

One note: the Vercel preview deploy should be checked to confirm the visual layout renders correctly.

Add Playwright e2e smoke test covering hero copy, portal grid with icons,
benefits section, nav, CTA buttons, platform highlights, and mobile layout.
Also add landing page checks to the existing smoke-test.mjs script.

Tests: added e2e/smoke/landing.spec.ts (16 tests across chromium + mobile)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
e2e/smoke/landing.spec.ts (1)

81-81: 💤 Low value

Consider aligning test name with what's actually verified.

The test name mentions "without overflow" but only verifies element visibility. For a smoke test this is acceptable, but you could either:

  1. Rename to something like "landing page renders key elements on mobile"
  2. Add explicit overflow verification if that's a critical concern
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/smoke/landing.spec.ts` at line 81, The test named "landing page renders
on mobile without overflow" only asserts element visibility; either update the
test name to reflect what it actually checks (e.g., rename the test to "landing
page renders key elements on mobile") or add an explicit overflow assertion:
after the existing visibility checks, evaluate page-level overflow (for example
using page.evaluate to compare document.documentElement.scrollWidth to
window.innerWidth or check computed overflow/overflowX on body/html) and assert
no horizontal overflow; modify the test() declaration or append the overflow
expect accordingly so the name matches behavior or the behavior matches the
name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@e2e/smoke/landing.spec.ts`:
- Line 81: The test named "landing page renders on mobile without overflow" only
asserts element visibility; either update the test name to reflect what it
actually checks (e.g., rename the test to "landing page renders key elements on
mobile") or add an explicit overflow assertion: after the existing visibility
checks, evaluate page-level overflow (for example using page.evaluate to compare
document.documentElement.scrollWidth to window.innerWidth or check computed
overflow/overflowX on body/html) and assert no horizontal overflow; modify the
test() declaration or append the overflow expect accordingly so the name matches
behavior or the behavior matches the name.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ec490af1-19d1-476a-a9a0-acfc181a04ba

📥 Commits

Reviewing files that changed from the base of the PR and between 1ba4f90 and 2722a47.

📒 Files selected for processing (2)
  • e2e/smoke/landing.spec.ts
  • scripts/smoke-test.mjs

BAWES added a commit that referenced this pull request May 21, 2026
…ds on PR #36

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Action casts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BAWES added a commit that referenced this pull request May 21, 2026
* feat: add certificate CRUD for candidate profile edit

- Add certificate_title, certificate_issuer, certificate_url fields to Prisma schema
- Add Zod-validated addCandidateCertificate and removeCandidateCertificate server actions
- Add certificate form section to CandidateEditForm with type select, title, issuer, dates, and URL
- Pass certificate data from edit page to form component
- Update data.ts to select and display new certificate fields

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: increase login page tap targets to 44px min-height for accessibility

Bumps min-height on three login/shell elements from 40px/42px to 44px
to meet WCAG 2.5.5 target size recommendations: nav links, theme toggle,
and landing brand.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test: add workflow pipeline tests for request lifecycle

Add tests for suggestion creation, application shortlist, interview
completion, invitation creation, and cross-role visibility.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* feat: restyle mobile tab bar CSS for bottom tab navigation with icons

- Replace floating pill bar with fixed full-width bottom tab bar
- Add icon+label stacked layout with flex-direction: column
- Add backdrop-filter blur, safe-area-inset-bottom support
- Add active indicator via border-top highlight
- Separate dark mode mobile tab bar styles
- Update workspace stage bottom padding from 88px to 76px

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: resolve FK constraint violations in workflow test cleanup

Clear note.suggestion_uuid and story.suggestion_uuid before deleting
suggestions to avoid circular FK errors during test data cleanup.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* feat: add interview management page for staff [STU-134]

Adds /staff/interviews list and detail pages with status management,
data fetching functions, G I keyboard shortcut, and nav integration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat: implement mobile bottom tab navigation with icons [STU-135]

- Add LucideIcon field to NavItem type and map icons for all roles
- Render icons in desktop WorkspaceNavigation and mobile tab bar
- Add staff Interviews nav item with Calendar icon
- Remove duplicate WorkspaceMobileNavigation when embedded in WorkspaceOS
- Redesign mobile tab bar: full-width fixed bottom bar with icon+label layout
- Add backdrop-filter blur, safe-area-inset-bottom, and active border-top indicator
- Update workspace stage bottom padding from 88px to 76px

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix: address CodeRabbit review findings for PR #31 [STU-170]

- Wrap candidateProfileUpdateTest DB mutations in try/finally so cleanup
  always runs even on assertion failure
- Add certificate CRUD workflow test with DB row verification
- Tighten suggestion test assertion with explicit note_uuid check
- Replace undefined --fg CSS variable with --foreground in
  candidateMissingFields hover rule
- Use strict z.enum(["true", "false"]) for certificate_type coercion
- Replace Number.isInteger(status) with explicit whitelist (2, 3) in
  updateInterviewAction
- Interview existence check before update already present (no change needed)

Tests: added certificate CRUD workflow test, tightened suggestion assertion

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix: restore main-compatible actions.ts and fix merge inconsistencies

Taking main's actions.ts (which includes approveIdRequest/rejectIdRequest)
and re-applying the STU-170 strict certificate_type enum fix. Also pulling
in main's WorkLogStaffActions and ID request page for consistency.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix: add rejection_reason to ID request detail select query for PR #31

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: resolve merge conflicts and remove non-existent certificate fields on PR #36

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: remove duplicate Prisma schema fields on PR #31

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
@BAWES BAWES closed this May 21, 2026
@BAWES BAWES reopened this May 21, 2026
…ts [STU-154]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

Deployment failed with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/khalid-proj?upgradeToPro=build-rate-limit

…orm parsing

These fields don't exist in the Prisma candidate_certificate model on main.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@BAWES BAWES merged commit ab0895d into main May 21, 2026
7 checks passed
@BAWES BAWES deleted the feature/STU-154-landing-ux-improvements branch May 21, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant