Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 14 additions & 22 deletions src/components/golf/calendar/MobileEventCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
AlertCircle,
} from 'lucide-react';
import { MobileRSVPButtons, type RSVPResponse } from './MobileRSVPButtons';
import { formatTime } from '@/lib/calendar/event-styles';
import { formatTime, getEventTypeConfig } from '@/lib/calendar/event-styles';
import type { EventType } from '@/lib/types/calendar';
Comment on lines +25 to +26

@coderabbitai coderabbitai Bot Jul 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import EventType through the public type barrel.

Use import type { EventType } from '@/lib/types' instead of the deep @/lib/types/calendar import.

As per coding guidelines, import types from @/lib/types for type definitions.

🤖 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/components/golf/calendar/MobileEventCard.tsx` around lines 25 - 26,
Update the EventType type import in MobileEventCard.tsx to use the public
'`@/lib/types`' barrel instead of the deep '`@/lib/types/calendar`' path, leaving
the formatTime and getEventTypeConfig import unchanged.

Source: Coding guidelines

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Skipped deliberately: @/lib/types/index.ts does not re-export the calendar module, so the barrel import would not compile. The deep @/lib/types/calendar path is the established idiom for these types — the sibling EventCard.tsx (pre-existing) imports EventType the same way. Expanding the barrel is out of scope for this fix (69 existing exports, collision risk with inline calendar-event types already in index.ts).


Generated by Claude Code

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f 'index.ts' src/lib/types
echo "---"
cat src/lib/types/index.ts 2>/dev/null | head -50
echo "---"
rg -n "EventType" src/lib/types/index.ts
echo "---"
rg -n "from '`@/lib/types`" src/components/golf/calendar/EventCard.tsx

Length of output: 6102


@njrini99-code Confirmed — src/lib/types/index.ts has no re-export of ./calendar, and src/components/golf/calendar/EventCard.tsx:8 already imports EventType the same way (from '@/lib/types/calendar'). The barrel import would indeed fail to compile as-is, and this is the established pattern for these types. Withdrawing this nit.


🧬 Code Graph Analysis Results

Relevant snippets

src/components/golf/calendar/MobileEventCard.tsx

  • (No additional snippets provided with line numbers; file contents already show the relevant imports/casts, e.g., type { EventType } from @/lib/types/calendar and casting event.event_type as EventType.)

src/lib/types/calendar.ts (lines 5-20)

export type EventType =
  | 'game'
  | 'practice'
  | 'scrimmage'
  | 'recruiting_visit'
  | 'camp'
  | 'tournament'
  | 'meeting'
  | 'workout'
  | 'class'
  | 'blocked_time'
  | 'qualifier'  // Golf-specific
  | 'travel'     // Golf-specific
  | 'showcase'   // Baseball-specific (recruiting-facing — event-ink 'pursuit')
  | 'tryout'     // Baseball-specific (recruiting-facing — event-ink 'pursuit')
  | 'other';

src/lib/calendar/event-styles.ts (lines 165-172)

export function getEventTypeConfig(type: EventType): EventTypeConfig {
  // Own-key guard: callers pass raw DB strings (cast to EventType), and a
  // plain index would resolve inherited keys like '__proto__' to a truthy
  // prototype object with no styling fields instead of the 'other' fallback.
  return Object.prototype.hasOwnProperty.call(eventTypeConfigs, type)
    ? eventTypeConfigs[type]
    : eventTypeConfigs.other;
}

src/lib/calendar/event-styles.ts (lines 177-196)

export function formatTime(timeString: string): string {
  // Handle time-only strings (HH:MM:SS or HH:MM)
  if (timeString && !timeString.includes('T') && !timeString.includes(' ')) {
    const parts = timeString.split(':').map(Number);
    const hours = parts[0] ?? 0;
    const minutes = parts[1] ?? 0;
    const period = hours >= 12 ? 'PM' : 'AM';
    const displayHour = hours % 12 || 12;
    const displayMinutes = String(minutes).padStart(2, '0');
    return `${displayHour}:${displayMinutes} ${period}`;
  }

  // Handle full datetime strings
  const date = new Date(timeString);
  return date.toLocaleTimeString('en-US', {
    hour: 'numeric',
    minute: '2-digit',
    hour12: true,
  });
}

src/hooks/useCalendarEvents.ts (lines 6-34)

export interface CalendarEvent {
  id: string;
  team_id: string;
  title: string;
  event_type: string; // Accepts any event type string for flexibility across sports
  start_date: string; // Mapped from start_time (timestamptz)
  end_date: string | null; // Mapped from end_time (timestamptz)
  start_time: string | null; // Raw start_time from DB
  end_time: string | null; // Raw end_time from DB
  location?: string | null;
  description?: string | null;
  status?: string;
  all_day?: boolean;
  recurring?: boolean;
  created_by?: string | null;
  requires_rsvp?: boolean;
  rsvp_deadline?: string | null;
  max_attendees?: number | null;
  rsvp_confirmed_count?: number;
  rsvp_maybe_count?: number;
  rsvp_declined_count?: number;
  rsvp_pending_count?: number;
  rsvp_total_count?: number;
  // Recurring-series identity. Series root has recurrence_rule populated and
  // parent_event_id null; sibling occurrences carry parent_event_id pointing
  // back to the root.
  parent_event_id?: string | null;
  recurrence_rule?: string | null;
}

src/components/golf/calendar/MobileRSVPButtons.tsx (line 20)

export type RSVPResponse = 'accepted' | 'tentative' | 'declined';

src/lib/utils.ts (lines 5-7)

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

import type { CalendarEvent } from '@/hooks/useCalendarEvents';
import { Button } from '@/components/ui/button';

Expand Down Expand Up @@ -91,17 +92,8 @@ export function MobileEventCard({
return onRsvp(response);
}, [onRsvp]);

// Get accent color based on event type (matches event-styles.ts)
const getAccentColor = () => {
switch (event.event_type) {
case 'practice': return 'bg-warm-400';
case 'tournament': return 'bg-primary-600';
case 'qualifier': return 'bg-amber-500';
case 'meeting': return 'bg-sky-500';
case 'travel': return 'bg-purple-500';
default: return 'bg-warm-400';
}
};
// Cast string type to EventType for styling (fallback to 'other' if not matched)
const config = getEventTypeConfig(event.event_type as EventType);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Baseball Types Lose Styling

When baseball calendar rows use known event types like showcase or tryout, this cast sends those strings through the shared golf/baseball styling lookup even though they are not in the shared EventType config. The new dot becomes the only visible event-type marker on mobile, so those events fall back to neutral other styling and become visually indistinguishable from miscellaneous events.

Context Used: Review for Helm Sports Labs (a commercial multi-sp... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/golf/calendar/MobileEventCard.tsx
Line: 96

Comment:
**Baseball Types Lose Styling**

When baseball calendar rows use known event types like `showcase` or `tryout`, this cast sends those strings through the shared golf/baseball styling lookup even though they are not in the shared `EventType` config. The new dot becomes the only visible event-type marker on mobile, so those events fall back to neutral `other` styling and become visually indistinguishable from miscellaneous events.

**Context Used:** Review for Helm Sports Labs (a commercial multi-sp... ([source](.greptile))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

Comment thread
coderabbitai[bot] marked this conversation as resolved.

return (
<div
Expand All @@ -115,12 +107,6 @@ export function MobileEventCard({
className
)}
>
{/* Left accent bar */}
<div className={cn(
'absolute left-0 top-0 bottom-0 w-1 rounded-l-2xl',
getAccentColor()
)} />

{/* Main card content - tappable */}
<Button variant="ghost"
type="button"
Expand Down Expand Up @@ -154,10 +140,16 @@ export function MobileEventCard({
</div>
)}

{/* Title */}
<h3 className="text-subhead font-medium text-warm-900 leading-snug truncate">
{event.title}
</h3>
{/* Title — leading ringed event-type dot (replaces the retired left accent stripe) */}
<div className="flex items-center gap-2">
<span
className={cn('h-1.5 w-1.5 rounded-full shrink-0 ring-[3px]', config.dotColor, config.dotRingColor)}
aria-hidden="true"
/>
<h3 className="text-subhead font-medium text-warm-900 leading-snug truncate min-w-0">
{event.title}
</h3>
</div>

{/* Time and location row */}
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 mt-1.5">
Expand Down
6 changes: 4 additions & 2 deletions src/components/golf/calendar/NotificationCenter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,12 @@ export function NotificationCenter() {

return (
<div className="relative">
{/* Bell Button */}
{/* Bell Button — overflow-visible overrides the Button primitive's
ripple-containing overflow-hidden, which was clipping the
-top-1/-right-1 unread badge below. */}
<Button variant="ghost"
onClick={handleToggle}
className="relative p-2.5 rounded-xl bg-cream-100/75 backdrop-blur-sm border border-warm-200/45 shadow-sm text-warm-500 hover:text-warm-800 hover:bg-cream-50/92 hover:shadow-md active:scale-95 transition-all duration-200"
className="relative overflow-visible p-2.5 rounded-xl bg-cream-100/75 backdrop-blur-sm border border-warm-200/45 shadow-sm text-warm-500 hover:text-warm-800 hover:bg-cream-50/92 hover:shadow-md active:scale-95 transition-all duration-200"
aria-label={unreadCount > 0 ? `Notifications, ${unreadCount} unread` : 'Notifications'}
aria-expanded={isOpen}
>
Comment on lines 154 to 159

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Bell icon button lacks tooltip 📜 Skill insight ☑ Accessibility

The icon-only bell Button has an aria-label but is not wrapped in a tooltip component. This
fails the requirement that icon-only buttons provide both a screen-reader label and a visible
tooltip.
Agent Prompt
## Issue description
The notification bell is an icon-only button without a tooltip.

## Issue Context
Compliance requires icon-only buttons to include both an accessible label and a tooltip for sighted users.

## Fix Focus Areas
- src/components/golf/calendar/NotificationCenter.tsx[149-172]
- src/components/ui/tooltip.tsx[57-80]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Expand Down
6 changes: 5 additions & 1 deletion src/components/ui/page-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -837,8 +837,12 @@ function CalendarHeader({
});
};

// w-full (not flex-shrink-0) on the root: this header renders as a flex
// child of PremiumCalendarClient's horizontal header row, inside a glass
// container with overflow:hidden — it must be allowed to shrink so the
// title truncates instead of the Today pill clipping at 390px.
return (
<header className="flex items-center justify-between gap-3 px-4 md:px-6 py-4 md:py-5 flex-shrink-0 min-w-0">
<header className="flex items-center justify-between gap-3 px-4 md:px-6 py-4 md:py-5 w-full min-w-0">
{/* Left: Title + Nav */}
<div className="flex items-center gap-3 md:gap-4 min-w-0 flex-1">
<IconButton variant="default"
Expand Down
13 changes: 13 additions & 0 deletions src/lib/calendar/event-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'primary',
bgColor: 'bg-primary-50/60',
dotColor: 'bg-primary-500',
dotRingColor: 'ring-primary-500/[0.18]',
textColor: 'text-primary-800',
showText: true,
},
Expand All @@ -20,6 +21,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'primary',
bgColor: 'bg-primary-50/60',
dotColor: 'bg-primary-600',
dotRingColor: 'ring-primary-600/[0.18]',
textColor: 'text-primary-800',
showText: true,
},
Expand All @@ -29,6 +31,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'amber',
bgColor: 'bg-amber-50/60',
dotColor: 'bg-amber-500',
dotRingColor: 'ring-amber-500/[0.18]',

@coderabbitai coderabbitai Bot Jul 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep the added ring classes on the approved design-token palette.

The new styles introduce amber, stone, teal, violet, orange, sky, rose, and purple families in src/lib/calendar/event-styles.ts:34-156. These are consumed directly by src/components/golf/calendar/MobileEventCard.tsx:146, but repository guidance permits only primary-*, destructive, warm-*, and cream-*. Map these styles to approved tokens or add sanctioned semantic event tokens before merging.

As per coding guidelines, use only canonical color families: primary-*, destructive, warm-*, and cream-*.

Also applies to: 44-44, 54-54, 64-64, 74-74, 84-84, 94-94, 104-104, 114-114, 124-156

🤖 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/lib/calendar/event-styles.ts` at line 34, Update the event style
definitions in event-styles.ts, including dotRingColor and the related amber,
stone, teal, violet, orange, sky, rose, and purple entries, to use only
canonical primary-*, destructive, warm-*, or cream-* classes. Preserve each
style’s intended semantic role and ensure MobileEventCard continues consuming
valid approved token classes.

Source: Coding guidelines

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Skipped deliberately: the amber/stone/teal/violet/orange/sky/rose/purple families are pre-existing in event-styles.ts — this PR only added a dotRingColor mirroring each entry's existing dotColor family. They are the sanctioned categorical event-type palette that EventCard (desktop) has consumed all along; the canonical-family rule (helm/no-banned-color) bans the legacy green aliases (helm-green-*, sf-green, emerald-*, raw green-*, #DC2626), not data-viz accent families, and ESLint passes on this file. Collapsing 13 event types onto 4 neutral families would destroy type distinguishability — if the palette should be re-tokenized, that's a design-system-wide change, not this bug-fix PR.


Generated by Claude Code

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@njrini99-code Fair point, and I've confirmed it against the file: every one of the flagged families (amber, stone, teal, violet, orange, sky, rose, purple) already exists on the corresponding dotColor/bgColor/textColor keys prior to this PR — this diff only adds dotRingColor mirroring the existing family per entry (e.g., qualifier.dotColor: 'bg-amber-500'dotRingColor: 'ring-amber-500/[0.18]'). No new color family is introduced. Since these are the established categorical event-type accents already consumed by the desktop EventCard, flagging them under the canonical UI-token rule was out of scope for this fix. Withdrawing the concern.

textColor: 'text-amber-800',
showText: true,
},
Expand All @@ -38,6 +41,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'stone',
bgColor: 'bg-stone-100/60',
dotColor: 'bg-stone-400',
dotRingColor: 'ring-stone-400/[0.18]',
textColor: 'text-stone-700',
showText: true,
},
Expand All @@ -47,6 +51,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'teal',
bgColor: 'bg-teal-50/60',
dotColor: 'bg-teal-500',
dotRingColor: 'ring-teal-500/[0.18]',
textColor: 'text-teal-800',
showText: true,
},
Expand All @@ -56,6 +61,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'violet',
bgColor: 'bg-violet-50/60',
dotColor: 'bg-violet-500',
dotRingColor: 'ring-violet-500/[0.18]',
textColor: 'text-violet-800',
showText: true,
},
Expand All @@ -65,6 +71,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'orange',
bgColor: 'bg-orange-50/60',
dotColor: 'bg-orange-500',
dotRingColor: 'ring-orange-500/[0.18]',
textColor: 'text-orange-800',
showText: true,
},
Expand All @@ -74,6 +81,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'sky',
bgColor: 'bg-sky-50/60',
dotColor: 'bg-sky-500',
dotRingColor: 'ring-sky-500/[0.18]',
textColor: 'text-sky-800',
showText: true,
},
Expand All @@ -83,6 +91,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'rose',
bgColor: 'bg-rose-50/60',
dotColor: 'bg-rose-500',
dotRingColor: 'ring-rose-500/[0.18]',
textColor: 'text-rose-800',
showText: true,
},
Expand All @@ -92,6 +101,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'stone',
bgColor: 'bg-stone-100/50',
dotColor: 'bg-stone-300',
dotRingColor: 'ring-stone-300/[0.18]',
textColor: 'text-stone-500',
showText: false,
},
Expand All @@ -101,6 +111,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'stone',
bgColor: 'bg-stone-100/40',
dotColor: 'bg-stone-200',
dotRingColor: 'ring-stone-200/[0.18]',
textColor: 'text-stone-400',
showText: false,
},
Expand All @@ -110,6 +121,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'purple',
bgColor: 'bg-purple-50/60',
dotColor: 'bg-purple-500',
dotRingColor: 'ring-purple-500/[0.18]',
textColor: 'text-purple-800',
showText: true,
},
Expand All @@ -119,6 +131,7 @@ const eventTypeConfigs: Record<EventType, EventTypeConfig> = {
color: 'stone',
bgColor: 'bg-stone-100/60',
dotColor: 'bg-stone-400',
dotRingColor: 'ring-stone-400/[0.18]',
textColor: 'text-stone-600',
showText: true,
},
Expand Down
2 changes: 2 additions & 0 deletions src/lib/types/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export interface EventTypeConfig {
bgColor: string;
/** Small leading category dot (replaces the former left-border stripe). */
dotColor: string;
/** 3px soft halo for the dot — dotColor's family at ~18% alpha (v3 accent-dot ring). */
dotRingColor: string;
textColor: string;
showText: boolean; // false for classes/blocked time
}
Loading