Test Competition
@@ -71,7 +71,7 @@ exports[`CompetitionListItem renders with all props (live and bookmarked) 1`] =
Jan 1 – 2, 2024
diff --git a/src/components/CompetitionSelect/CompetitionSelect.tsx b/src/components/CompetitionSelect/CompetitionSelect.tsx
index fce3501..8527f9e 100644
--- a/src/components/CompetitionSelect/CompetitionSelect.tsx
+++ b/src/components/CompetitionSelect/CompetitionSelect.tsx
@@ -1,8 +1,10 @@
import { useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
+import type { ClassNamesConfig, StylesConfig } from 'react-select';
import AsyncSelect from 'react-select/async';
import { OptionProps } from 'react-select/dist/declarations/src';
import { fetchSearchCompetition } from '@/lib/api';
+import { useUserSettings } from '@/providers/UserSettingsProvider';
import { CompetitionListItem } from '../CompetitionListItem';
export interface CompetitionSelectProps {
@@ -12,6 +14,8 @@ export interface CompetitionSelectProps {
export const CompetitionSelect = ({ onSelect, className }: CompetitionSelectProps) => {
const { t } = useTranslation();
+ const { effectiveTheme } = useUserSettings();
+ const isDark = effectiveTheme === 'dark';
const loadOptions = useDebounced(async (inputValue: string) => {
try {
@@ -32,9 +36,83 @@ export const CompetitionSelect = ({ onSelect, className }: CompetitionSelectProp
[onSelect],
);
+ const classNames: ClassNamesConfig = {
+ control: (state) =>
+ [
+ // Base sizing and rounded look
+ 'min-h-[38px] rounded-md',
+ // Backgrounds
+ 'bg-white dark:bg-gray-800',
+ // Borders + focus
+ 'border border-gray-200 dark:border-gray-700',
+ state.isFocused
+ ? 'ring-1 ring-blue-500 dark:ring-blue-400 border-blue-500 dark:border-blue-400'
+ : '',
+ ].join(' '),
+ valueContainer: () => 'text-gray-900 dark:text-white',
+ singleValue: () => 'text-gray-900 dark:text-white',
+ input: () => 'text-gray-900 dark:text-white',
+ placeholder: () => 'text-gray-600 dark:text-gray-400',
+ indicatorsContainer: () => 'text-gray-500 dark:text-gray-400',
+ dropdownIndicator: (state) =>
+ [
+ 'text-gray-500 dark:text-gray-400',
+ state.isFocused ? 'text-blue-600 dark:text-blue-400' : '',
+ ].join(' '),
+ clearIndicator: () =>
+ 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300',
+ menu: () => 'bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-md',
+ menuList: () => 'bg-white dark:bg-gray-800',
+ option: (state) =>
+ [
+ 'text-gray-900 dark:text-white',
+ state.isFocused ? 'bg-gray-100 dark:bg-gray-700' : '',
+ state.isSelected ? 'bg-blue-100 dark:bg-blue-900' : '',
+ ].join(' '),
+ noOptionsMessage: () => 'text-gray-600 dark:text-gray-400',
+ };
+
+ const styles: StylesConfig = {
+ control: (base, state) => ({
+ ...base,
+ backgroundColor: 'transparent',
+ borderColor: state.isFocused ? (isDark ? '#60a5fa' : '#3b82f6') : base.borderColor,
+ boxShadow: state.isFocused ? `0 0 0 1px ${isDark ? '#60a5fa' : '#3b82f6'}` : 'none',
+ ':hover': {
+ ...base[':hover'],
+ borderColor: state.isFocused ? (isDark ? '#60a5fa' : '#3b82f6') : base.borderColor,
+ },
+ }),
+ menu: (base) => ({
+ ...base,
+ backgroundColor: 'transparent',
+ }),
+ menuList: (base) => ({
+ ...base,
+ backgroundColor: 'transparent',
+ }),
+ option: (base) => ({
+ ...base,
+ backgroundColor: 'transparent',
+ }),
+ input: (base) => ({
+ ...base,
+ color: isDark ? '#ffffff' : '#111827', // gray-900
+ }),
+ singleValue: (base) => ({
+ ...base,
+ color: isDark ? '#ffffff' : '#111827', // gray-900
+ }),
+ placeholder: (base) => ({
+ ...base,
+ color: isDark ? '#9ca3af' : '#4b5563', // dark: gray-400, light: gray-600
+ }),
+ };
+
return (
className={className}
+ classNamePrefix="cg-select"
cacheOptions
loadOptions={loadOptions}
placeholder={t('common.competitionSelect.placeholder')}
@@ -42,6 +120,8 @@ export const CompetitionSelect = ({ onSelect, className }: CompetitionSelectProp
components={{
Option: CompetitionOption,
}}
+ classNames={classNames}
+ styles={styles}
onChange={handleSelectOption}
/>
);
diff --git a/src/components/ExternalLink/ExternalLink.tsx b/src/components/ExternalLink/ExternalLink.tsx
index 0b557d5..eb2ab95 100644
--- a/src/components/ExternalLink/ExternalLink.tsx
+++ b/src/components/ExternalLink/ExternalLink.tsx
@@ -15,7 +15,7 @@ export function ExternalLink({
target="_blank"
rel="noreferrer"
className={classNames(
- 'flex align-center justify-between w-full bg-blue-200 px-4 py-2 rounded hover:opacity-80',
+ 'flex align-center justify-between w-full bg-blue-200 dark:bg-blue-700 dark:hover:bg-blue-600 px-4 py-2 rounded hover:opacity-80',
className,
)}>
{children}
diff --git a/src/components/LastFetchedAt/LastFetchedAt.tsx b/src/components/LastFetchedAt/LastFetchedAt.tsx
index 38f659a..7a7c069 100644
--- a/src/components/LastFetchedAt/LastFetchedAt.tsx
+++ b/src/components/LastFetchedAt/LastFetchedAt.tsx
@@ -9,7 +9,7 @@ export const LastFetchedAt = ({ lastFetchedAt }: LastFetchAtProps) => {
const { t } = useTranslation();
return (
-
+
{t('common.lastFetched', {
date: intlFormatDistance(lastFetchedAt, new Date(), {
locale: navigator.language,
diff --git a/src/components/LinkButton/LinkButton.tsx b/src/components/LinkButton/LinkButton.tsx
index 0b0e0f0..4b76cc4 100644
--- a/src/components/LinkButton/LinkButton.tsx
+++ b/src/components/LinkButton/LinkButton.tsx
@@ -4,20 +4,26 @@ import { Link, LinkProps } from 'react-router-dom';
export interface LinkButtonProps {
to: LinkProps['to'];
title: string;
- color: 'blue' | 'green';
+ variant?: 'blue' | 'green' | 'gray' | 'light';
className?: string;
}
-export const LinkButton = ({ to, title, color, className }: LinkButtonProps) => {
+export const LinkButton = ({ to, title, variant = 'blue', className }: LinkButtonProps) => {
+ const variantClasses = {
+ blue: 'bg-blue-200 hover:bg-blue-300 dark:bg-blue-800 dark:hover:bg-blue-600 text-gray-900 dark:text-white border-blue-300 dark:border-blue-600',
+ green:
+ 'bg-green-200 hover:bg-green-300 dark:bg-green-700 dark:hover:bg-green-600 text-gray-900 dark:text-white border-green-300 dark:border-green-600',
+ gray: 'bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-900 dark:text-white border-gray-300 dark:border-gray-600',
+ light:
+ 'bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-900 dark:text-white border-gray-300 dark:border-gray-600',
+ };
+
return (
{title}
diff --git a/src/components/Notebox/Notebox.tsx b/src/components/Notebox/Notebox.tsx
index c76f712..8ea782d 100644
--- a/src/components/Notebox/Notebox.tsx
+++ b/src/components/Notebox/Notebox.tsx
@@ -10,7 +10,7 @@ export function NoteBox({ text, prefix = 'Note', className }: NoteBoxProps) {
return (
{prefix && {prefix}: }
diff --git a/src/components/Pill/Pill.tsx b/src/components/Pill/Pill.tsx
index ab8b124..1e3f99f 100644
--- a/src/components/Pill/Pill.tsx
+++ b/src/components/Pill/Pill.tsx
@@ -13,3 +13,39 @@ export const Pill = ({ className, ...props }: PillProps) => {
/>
);
};
+
+export const BreadcrumbPill = ({ className, ...props }: PillProps) => {
+ return (
+
+ );
+};
+
+export const BaseAssignmentPill = ({ className, ...props }: PillProps) => {
+ return (
+
+ );
+};
+
+export const RoomPill = ({ className, ...props }: PillProps) => {
+ return (
+
+ );
+};
diff --git a/src/components/PinCompetitionButton/PinCompetitionButton.tsx b/src/components/PinCompetitionButton/PinCompetitionButton.tsx
index cb7f003..2e520e0 100644
--- a/src/components/PinCompetitionButton/PinCompetitionButton.tsx
+++ b/src/components/PinCompetitionButton/PinCompetitionButton.tsx
@@ -11,7 +11,7 @@ export const PinCompetitionButton = ({ competitionId }: { competitionId: string
return (
{
if (isPinned) {
unpinCompetition(competitionId);
diff --git a/src/containers/Competitors/CompetitorListItem.tsx b/src/containers/Competitors/CompetitorListItem.tsx
index 9aa8a0f..c6bd324 100644
--- a/src/containers/Competitors/CompetitorListItem.tsx
+++ b/src/containers/Competitors/CompetitorListItem.tsx
@@ -20,7 +20,7 @@ export const CompetitorListItem = ({
return (
-
+
{highlight && (
@@ -35,7 +35,7 @@ export const CompetitorListItem = ({
{person.name}
{highlight && (
-
+
{t('competition.competitors.viewMyAssignments')}{' '}
@@ -43,14 +43,14 @@ export const CompetitorListItem = ({
{bookmarked && !currentAssignmentCode && (
-
-
+
+
)}
{currentAssignmentCode ? (
diff --git a/src/containers/Competitors/Competitors.tsx b/src/containers/Competitors/Competitors.tsx
index 627f100..c30863c 100644
--- a/src/containers/Competitors/Competitors.tsx
+++ b/src/containers/Competitors/Competitors.tsx
@@ -50,7 +50,7 @@ export const Competitors = ({ wcif }: { wcif: Competition }) => {
);
return (
-
+
{me && (
{
-
+
{t('common.search')}
setInput(e.target.value)}
diff --git a/src/containers/PersonalBests/PersonalBests.tsx b/src/containers/PersonalBests/PersonalBests.tsx
index d40c341..c727403 100644
--- a/src/containers/PersonalBests/PersonalBests.tsx
+++ b/src/containers/PersonalBests/PersonalBests.tsx
@@ -19,17 +19,17 @@ export function PersonalBestsContainer({ wcif, person }: PersonalBestsContainerP
return (
<>
-
-
+
+
-
+
-
-
- {t('competition.personalRecords.type')}
+
+
+ {t('competition.personalRecords.type')}
{t('competition.personalRecords.best')}
{t('common.wca.recordType.WR')}
{t('common.wca.recordType.CR')}
{t('common.wca.recordType.NR')}
-
+
{wcif.events
.filter((event) => person?.registration?.eventIds.includes(event.id))
.map((event) => {
@@ -65,7 +65,9 @@ export function PersonalBestsContainer({ wcif, person }: PersonalBestsContainerP
return (
-
+
-
+
diff --git a/src/containers/PersonalSchedule/Assignments.tsx b/src/containers/PersonalSchedule/Assignments.tsx
index d6ac05c..46a303b 100644
--- a/src/containers/PersonalSchedule/Assignments.tsx
+++ b/src/containers/PersonalSchedule/Assignments.tsx
@@ -59,11 +59,11 @@ export function Assignments({ wcif, person, showStationNumber }: AssignmentsProp
return (
<>
-
+
-
-
+
+
{t('competition.personalSchedule.activity')}
{t('competition.personalSchedule.time')}
@@ -87,9 +87,9 @@ export function Assignments({ wcif, person, showStationNumber }: AssignmentsProp
toggleDate(date)}>
+ className="text-base font-bold cursor-pointer select-none md:text-lg bg-slate-50 dark:bg-gray-800">
-
+
{dateParts.find((i) => i.type === 'weekday')?.value || date}
{collapsed ? ' ▼' : ' ▲'}
diff --git a/src/containers/PersonalSchedule/PersonHeader.tsx b/src/containers/PersonalSchedule/PersonHeader.tsx
index c6ceba3..c8ffafb 100644
--- a/src/containers/PersonalSchedule/PersonHeader.tsx
+++ b/src/containers/PersonalSchedule/PersonHeader.tsx
@@ -46,11 +46,11 @@ export const PersonHeader: React.FC = ({ person }) => {
const avatarUrl = person?.avatar?.thumbUrl || person?.avatar?.url || fallbackAvatarUrl;
- const avatar = ;
+ const avatar = ;
return (
<>
-
+
{person.wcaId ? (
{avatar}
@@ -59,12 +59,13 @@ export const PersonHeader: React.FC
= ({ person }) => {
avatar
)}
-
+
{person.name}
{person.registrantId}
{
if (isPinned) {
unpinPerson(person.registrantId);
@@ -82,7 +83,7 @@ export const PersonHeader: React.FC = ({ person }) => {
)}
- {person.wcaId &&
{person.wcaId} }
+ {person.wcaId &&
{person.wcaId} }
@@ -96,11 +97,10 @@ export const PersonHeader: React.FC = ({ person }) => {
{person.wcaId && (
<>
-
-
+
+
diff --git a/src/containers/PersonalSchedule/PersonalExtraAssignment.tsx b/src/containers/PersonalSchedule/PersonalExtraAssignment.tsx
index d776597..4519750 100644
--- a/src/containers/PersonalSchedule/PersonalExtraAssignment.tsx
+++ b/src/containers/PersonalSchedule/PersonalExtraAssignment.tsx
@@ -1,5 +1,5 @@
import classNames from 'classnames';
-import { Pill } from '@/components/Pill';
+import { BaseAssignmentPill } from '@/components/Pill';
import { worldsAssignmentMap } from './constants';
export interface ExtraAssignmentProps {
@@ -39,10 +39,13 @@ export const ExtraAssignment = ({
return (
+ className={classNames(
+ 'table-row text-xs sm:text-sm hover:bg-slate-100 dark:hover:bg-gray-700 border-y border-gray-200 dark:border-gray-700',
+ {
+ 'opacity-40': isOver,
+ 'bg-op': isCurrent,
+ },
+ )}>
{formattedStartTime} - {formattedEndTime}
@@ -51,14 +54,14 @@ export const ExtraAssignment = ({
{room ? (
-
-
+
{room.name}
-
+
) : (
diff --git a/src/containers/PersonalSchedule/PersonalNormalAssignment.tsx b/src/containers/PersonalSchedule/PersonalNormalAssignment.tsx
index a8bd02f..dd18a7b 100644
--- a/src/containers/PersonalSchedule/PersonalNormalAssignment.tsx
+++ b/src/containers/PersonalSchedule/PersonalNormalAssignment.tsx
@@ -2,7 +2,7 @@ import { Assignment } from '@wca/helpers';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { AssignmentLabel } from '@/components';
-import { Pill } from '@/components/Pill';
+import { BaseAssignmentPill } from '@/components/Pill';
import { parseActivityCodeFlexible } from '@/lib/activityCodes';
import { ActivityWithRoomOrParent } from '@/lib/types';
import { roundTime } from '@/lib/utils';
@@ -59,32 +59,35 @@ export const PersonalNormalAssignment = ({
backgroundColor: `${room.color}25`,
}),
}}
- className={classNames('table-row text-xs sm:text-sm hover:bg-slate-100', {
- 'opacity-40': isOver,
- 'bg-op': isCurrent,
- 'border-t': showTopBorder,
- 'border-b': showBottomBorder,
- })}
+ className={classNames(
+ 'table-row text-xs sm:text-sm hover:bg-slate-100 dark:hover:bg-gray-700',
+ {
+ 'opacity-40': isOver,
+ 'bg-op': isCurrent,
+ 'border-t border-gray-200 dark:border-gray-700': showTopBorder,
+ 'border-b border-gray-200 dark:border-gray-700': showBottomBorder,
+ },
+ )}
to={`/competitions/${competitionId}/activities/${assignment.activityId}`}>
{showTopBorder && (
-
+
{formatBriefActivityName(activity)}
)}
{formattedStartTime}
-
+
- {groupNumber}
+ {groupNumber}
{showRoom && (
-
-
+
{room.name}
-
+
)}
{showStationNumber && {assignment.stationNumber} }
diff --git a/src/containers/PersonalSchedule/PersonalSchedule.tsx b/src/containers/PersonalSchedule/PersonalSchedule.tsx
index a9d5db9..9a64ba9 100644
--- a/src/containers/PersonalSchedule/PersonalSchedule.tsx
+++ b/src/containers/PersonalSchedule/PersonalSchedule.tsx
@@ -17,12 +17,12 @@ export function PersonalScheduleContainer({ person }: PersonalScheduleContainerP
const anyAssignmentsHasStationNumber = !!person.assignments?.some((a) => a.stationNumber);
return (
-