Diff to HTML by rtfpessoa

Files changed (217) hide show
  1. frontend/cypress/global.d.ts +3 -3
  2. frontend/cypress/support/auth-provider-commands/cognito.ts +1 -1
  3. frontend/src/App.tsx +1 -1
  4. frontend/src/ErrorBoundary.tsx +1 -1
  5. frontend/src/ThemeProvider.tsx +1 -1
  6. frontend/src/api/Api.tsx +1 -1
  7. frontend/src/api/Request.tsx +2 -2
  8. frontend/src/api/cache/Cache.tsx +2 -2
  9. frontend/src/api/clubApi.ts +10 -10
  10. frontend/src/api/courseApi.ts +5 -5
  11. frontend/src/api/emailApi.ts +2 -2
  12. frontend/src/api/eventApi.ts +10 -10
  13. frontend/src/api/examApi.tsx +2 -2
  14. frontend/src/api/explorerApi.tsx +4 -4
  15. frontend/src/api/gameApi.ts +19 -19
  16. frontend/src/api/graduationApi.ts +5 -5
  17. frontend/src/api/newsfeedApi.tsx +6 -6
  18. frontend/src/api/notificationApi.ts +4 -4
  19. frontend/src/api/paymentApi.ts +2 -2
  20. frontend/src/api/requirementApi.ts +5 -5
  21. frontend/src/api/scoreboardApi.ts +7 -7
  22. frontend/src/api/tournamentApi.ts +7 -7
  23. frontend/src/api/userApi.ts +17 -17
  24. frontend/src/api/yearReviewApi.ts +2 -2
  25. frontend/src/auth/ForgotPasswordPage.tsx +5 -5
  26. frontend/src/auth/SigninPage.tsx +4 -4
  27. frontend/src/auth/SignupPage.tsx +3 -3
  28. frontend/src/auth/VerifyEmailPage.tsx +2 -2
  29. frontend/src/board/Board.tsx +7 -7
  30. frontend/src/board/pgn/KeyboardHandler.tsx +1 -1
  31. frontend/src/board/pgn/Nag.ts +3 -3
  32. frontend/src/board/pgn/PgnBoard.tsx +3 -3
  33. frontend/src/board/pgn/PlayerHeader.tsx +8 -8
  34. frontend/src/board/pgn/VariationDialog.tsx +7 -7
  35. frontend/src/board/pgn/annotations/AnnotationWarnings.tsx +4 -4
  36. frontend/src/board/pgn/annotations/warningRules.ts +1 -1
  37. frontend/src/board/pgn/boardTools/boardButtons/StatusIcon.tsx +2 -2
  38. frontend/src/board/pgn/boardTools/underboard/ClockEditor.tsx +5 -5
  39. frontend/src/board/pgn/boardTools/underboard/ClockTextField.tsx +11 -11
  40. frontend/src/board/pgn/boardTools/underboard/ClockUsage.tsx +5 -5
  41. frontend/src/board/pgn/boardTools/underboard/Editor.tsx +15 -15
  42. frontend/src/board/pgn/boardTools/underboard/Tags.tsx +3 -3
  43. frontend/src/board/pgn/boardTools/underboard/Underboard.tsx +1 -1
  44. frontend/src/board/pgn/boardTools/underboard/comments/Comment.tsx +10 -10
  45. frontend/src/board/pgn/boardTools/underboard/comments/CommentEditor.tsx +2 -2
  46. frontend/src/board/pgn/boardTools/underboard/comments/Comments.tsx +5 -5
  47. frontend/src/board/pgn/boardTools/underboard/comments/ReplyEditor.tsx +1 -1
  48. frontend/src/board/pgn/boardTools/underboard/settings/EditorSettings.tsx +1 -1
  49. frontend/src/board/pgn/boardTools/underboard/settings/GameSettings.tsx +3 -3
  50. frontend/src/board/pgn/boardTools/underboard/settings/KeyboardShortcuts.tsx +2 -2
  51. frontend/src/board/pgn/boardTools/underboard/settings/RequestReviewDialog.tsx +4 -4
  52. frontend/src/board/pgn/boardTools/underboard/settings/ViewerSettings.tsx +8 -8
  53. frontend/src/board/pgn/explorer/Database.tsx +5 -5
  54. frontend/src/board/pgn/explorer/Explorer.tsx +2 -2
  55. frontend/src/board/pgn/explorer/Header.tsx +5 -5
  56. frontend/src/board/pgn/pgnText/Comment.tsx +1 -1
  57. frontend/src/board/pgn/pgnText/GameComment.tsx +2 -3
  58. frontend/src/board/pgn/pgnText/Interrupt.tsx +1 -1
  59. frontend/src/board/pgn/pgnText/Lines.tsx +3 -3
  60. frontend/src/board/pgn/pgnText/MoveButton.tsx +4 -4
  61. frontend/src/board/pgn/pgnText/MoveDisplay.tsx +1 -1
  62. frontend/src/board/pgn/pgnText/Result.tsx +1 -1
  63. frontend/src/board/pgn/pgnText/Variation.tsx +1 -1
  64. frontend/src/board/puzzle/HintSection.tsx +3 -3
  65. frontend/src/board/puzzle/PuzzleBoard.tsx +3 -3
  66. frontend/src/calendar/AvailabilityBooker.tsx +5 -5
  67. frontend/src/calendar/CalendarPage.tsx +1 -1
  68. frontend/src/calendar/CoachingBooker.tsx +1 -1
  69. frontend/src/calendar/EventBooker.tsx +3 -3
  70. frontend/src/calendar/eventEditor/AvailabilityEditor.tsx +3 -3
  71. frontend/src/calendar/eventEditor/CoachingEditor.tsx +8 -8
  72. frontend/src/calendar/eventEditor/DojoEventEditor.tsx +1 -1
  73. frontend/src/calendar/eventEditor/EventEditor.tsx +1 -1
  74. frontend/src/calendar/eventEditor/form/CohortsFormSection.tsx +2 -2
  75. frontend/src/calendar/eventEditor/form/DescriptionFormSection.tsx +1 -1
  76. frontend/src/calendar/eventEditor/form/LocationFormSection.tsx +1 -1
  77. frontend/src/calendar/eventEditor/form/MaxParticipantsFormSection.tsx +1 -1
  78. frontend/src/calendar/eventEditor/form/TimesFormSection.tsx +2 -2
  79. frontend/src/calendar/eventEditor/form/TitleFormSection.tsx +1 -1
  80. frontend/src/calendar/eventEditor/useEventEditor.ts +10 -10
  81. frontend/src/calendar/eventViewer/CoachingViewer.tsx +1 -1
  82. frontend/src/calendar/eventViewer/LigaTournamentViewer.tsx +1 -1
  83. frontend/src/calendar/eventViewer/MeetingViewer.tsx +1 -1
  84. frontend/src/calendar/filters/CalendarFilters.tsx +20 -20
  85. frontend/src/calendar/filters/TimezoneFilter.tsx +7 -7
  86. frontend/src/clubs/ClubDetailsPage.tsx +8 -8
  87. frontend/src/clubs/ClubFilters.tsx +3 -3
  88. frontend/src/clubs/ClubGrid.tsx +1 -1
  89. frontend/src/clubs/ClubJoinRequestDialog.tsx +1 -1
  90. frontend/src/clubs/CreateClubPage.tsx +10 -10
  91. frontend/src/clubs/JoinRequestsTab.tsx +3 -3
  92. frontend/src/clubs/ListClubsPage.tsx +2 -2
  93. frontend/src/coaching/coaches/courseEditor/CourseEditorPage.tsx +35 -35
  94. frontend/src/coaching/coaches/courseEditor/PurchaseCoursePreview.tsx +2 -2
  95. frontend/src/coaching/customers/CoachesTab.tsx +2 -2
  96. frontend/src/coaching/customers/CoachingList.tsx +1 -1
  97. frontend/src/coaching/customers/CoachingPage.tsx +1 -1
  98. frontend/src/courses/list/CourseFilters.tsx +5 -5
  99. frontend/src/courses/list/CourseListItem.tsx +1 -1
  100. frontend/src/courses/localStorage.ts +1 -1
  101. frontend/src/courses/view/CoursePage.tsx +7 -7
  102. frontend/src/courses/view/ExercisesModule.tsx +1 -4
  103. frontend/src/courses/view/PgnSelector.tsx +3 -3
  104. frontend/src/database/club.ts +2 -6
  105. frontend/src/database/graduation.ts +1 -3
  106. frontend/src/database/notification.ts +7 -13
  107. frontend/src/database/requirement.ts +7 -18
  108. frontend/src/database/scoreboard.ts +2 -3
  109. frontend/src/database/statistics.ts +3 -9
  110. frontend/src/database/user.ts +5 -7
  111. frontend/src/dojoDigest/UnsubscribePage.tsx +1 -1
  112. frontend/src/games/edit/EditGamePage.tsx +1 -1
  113. frontend/src/games/edit/GameSubmissionForm.tsx +11 -11
  114. frontend/src/games/edit/SubmitGamePreflight.tsx +13 -13
  115. frontend/src/games/list/ListGamesPage.tsx +4 -4
  116. frontend/src/games/list/SearchFilters.tsx +9 -9
  117. frontend/src/games/list/pagination.ts +2 -2
  118. frontend/src/games/review/ReviewQueuePage.tsx +4 -4
  119. frontend/src/games/view/DeleteGameButton.tsx +2 -2
  120. frontend/src/games/view/GamePage.tsx +2 -2
  121. frontend/src/games/view/PgnErrorBoundary.tsx +2 -3
  122. frontend/src/help/AuthenticatedHelp.tsx +11 -11
  123. frontend/src/help/SupportTicket.tsx +4 -4
  124. frontend/src/help/UnauthenticatedHelp.tsx +3 -3
  125. frontend/src/index.tsx +1 -1
  126. frontend/src/landing/JoinToday.tsx +2 -2
  127. frontend/src/landing/LandingPage.tsx +2 -2
  128. frontend/src/landing/WhatsIncluded.tsx +1 -1
  129. frontend/src/material/MemorizeGamesPage.tsx +1 -1
  130. frontend/src/material/ModelGamesPage.tsx +1 -1
  131. frontend/src/meeting/CancelMeetingButton.tsx +3 -3
  132. frontend/src/meeting/ListMeetingsPage.tsx +1 -1
  133. frontend/src/meeting/MeetingMessages.tsx +2 -2
  134. frontend/src/meeting/MeetingPage.tsx +1 -1
  135. frontend/src/navbar/NavbarMenu.tsx +36 -36
  136. frontend/src/navbar/PawnIcon.tsx +2 -2
  137. frontend/src/navbar/ProfileButton.tsx +3 -3
  138. frontend/src/navbar/UnauthenticatedMenu.tsx +17 -17
  139. frontend/src/newsfeed/detail/CommentEditor.tsx +1 -1
  140. frontend/src/newsfeed/detail/NewsfeedDetailPage.tsx +2 -2
  141. frontend/src/newsfeed/detail/ReactionList.tsx +2 -2
  142. frontend/src/newsfeed/list/MultipleSelectChip.tsx +1 -1
  143. frontend/src/newsfeed/list/NewsfeedList.tsx +2 -2
  144. frontend/src/profile/GamesTab.tsx +2 -2
  145. frontend/src/profile/GraduationDialog.tsx +3 -3
  146. frontend/src/profile/ProfilePage.tsx +4 -4
  147. frontend/src/profile/SwitchCohortPrompt.tsx +4 -4
  148. frontend/src/profile/activity/ActivityPieChart.tsx +3 -3
  149. frontend/src/profile/activity/ActivityTimeline.tsx +2 -2
  150. frontend/src/profile/activity/PieChart.tsx +1 -1
  151. frontend/src/profile/activity/activity.ts +9 -9
  152. frontend/src/profile/creator/DiscordForm.tsx +3 -3
  153. frontend/src/profile/creator/ExtraRatingSystemsForm.tsx +6 -6
  154. frontend/src/profile/creator/PersonalInfoForm.tsx +3 -3
  155. frontend/src/profile/creator/PreferredRatingSystemForm.tsx +3 -3
  156. frontend/src/profile/creator/ProfileCreatorPage.tsx +3 -3
  157. frontend/src/profile/creator/ReferralSourceForm.tsx +2 -2
  158. frontend/src/profile/editor/NotificationSettingsEditor.tsx +3 -3
  159. frontend/src/profile/editor/ProfileEditorPage.tsx +37 -37
  160. frontend/src/profile/editor/SubscriptionManager.tsx +2 -2
  161. frontend/src/profile/followers/FollowersList.tsx +1 -1
  162. frontend/src/profile/info/CoachChip.tsx +1 -1
  163. frontend/src/profile/info/DiscordChip.tsx +2 -2
  164. frontend/src/profile/progress/CustomTaskEditor.tsx +9 -9
  165. frontend/src/profile/progress/CustomTaskProgressItem.tsx +4 -4
  166. frontend/src/profile/progress/ProgressCategory.tsx +3 -3
  167. frontend/src/profile/progress/ProgressDialog.tsx +3 -3
  168. frontend/src/profile/progress/ProgressHistory.tsx +7 -7
  169. frontend/src/profile/progress/ProgressItem.tsx +9 -9
  170. frontend/src/profile/progress/ProgressTab.tsx +5 -5
  171. frontend/src/profile/progress/ProgressUpdater.tsx +6 -6
  172. frontend/src/profile/stats/RatingCard.tsx +2 -2
  173. frontend/src/profile/yearReview/DojoPointSection.tsx +3 -3
  174. frontend/src/profile/yearReview/TimeSection.tsx +1 -1
  175. frontend/src/profile/yearReview/YearReviewPage.tsx +2 -2
  176. frontend/src/react-app-env.d.ts +3 -3
  177. frontend/src/recent/FeaturedGames.tsx +1 -1
  178. frontend/src/recent/RecentGraduates.tsx +3 -3
  179. frontend/src/requirements/CustomTaskDisplay.tsx +4 -4
  180. frontend/src/requirements/Position.tsx +1 -1
  181. frontend/src/requirements/RequirementDisplay.tsx +10 -11
  182. frontend/src/requirements/RequirementPage.tsx +2 -2
  183. frontend/src/scoreboard/Scoreboard.tsx +3 -3
  184. frontend/src/scoreboard/ScoreboardCheck.tsx +2 -2
  185. frontend/src/scoreboard/ScoreboardPage.tsx +2 -2
  186. frontend/src/scoreboard/ScoreboardProgress.tsx +2 -2
  187. frontend/src/scoreboard/ScoreboardViewSelector.tsx +2 -2
  188. frontend/src/scoreboard/club/ClubScoreboardPage.tsx +2 -2
  189. frontend/src/scoreboard/scoreboardData.tsx +3 -3
  190. frontend/src/scoreboard/search/SeachPage.tsx +18 -18
  191. frontend/src/style/style.ts +4 -4
  192. frontend/src/tactics/CompletedTacticsExamPgnSelector.tsx +2 -2
  193. frontend/src/tactics/ExamStatistics.tsx +2 -2
  194. frontend/src/tactics/TacticsExamPage.tsx +7 -7
  195. frontend/src/tactics/TacticsExamPgnSelector.tsx +8 -8
  196. frontend/src/tactics/instructions/TacticsInstructionsPage.tsx +1 -1
  197. frontend/src/tactics/list/ExamsTable.tsx +3 -3
  198. frontend/src/tactics/list/ListTacticsExamsPage.tsx +1 -1
  199. frontend/src/tactics/tactics.ts +6 -6
  200. frontend/src/tournaments/LeaderboardTab.tsx +5 -5
  201. frontend/src/tournaments/TournamentCalendarFilters.tsx +6 -6
  202. frontend/src/tournaments/TournamentsPage.tsx +1 -1
  203. frontend/src/tournaments/openClassical/DetailsPage.tsx +5 -5
  204. frontend/src/tournaments/openClassical/PairingsTable.tsx +1 -1
  205. frontend/src/tournaments/openClassical/RegistrationPage.tsx +14 -14
  206. frontend/src/tournaments/openClassical/SubmitResultsPage.tsx +10 -10
  207. frontend/src/tournaments/openClassical/admin/AdminPage.tsx +1 -1
  208. frontend/src/tournaments/openClassical/admin/BannedPlayersTab.tsx +3 -3
  209. frontend/src/tournaments/openClassical/admin/CompleteTournament.tsx +5 -5
  210. frontend/src/tournaments/openClassical/admin/Editor.tsx +6 -6
  211. frontend/src/tournaments/openClassical/admin/EmailPairingsButton.tsx +1 -1
  212. frontend/src/tournaments/openClassical/admin/PairingsTab.tsx +10 -10
  213. frontend/src/tournaments/openClassical/admin/PlayersTab.tsx +5 -5
  214. frontend/src/tutorial/Tutorial.tsx +2 -2
  215. frontend/src/upsell/PriceMatrix.tsx +2 -2
  216. frontend/src/upsell/UpsellDialog.tsx +2 -2
  217. frontend/src/upsell/UpsellPage.tsx +1 -1
frontend/cypress/global.d.ts CHANGED
@@ -10,7 +10,7 @@ declare global {
10
10
  * @param dataCyAttribute The value of the data-cy attribute to get.
11
11
  * @param args Optional args to pass to cy.get()
12
12
  */
13
- getBySel(dataCyAttribute: string, args?: any): Chainable<JQuery<HTMLElement>>;
13
+ getBySel(dataCyAttribute: string, args?: any): Chainable<JQuery>;
14
14
 
15
15
  /**
16
16
  * Finds an element based on its data-cy attribute.
@@ -20,7 +20,7 @@ declare global {
20
20
  findBySel(
21
21
  dataCyAttribute: string,
22
22
  args?: any
23
- ): Chainable<JQuery<HTMLElement>>;
23
+ ): Chainable<JQuery>;
24
24
 
25
25
  /**
26
26
  * Logs into AWS Cognito via the Amplify Auth API, bypassing the login screen.
@@ -32,7 +32,7 @@ declare global {
32
32
  sessionId: string,
33
33
  email: string,
34
34
  password: string
35
- ): Chainable<any>;
35
+ ): Chainable;
36
36
 
37
37
  /**
38
38
  * Intercepts a request to the site's API.
frontend/cypress/support/auth-provider-commands/cognito.ts CHANGED
@@ -60,7 +60,7 @@ Cypress.Commands.add('loginByCognitoApi', (sessionId, email, password) => {
60
60
  cy.session(
61
61
  `${sessionId}-${email}`,
62
62
  () => {
63
- return loginToCognito(email, password);
63
+ loginToCognito(email, password);
64
64
  },
65
65
  {
66
66
  validate() {
frontend/src/App.tsx CHANGED
@@ -253,7 +253,7 @@ function App() {
253
253
  <ThemeProvider>
254
254
  <LocalizationProvider
255
255
  dateAdapter={AdapterLuxon}
256
- adapterLocale={navigator.languages?.[0]}
256
+ adapterLocale={navigator.languages[0]}
257
257
  >
258
258
  <RouterProvider router={router} />
259
259
  </LocalizationProvider>
frontend/src/ErrorBoundary.tsx CHANGED
@@ -10,7 +10,7 @@ interface ErrorBoundaryState {
10
10
  info: any;
11
11
  }
12
12
 
13
- class ErrorBoundary extends Component<any, ErrorBoundaryState, any> {
13
+ class ErrorBoundary extends Component<any, ErrorBoundaryState> {
14
14
  constructor(props: any) {
15
15
  super(props);
16
16
  this.state = { hasError: false, error: null, info: null };
frontend/src/ThemeProvider.tsx CHANGED
@@ -50,7 +50,7 @@ export function useLightMode(): boolean {
50
50
  export function useWindowSizeEffect(handler: () => void) {
51
51
  useEffect(() => {
52
52
  window.addEventListener('resize', handler);
53
- return () => window.removeEventListener('resize', handler);
53
+ return () => { window.removeEventListener('resize', handler); };
54
54
  }, [handler]);
55
55
  }
56
56
 
frontend/src/api/Api.tsx CHANGED
@@ -186,7 +186,7 @@ export function useApi() {
186
186
  */
187
187
  export function ApiProvider({ children }: { children: ReactNode }) {
188
188
  const auth = useAuth();
189
- const idToken = auth.user?.cognitoUser?.session?.idToken.jwtToken ?? '';
189
+ const idToken = auth.user?.cognitoUser?.session.idToken.jwtToken ?? '';
190
190
 
191
191
  const value = useMemo(() => {
192
192
  return {
frontend/src/api/Request.tsx CHANGED
@@ -132,12 +132,12 @@ export function RequestSnackbar<T = any>({
132
132
  defaultErrorMessage,
133
133
  defaultSuccessMessage,
134
134
  }: RequestSnackbarProps<T>) {
135
- let displayError =
135
+ const displayError =
136
136
  (showError === undefined || showError) &&
137
137
  request.status === RequestStatus.Failure &&
138
138
  request.error !== undefined;
139
139
 
140
- let displaySuccess = showSuccess && request.data !== undefined;
140
+ const displaySuccess = showSuccess && request.data !== undefined;
141
141
 
142
142
  let errorMessage =
143
143
  request.error?.response?.data?.message ||
frontend/src/api/cache/Cache.tsx CHANGED
@@ -119,7 +119,7 @@ function useIdentifiableCache<T>(key?: string): IdentifiableCache<T> {
119
119
  /**
120
120
  * CacheContextType defines the type of the cache as available through CacheProvider
121
121
  */
122
- type CacheContextType = {
122
+ interface CacheContextType {
123
123
  isLoading: boolean;
124
124
  setIsLoading: (arg: boolean) => void;
125
125
 
@@ -131,7 +131,7 @@ type CacheContextType = {
131
131
 
132
132
  imageBypass: number;
133
133
  setImageBypass: (v: number) => void;
134
- };
134
+ }
135
135
 
136
136
  const CacheContext = createContext<CacheContextType>(null!);
137
137
 
frontend/src/api/clubApi.ts CHANGED
@@ -8,13 +8,13 @@ import { User } from '../database/user';
8
8
  const BASE_URL = getConfig().api.baseUrl;
9
9
 
10
10
  /** Provides an API for interacting with clubs. */
11
- export type ClubApiContextType = {
11
+ export interface ClubApiContextType {
12
12
  /**
13
13
  * Creates the given club.
14
14
  * @param club The club to create.
15
15
  * @returns An AxiosResponse containing the created club.
16
16
  */
17
- createClub: (club: Partial<Club>) => Promise<AxiosResponse<ClubDetails, any>>;
17
+ createClub: (club: Partial<Club>) => Promise<AxiosResponse<ClubDetails>>;
18
18
 
19
19
  /**
20
20
  * Updates the club with the given id.
@@ -25,7 +25,7 @@ export type ClubApiContextType = {
25
25
  updateClub: (
26
26
  id: string,
27
27
  update: Partial<Club>,
28
- ) => Promise<AxiosResponse<ClubDetails, any>>;
28
+ ) => Promise<AxiosResponse<ClubDetails>>;
29
29
 
30
30
  /**
31
31
  * Fetches the full list of clubs in the database.
@@ -43,7 +43,7 @@ export type ClubApiContextType = {
43
43
  getClub: (
44
44
  id: string,
45
45
  scoreboard?: boolean,
46
- ) => Promise<AxiosResponse<GetClubResponse, any>>;
46
+ ) => Promise<AxiosResponse<GetClubResponse>>;
47
47
 
48
48
  /**
49
49
  * Fetches the clubs with the given ids.
@@ -59,7 +59,7 @@ export type ClubApiContextType = {
59
59
  * @returns An AxiosResponse containing the club's updated details and
60
60
  * additional scoreboard entries.
61
61
  */
62
- joinClub: (id: string) => Promise<AxiosResponse<GetClubResponse, any>>;
62
+ joinClub: (id: string) => Promise<AxiosResponse<GetClubResponse>>;
63
63
 
64
64
  /**
65
65
  * Sends a request to join the given club.
@@ -70,7 +70,7 @@ export type ClubApiContextType = {
70
70
  requestToJoinClub: (
71
71
  id: string,
72
72
  notes: string,
73
- ) => Promise<AxiosResponse<ClubDetails, any>>;
73
+ ) => Promise<AxiosResponse<ClubDetails>>;
74
74
 
75
75
  /**
76
76
  * Applies the given status to the given club join request.
@@ -83,15 +83,15 @@ export type ClubApiContextType = {
83
83
  clubId: string,
84
84
  username: string,
85
85
  status: ClubJoinRequestStatus,
86
- ) => Promise<AxiosResponse<GetClubResponse, any>>;
86
+ ) => Promise<AxiosResponse<GetClubResponse>>;
87
87
 
88
88
  /**
89
89
  * Leaves the club with the given id.
90
90
  * @param clubId The id of the club to leave.
91
91
  * @returns An AxiosResponse containing the club's updated details.
92
92
  */
93
- leaveClub: (clubId: string) => Promise<AxiosResponse<ClubDetails, any>>;
94
- };
93
+ leaveClub: (clubId: string) => Promise<AxiosResponse<ClubDetails>>;
94
+ }
95
95
 
96
96
  /**
97
97
  * Creates the given club.
@@ -129,7 +129,7 @@ interface ListClubsResponse {
129
129
  * @returns A list of all clubs in the database.
130
130
  */
131
131
  export async function listClubs(startKey?: string) {
132
- let params = { startKey };
132
+ const params = { startKey };
133
133
  const result: Club[] = [];
134
134
 
135
135
  do {
frontend/src/api/courseApi.ts CHANGED
@@ -8,7 +8,7 @@ const BASE_URL = getConfig().api.baseUrl;
8
8
  /**
9
9
  * CourseApiContextType provides an API for interacting with Courses.
10
10
  */
11
- export type CourseApiContextType = {
11
+ export interface CourseApiContextType {
12
12
  /**
13
13
  * getCourse returns the course with the provided type and id.
14
14
  * @param type The type of the course.
@@ -57,8 +57,8 @@ export type CourseApiContextType = {
57
57
  * @param course The Course to save.
58
58
  * @returns An AxiosResponse containing the Course as saved in the database.
59
59
  */
60
- setCourse: (course: Course) => Promise<AxiosResponse<Course, any>>;
61
- };
60
+ setCourse: (course: Course) => Promise<AxiosResponse<Course>>;
61
+ }
62
62
 
63
63
  /** A response to a getCourse request. */
64
64
  export interface GetCourseResponse {
@@ -172,11 +172,11 @@ export function purchaseCourse(
172
172
  purchaseOption?: string,
173
173
  cancelUrl?: string
174
174
  ) {
175
- let url = idToken
175
+ const url = idToken
176
176
  ? `${BASE_URL}/courses/${type}/${id}/purchase`
177
177
  : `${BASE_URL}/public/courses/${type}/${id}/purchase`;
178
178
 
179
- let headers = idToken
179
+ const headers = idToken
180
180
  ? {
181
181
  Authorization: 'Bearer ' + idToken,
182
182
  }
frontend/src/api/emailApi.ts CHANGED
@@ -4,11 +4,11 @@ import { getConfig } from '../config';
4
4
 
5
5
  const BASE_URL = getConfig().api.baseUrl;
6
6
 
7
- export type EmailApiContextType = {
7
+ export interface EmailApiContextType {
8
8
  createSupportTicket: (
9
9
  request: SupportTicketRequest,
10
10
  ) => Promise<AxiosResponse<SupportTicketResponse>>;
11
- };
11
+ }
12
12
 
13
13
  export interface SupportTicketRequest {
14
14
  name: string;
frontend/src/api/eventApi.ts CHANGED
@@ -9,7 +9,7 @@ const BASE_URL = getConfig().api.baseUrl;
9
9
  /**
10
10
  * EventApiContextType provides an API for interacting with Events.
11
11
  */
12
- export type EventApiContextType = {
12
+ export interface EventApiContextType {
13
13
  /**
14
14
  * bookEvent books the provided Event. If the Event is 1 on 1, then startTime
15
15
  * and type can also be included in the request.
@@ -22,35 +22,35 @@ export type EventApiContextType = {
22
22
  id: string,
23
23
  startTime?: Date,
24
24
  type?: string
25
- ) => Promise<AxiosResponse<BookEventResponse, any>>;
25
+ ) => Promise<AxiosResponse<BookEventResponse>>;
26
26
 
27
27
  /**
28
28
  * Returns a Stripe Checkout URL for the given Event. The caller must already be a particpiant of the Event.
29
29
  * @param id The id of the Event to get the Checkout URL for.
30
30
  * @returns The Stripe Checkout URL for the Event.
31
31
  */
32
- getEventCheckout: (id: string) => Promise<AxiosResponse<CheckoutEventResponse, any>>;
32
+ getEventCheckout: (id: string) => Promise<AxiosResponse<CheckoutEventResponse>>;
33
33
 
34
34
  /**
35
35
  * cancelEvent cancels the Event with the provided id.
36
36
  * @param id The Event id to cancel.
37
37
  * @returns An AxiosReponse containing the updated Event.
38
38
  */
39
- cancelEvent: (id: string) => Promise<AxiosResponse<Event, any>>;
39
+ cancelEvent: (id: string) => Promise<AxiosResponse<Event>>;
40
40
 
41
41
  /**
42
42
  * deleteEvent deletes the provided Event from the database.
43
43
  * @param id The id of the Event to delete.
44
44
  * @returns An AxiosResponse containing the deleted Event.
45
45
  */
46
- deleteEvent: (id: string) => Promise<AxiosResponse<Event, any>>;
46
+ deleteEvent: (id: string) => Promise<AxiosResponse<Event>>;
47
47
 
48
48
  /**
49
49
  * getEvent returns the Event with the provided id.
50
50
  * @param id The Event id to fetch.
51
51
  * @returns An AxiosResponse containing the Event.
52
52
  */
53
- getEvent: (id: string) => Promise<AxiosResponse<Event, any>>;
53
+ getEvent: (id: string) => Promise<AxiosResponse<Event>>;
54
54
 
55
55
  /**
56
56
  * listEvents returns a list of all upcoming Events. If the current user is not logged in,
@@ -65,7 +65,7 @@ export type EventApiContextType = {
65
65
  * @param event The Event to save.
66
66
  * @returns An AxiosResponse containing the Event as saved in the database.
67
67
  */
68
- setEvent: (event: Event) => Promise<AxiosResponse<Event, any>>;
68
+ setEvent: (event: Event) => Promise<AxiosResponse<Event>>;
69
69
 
70
70
  /**
71
71
  * Adds the given message to the given event.
@@ -73,8 +73,8 @@ export type EventApiContextType = {
73
73
  * @param content The text content of the message.
74
74
  * @returns An AxiosResponse containing the updated Event.
75
75
  */
76
- createMessage: (id: string, content: string) => Promise<AxiosResponse<Event, any>>;
77
- };
76
+ createMessage: (id: string, content: string) => Promise<AxiosResponse<Event>>;
77
+ }
78
78
 
79
79
  export interface BookEventResponse {
80
80
  event: Event;
@@ -177,7 +177,7 @@ interface ListEventsResponse {
177
177
  * @returns A list of Events.
178
178
  */
179
179
  export async function listEvents(idToken: string, startKey?: string) {
180
- let params = { startKey };
180
+ const params = { startKey };
181
181
  const result: Event[] = [];
182
182
 
183
183
  do {
frontend/src/api/examApi.tsx CHANGED
@@ -4,7 +4,7 @@ import { Exam, ExamAnswer, ExamAttempt, ExamType } from '../database/exam';
4
4
 
5
5
  const BASE_URL = getConfig().api.baseUrl;
6
6
 
7
- export type ExamApiContextType = {
7
+ export interface ExamApiContextType {
8
8
  /**
9
9
  * Fetches a list of exams with the provided type.
10
10
  * @param type The type of exam to fetch.
@@ -33,7 +33,7 @@ export type ExamApiContextType = {
33
33
  * @returns An AxiosResponse containing the requested ExamAnswer.
34
34
  */
35
35
  getExamAnswer: (id: string) => Promise<AxiosResponse<ExamAnswer>>;
36
- };
36
+ }
37
37
 
38
38
  interface ListExamsResponse {
39
39
  exams: Exam[];
frontend/src/api/explorerApi.tsx CHANGED
@@ -12,13 +12,13 @@ const BASE_URL = getConfig().api.baseUrl;
12
12
  /**
13
13
  * Provides an API for interacting with the position explorer.
14
14
  */
15
- export type ExplorerApiContextType = {
15
+ export interface ExplorerApiContextType {
16
16
  /**
17
17
  * Gets the ExplorerPosition with the provided FEN.
18
18
  * @param fen The FEN to fetch.
19
19
  * @returns The ExplorerPosition, if it exists.
20
20
  */
21
- getPosition: (fen: string) => Promise<AxiosResponse<GetExplorerPositionResult, any>>;
21
+ getPosition: (fen: string) => Promise<AxiosResponse<GetExplorerPositionResult>>;
22
22
 
23
23
  /**
24
24
  * Creates, updates or deletes an ExplorerPositionFollower with the provided parameters.
@@ -27,8 +27,8 @@ export type ExplorerApiContextType = {
27
27
  */
28
28
  followPosition: (
29
29
  request: FollowPositionRequest
30
- ) => Promise<AxiosResponse<ExplorerPositionFollower | null, any>>;
31
- };
30
+ ) => Promise<AxiosResponse<ExplorerPositionFollower | null>>;
31
+ }
32
32
 
33
33
  /** The result from a GetExplorerPosition request. */
34
34
  export interface GetExplorerPositionResult {
frontend/src/api/gameApi.ts CHANGED
@@ -5,7 +5,7 @@ import { Game, GameInfo, GameReviewType, PositionComment } from '../database/gam
5
5
 
6
6
  const BASE_URL = getConfig().api.baseUrl;
7
7
 
8
- export type GameApiContextType = {
8
+ export interface GameApiContextType {
9
9
  /**
10
10
  * createGame saves the provided game in the database.
11
11
  * @param req The CreateGameRequest.
@@ -13,7 +13,7 @@ export type GameApiContextType = {
13
13
  */
14
14
  createGame: (
15
15
  req: CreateGameRequest,
16
- ) => Promise<AxiosResponse<Game | EditGameResponse, any>>;
16
+ ) => Promise<AxiosResponse<Game | EditGameResponse>>;
17
17
 
18
18
  /**
19
19
  * getGame returns the requested game.
@@ -21,7 +21,7 @@ export type GameApiContextType = {
21
21
  * @param id The id of the game.
22
22
  * @returns An AxiosResponse containing the requested game.
23
23
  */
24
- getGame: (cohort: string, id: string) => Promise<AxiosResponse<Game, any>>;
24
+ getGame: (cohort: string, id: string) => Promise<AxiosResponse<Game>>;
25
25
 
26
26
  /**
27
27
  * featureGame sets the featured status of the provided game.
@@ -34,7 +34,7 @@ export type GameApiContextType = {
34
34
  cohort: string,
35
35
  id: string,
36
36
  featured: string,
37
- ) => Promise<AxiosResponse<Game, any>>;
37
+ ) => Promise<AxiosResponse<Game>>;
38
38
 
39
39
  /**
40
40
  * updateGame overwrites the PGN data of the provided game.
@@ -48,7 +48,7 @@ export type GameApiContextType = {
48
48
  cohort: string,
49
49
  id: string,
50
50
  req: CreateGameRequest,
51
- ) => Promise<AxiosResponse<Game | EditGameResponse, any>>;
51
+ ) => Promise<AxiosResponse<Game | EditGameResponse>>;
52
52
 
53
53
  /**
54
54
  * deleteGame removes the specified game from the database. The caller
@@ -57,7 +57,7 @@ export type GameApiContextType = {
57
57
  * @param id The id of the game.
58
58
  * @returns The delete Game.
59
59
  */
60
- deleteGame: (cohort: string, id: string) => Promise<AxiosResponse<Game, any>>;
60
+ deleteGame: (cohort: string, id: string) => Promise<AxiosResponse<Game>>;
61
61
 
62
62
  /**
63
63
  * listGamesByCohort returns a list of GameInfo objects corresponding to the provided cohort,
@@ -73,7 +73,7 @@ export type GameApiContextType = {
73
73
  startKey?: string,
74
74
  startDate?: string,
75
75
  endDate?: string,
76
- ) => Promise<AxiosResponse<ListGamesResponse, any>>;
76
+ ) => Promise<AxiosResponse<ListGamesResponse>>;
77
77
 
78
78
  /**
79
79
  * listGamesByOwner returns a list of GameInfo objects owned by the provided user,
@@ -94,7 +94,7 @@ export type GameApiContextType = {
94
94
  endDate?: string,
95
95
  player?: string,
96
96
  color?: string,
97
- ) => Promise<AxiosResponse<ListGamesResponse, any>>;
97
+ ) => Promise<AxiosResponse<ListGamesResponse>>;
98
98
 
99
99
  /**
100
100
  * listGamesByOpening returns a list of GameInfo objects with the provided ECO code,
@@ -110,7 +110,7 @@ export type GameApiContextType = {
110
110
  startKey?: string,
111
111
  startDate?: string,
112
112
  endDate?: string,
113
- ) => Promise<AxiosResponse<ListGamesResponse, any>>;
113
+ ) => Promise<AxiosResponse<ListGamesResponse>>;
114
114
 
115
115
  /**
116
116
  * listGamesByPosition returns a list of GameInfo objects matching the provided FEN,
@@ -122,7 +122,7 @@ export type GameApiContextType = {
122
122
  listGamesByPosition: (
123
123
  fen: string,
124
124
  startKey?: string,
125
- ) => Promise<AxiosResponse<ListGamesResponse, any>>;
125
+ ) => Promise<AxiosResponse<ListGamesResponse>>;
126
126
 
127
127
  /**
128
128
  * listFeaturedGames returns a list of games featured in the past month.
@@ -138,7 +138,7 @@ export type GameApiContextType = {
138
138
  */
139
139
  listGamesForReview: (
140
140
  startKey?: string,
141
- ) => Promise<AxiosResponse<ListGamesResponse, any>>;
141
+ ) => Promise<AxiosResponse<ListGamesResponse>>;
142
142
 
143
143
  /**
144
144
  * createComment adds the given content as a comment on the given game.
@@ -152,7 +152,7 @@ export type GameApiContextType = {
152
152
  id: string,
153
153
  comment: PositionComment,
154
154
  existingComments: boolean,
155
- ) => Promise<AxiosResponse<Game, any>>;
155
+ ) => Promise<AxiosResponse<Game>>;
156
156
 
157
157
  /**
158
158
  * Updates a comment on a game. The full updated game is returned.
@@ -188,7 +188,7 @@ export type GameApiContextType = {
188
188
  * @returns An AxiosResponse containing the updated game.
189
189
  */
190
190
  markReviewed: (cohort: string, id: string) => Promise<AxiosResponse<Game>>;
191
- };
191
+ }
192
192
 
193
193
  export enum GameSubmissionType {
194
194
  LichessChapter = 'lichessChapter',
@@ -341,7 +341,7 @@ export function listGamesByCohort(
341
341
  startDate?: string,
342
342
  endDate?: string,
343
343
  ) {
344
- let params = { startDate, endDate, startKey };
344
+ const params = { startDate, endDate, startKey };
345
345
  cohort = encodeURIComponent(cohort);
346
346
  return axios.get<ListGamesResponse>(BASE_URL + `/game/${cohort}`, {
347
347
  params,
@@ -371,7 +371,7 @@ export function listGamesByOwner(
371
371
  player?: string,
372
372
  color?: string,
373
373
  ) {
374
- let params = { owner, startKey, startDate, endDate, player, color };
374
+ const params = { owner, startKey, startDate, endDate, player, color };
375
375
  return axios.get<ListGamesResponse>(BASE_URL + '/game', {
376
376
  params,
377
377
  headers: {
@@ -397,7 +397,7 @@ export function listGamesByOpening(
397
397
  startDate?: string,
398
398
  endDate?: string,
399
399
  ) {
400
- let params = { eco, startKey, startDate, endDate };
400
+ const params = { eco, startKey, startDate, endDate };
401
401
  return axios.get<ListGamesResponse>(BASE_URL + '/game/opening', {
402
402
  params,
403
403
  headers: {
@@ -415,7 +415,7 @@ export function listGamesByOpening(
415
415
  * @returns A list of games matching the provided FEN.
416
416
  */
417
417
  export function listGamesByPosition(idToken: string, fen: string, startKey?: string) {
418
- let params = { fen, startKey };
418
+ const params = { fen, startKey };
419
419
  return axios.get<ListGamesResponse>(BASE_URL + '/game/position', {
420
420
  params,
421
421
  headers: {
@@ -431,7 +431,7 @@ export function listGamesByPosition(idToken: string, fen: string, startKey?: str
431
431
  * @returns A list of featured games.
432
432
  */
433
433
  export async function listFeaturedGames(idToken: string, startKey?: string) {
434
- let params = { startKey };
434
+ const params = { startKey };
435
435
  const result: GameInfo[] = [];
436
436
 
437
437
  do {
@@ -520,7 +520,7 @@ export function updateComment(idToken: string, update: UpdateCommentRequest) {
520
520
  });
521
521
  }
522
522
 
523
- export interface DeleteCommentRequest extends Omit<UpdateCommentRequest, 'content'> {}
523
+ export type DeleteCommentRequest = Omit<UpdateCommentRequest, 'content'>
524
524
 
525
525
  /**
526
526
  * Deletes a comment on a game. The full updated game is returned.
frontend/src/api/graduationApi.ts CHANGED
@@ -5,7 +5,7 @@ import { Graduation } from '../database/graduation';
5
5
 
6
6
  const BASE_URL = getConfig().api.baseUrl;
7
7
 
8
- export type GraduationApiContextType = {
8
+ export interface GraduationApiContextType {
9
9
  /**
10
10
  * listGraduationsByCohort returns a list of graduations matching the provided cohort.
11
11
  * @param cohort The cohort to search for when matching graduations.
@@ -31,7 +31,7 @@ export type GraduationApiContextType = {
31
31
  * @returns A list of graduations.
32
32
  */
33
33
  listGraduationsByDate: (startKey?: string) => Promise<Graduation[]>;
34
- };
34
+ }
35
35
 
36
36
  interface ListGraduationsResponse {
37
37
  graduations: Graduation[];
@@ -72,7 +72,7 @@ export async function listGraduationsByCohort(
72
72
  cohort: string,
73
73
  startKey?: string
74
74
  ) {
75
- let params = { startKey };
75
+ const params = { startKey };
76
76
  return listGraduations(BASE_URL + `/graduations/${cohort}`, params, idToken);
77
77
  }
78
78
 
@@ -88,7 +88,7 @@ export async function listGraduationsByOwner(
88
88
  username: string,
89
89
  startKey?: string
90
90
  ) {
91
- let params = { startKey };
91
+ const params = { startKey };
92
92
  return listGraduations(BASE_URL + `/graduations/owner/${username}`, params, idToken);
93
93
  }
94
94
 
@@ -99,6 +99,6 @@ export async function listGraduationsByOwner(
99
99
  * @returns A list of graduations.
100
100
  */
101
101
  export async function listGraduationsByDate(idToken: string, startKey?: string) {
102
- let params = { startKey };
102
+ const params = { startKey };
103
103
  return listGraduations(BASE_URL + `/graduations`, params, idToken);
104
104
  }
frontend/src/api/newsfeedApi.tsx CHANGED
@@ -5,7 +5,7 @@ import { TimelineEntry } from '../database/timeline';
5
5
 
6
6
  const BASE_URL = getConfig().api.baseUrl;
7
7
 
8
- export type NewsfeedApiContextType = {
8
+ export interface NewsfeedApiContextType {
9
9
  /**
10
10
  * Fetches the timeline entry with the provided owner and id.
11
11
  * @param owner The owner of the timeline entry.
@@ -15,7 +15,7 @@ export type NewsfeedApiContextType = {
15
15
  getNewsfeedItem: (
16
16
  owner: string,
17
17
  id: string
18
- ) => Promise<AxiosResponse<TimelineEntry, any>>;
18
+ ) => Promise<AxiosResponse<TimelineEntry>>;
19
19
 
20
20
  /**
21
21
  * Fetches a page of the provided newsfeed.
@@ -28,7 +28,7 @@ export type NewsfeedApiContextType = {
28
28
  newsfeedIds: string[],
29
29
  skipLastFetch?: boolean,
30
30
  startKey?: string
31
- ) => Promise<AxiosResponse<ListNewsfeedResponse, any>>;
31
+ ) => Promise<AxiosResponse<ListNewsfeedResponse>>;
32
32
 
33
33
  /**
34
34
  * Adds the given content as a comment on the given TimelineEntry.
@@ -39,7 +39,7 @@ export type NewsfeedApiContextType = {
39
39
  createNewsfeedComment: (
40
40
  props: { owner: string; id: string },
41
41
  content: string
42
- ) => Promise<AxiosResponse<TimelineEntry, any>>;
42
+ ) => Promise<AxiosResponse<TimelineEntry>>;
43
43
 
44
44
  /**
45
45
  * Sets the provided reaction types on the given TimelineEntry.
@@ -52,8 +52,8 @@ export type NewsfeedApiContextType = {
52
52
  owner: string,
53
53
  id: string,
54
54
  types: string[]
55
- ) => Promise<AxiosResponse<TimelineEntry, any>>;
56
- };
55
+ ) => Promise<AxiosResponse<TimelineEntry>>;
56
+ }
57
57
 
58
58
  /**
59
59
  * Fetches the timeline entry with the provided owner and id.
frontend/src/api/notificationApi.ts CHANGED
@@ -8,7 +8,7 @@ const BASE_URL = getConfig().api.baseUrl;
8
8
  /**
9
9
  * NotificationApiContextType provides an API for interacting with Notifications.
10
10
  */
11
- export type NotificationApiContextType = {
11
+ export interface NotificationApiContextType {
12
12
  /**
13
13
  * listNotifications returns a list of Notifications for the current signed-in user.
14
14
  * @param startKey The startKey to use when searching for Notifications.
@@ -16,15 +16,15 @@ export type NotificationApiContextType = {
16
16
  */
17
17
  listNotifications: (
18
18
  startKey?: string
19
- ) => Promise<AxiosResponse<ListNotificationsResponse, any>>;
19
+ ) => Promise<AxiosResponse<ListNotificationsResponse>>;
20
20
 
21
21
  /**
22
22
  * deleteNotification deletes the Notification with the provided id.
23
23
  * @param id The id of the Notification to delete.
24
24
  * @returns An empty AxiosResponse.
25
25
  */
26
- deleteNotification: (id: string) => Promise<AxiosResponse<void, any>>;
27
- };
26
+ deleteNotification: (id: string) => Promise<AxiosResponse<void>>;
27
+ }
28
28
 
29
29
  /**
30
30
  * The response from a listNotifications request.
frontend/src/api/paymentApi.ts CHANGED
@@ -6,7 +6,7 @@ import { StripeAccount } from '../database/payment';
6
6
 
7
7
  const BASE_URL = getConfig().api.baseUrl;
8
8
 
9
- export type PaymentApiContextType = {
9
+ export interface PaymentApiContextType {
10
10
  /**
11
11
  * Creates a subscription checkout session.
12
12
  * @param request The SubscriptionCheckoutRequest.
@@ -39,7 +39,7 @@ export type PaymentApiContextType = {
39
39
  * @returns A stripe login link URL.
40
40
  */
41
41
  paymentAccountLogin: () => Promise<AxiosResponse<StripeUrlResponse>>;
42
- };
42
+ }
43
43
 
44
44
  /** A request to create a subscription checkout session. */
45
45
  export interface SubscriptionCheckoutRequest {
frontend/src/api/requirementApi.ts CHANGED
@@ -5,13 +5,13 @@ import { Requirement } from '../database/requirement';
5
5
 
6
6
  const BASE_URL = getConfig().api.baseUrl;
7
7
 
8
- export type RequirementApiContextType = {
8
+ export interface RequirementApiContextType {
9
9
  /**
10
10
  * getRequirement fetches the requirement with the provided id.
11
11
  * @param id The id of the requirement to fetch.
12
12
  * @returns The requirement with the provided id.
13
13
  */
14
- getRequirement: (id: string) => Promise<AxiosResponse<Requirement, any>>;
14
+ getRequirement: (id: string) => Promise<AxiosResponse<Requirement>>;
15
15
 
16
16
  /**
17
17
  * listRequirements returns a list of requirements matching the provided cohort.
@@ -32,8 +32,8 @@ export type RequirementApiContextType = {
32
32
  */
33
33
  setRequirement: (
34
34
  requirement: Requirement
35
- ) => Promise<AxiosResponse<Requirement, any>>;
36
- };
35
+ ) => Promise<AxiosResponse<Requirement>>;
36
+ }
37
37
 
38
38
  /**
39
39
  * getRequirement fetches the requirement with the provided id.
@@ -68,7 +68,7 @@ export async function listRequirements(
68
68
  scoreboardOnly: boolean,
69
69
  startKey?: string
70
70
  ) {
71
- let params = { scoreboardOnly, startKey };
71
+ const params = { scoreboardOnly, startKey };
72
72
  const result: Requirement[] = [];
73
73
 
74
74
  do {
frontend/src/api/scoreboardApi.ts CHANGED
@@ -9,17 +9,17 @@ const BASE_URL = getConfig().api.baseUrl;
9
9
  /**
10
10
  * ScoreboardApiContextType provides an API for fetching the scoreboard.
11
11
  */
12
- export type ScoreboardApiContextType = {
12
+ export interface ScoreboardApiContextType {
13
13
  /**
14
14
  * Returns the scoreboard data for the given scoreboard type.
15
15
  * @param type The scoreboard type to get. Valid values are `dojo`, `following` or a cohort.
16
16
  * @returns A list of User or ScoreboardSummary objects.
17
17
  */
18
- getScoreboard: (type: string) => Promise<Array<User | ScoreboardSummary>>;
19
- };
18
+ getScoreboard: (type: string) => Promise<(User | ScoreboardSummary)[]>;
19
+ }
20
20
 
21
21
  interface GetScoreboardResponse {
22
- data: Array<User | ScoreboardSummary>;
22
+ data: (User | ScoreboardSummary)[];
23
23
  lastEvaluatedKey: string;
24
24
  }
25
25
 
@@ -32,9 +32,9 @@ interface GetScoreboardResponse {
32
32
  export async function getScoreboard(
33
33
  idToken: string,
34
34
  type: string
35
- ): Promise<Array<User | ScoreboardSummary>> {
36
- let params = { startKey: '' };
37
- const result: Array<User | ScoreboardSummary> = [];
35
+ ): Promise<(User | ScoreboardSummary)[]> {
36
+ const params = { startKey: '' };
37
+ const result: (User | ScoreboardSummary)[] = [];
38
38
 
39
39
  do {
40
40
  const resp = await axios.get<GetScoreboardResponse>(
frontend/src/api/tournamentApi.ts CHANGED
@@ -13,7 +13,7 @@ const BASE_URL = getConfig().api.baseUrl;
13
13
  export type TimePeriod = 'monthly' | 'yearly';
14
14
  export type TimeControl = 'blitz' | 'rapid' | 'classical';
15
15
 
16
- export type TournamentApiContextType = {
16
+ export interface TournamentApiContextType {
17
17
  /**
18
18
  * getLeaderboard returns the requested leaderboard.
19
19
  * @param site The site the leaderboard is for.
@@ -37,7 +37,7 @@ export type TournamentApiContextType = {
37
37
  * current tournament will be returned.
38
38
  * @returns An AxiosResponse containing the requested OpenClassical.
39
39
  */
40
- getOpenClassical: (startsAt?: string) => Promise<AxiosResponse<OpenClassical, any>>;
40
+ getOpenClassical: (startsAt?: string) => Promise<AxiosResponse<OpenClassical>>;
41
41
 
42
42
  /**
43
43
  * Submits a request to register for the Open Classical.
@@ -46,7 +46,7 @@ export type TournamentApiContextType = {
46
46
  */
47
47
  registerForOpenClassical: (
48
48
  req: OpenClassicalRegistrationRequest,
49
- ) => Promise<AxiosResponse<void, any>>;
49
+ ) => Promise<AxiosResponse<void>>;
50
50
 
51
51
  /**
52
52
  * Submits a request to enter results for the Open Classical.
@@ -55,7 +55,7 @@ export type TournamentApiContextType = {
55
55
  */
56
56
  submitResultsForOpenClassical: (
57
57
  req: OpenClassicalSubmitResultsRequest,
58
- ) => Promise<AxiosResponse<OpenClassical, any>>;
58
+ ) => Promise<AxiosResponse<OpenClassical>>;
59
59
 
60
60
  /**
61
61
  * Sets the pairings using the given request. Only admins and tournament
@@ -65,7 +65,7 @@ export type TournamentApiContextType = {
65
65
  */
66
66
  putOpenClassicalPairings: (
67
67
  req: OpenClassicalPutPairingsRequest,
68
- ) => Promise<AxiosResponse<OpenClassical, any>>;
68
+ ) => Promise<AxiosResponse<OpenClassical>>;
69
69
 
70
70
  /**
71
71
  * Returns a list of previous open classicals.
@@ -83,7 +83,7 @@ export type TournamentApiContextType = {
83
83
  adminGetRegistrations: (
84
84
  region: string,
85
85
  section: string,
86
- ) => Promise<AxiosResponse<any, any>>;
86
+ ) => Promise<AxiosResponse>;
87
87
 
88
88
  /**
89
89
  * Bans the given player from the open classical.
@@ -146,7 +146,7 @@ export type TournamentApiContextType = {
146
146
  adminCompleteTournament: (
147
147
  nextStartDate: string,
148
148
  ) => Promise<AxiosResponse<OpenClassical>>;
149
- };
149
+ }
150
150
 
151
151
  /** A request to register for the Open Classical. */
152
152
  export interface OpenClassicalRegistrationRequest {
frontend/src/api/userApi.ts CHANGED
@@ -12,25 +12,25 @@ const BASE_URL = getConfig().api.baseUrl;
12
12
  /**
13
13
  * UserApiContextType provides an API for interacting with the current signed-in user.
14
14
  */
15
- export type UserApiContextType = {
15
+ export interface UserApiContextType {
16
16
  /**
17
17
  * checkUserAccess returns a 200 OK if the current signed-in user has an active subscription
18
18
  * on chessdojo.shop and an error otherwise.
19
19
  * @returns An empty AxiosResponse if the current user has an active subscription.
20
20
  */
21
- checkUserAccess: () => Promise<AxiosResponse<any, any>>;
21
+ checkUserAccess: () => Promise<AxiosResponse>;
22
22
 
23
23
  /**
24
24
  * getUser returns the current signed-in user.
25
25
  * @returns An AxiosResponse containing the current user in the data field.
26
26
  */
27
- getUser: () => Promise<AxiosResponse<User, any>>;
27
+ getUser: () => Promise<AxiosResponse<User>>;
28
28
 
29
29
  /**
30
30
  * getUserPublic returns the user with the provided username.
31
31
  * @returns An AxiosResponse containing the provided user in the data field.
32
32
  */
33
- getUserPublic: (username: string) => Promise<AxiosResponse<User, any>>;
33
+ getUserPublic: (username: string) => Promise<AxiosResponse<User>>;
34
34
 
35
35
  /**
36
36
  * listUserTimeline returns a list of the provided user's timeline entries.
@@ -69,7 +69,7 @@ export type UserApiContextType = {
69
69
  updateUser: (
70
70
  update: Partial<User>,
71
71
  autopickCohort?: boolean,
72
- ) => Promise<AxiosResponse<User, any>>;
72
+ ) => Promise<AxiosResponse<User>>;
73
73
 
74
74
  /**
75
75
  * updateUserProgress updates the current user's progress on the provided requirement.
@@ -88,7 +88,7 @@ export type UserApiContextType = {
88
88
  incrementalMinutesSpent: number,
89
89
  date: DateTime | null,
90
90
  notes: string,
91
- ) => Promise<AxiosResponse<User, any>>;
91
+ ) => Promise<AxiosResponse<User>>;
92
92
 
93
93
  /**
94
94
  * updateUserTimeline sets the current user's timeline for the provided requirement.
@@ -107,26 +107,26 @@ export type UserApiContextType = {
107
107
  deleted: TimelineEntry[],
108
108
  count: number,
109
109
  minutesSpent: number,
110
- ) => Promise<AxiosResponse<User, any>>;
110
+ ) => Promise<AxiosResponse<User>>;
111
111
 
112
112
  /**
113
113
  * graduate creates a new graduation object for the given user and updates them to the next cohort.
114
114
  * @param comments The comments the user wants to add to their graduation object.
115
115
  * @returns An AxiosResponse containing the new graduation object and the user update.
116
116
  */
117
- graduate: (comments: string) => Promise<AxiosResponse<GraduationResponse, any>>;
117
+ graduate: (comments: string) => Promise<AxiosResponse<GraduationResponse>>;
118
118
 
119
119
  /**
120
120
  * @returns An AxiosResponse containing the user statistics.
121
121
  */
122
- getUserStatistics: () => Promise<AxiosResponse<UserStatistics, any>>;
122
+ getUserStatistics: () => Promise<AxiosResponse<UserStatistics>>;
123
123
 
124
124
  /**
125
125
  * Fetches the FollowerEntry for the current signed-in user and the given poster, if it exists.
126
126
  * @param poster The person being followed.
127
127
  * @returns The FollowerEntry or null if it does not exist.
128
128
  */
129
- getFollower: (poster: string) => Promise<AxiosResponse<FollowerEntry | null, any>>;
129
+ getFollower: (poster: string) => Promise<AxiosResponse<FollowerEntry | null>>;
130
130
 
131
131
  /**
132
132
  * Edits the follower state of the current signed-in user for the given poster.
@@ -137,7 +137,7 @@ export type UserApiContextType = {
137
137
  editFollower: (
138
138
  poster: string,
139
139
  action: 'follow' | 'unfollow',
140
- ) => Promise<AxiosResponse<FollowerEntry | null, any>>;
140
+ ) => Promise<AxiosResponse<FollowerEntry | null>>;
141
141
 
142
142
  /**
143
143
  * Fetches a list of followers for the given user.
@@ -148,7 +148,7 @@ export type UserApiContextType = {
148
148
  listFollowers: (
149
149
  username: string,
150
150
  startKey?: string,
151
- ) => Promise<AxiosResponse<ListFollowersResponse, any>>;
151
+ ) => Promise<AxiosResponse<ListFollowersResponse>>;
152
152
 
153
153
  /**
154
154
  * Fetches the list of users the given user is following.
@@ -159,8 +159,8 @@ export type UserApiContextType = {
159
159
  listFollowing: (
160
160
  username: string,
161
161
  startKey?: string,
162
- ) => Promise<AxiosResponse<ListFollowersResponse, any>>;
163
- };
162
+ ) => Promise<AxiosResponse<ListFollowersResponse>>;
163
+ }
164
164
 
165
165
  /**
166
166
  * checkUserAccess returns a 200 OK if the current signed-in user has an active subscription
@@ -215,7 +215,7 @@ export async function listUserTimeline(
215
215
  owner: string,
216
216
  startKey?: string,
217
217
  ) {
218
- let params = { startKey };
218
+ const params = { startKey };
219
219
  const resp = await axios.get<ListUserTimelineResponse>(
220
220
  `${BASE_URL}/user/${owner}/timeline`,
221
221
  {
@@ -245,7 +245,7 @@ export async function listUsersByCohort(
245
245
  cohort: string,
246
246
  startKey?: string,
247
247
  ) {
248
- let params = { startKey };
248
+ const params = { startKey };
249
249
  const result: User[] = [];
250
250
  do {
251
251
  const resp = await axios.get<ListUsersResponse>(BASE_URL + `/user/${cohort}`, {
@@ -268,7 +268,7 @@ export async function listUsersByCohort(
268
268
  * @returns A list of users matching the provided query and fields.
269
269
  */
270
270
  export async function searchUsers(query: string, fields: string[], startKey?: string) {
271
- let params = { query, fields: fields.join(','), startKey };
271
+ const params = { query, fields: fields.join(','), startKey };
272
272
  const result: User[] = [];
273
273
 
274
274
  do {
frontend/src/api/yearReviewApi.ts CHANGED
@@ -7,7 +7,7 @@ const BASE_URL = getConfig().api.baseUrl;
7
7
  /**
8
8
  * Provides an API for interacting with year reviews.
9
9
  */
10
- export type YearReviewApiContextType = {
10
+ export interface YearReviewApiContextType {
11
11
  /**
12
12
  * Fetches the year review for the provided user and year.
13
13
  * @param username The username to fetch.
@@ -15,7 +15,7 @@ export type YearReviewApiContextType = {
15
15
  * @returns The year review for the given user and year.
16
16
  */
17
17
  getYearReview: (username: string, year: string) => Promise<AxiosResponse<YearReview>>;
18
- };
18
+ }
19
19
 
20
20
  /**
21
21
  * Fetches the year review for the provided user and year.
frontend/src/auth/ForgotPasswordPage.tsx CHANGED
@@ -110,7 +110,7 @@ const ForgotPasswordPage = () => {
110
110
  {step === ForgotPasswordStep.Confirm && (
111
111
  <ConfirmStep
112
112
  email={email}
113
- onSuccess={() => setStep(ForgotPasswordStep.Success)}
113
+ onSuccess={() => { setStep(ForgotPasswordStep.Success); }}
114
114
  onCancel={onCancel}
115
115
  />
116
116
  )}
@@ -163,7 +163,7 @@ const StartStep: React.FC<StartStepProps> = ({
163
163
  label='Email'
164
164
  variant='outlined'
165
165
  value={email}
166
- onChange={(event) => setEmail(event.target.value)}
166
+ onChange={(event) => { setEmail(event.target.value); }}
167
167
  error={!!emailError}
168
168
  helperText={emailError}
169
169
  onKeyDown={onKeyDown}
@@ -274,7 +274,7 @@ const ConfirmStep: React.FC<ConfirmStepProps> = ({ email, onSuccess, onCancel })
274
274
  label='Recovery Code'
275
275
  variant='outlined'
276
276
  value={code}
277
- onChange={(event) => setCode(event.target.value)}
277
+ onChange={(event) => { setCode(event.target.value); }}
278
278
  error={!!codeError}
279
279
  helperText={codeError}
280
280
  />
@@ -286,7 +286,7 @@ const ConfirmStep: React.FC<ConfirmStepProps> = ({ email, onSuccess, onCancel })
286
286
  type='password'
287
287
  variant='outlined'
288
288
  value={password}
289
- onChange={(event) => setPassword(event.target.value)}
289
+ onChange={(event) => { setPassword(event.target.value); }}
290
290
  error={!!passwordError}
291
291
  helperText={passwordError}
292
292
  />
@@ -298,7 +298,7 @@ const ConfirmStep: React.FC<ConfirmStepProps> = ({ email, onSuccess, onCancel })
298
298
  type='password'
299
299
  variant='outlined'
300
300
  value={passwordConfirm}
301
- onChange={(event) => setPasswordConfirm(event.target.value)}
301
+ onChange={(event) => { setPasswordConfirm(event.target.value); }}
302
302
  error={!!passwordError}
303
303
  helperText={passwordError}
304
304
  onKeyDown={onKeyDown}
frontend/src/auth/SigninPage.tsx CHANGED
@@ -99,7 +99,7 @@ const SigninPage = () => {
99
99
  label='Email'
100
100
  variant='outlined'
101
101
  value={email}
102
- onChange={(event) => setEmail(event.target.value)}
102
+ onChange={(event) => { setEmail(event.target.value); }}
103
103
  error={!!errors.email}
104
104
  helperText={errors.email}
105
105
  />
@@ -110,7 +110,7 @@ const SigninPage = () => {
110
110
  type='password'
111
111
  variant='outlined'
112
112
  value={password}
113
- onChange={(event) => setPassword(event.target.value)}
113
+ onChange={(event) => { setPassword(event.target.value); }}
114
114
  onKeyDown={onKeyDown}
115
115
  error={!!errors.password}
116
116
  helperText={errors.password}
@@ -134,7 +134,7 @@ const SigninPage = () => {
134
134
  data-cy='signup-button'
135
135
  variant='text'
136
136
  sx={{ textTransform: 'none' }}
137
- onClick={() => navigate('/signup')}
137
+ onClick={() => { navigate('/signup'); }}
138
138
  >
139
139
  No account? Sign Up
140
140
  </Button>
@@ -142,7 +142,7 @@ const SigninPage = () => {
142
142
  data-cy='forgot-password-button'
143
143
  variant='text'
144
144
  sx={{ textTransform: 'none', alignSelf: 'end' }}
145
- onClick={() => navigate('/forgot-password')}
145
+ onClick={() => { navigate('/forgot-password'); }}
146
146
  >
147
147
  Forgot password?
148
148
  </Button>
frontend/src/auth/SignupPage.tsx CHANGED
@@ -108,7 +108,7 @@ const SignupPage = () => {
108
108
  label='Name'
109
109
  variant='outlined'
110
110
  value={name}
111
- onChange={(event) => setName(event.target.value)}
111
+ onChange={(event) => { setName(event.target.value); }}
112
112
  error={!!errors.name}
113
113
  helperText={errors.name}
114
114
  />
@@ -118,7 +118,7 @@ const SignupPage = () => {
118
118
  label='Email'
119
119
  variant='outlined'
120
120
  value={email}
121
- onChange={(event) => setEmail(event.target.value)}
121
+ onChange={(event) => { setEmail(event.target.value); }}
122
122
  error={!!errors.email}
123
123
  helperText={errors.email}
124
124
  />
@@ -129,7 +129,7 @@ const SignupPage = () => {
129
129
  type='password'
130
130
  variant='outlined'
131
131
  value={password}
132
- onChange={(event) => setPassword(event.target.value)}
132
+ onChange={(event) => { setPassword(event.target.value); }}
133
133
  error={!!errors.password}
134
134
  helperText={errors.password}
135
135
  onKeyDown={onKeyDown}
frontend/src/auth/VerifyEmailPage.tsx CHANGED
@@ -10,7 +10,7 @@ const VerifyEmailPage = () => {
10
10
  const auth = useAuth();
11
11
 
12
12
  const navigate = useNavigate();
13
- const locationState = useLocation().state as any;
13
+ const locationState = useLocation().state;
14
14
 
15
15
  const username: string = locationState?.username;
16
16
  const email: string = locationState?.email;
@@ -125,7 +125,7 @@ const VerifyEmailPage = () => {
125
125
  label='Verification Code'
126
126
  variant='outlined'
127
127
  value={code}
128
- onChange={(event) => setCode(event.target.value)}
128
+ onChange={(event) => { setCode(event.target.value); }}
129
129
  onKeyDown={onKeyDown}
130
130
  error={!!codeError}
131
131
  helperText={codeError}
frontend/src/board/Board.tsx CHANGED
@@ -74,7 +74,7 @@ export function toShapes(chess?: Chess): DrawShape[] {
74
74
  }
75
75
 
76
76
  const commentDiag = currentMove.commentDiag;
77
- let result: DrawShape[] = [];
77
+ const result: DrawShape[] = [];
78
78
  if (commentDiag) {
79
79
  if (commentDiag.colorArrows) {
80
80
  for (const comm of commentDiag.colorArrows) {
@@ -246,14 +246,14 @@ const Board: React.FC<BoardProps> = ({ config, onInitialize, onMove }) => {
246
246
  dests: config?.movable?.dests || toDests(chess),
247
247
  events: {
248
248
  after: (orig, dest) =>
249
- checkPromotion(
249
+ { checkPromotion(
250
250
  board,
251
251
  chess,
252
252
  orig,
253
253
  dest,
254
254
  onStartPromotion,
255
255
  onMove ? onMove : defaultOnMove,
256
- ),
256
+ ); },
257
257
  },
258
258
  },
259
259
  lastMove: [],
@@ -296,14 +296,14 @@ const Board: React.FC<BoardProps> = ({ config, onInitialize, onMove }) => {
296
296
  movable: {
297
297
  events: {
298
298
  after: (orig, dest) =>
299
- checkPromotion(
299
+ { checkPromotion(
300
300
  board,
301
301
  chess,
302
302
  orig,
303
303
  dest,
304
304
  onStartPromotion,
305
305
  onMove ? onMove : defaultOnMove,
306
- ),
306
+ ); },
307
307
  },
308
308
  },
309
309
  addPieceZIndex: pieceStyle === PieceStyle.ThreeD,
@@ -339,7 +339,7 @@ const Board: React.FC<BoardProps> = ({ config, onInitialize, onMove }) => {
339
339
  <DialogContent>
340
340
  <Stack direction='row'>
341
341
  {promotionPieces.map((piece) => (
342
- <Button key={piece} onClick={() => onFinishPromotion(piece)}>
342
+ <Button key={piece} onClick={() => { onFinishPromotion(piece); }}>
343
343
  <Box
344
344
  sx={{
345
345
  width: '75px',
@@ -347,7 +347,7 @@ const Board: React.FC<BoardProps> = ({ config, onInitialize, onMove }) => {
347
347
  backgroundSize: 'cover',
348
348
  backgroundImage: `url(${
349
349
  promotion
350
- ? `https://www.chess.com/chess-themes/pieces/bases/150/${promotion?.color[0]}${piece}.png`
350
+ ? `https://www.chess.com/chess-themes/pieces/bases/150/${promotion.color[0]}${piece}.png`
351
351
  : ''
352
352
  })`,
353
353
  }}
frontend/src/board/pgn/KeyboardHandler.tsx CHANGED
@@ -63,7 +63,7 @@ const KeyboardHandler: React.FC<KeyboardHandlerProps> = ({ underboardRef }) => {
63
63
  event.preventDefault();
64
64
  event.stopPropagation();
65
65
 
66
- keyboardShortcutHandlers[matchedAction]?.(chess, board, {
66
+ keyboardShortcutHandlers[matchedAction](chess, board, {
67
67
  underboardApi: underboardRef.current,
68
68
  toggleOrientation,
69
69
  setVariationDialogMove:
frontend/src/board/pgn/Nag.ts CHANGED
@@ -207,7 +207,7 @@ export function getNagInSet(nagSet: Nag[], nags: string[] | undefined): Nag {
207
207
  }
208
208
 
209
209
  for (const nag of nags) {
210
- let stdNag = getStandardNag(nag);
210
+ const stdNag = getStandardNag(nag);
211
211
  if (nagSet.includes(stdNag)) {
212
212
  return stdNag;
213
213
  }
@@ -251,8 +251,8 @@ export function setNagsInSet(newNags: Nag[], nagSet: Nag[], nags?: string[]): Na
251
251
  }
252
252
 
253
253
  export function compareNags(lhs: Nag, rhs: Nag): number {
254
- let lhsNum = parseInt(lhs.slice(1));
255
- let rhsNum = parseInt(rhs.slice(1));
254
+ const lhsNum = parseInt(lhs.slice(1));
255
+ const rhsNum = parseInt(rhs.slice(1));
256
256
 
257
257
  if (lhsNum < rhsNum) {
258
258
  return -1;
frontend/src/board/pgn/PgnBoard.tsx CHANGED
@@ -28,14 +28,14 @@ interface ChessConfig {
28
28
  disableTakebacks?: Color | 'both';
29
29
  }
30
30
 
31
- type ChessContextType = {
31
+ interface ChessContextType {
32
32
  chess?: Chess;
33
33
  board?: BoardApi;
34
34
  config?: ChessConfig;
35
35
  toggleOrientation?: () => void;
36
36
  keydownMap?: React.MutableRefObject<Record<string, boolean>>;
37
37
  slots?: PgnBoardSlots;
38
- };
38
+ }
39
39
 
40
40
  export const ChessContext = createContext<ChessContextType>({});
41
41
 
@@ -130,7 +130,7 @@ const PgnBoard = forwardRef<PgnBoardApi, PgnBoardProps>(
130
130
 
131
131
  const onClickMove = useCallback(
132
132
  (move: Move | null) => {
133
- chess?.seek(move);
133
+ chess.seek(move);
134
134
  reconcile(chess, board);
135
135
  },
136
136
  [chess, board],
frontend/src/board/pgn/PlayerHeader.tsx CHANGED
@@ -26,7 +26,7 @@ export function getInitialClock(pgn?: Pgn): string | undefined {
26
26
 
27
27
  const descriptor = timeControl.split(':')[0];
28
28
  const time = descriptor.split('/').slice(-1)[0];
29
- const startTime = parseInt(time?.split('+')[0]);
29
+ const startTime = parseInt(time.split('+')[0]);
30
30
  if (isNaN(startTime) || startTime <= 0) {
31
31
  return undefined;
32
32
  }
@@ -41,7 +41,7 @@ export function getInitialClock(pgn?: Pgn): string | undefined {
41
41
  result += `${minutes.toLocaleString(undefined, { minimumIntegerDigits: 2 })}:`;
42
42
 
43
43
  const seconds = (startTime % 3600) % 60;
44
- result += `${seconds.toLocaleString(undefined, { minimumIntegerDigits: 2 })}`;
44
+ result += seconds.toLocaleString(undefined, { minimumIntegerDigits: 2 });
45
45
 
46
46
  return result;
47
47
  }
@@ -131,7 +131,7 @@ const PlayerHeader: React.FC<PlayerHeaderProps> = ({ type }) => {
131
131
  };
132
132
 
133
133
  chess.addObserver(observer);
134
- return () => chess.removeObserver(observer);
134
+ return () => { chess.removeObserver(observer); };
135
135
  }
136
136
  }, [chess, setForceRender]);
137
137
 
@@ -142,13 +142,13 @@ const PlayerHeader: React.FC<PlayerHeaderProps> = ({ type }) => {
142
142
  return <EmptyHeader type={type} light={light} />;
143
143
  }
144
144
 
145
- const currentMove = chess?.currentMove();
145
+ const currentMove = chess.currentMove();
146
146
 
147
147
  let playerName = '';
148
148
  let playerElo = '';
149
149
  let playerResult = '';
150
150
  let move: Move | null | undefined = currentMove;
151
- let clockCommand: 'emt' | 'clk' = move?.commentDiag?.emt ? 'emt' : 'clk';
151
+ const clockCommand: 'emt' | 'clk' = move?.commentDiag?.emt ? 'emt' : 'clk';
152
152
  let color: 'w' | 'b' = 'w';
153
153
 
154
154
  if (
@@ -157,7 +157,7 @@ const PlayerHeader: React.FC<PlayerHeaderProps> = ({ type }) => {
157
157
  ) {
158
158
  playerName = pgn.header.tags.Black;
159
159
  playerElo = pgn.header.tags.BlackElo;
160
- const resultTokens = pgn.header.tags.Result?.split('-');
160
+ const resultTokens = pgn.header.tags.Result.split('-');
161
161
  if (resultTokens && resultTokens.length > 1) {
162
162
  playerResult = resultTokens[1];
163
163
  }
@@ -168,7 +168,7 @@ const PlayerHeader: React.FC<PlayerHeaderProps> = ({ type }) => {
168
168
  } else {
169
169
  playerName = pgn.header.tags.White;
170
170
  playerElo = pgn.header.tags.WhiteElo;
171
- const resultTokens = pgn.header.tags.Result?.split('-');
171
+ const resultTokens = pgn.header.tags.Result.split('-');
172
172
  if (resultTokens && resultTokens.length > 1) {
173
173
  playerResult = resultTokens[0];
174
174
  }
@@ -302,7 +302,7 @@ const CapturedMaterial: React.FC<{ move: Move | null; color: 'w' | 'b' }> = ({
302
302
  return null;
303
303
  }
304
304
 
305
- let materialDifference = move.materialDifference;
305
+ const materialDifference = move.materialDifference;
306
306
  let displayedMaterialDiff = '';
307
307
  if (color === 'w' && materialDifference > 0) {
308
308
  displayedMaterialDiff = `+${materialDifference}`;
frontend/src/board/pgn/VariationDialog.tsx CHANGED
@@ -57,7 +57,7 @@ const VariationDialog: React.FC<VariationDialogProps> = ({ move, setMove }) => {
57
57
  } else if (event.key === 'ArrowLeft' || event.key === 'Escape') {
58
58
  setMove(null);
59
59
  } else if (event.key >= '0' && event.key <= '9') {
60
- let index = parseInt(event.key);
60
+ const index = parseInt(event.key);
61
61
  if (index === 0 && move.variations.length > 8) {
62
62
  // 0 is out of order to match its position on the keyboard
63
63
  selectMove(move.variations[8][0]);
@@ -69,7 +69,7 @@ const VariationDialog: React.FC<VariationDialogProps> = ({ move, setMove }) => {
69
69
  }
70
70
  };
71
71
  window.addEventListener('keydown', onKeyDown);
72
- return () => window.removeEventListener('keydown', onKeyDown);
72
+ return () => { window.removeEventListener('keydown', onKeyDown); };
73
73
  }, [move, selected, setMove, selectMove]);
74
74
 
75
75
  if (!move.variations || move.variations.length === 0) {
@@ -79,7 +79,7 @@ const VariationDialog: React.FC<VariationDialogProps> = ({ move, setMove }) => {
79
79
  return (
80
80
  <Dialog
81
81
  open
82
- onClose={() => setMove(null)}
82
+ onClose={() => { setMove(null); }}
83
83
  classes={{
84
84
  container: BlockBoardKeyboardShortcuts,
85
85
  }}
@@ -94,13 +94,13 @@ const VariationDialog: React.FC<VariationDialogProps> = ({ move, setMove }) => {
94
94
  <List>
95
95
  <ListItemButton
96
96
  selected={selected === 0}
97
- onClick={() => selectMove(move)}
97
+ onClick={() => { selectMove(move); }}
98
98
  >
99
99
  <ListItemText sx={{ color: getTextColor(move) }}>
100
100
  {move.san}
101
101
  {move.nags
102
102
  ?.sort(compareNags)
103
- .map((n) => nags[getStandardNag(n)]?.label || '')
103
+ .map((n) => nags[getStandardNag(n)].label || '')
104
104
  .join('')}
105
105
  </ListItemText>
106
106
  <Typography variant='body2'>1</Typography>
@@ -115,13 +115,13 @@ const VariationDialog: React.FC<VariationDialogProps> = ({ move, setMove }) => {
115
115
  <ListItemButton
116
116
  key={variation[0].san}
117
117
  selected={selected === i + 1}
118
- onClick={() => selectMove(variation[0])}
118
+ onClick={() => { selectMove(variation[0]); }}
119
119
  >
120
120
  <ListItemText sx={{ color: getTextColor(variation[0]) }}>
121
121
  {variation[0].san}
122
122
  {variation[0].nags
123
123
  ?.sort(compareNags)
124
- .map((n) => nags[getStandardNag(n)]?.label || '')
124
+ .map((n) => nags[getStandardNag(n)].label || '')
125
125
  .join('')}
126
126
  </ListItemText>
127
127
  {i < 9 && (
frontend/src/board/pgn/annotations/AnnotationWarnings.tsx CHANGED
@@ -58,7 +58,7 @@ const AnnotationWarnings = () => {
58
58
  };
59
59
 
60
60
  chess.addObserver(observer);
61
- return () => chess.removeObserver(observer);
61
+ return () => { chess.removeObserver(observer); };
62
62
  }
63
63
  }, [chess, setForceRender]);
64
64
 
@@ -88,7 +88,7 @@ const AnnotationWarnings = () => {
88
88
  <Button
89
89
  size='small'
90
90
  color='inherit'
91
- onClick={() => setShowDetails(true)}
91
+ onClick={() => { setShowDetails(true); }}
92
92
  >
93
93
  Details
94
94
  </Button>
@@ -100,7 +100,7 @@ const AnnotationWarnings = () => {
100
100
  }.`}
101
101
  </Alert>
102
102
 
103
- <Dialog open={showDetails} onClose={() => setShowDetails(false)}>
103
+ <Dialog open={showDetails} onClose={() => { setShowDetails(false); }}>
104
104
  <DialogTitle component='div'>
105
105
  <Typography variant='h5'>Annotation Warnings</Typography>
106
106
  </DialogTitle>
@@ -128,7 +128,7 @@ const AnnotationWarnings = () => {
128
128
  width: 'fit-content',
129
129
  ml: -1,
130
130
  }}
131
- onClick={() => onClickMove(m)}
131
+ onClick={() => { onClickMove(m); }}
132
132
  >
133
133
  {getMoveText(m)}
134
134
  </Button>
frontend/src/board/pgn/annotations/warningRules.ts CHANGED
@@ -10,7 +10,7 @@ export interface WarningRule {
10
10
  export interface Warning {
11
11
  displayName: string;
12
12
  description: string;
13
- moves: Array<Move | null>;
13
+ moves: (Move | null)[];
14
14
  }
15
15
 
16
16
  const ChesscomCommentAfterRegex =
frontend/src/board/pgn/boardTools/boardButtons/StatusIcon.tsx CHANGED
@@ -13,7 +13,7 @@ import { toDojoDateString, toDojoTimeString } from '../../../../calendar/display
13
13
  import { Game } from '../../../../database/game';
14
14
  import { useChess } from '../../PgnBoard';
15
15
 
16
- const useDebounce = (callback: (...args: any) => void, delay: number = 6000) => {
16
+ const useDebounce = (callback: (...args: any) => void, delay = 6000) => {
17
17
  const ref = useRef<any>();
18
18
 
19
19
  useEffect(() => {
@@ -97,7 +97,7 @@ const StatusIcon: React.FC<StatusIconProps> = ({ game }) => {
97
97
  };
98
98
 
99
99
  chess.addObserver(observer);
100
- return () => chess.removeObserver(observer);
100
+ return () => { chess.removeObserver(observer); };
101
101
  }
102
102
  }, [chess, game, initialPgn, setInitialPgn, debouncedOnSave, setHasChanges]);
103
103
 
frontend/src/board/pgn/boardTools/underboard/ClockEditor.tsx CHANGED
@@ -21,7 +21,7 @@ export function convertSecondsToDateTime(seconds: number | undefined): DateTime
21
21
  }
22
22
 
23
23
  function convertDateTimeToClock(date: DateTime | null): string {
24
- if (!date || !date.isValid) {
24
+ if (!date?.isValid) {
25
25
  return '';
26
26
  }
27
27
  return formatTime(date.hour * 3600 + date.minute * 60 + date.second);
@@ -63,8 +63,8 @@ const ClockEditor = () => {
63
63
  return null;
64
64
  }
65
65
 
66
- const initialClock = getInitialClock(chess?.pgn);
67
- const increment = getIncrement(chess?.pgn);
66
+ const initialClock = getInitialClock(chess.pgn);
67
+ const increment = getIncrement(chess.pgn);
68
68
 
69
69
  const moves = chess.history();
70
70
  const grid = [];
@@ -99,7 +99,7 @@ const ClockEditor = () => {
99
99
  label='Starting Time'
100
100
  format='HH:mm:ss'
101
101
  value={convertSecondsToDateTime(initialClock)}
102
- onChange={(value) => handleInitialClock(chess, increment, value)}
102
+ onChange={(value) => { handleInitialClock(chess, increment, value); }}
103
103
  fullWidth
104
104
  />
105
105
  </Grid2>
@@ -109,7 +109,7 @@ const ClockEditor = () => {
109
109
  id={BlockBoardKeyboardShortcuts}
110
110
  label='Increment (Sec)'
111
111
  value={`${increment}`}
112
- onChange={(e) => handleIncrement(chess, initialClock, e.target.value)}
112
+ onChange={(e) => { handleIncrement(chess, initialClock, e.target.value); }}
113
113
  fullWidth
114
114
  />
115
115
  </Grid2>
frontend/src/board/pgn/boardTools/underboard/ClockTextField.tsx CHANGED
@@ -45,7 +45,7 @@ const ClockTextField: React.FC<ClockTextFieldProps> = ({
45
45
  convertClockToSeconds(move.commentDiag?.clk),
46
46
  ) || defaultDateTime
47
47
  }
48
- onChange={(value) => onChangeClock(chess, move, value)}
48
+ onChange={(value) => { onChangeClock(chess, move, value); }}
49
49
  fullWidth
50
50
  />
51
51
  );
@@ -62,13 +62,13 @@ const ClockTextField: React.FC<ClockTextFieldProps> = ({
62
62
  value={timeSlots.hours}
63
63
  disabled={!move}
64
64
  onChange={(event) =>
65
- onChangeTimeSlot(
65
+ { onChangeTimeSlot(
66
66
  'hours',
67
67
  event.target.value,
68
68
  timeSlots,
69
69
  chess,
70
70
  move,
71
- )
71
+ ); }
72
72
  }
73
73
  fullWidth
74
74
  />
@@ -78,13 +78,13 @@ const ClockTextField: React.FC<ClockTextFieldProps> = ({
78
78
  value={timeSlots.minutes}
79
79
  disabled={!move}
80
80
  onChange={(event) =>
81
- onChangeTimeSlot(
81
+ { onChangeTimeSlot(
82
82
  'minutes',
83
83
  event.target.value,
84
84
  timeSlots,
85
85
  chess,
86
86
  move,
87
- )
87
+ ); }
88
88
  }
89
89
  fullWidth
90
90
  />
@@ -94,13 +94,13 @@ const ClockTextField: React.FC<ClockTextFieldProps> = ({
94
94
  value={timeSlots.seconds}
95
95
  disabled={!move}
96
96
  onChange={(event) =>
97
- onChangeTimeSlot(
97
+ { onChangeTimeSlot(
98
98
  'seconds',
99
99
  event.target.value,
100
100
  timeSlots,
101
101
  chess,
102
102
  move,
103
- )
103
+ ); }
104
104
  }
105
105
  fullWidth
106
106
  />
@@ -126,9 +126,9 @@ function getTimeSlotsFromMove(move: Move): TimeSlots {
126
126
  }
127
127
 
128
128
  const slots = clock.split(':');
129
- let seconds = parseFloat(slots[slots.length - 1] || '0');
130
- let minutes = parseInt(slots[slots.length - 2] || '0');
131
- let hours = parseInt(slots[slots.length - 3] || '0');
129
+ const seconds = parseFloat(slots[slots.length - 1] || '0');
130
+ const minutes = parseInt(slots[slots.length - 2] || '0');
131
+ const hours = parseInt(slots[slots.length - 3] || '0');
132
132
 
133
133
  return {
134
134
  hours,
@@ -160,7 +160,7 @@ function onChangeTimeSlot(
160
160
  function getClockFromTimeSlots(slots: TimeSlots): string {
161
161
  const seconds = slots.seconds % 60;
162
162
  let minutes = slots.minutes + Math.floor(slots.seconds / 60);
163
- let hours = slots.hours + Math.floor(minutes / 60);
163
+ const hours = slots.hours + Math.floor(minutes / 60);
164
164
  minutes = minutes % 60;
165
165
 
166
166
  return formatTime(hours * 3600 + minutes * 60 + seconds);
frontend/src/board/pgn/boardTools/underboard/ClockUsage.tsx CHANGED
@@ -23,7 +23,7 @@ const primaryAxis: AxisOptions<Datum> = {
23
23
  },
24
24
  };
25
25
 
26
- const secondaryAxes: Array<AxisOptions<Datum>> = [
26
+ const secondaryAxes: AxisOptions<Datum>[] = [
27
27
  {
28
28
  getValue: (datum) => datum.seconds,
29
29
  min: 0,
@@ -42,7 +42,7 @@ const barAxis: AxisOptions<Datum> = {
42
42
  },
43
43
  };
44
44
 
45
- const secondaryBarAxis: Array<AxisOptions<Datum>> = [
45
+ const secondaryBarAxis: AxisOptions<Datum>[] = [
46
46
  {
47
47
  ...secondaryAxes[0],
48
48
  position: 'bottom',
@@ -65,7 +65,7 @@ export function formatTime(value: number): string {
65
65
  result += `${minutes.toLocaleString(undefined, { minimumIntegerDigits: 2 })}:`;
66
66
 
67
67
  const seconds = (value % 3600) % 60;
68
- result += `${seconds.toLocaleString(undefined, { minimumIntegerDigits: 2 })}`;
68
+ result += seconds.toLocaleString(undefined, { minimumIntegerDigits: 2 });
69
69
  return result;
70
70
  }
71
71
 
@@ -81,7 +81,7 @@ export function getInitialClock(pgn?: Pgn): number {
81
81
 
82
82
  const descriptor = timeControl.split(':')[0];
83
83
  const time = descriptor.split('/').slice(-1)[0];
84
- const startTime = parseInt(time?.split('+')[0]);
84
+ const startTime = parseInt(time.split('+')[0]);
85
85
  if (isNaN(startTime) || startTime <= 0) {
86
86
  return 0;
87
87
  }
@@ -209,7 +209,7 @@ const ClockUsage: React.FC<ClockUsageProps> = ({ showEditor }) => {
209
209
  };
210
210
 
211
211
  chess.addObserver(observer);
212
- return () => chess.removeObserver(observer);
212
+ return () => { chess.removeObserver(observer); };
213
213
  }
214
214
  }, [chess, setForceRender, showEditor]);
215
215
 
frontend/src/board/pgn/boardTools/underboard/Editor.tsx CHANGED
@@ -94,7 +94,7 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
94
94
  };
95
95
 
96
96
  chess.addObserver(observer);
97
- return () => chess.removeObserver(observer);
97
+ return () => { chess.removeObserver(observer); };
98
98
  }
99
99
  }, [chess, setForceRender]);
100
100
 
@@ -152,7 +152,7 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
152
152
  format='HH:mm:ss'
153
153
  value={convertSecondsToDateTime(initialClock)}
154
154
  onChange={(value) =>
155
- handleInitialClock(chess, increment, value)
155
+ { handleInitialClock(chess, increment, value); }
156
156
  }
157
157
  fullWidth
158
158
  />
@@ -164,11 +164,11 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
164
164
  label='Increment (Sec)'
165
165
  value={`${increment}`}
166
166
  onChange={(e) =>
167
- handleIncrement(
167
+ { handleIncrement(
168
168
  chess,
169
169
  initialClock,
170
170
  e.target.value,
171
- )
171
+ ); }
172
172
  }
173
173
  fullWidth
174
174
  />
@@ -182,10 +182,10 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
182
182
  label='Comments'
183
183
  id={BlockBoardKeyboardShortcuts}
184
184
  multiline
185
- minRows={Boolean(move) ? (isMainline ? 3 : 7) : 15}
186
- maxRows={Boolean(move) ? 9 : 15}
185
+ minRows={move ? (isMainline ? 3 : 7) : 15}
186
+ maxRows={move ? 9 : 15}
187
187
  value={comment}
188
- onChange={(event) => chess?.setComment(event.target.value)}
188
+ onChange={(event) => { chess.setComment(event.target.value); }}
189
189
  fullWidth
190
190
  />
191
191
 
@@ -194,7 +194,7 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
194
194
  <Stack spacing={1}>
195
195
  <ToggleButtonGroup
196
196
  exclusive
197
- value={getNagInSet(moveNags, chess?.currentMove()?.nags)}
197
+ value={getNagInSet(moveNags, chess.currentMove()?.nags)}
198
198
  onChange={handleExclusiveNag(moveNags)}
199
199
  >
200
200
  {moveNags.map((nag) => (
@@ -209,7 +209,7 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
209
209
 
210
210
  <ToggleButtonGroup
211
211
  exclusive
212
- value={getNagInSet(evalNags, chess?.currentMove()?.nags)}
212
+ value={getNagInSet(evalNags, chess.currentMove()?.nags)}
213
213
  onChange={handleExclusiveNag(evalNags)}
214
214
  >
215
215
  {evalNags.map((nag) => (
@@ -225,7 +225,7 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
225
225
  <ToggleButtonGroup
226
226
  value={getNagsInSet(
227
227
  positionalNags,
228
- chess?.currentMove()?.nags,
228
+ chess.currentMove()?.nags,
229
229
  )}
230
230
  onChange={handleMultiNags(positionalNags)}
231
231
  >
@@ -244,8 +244,8 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
244
244
  <Button
245
245
  startIcon={<CheckIcon />}
246
246
  variant='outlined'
247
- disabled={chess?.isInMainline(move) || takebacksDisabled}
248
- onClick={() => chess?.promoteVariation(move, true)}
247
+ disabled={chess.isInMainline(move) || takebacksDisabled}
248
+ onClick={() => { chess.promoteVariation(move, true); }}
249
249
  >
250
250
  Make main line
251
251
  </Button>
@@ -253,16 +253,16 @@ const Editor: React.FC<EditorProps> = ({ focusEditor, setFocusEditor }) => {
253
253
  startIcon={<ArrowUpwardIcon />}
254
254
  variant='outlined'
255
255
  disabled={
256
- !chess?.canPromoteVariation(move) || takebacksDisabled
256
+ !chess.canPromoteVariation(move) || takebacksDisabled
257
257
  }
258
- onClick={() => chess?.promoteVariation(move)}
258
+ onClick={() => { chess.promoteVariation(move); }}
259
259
  >
260
260
  Move variation up
261
261
  </Button>
262
262
  <Button
263
263
  startIcon={<DeleteIcon />}
264
264
  variant='outlined'
265
- onClick={() => chess?.delete(move)}
265
+ onClick={() => { chess.delete(move); }}
266
266
  disabled={!config?.allowMoveDeletion || takebacksDisabled}
267
267
  >
268
268
  Delete from here
frontend/src/board/pgn/boardTools/underboard/Tags.tsx CHANGED
@@ -135,7 +135,7 @@ const Tags: React.FC<TagsProps> = ({ game, allowEdits }) => {
135
135
  };
136
136
 
137
137
  chess.addObserver(observer);
138
- return () => chess.removeObserver(observer);
138
+ return () => { chess.removeObserver(observer); };
139
139
  }
140
140
  }, [chess]);
141
141
 
@@ -159,7 +159,7 @@ const Tags: React.FC<TagsProps> = ({ game, allowEdits }) => {
159
159
  rows.push({ name: 'Cohort', value: game.cohort });
160
160
  }
161
161
 
162
- rows.push(...defaultTags.map((name) => ({ name, value: tags?.[name] || '' })));
162
+ rows.push(...defaultTags.map((name) => ({ name, value: tags[name] || '' })));
163
163
 
164
164
  for (const [tag, value] of Object.entries(tags || {})) {
165
165
  if (!defaultTags.includes(tag) && !uneditableTags.includes(tag)) {
@@ -168,7 +168,7 @@ const Tags: React.FC<TagsProps> = ({ game, allowEdits }) => {
168
168
  }
169
169
 
170
170
  for (const tag of uneditableTags) {
171
- rows.push({ name: tag, value: tags?.[tag] || '' });
171
+ rows.push({ name: tag, value: tags[tag] || '' });
172
172
  }
173
173
 
174
174
  return (
frontend/src/board/pgn/boardTools/underboard/Underboard.tsx CHANGED
@@ -117,7 +117,7 @@ const Underboard = forwardRef<UnderboardApi, UnderboardProps>(
117
117
  ? initialTab
118
118
  : isOwner
119
119
  ? DefaultUnderboardTab.Editor
120
- : Boolean(game)
120
+ : game
121
121
  ? DefaultUnderboardTab.Comments
122
122
  : DefaultUnderboardTab.Explorer,
123
123
  );
frontend/src/board/pgn/boardTools/underboard/comments/Comment.tsx CHANGED
@@ -77,7 +77,7 @@ const BaseComment: React.FC<BaseCommentProps> = ({
77
77
  <Stack direction='row' spacing={0.5}>
78
78
  {!expanded && (
79
79
  <Tooltip title='Expand Comment'>
80
- <IconButton onClick={() => setExpanded(true)} size='small'>
80
+ <IconButton onClick={() => { setExpanded(true); }} size='small'>
81
81
  <ExpandMore
82
82
  fontSize='small'
83
83
  sx={{ color: 'text.secondary' }}
@@ -102,7 +102,7 @@ const BaseComment: React.FC<BaseCommentProps> = ({
102
102
  borderColor: 'primary.main',
103
103
  },
104
104
  }}
105
- onClick={() => setExpanded(false)}
105
+ onClick={() => { setExpanded(false); }}
106
106
  >
107
107
  <Divider orientation='vertical' />
108
108
  </Stack>
@@ -122,7 +122,7 @@ const BaseComment: React.FC<BaseCommentProps> = ({
122
122
  {isReplying ? (
123
123
  <ReplyEditor
124
124
  parent={comment}
125
- onCancel={() => setIsReplying(false)}
125
+ onCancel={() => { setIsReplying(false); }}
126
126
  />
127
127
  ) : (
128
128
  !isReadonly &&
@@ -131,7 +131,7 @@ const BaseComment: React.FC<BaseCommentProps> = ({
131
131
  <Button
132
132
  size='small'
133
133
  sx={{ textTransform: 'none', minWidth: 0 }}
134
- onClick={() => setIsReplying(true)}
134
+ onClick={() => { setIsReplying(true); }}
135
135
  >
136
136
  reply
137
137
  </Button>
@@ -225,7 +225,7 @@ const EditableComment: React.FC<CommentProps> = ({ comment }) => {
225
225
  <TextField
226
226
  id={BlockBoardKeyboardShortcuts}
227
227
  value={editValue}
228
- onChange={(e) => setEditValue(e.target.value)}
228
+ onChange={(e) => { setEditValue(e.target.value); }}
229
229
  onKeyDown={onKeyDown}
230
230
  size='small'
231
231
  sx={{ pt: 0.5 }}
@@ -238,7 +238,7 @@ const EditableComment: React.FC<CommentProps> = ({ comment }) => {
238
238
  <Button
239
239
  size='small'
240
240
  sx={{ textTransform: 'none' }}
241
- onClick={() => setEditValue(undefined)}
241
+ onClick={() => { setEditValue(undefined); }}
242
242
  disabled={request.isLoading()}
243
243
  >
244
244
  cancel
@@ -262,14 +262,14 @@ const EditableComment: React.FC<CommentProps> = ({ comment }) => {
262
262
  <Button
263
263
  size='small'
264
264
  sx={{ textTransform: 'none', minWidth: 0 }}
265
- onClick={() => setEditValue(comment.content)}
265
+ onClick={() => { setEditValue(comment.content); }}
266
266
  >
267
267
  edit
268
268
  </Button>
269
269
  <Button
270
270
  size='small'
271
271
  sx={{ textTransform: 'none', minWidth: 0 }}
272
- onClick={() => setShowDelete(true)}
272
+ onClick={() => { setShowDelete(true); }}
273
273
  >
274
274
  delete
275
275
  </Button>
@@ -282,7 +282,7 @@ const EditableComment: React.FC<CommentProps> = ({ comment }) => {
282
282
  <Dialog
283
283
  open={showDelete}
284
284
  onClose={
285
- deleteRequest.isLoading() ? undefined : () => setShowDelete(false)
285
+ deleteRequest.isLoading() ? undefined : () => { setShowDelete(false); }
286
286
  }
287
287
  >
288
288
  <DialogTitle>Delete Comment?</DialogTitle>
@@ -294,7 +294,7 @@ const EditableComment: React.FC<CommentProps> = ({ comment }) => {
294
294
  <DialogActions>
295
295
  <Button
296
296
  disabled={deleteRequest.isLoading()}
297
- onClick={() => setShowDelete(false)}
297
+ onClick={() => { setShowDelete(false); }}
298
298
  >
299
299
  Cancel
300
300
  </Button>
frontend/src/board/pgn/boardTools/underboard/comments/CommentEditor.tsx CHANGED
@@ -58,7 +58,7 @@ const CommentEditor: React.FC<CommentEditorProps> = ({ focusEditor, setFocusEdit
58
58
  parentIds: '',
59
59
  replies: {},
60
60
  };
61
- const existingComments = Boolean(game.positionComments?.[positionComment.fen]);
61
+ const existingComments = Boolean(game.positionComments[positionComment.fen]);
62
62
 
63
63
  request.onStart();
64
64
  api.createComment(game.cohort, game.id, positionComment, existingComments)
@@ -100,7 +100,7 @@ const CommentEditor: React.FC<CommentEditorProps> = ({ focusEditor, setFocusEdit
100
100
  fullWidth
101
101
  multiline
102
102
  value={comment}
103
- onChange={(e) => setComment(e.target.value)}
103
+ onChange={(e) => { setComment(e.target.value); }}
104
104
  onKeyDown={onKeyDown}
105
105
  disabled={request.isLoading()}
106
106
  maxRows={8}
frontend/src/board/pgn/boardTools/underboard/comments/Comments.tsx CHANGED
@@ -30,9 +30,9 @@ export enum SortBy {
30
30
  Oldest = 'OLDEST',
31
31
  }
32
32
 
33
- type PositionCommentSortContextType = {
33
+ interface PositionCommentSortContextType {
34
34
  sortBy: SortBy;
35
- };
35
+ }
36
36
 
37
37
  const PositionCommentSortContext = createContext<PositionCommentSortContextType>({
38
38
  sortBy: SortBy.Newest,
@@ -76,7 +76,7 @@ const Comments: React.FC<CommentsProps> = ({
76
76
  },
77
77
  };
78
78
  chess.addObserver(observer);
79
- return () => chess.removeObserver(observer);
79
+ return () => { chess.removeObserver(observer); };
80
80
  }
81
81
  }, [chess, setForceRender]);
82
82
 
@@ -95,7 +95,7 @@ const Comments: React.FC<CommentsProps> = ({
95
95
  label='Show Comments From'
96
96
  select
97
97
  value={view}
98
- onChange={(e) => setView(e.target.value as View)}
98
+ onChange={(e) => { setView(e.target.value as View); }}
99
99
  fullWidth
100
100
  size='small'
101
101
  >
@@ -109,7 +109,7 @@ const Comments: React.FC<CommentsProps> = ({
109
109
  label='Sort By'
110
110
  select
111
111
  value={sortBy}
112
- onChange={(e) => setSortBy(e.target.value as SortBy)}
112
+ onChange={(e) => { setSortBy(e.target.value as SortBy); }}
113
113
  fullWidth
114
114
  size='small'
115
115
  >
frontend/src/board/pgn/boardTools/underboard/comments/ReplyEditor.tsx CHANGED
@@ -79,7 +79,7 @@ const ReplyEditor: React.FC<ReplyEditorProps> = ({ parent, onCancel }) => {
79
79
  size='small'
80
80
  placeholder={`Reply to ${parent.owner.displayName} (${parent.owner.cohort})`}
81
81
  value={value}
82
- onChange={(e) => setValue(e.target.value)}
82
+ onChange={(e) => { setValue(e.target.value); }}
83
83
  onKeyDown={onKeyDown}
84
84
  disabled={request.isLoading()}
85
85
  multiline
frontend/src/board/pgn/boardTools/underboard/settings/EditorSettings.tsx CHANGED
@@ -21,7 +21,7 @@ const EditorSettings = () => {
21
21
  select
22
22
  label='Clock Field Format'
23
23
  value={clockFieldFormat}
24
- onChange={(e) => setClockFieldFormat(e.target.value)}
24
+ onChange={(e) => { setClockFieldFormat(e.target.value); }}
25
25
  >
26
26
  <MenuItem value={ClockFieldFormat.SingleField}>Single Field</MenuItem>
27
27
  <MenuItem value={ClockFieldFormat.ThreeField}>Three Fields</MenuItem>
frontend/src/board/pgn/boardTools/underboard/settings/GameSettings.tsx CHANGED
@@ -77,7 +77,7 @@ const GameSettings: React.FC<GameSettingsProps> = ({ game, onSaveGame }) => {
77
77
  <RadioGroup
78
78
  row
79
79
  value={visibility}
80
- onChange={(e) => setVisibility(e.target.value)}
80
+ onChange={(e) => { setVisibility(e.target.value); }}
81
81
  >
82
82
  <FormControlLabel
83
83
  value='public'
@@ -102,7 +102,7 @@ const GameSettings: React.FC<GameSettingsProps> = ({ game, onSaveGame }) => {
102
102
  <RadioGroup
103
103
  row
104
104
  value={orientation}
105
- onChange={(e) => setOrientation(e.target.value)}
105
+ onChange={(e) => { setOrientation(e.target.value); }}
106
106
  >
107
107
  <FormControlLabel
108
108
  value='white'
@@ -130,7 +130,7 @@ const GameSettings: React.FC<GameSettingsProps> = ({ game, onSaveGame }) => {
130
130
 
131
131
  <RequestReviewDialog game={game} />
132
132
 
133
- <Button variant='outlined' onClick={() => navigate('edit')}>
133
+ <Button variant='outlined' onClick={() => { navigate('edit'); }}>
134
134
  Replace PGN
135
135
  </Button>
136
136
  <DeleteGameButton variant='contained' game={game} />
frontend/src/board/pgn/boardTools/underboard/settings/KeyboardShortcuts.tsx CHANGED
@@ -686,7 +686,7 @@ const KeyboardShortcuts = () => {
686
686
  fullWidth
687
687
  select
688
688
  value={binding.modifier}
689
- onChange={(e) => onChangeModifier(a, e.target.value)}
689
+ onChange={(e) => { onChangeModifier(a, e.target.value); }}
690
690
  SelectProps={{
691
691
  displayEmpty: true,
692
692
  }}
@@ -709,7 +709,7 @@ const KeyboardShortcuts = () => {
709
709
  width: 1,
710
710
  height: '36.5px',
711
711
  }}
712
- onClick={() => onOpenEditor(a)}
712
+ onClick={() => { onOpenEditor(a); }}
713
713
  >
714
714
  {displayKey(binding.key)}
715
715
  </Button>
frontend/src/board/pgn/boardTools/underboard/settings/RequestReviewDialog.tsx CHANGED
@@ -43,11 +43,11 @@ interface RequestReviewDialogProps {
43
43
 
44
44
  const RequestReviewDialog: React.FC<RequestReviewDialogProps> = ({ game }) => {
45
45
  const [open, setOpen] = useState(false);
46
- const onClose = () => setOpen(false);
46
+ const onClose = () => { setOpen(false); };
47
47
 
48
48
  return (
49
49
  <>
50
- <Button variant='contained' onClick={() => setOpen(true)}>
50
+ <Button variant='contained' onClick={() => { setOpen(true); }}>
51
51
  {!game.review
52
52
  ? 'Request Sensei Review'
53
53
  : game.review.reviewedAt
@@ -175,7 +175,7 @@ const SubmitDialogContent: React.FC<{
175
175
  <FormLabel>Review Type</FormLabel>
176
176
  <RadioGroup
177
177
  value={reviewType}
178
- onChange={(e) => setReviewType(e.target.value as GameReviewType)}
178
+ onChange={(e) => { setReviewType(e.target.value as GameReviewType); }}
179
179
  >
180
180
  <FormControlLabel
181
181
  value={GameReviewType.Quick}
@@ -197,7 +197,7 @@ const SubmitDialogContent: React.FC<{
197
197
  control={
198
198
  <Checkbox
199
199
  checked={isConfirmed}
200
- onChange={(e) => setIsConfirmed(e.target.checked)}
200
+ onChange={(e) => { setIsConfirmed(e.target.checked); }}
201
201
  sx={{
202
202
  color:
203
203
  errors.isConfirmed && !isConfirmed
frontend/src/board/pgn/boardTools/underboard/settings/ViewerSettings.tsx CHANGED
@@ -102,7 +102,7 @@ const ViewerSettings = () => {
102
102
  select
103
103
  label='Board Style'
104
104
  value={boardStyle}
105
- onChange={(e) => setBoardStyle(e.target.value)}
105
+ onChange={(e) => { setBoardStyle(e.target.value); }}
106
106
  >
107
107
  <MenuItem value={BoardStyle.Standard}>Standard</MenuItem>
108
108
  <MenuItem value={BoardStyle.CherryBlossom}>Cherry Blossom</MenuItem>
@@ -117,7 +117,7 @@ const ViewerSettings = () => {
117
117
  select
118
118
  label='Piece Style'
119
119
  value={pieceStyle}
120
- onChange={(e) => setPieceStyle(e.target.value)}
120
+ onChange={(e) => { setPieceStyle(e.target.value); }}
121
121
  >
122
122
  <MenuItem value={PieceStyle.Standard}>Standard</MenuItem>
123
123
  <MenuItem value={PieceStyle.Cburnett}>Cburnett</MenuItem>
@@ -133,7 +133,7 @@ const ViewerSettings = () => {
133
133
  select
134
134
  label='Coordinate Style'
135
135
  value={coordinateStyle}
136
- onChange={(e) => setCoordinateStyle(e.target.value as CoordinateStyle)}
136
+ onChange={(e) => { setCoordinateStyle(e.target.value as CoordinateStyle); }}
137
137
  >
138
138
  <MenuItem value={CoordinateStyle.None}>None</MenuItem>
139
139
  <MenuItem value={CoordinateStyle.RankFileOnly}>
@@ -146,7 +146,7 @@ const ViewerSettings = () => {
146
146
  select
147
147
  label='Go to Start/End Button Behavior'
148
148
  value={goToEndBehavior}
149
- onChange={(e) => setGoToEndBehavior(e.target.value)}
149
+ onChange={(e) => { setGoToEndBehavior(e.target.value); }}
150
150
  >
151
151
  <MenuItem value={GoToEndButtonBehavior.SingleClick}>
152
152
  Single Click
@@ -161,7 +161,7 @@ const ViewerSettings = () => {
161
161
  select
162
162
  label='Variation Behavior'
163
163
  value={variationBehavior}
164
- onChange={(e) => setVariationBehavior(e.target.value)}
164
+ onChange={(e) => { setVariationBehavior(e.target.value); }}
165
165
  >
166
166
  <MenuItem value={VariationBehavior.None}>None</MenuItem>
167
167
  <MenuItem value={VariationBehavior.Dialog}>Prompt in Dialog</MenuItem>
@@ -171,7 +171,7 @@ const ViewerSettings = () => {
171
171
  select
172
172
  label='Captured Material Display'
173
173
  value={capturedMaterialBehavior}
174
- onChange={(e) => setCapturedMaterialBehavior(e.target.value)}
174
+ onChange={(e) => { setCapturedMaterialBehavior(e.target.value); }}
175
175
  >
176
176
  <MenuItem value={CapturedMaterialBehavior.None}>None</MenuItem>
177
177
  <MenuItem value={CapturedMaterialBehavior.Difference}>
@@ -187,7 +187,7 @@ const ViewerSettings = () => {
187
187
  control={
188
188
  <Checkbox
189
189
  checked={showLegalMoves}
190
- onChange={(e) => setShowLegalMoves(e.target.checked)}
190
+ onChange={(e) => { setShowLegalMoves(e.target.checked); }}
191
191
  />
192
192
  }
193
193
  label='Show legal moves'
@@ -197,7 +197,7 @@ const ViewerSettings = () => {
197
197
  control={
198
198
  <Checkbox
199
199
  checked={showMoveTimes}
200
- onChange={(e) => setShowMoveTimes(e.target.checked)}
200
+ onChange={(e) => { setShowMoveTimes(e.target.checked); }}
201
201
  />
202
202
  }
203
203
  label='Show elapsed time next to move'
frontend/src/board/pgn/explorer/Database.tsx CHANGED
@@ -85,11 +85,11 @@ const Database: React.FC<DatabaseProps> = ({
85
85
  [minCohort, maxCohort]
86
86
  );
87
87
 
88
- const sortedMoves: Array<ExplorerMove | LichessExplorerMove> = useMemo(() => {
88
+ const sortedMoves: (ExplorerMove | LichessExplorerMove)[] = useMemo(() => {
89
89
  if (!isExplorerPosition(position)) {
90
90
  return position?.moves || [];
91
91
  }
92
- return Object.values(position?.moves || [])
92
+ return Object.values(position.moves || [])
93
93
  .filter((move) => {
94
94
  return cohortRange.some((cohort) => {
95
95
  const result = move.results[cohort] || {};
@@ -132,7 +132,7 @@ const Database: React.FC<DatabaseProps> = ({
132
132
  }, [position]);
133
133
 
134
134
  const totalGames = isExplorerPosition(position)
135
- ? getGameCount(position?.results || {}, cohortRange)
135
+ ? getGameCount(position.results || {}, cohortRange)
136
136
  : position
137
137
  ? position.white + position.black + position.draws
138
138
  : 0;
@@ -290,7 +290,7 @@ const Database: React.FC<DatabaseProps> = ({
290
290
  fullWidth
291
291
  label='Min Cohort'
292
292
  value={minCohort}
293
- onChange={(e) => setMinCohort(e.target.value)}
293
+ onChange={(e) => { setMinCohort(e.target.value); }}
294
294
  >
295
295
  {dojoCohorts.map((cohort) => (
296
296
  <MenuItem key={cohort} value={cohort}>
@@ -305,7 +305,7 @@ const Database: React.FC<DatabaseProps> = ({
305
305
  fullWidth
306
306
  label='Max Cohort'
307
307
  value={maxCohort}
308
- onChange={(e) => setMaxCohort(e.target.value)}
308
+ onChange={(e) => { setMaxCohort(e.target.value); }}
309
309
  >
310
310
  {dojoCohorts.map((cohort, i) => (
311
311
  <MenuItem
frontend/src/board/pgn/explorer/Explorer.tsx CHANGED
@@ -35,7 +35,7 @@ const Explorer = () => {
35
35
 
36
36
  setFen(chess.fen());
37
37
  chess.addObserver(observer);
38
- return () => chess.removeObserver(observer);
38
+ return () => { chess.removeObserver(observer); };
39
39
  }
40
40
  }, [chess, setFen]);
41
41
 
@@ -70,7 +70,7 @@ const Explorer = () => {
70
70
  <TabContext value={tab}>
71
71
  <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
72
72
  <TabList
73
- onChange={(_, val) => setTab(val)}
73
+ onChange={(_, val) => { setTab(val); }}
74
74
  aria-label='Position database type'
75
75
  >
76
76
  <Tab label='Dojo Database' value='dojo' />
frontend/src/board/pgn/explorer/Header.tsx CHANGED
@@ -85,7 +85,7 @@ const Header: React.FC<HeaderProps> = ({
85
85
  >
86
86
  <IconButton
87
87
  color={follower ? 'success' : 'info'}
88
- onClick={() => setShowFollowDialog(!isFreeTier)}
88
+ onClick={() => { setShowFollowDialog(!isFreeTier); }}
89
89
  disabled={isFreeTier}
90
90
  >
91
91
  {follower ? <BookmarkAddedIcon /> : <BookmarkAddIcon />}
@@ -98,7 +98,7 @@ const Header: React.FC<HeaderProps> = ({
98
98
  fen={fen}
99
99
  follower={follower}
100
100
  open={showFollowDialog}
101
- onClose={() => setShowFollowDialog(false)}
101
+ onClose={() => { setShowFollowDialog(false); }}
102
102
  initialMinCohort={minCohort}
103
103
  initialMaxCohort={maxCohort}
104
104
  setFollower={setFollower}
@@ -196,7 +196,7 @@ const FollowDialog: React.FC<FollowDialogProps> = ({
196
196
  fullWidth
197
197
  label='Min Cohort'
198
198
  value={minCohort}
199
- onChange={(e) => setMinCohort(e.target.value)}
199
+ onChange={(e) => { setMinCohort(e.target.value); }}
200
200
  >
201
201
  {dojoCohorts.map((cohort) => (
202
202
  <MenuItem key={cohort} value={cohort}>
@@ -210,7 +210,7 @@ const FollowDialog: React.FC<FollowDialogProps> = ({
210
210
  fullWidth
211
211
  label='Max Cohort'
212
212
  value={maxCohort}
213
- onChange={(e) => setMaxCohort(e.target.value)}
213
+ onChange={(e) => { setMaxCohort(e.target.value); }}
214
214
  >
215
215
  {dojoCohorts.map((cohort) => (
216
216
  <MenuItem key={cohort} value={cohort}>
@@ -225,7 +225,7 @@ const FollowDialog: React.FC<FollowDialogProps> = ({
225
225
  control={
226
226
  <Checkbox
227
227
  checked={disableVariations}
228
- onChange={(e) => setDisableVariations(e.target.checked)}
228
+ onChange={(e) => { setDisableVariations(e.target.checked); }}
229
229
  />
230
230
  }
231
231
  label={
frontend/src/board/pgn/pgnText/Comment.tsx CHANGED
@@ -25,7 +25,7 @@ const Comment: React.FC<CommentProps> = ({ move, type, inline }) => {
25
25
  };
26
26
 
27
27
  chess.addObserver(observer);
28
- return () => chess.removeObserver(observer);
28
+ return () => { chess.removeObserver(observer); };
29
29
  }
30
30
  }, [chess, move, setForceRender]);
31
31
 
frontend/src/board/pgn/pgnText/GameComment.tsx CHANGED
@@ -21,13 +21,12 @@ const GameComment = () => {
21
21
  };
22
22
 
23
23
  chess.addObserver(observer);
24
- return () => chess.removeObserver(observer);
24
+ return () => { chess.removeObserver(observer); };
25
25
  }
26
26
  }, [chess, setForceRender]);
27
27
 
28
28
  if (
29
- !chess?.pgn.gameComment ||
30
- !chess.pgn.gameComment.comment ||
29
+ !chess?.pgn.gameComment.comment ||
31
30
  chess.pgn.gameComment.comment.trim() === '[#]'
32
31
  ) {
33
32
  return null;
frontend/src/board/pgn/pgnText/Interrupt.tsx CHANGED
@@ -7,7 +7,7 @@ import Lines from './Lines';
7
7
  export function hasInterrupt(move: Move): boolean {
8
8
  return (
9
9
  (move.commentAfter?.trim().length || 0) > 0 ||
10
- move.variations?.some((v) => v.length > 0)
10
+ move.variations.some((v) => v.length > 0)
11
11
  );
12
12
  }
13
13
 
frontend/src/board/pgn/pgnText/Lines.tsx CHANGED
@@ -31,7 +31,7 @@ const Line: React.FC<LineProps> = ({ line, depth, onClickMove, handleScroll }) =
31
31
  };
32
32
 
33
33
  chess.addObserver(observer);
34
- return () => chess.removeObserver(observer);
34
+ return () => { chess.removeObserver(observer); };
35
35
  }
36
36
  }, [chess, line, setForceRender]);
37
37
 
@@ -97,7 +97,7 @@ interface LinesProps {
97
97
  const Lines: React.FC<LinesProps> = ({ lines, depth, onClickMove, handleScroll }) => {
98
98
  const [expanded, setExpanded] = useState(true);
99
99
  const expandRef = useRef<HTMLHRElement>(null);
100
- let d = depth || 0;
100
+ const d = depth || 0;
101
101
 
102
102
  const onCollapse = () => {
103
103
  setExpanded(false);
@@ -163,7 +163,7 @@ const Lines: React.FC<LinesProps> = ({ lines, depth, onClickMove, handleScroll }
163
163
  mb: '2px',
164
164
  cursor: 'pointer',
165
165
  }}
166
- onClick={() => setExpanded(true)}
166
+ onClick={() => { setExpanded(true); }}
167
167
  >
168
168
  <Typography variant='caption' color='background.paper'>
169
169
  +{lines.length}
frontend/src/board/pgn/pgnText/MoveButton.tsx CHANGED
@@ -31,7 +31,7 @@ import { useChess } from '../PgnBoard';
31
31
 
32
32
  export function getTextColor(move: Move, inline?: boolean): string {
33
33
  for (const nag of move.nags || []) {
34
- const color = nags[getStandardNag(nag)]?.color;
34
+ const color = nags[getStandardNag(nag)].color;
35
35
  if (color) {
36
36
  return color;
37
37
  }
@@ -113,7 +113,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
113
113
  minWidth: inline ? 'fit-content' : undefined,
114
114
  display: inline ? 'inline-block' : undefined,
115
115
  }}
116
- onClick={() => onClickMove(move)}
116
+ onClick={() => { onClickMove(move); }}
117
117
  onContextMenu={onRightClick}
118
118
  {...longPress()}
119
119
  >
@@ -271,7 +271,7 @@ const MoveButton: React.FC<MoveButtonProps> = ({
271
271
  };
272
272
 
273
273
  chess.addObserver(observer);
274
- return () => chess.removeObserver(observer);
274
+ return () => { chess.removeObserver(observer); };
275
275
  }
276
276
  }, [
277
277
  chess,
@@ -318,7 +318,7 @@ const MoveButton: React.FC<MoveButtonProps> = ({
318
318
  setMenuAnchorEl(undefined);
319
319
  };
320
320
 
321
- let moveText = move.san;
321
+ const moveText = move.san;
322
322
 
323
323
  if (inline) {
324
324
  let text = '';
frontend/src/board/pgn/pgnText/MoveDisplay.tsx CHANGED
@@ -77,7 +77,7 @@ const MoveDisplay: React.FC<MoveProps> = ({ move, handleScroll, onClickMove }) =
77
77
  };
78
78
 
79
79
  chess.addObserver(observer);
80
- return () => chess.removeObserver(observer);
80
+ return () => { chess.removeObserver(observer); };
81
81
  }
82
82
  }, [chess, move, setForceRender, setNeedReminder]);
83
83
 
frontend/src/board/pgn/pgnText/Result.tsx CHANGED
@@ -20,7 +20,7 @@ const Result = () => {
20
20
  };
21
21
 
22
22
  chess.addObserver(observer);
23
- return () => chess.removeObserver(observer);
23
+ return () => { chess.removeObserver(observer); };
24
24
  }
25
25
  }, [chess, setForceRender]);
26
26
 
frontend/src/board/pgn/pgnText/Variation.tsx CHANGED
@@ -46,7 +46,7 @@ const Variation: React.FC<VariationProps> = ({ handleScroll, onClickMove }) => {
46
46
  };
47
47
 
48
48
  chess.addObserver(observer);
49
- return () => chess.removeObserver(observer);
49
+ return () => { chess.removeObserver(observer); };
50
50
  }
51
51
  }, [chess, setForceRender]);
52
52
 
frontend/src/board/puzzle/HintSection.tsx CHANGED
@@ -102,7 +102,7 @@ const IncorrectMoveHint: React.FC<HintSectionProps> = ({
102
102
  disableElevation
103
103
  color='error'
104
104
  sx={{ flexGrow: 1 }}
105
- onClick={() => onRetry(board, chess)}
105
+ onClick={() => { onRetry(board, chess); }}
106
106
  >
107
107
  Retry
108
108
  <br />
@@ -157,7 +157,7 @@ const CorrectMoveHint: React.FC<HintSectionProps> = ({
157
157
  disableElevation
158
158
  color='success'
159
159
  sx={{ flexGrow: 1 }}
160
- onClick={() => onNext(board, chess)}
160
+ onClick={() => { onNext(board, chess); }}
161
161
  >
162
162
  Next
163
163
  <br />
@@ -243,7 +243,7 @@ const CompleteHint: React.FC<HintSectionProps> = ({
243
243
  variant='contained'
244
244
  disableElevation
245
245
  sx={{ flexGrow: 1 }}
246
- onClick={() => onRestart(board, chess)}
246
+ onClick={() => { onRestart(board, chess); }}
247
247
  >
248
248
  Restart
249
249
  </Button>
frontend/src/board/puzzle/PuzzleBoard.tsx CHANGED
@@ -112,7 +112,7 @@ const PuzzleBoard: React.FC<PuzzleBoardProps> = ({
112
112
  chess.lastMove() === chess.currentMove() ||
113
113
  chess.hasNagInRange(10, 140)
114
114
  ) {
115
- return onComplete(board, chess);
115
+ onComplete(board, chess); return;
116
116
  }
117
117
  setStatus(Status.CorrectMove);
118
118
  setLastCorrectMove(chess.currentMove());
@@ -141,11 +141,11 @@ const PuzzleBoard: React.FC<PuzzleBoardProps> = ({
141
141
  const onNext = (board: BoardApi | undefined, chess: Chess) => {
142
142
  const nextMove = chess.nextMove();
143
143
  if (!nextMove) {
144
- return onComplete(board, chess);
144
+ onComplete(board, chess); return;
145
145
  }
146
146
  chess.seek(nextMove);
147
147
  if (chess.lastMove() === nextMove || chess.hasNagInRange(10, 140, nextMove)) {
148
- return onComplete(board, chess);
148
+ onComplete(board, chess); return;
149
149
  }
150
150
 
151
151
  board?.move(nextMove.from, nextMove.to);
frontend/src/calendar/AvailabilityBooker.tsx CHANGED
@@ -151,7 +151,7 @@ const AvailabilityBooker: React.FC<AvailabilityBookerProps> = ({ availability })
151
151
 
152
152
  const confirmBooking = () => {
153
153
  if (isGroup) {
154
- return confirmGroupBooking();
154
+ confirmGroupBooking(); return;
155
155
  }
156
156
  confirmSoloBooking();
157
157
  };
@@ -173,7 +173,7 @@ const AvailabilityBooker: React.FC<AvailabilityBookerProps> = ({ availability })
173
173
  <Button
174
174
  data-cy='cancel-button'
175
175
  color='inherit'
176
- onClick={() => navigate('/calendar')}
176
+ onClick={() => { navigate('/calendar'); }}
177
177
  disabled={request.status === RequestStatus.Loading}
178
178
  >
179
179
  Cancel
@@ -273,9 +273,9 @@ const AvailabilityBooker: React.FC<AvailabilityBookerProps> = ({ availability })
273
273
  name='radio-buttons-group'
274
274
  value={selectedType}
275
275
  onChange={(event) =>
276
- setSelectedType(
276
+ { setSelectedType(
277
277
  event.target.value as AvailabilityType,
278
- )
278
+ ); }
279
279
  }
280
280
  >
281
281
  {availability.types?.map((t) => (
@@ -295,7 +295,7 @@ const AvailabilityBooker: React.FC<AvailabilityBookerProps> = ({ availability })
295
295
  <TimePicker
296
296
  label='Start Time'
297
297
  value={startTime}
298
- onChange={(value) => setStartTime(value)}
298
+ onChange={(value) => { setStartTime(value); }}
299
299
  slotProps={{
300
300
  textField: {
301
301
  fullWidth: true,
frontend/src/calendar/CalendarPage.tsx CHANGED
@@ -437,7 +437,7 @@ export default function CalendarPage() {
437
437
  <Snackbar
438
438
  open={canceled}
439
439
  autoHideDuration={6000}
440
- onClose={() => setCanceled(false)}
440
+ onClose={() => { setCanceled(false); }}
441
441
  anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
442
442
  message='Meeting canceled'
443
443
  />
frontend/src/calendar/CoachingBooker.tsx CHANGED
@@ -85,7 +85,7 @@ const CoachingBooker: React.FC<CoachingBookerProps> = ({ event }) => {
85
85
  <Button
86
86
  data-cy='cancel-button'
87
87
  color='inherit'
88
- onClick={() => navigate('/calendar')}
88
+ onClick={() => { navigate('/calendar'); }}
89
89
  disabled={request.status === RequestStatus.Loading}
90
90
  >
91
91
  Cancel
frontend/src/calendar/EventBooker.tsx CHANGED
@@ -7,9 +7,9 @@ import { EventType } from '../database/event';
7
7
  import AvailabilityBooker from './AvailabilityBooker';
8
8
  import CoachingBooker from './CoachingBooker';
9
9
 
10
- type EventBookerProps = {
10
+ interface EventBookerProps {
11
11
  id: string;
12
- };
12
+ }
13
13
 
14
14
  const EventBooker = () => {
15
15
  const navigate = useNavigate();
@@ -33,7 +33,7 @@ const EventBooker = () => {
33
33
  variant='filled'
34
34
  severity='error'
35
35
  sx={{ width: '100%' }}
36
- onClose={() => navigate('/calendar')}
36
+ onClose={() => { navigate('/calendar'); }}
37
37
  >
38
38
  This event cannot be found. It is either fully booked, deleted or not
39
39
  available to your cohort.
frontend/src/calendar/eventEditor/AvailabilityEditor.tsx CHANGED
@@ -214,7 +214,7 @@ const AvailabilityEditor: React.FC<AvailabilityEditorProps> = ({ editor }) => {
214
214
  <Checkbox
215
215
  checked={allAvailabilityTypes}
216
216
  onChange={(event) =>
217
- setAllAvailabilityTypes(event.target.checked)
217
+ { setAllAvailabilityTypes(event.target.checked); }
218
218
  }
219
219
  />
220
220
  }
@@ -231,10 +231,10 @@ const AvailabilityEditor: React.FC<AvailabilityEditorProps> = ({ editor }) => {
231
231
  availabilityTypes[type]
232
232
  }
233
233
  onChange={(event) =>
234
- setAvailabilityType(
234
+ { setAvailabilityType(
235
235
  type,
236
236
  event.target.checked,
237
- )
237
+ ); }
238
238
  }
239
239
  />
240
240
  }
frontend/src/calendar/eventEditor/CoachingEditor.tsx CHANGED
@@ -67,7 +67,7 @@ export function validateCoachingEditor(
67
67
  errors.location = 'This field is required';
68
68
  }
69
69
 
70
- let maxParticipants: number = -1;
70
+ let maxParticipants = -1;
71
71
  if (!editor.maxParticipants.trim()) {
72
72
  errors.maxParticipants = 'This field is required';
73
73
  } else {
@@ -83,7 +83,7 @@ export function validateCoachingEditor(
83
83
  }
84
84
  }
85
85
 
86
- let fullPrice: number = -1;
86
+ let fullPrice = -1;
87
87
  if (!editor.fullPrice.trim()) {
88
88
  errors.fullPrice = 'This field is required';
89
89
  } else {
@@ -94,7 +94,7 @@ export function validateCoachingEditor(
94
94
  }
95
95
  }
96
96
 
97
- let currentPrice: number = -1;
97
+ let currentPrice = -1;
98
98
  if (editor.currentPrice.trim()) {
99
99
  const [price, error] = validatePrice(editor.currentPrice);
100
100
  currentPrice = price;
@@ -170,7 +170,7 @@ const CoachingEditor: React.FC<CoachingEditorProps> = ({ editor }) => {
170
170
  <Button
171
171
  color='inherit'
172
172
  size='small'
173
- onClick={() => navigate('/coach')}
173
+ onClick={() => { navigate('/coach'); }}
174
174
  >
175
175
  Open Portal
176
176
  </Button>
@@ -259,7 +259,7 @@ const CoachingEditor: React.FC<CoachingEditorProps> = ({ editor }) => {
259
259
  control={
260
260
  <Checkbox
261
261
  checked={hideParticipants}
262
- onChange={(e) => setHideParticipants(e.target.checked)}
262
+ onChange={(e) => { setHideParticipants(e.target.checked); }}
263
263
  />
264
264
  }
265
265
  label='Hide participant list when booking? If checked, users will only see other participants after they have booked.'
@@ -281,7 +281,7 @@ const CoachingEditor: React.FC<CoachingEditorProps> = ({ editor }) => {
281
281
  control={
282
282
  <Checkbox
283
283
  checked={bookableByFreeUsers}
284
- onChange={(e) => setBookableByFreeUsers(e.target.checked)}
284
+ onChange={(e) => { setBookableByFreeUsers(e.target.checked); }}
285
285
  />
286
286
  }
287
287
  label='Allow free-tier users to see and book this event'
@@ -296,7 +296,7 @@ const CoachingEditor: React.FC<CoachingEditorProps> = ({ editor }) => {
296
296
  label='Full Price'
297
297
  variant='outlined'
298
298
  value={fullPrice}
299
- onChange={(e) => setFullPrice(e.target.value)}
299
+ onChange={(e) => { setFullPrice(e.target.value); }}
300
300
  error={Boolean(errors.fullPrice)}
301
301
  helperText={errors.fullPrice}
302
302
  InputProps={{
@@ -310,7 +310,7 @@ const CoachingEditor: React.FC<CoachingEditorProps> = ({ editor }) => {
310
310
  label='Sale Price'
311
311
  variant='outlined'
312
312
  value={currentPrice}
313
- onChange={(e) => setCurrentPrice(e.target.value)}
313
+ onChange={(e) => { setCurrentPrice(e.target.value); }}
314
314
  error={Boolean(errors.currentPrice)}
315
315
  helperText={
316
316
  errors.currentPrice ||
frontend/src/calendar/eventEditor/DojoEventEditor.tsx CHANGED
@@ -107,7 +107,7 @@ const DojoEventEditor: React.FC<DojoEventEditorProps> = ({ editor }) => {
107
107
  control={
108
108
  <Checkbox
109
109
  checked={hideFromPublicDiscord}
110
- onChange={(e) => setHideFromPublicDiscord(e.target.checked)}
110
+ onChange={(e) => { setHideFromPublicDiscord(e.target.checked); }}
111
111
  />
112
112
  }
113
113
  />
frontend/src/calendar/eventEditor/EventEditor.tsx CHANGED
@@ -164,7 +164,7 @@ const EventEditor: React.FC<EventEditorProps> = ({ scheduler }) => {
164
164
  <RadioGroup
165
165
  value={editor.type}
166
166
  onChange={(e) =>
167
- editor.setType(e.target.value as EventType)
167
+ { editor.setType(e.target.value as EventType); }
168
168
  }
169
169
  >
170
170
  <FormControlLabel
frontend/src/calendar/eventEditor/form/CohortsFormSection.tsx CHANGED
@@ -38,7 +38,7 @@ const CohortsFormSection: React.FC<CohortsFormSectionProps> = ({
38
38
  control={
39
39
  <Checkbox
40
40
  checked={allCohorts}
41
- onChange={(event) => setAllCohorts(event.target.checked)}
41
+ onChange={(event) => { setAllCohorts(event.target.checked); }}
42
42
  />
43
43
  }
44
44
  label='All Cohorts'
@@ -52,7 +52,7 @@ const CohortsFormSection: React.FC<CohortsFormSectionProps> = ({
52
52
  data-cy={`cohort-checkbox-${cohort}`}
53
53
  checked={allCohorts || cohorts[cohort]}
54
54
  onChange={(event) =>
55
- setCohort(cohort, event.target.checked)
55
+ { setCohort(cohort, event.target.checked); }
56
56
  }
57
57
  />
58
58
  }
frontend/src/calendar/eventEditor/form/DescriptionFormSection.tsx CHANGED
@@ -28,7 +28,7 @@ const DescriptionFormSection: React.FC<DescriptionFormSectionProps> = ({
28
28
  minRows={3}
29
29
  maxRows={3}
30
30
  value={description}
31
- onChange={(event) => setDescription(event.target.value)}
31
+ onChange={(event) => { setDescription(event.target.value); }}
32
32
  error={Boolean(error)}
33
33
  helperText={error}
34
34
  />
frontend/src/calendar/eventEditor/form/LocationFormSection.tsx CHANGED
@@ -29,7 +29,7 @@ const LocationFormSection: React.FC<LocationFormSectionProps> = ({
29
29
  label='Location'
30
30
  variant='outlined'
31
31
  value={location}
32
- onChange={(event) => setLocation(event.target.value)}
32
+ onChange={(event) => { setLocation(event.target.value); }}
33
33
  helperText={error || helperText}
34
34
  error={Boolean(error)}
35
35
  />
frontend/src/calendar/eventEditor/form/MaxParticipantsFormSection.tsx CHANGED
@@ -31,7 +31,7 @@ const MaxParticipantsFormSection: React.FC<MaxParticipantsFormSectionProps> = ({
31
31
  inputMode: 'numeric',
32
32
  pattern: '[0-9]*',
33
33
  }}
34
- onChange={(event) => setMaxParticipants(event.target.value)}
34
+ onChange={(event) => { setMaxParticipants(event.target.value); }}
35
35
  helperText={error || helperText}
36
36
  error={Boolean(error)}
37
37
  />
frontend/src/calendar/eventEditor/form/TimesFormSection.tsx CHANGED
@@ -41,7 +41,7 @@ const TimesFormSection: React.FC<TimesFormSectionProps> = ({
41
41
  <DateTimePicker
42
42
  label='Start Time'
43
43
  value={start}
44
- onChange={(value) => setStart(value)}
44
+ onChange={(value) => { setStart(value); }}
45
45
  slotProps={{
46
46
  textField: {
47
47
  id: 'start-time',
@@ -57,7 +57,7 @@ const TimesFormSection: React.FC<TimesFormSectionProps> = ({
57
57
  <DateTimePicker
58
58
  label='End Time'
59
59
  value={end}
60
- onChange={(value) => setEnd(value)}
60
+ onChange={(value) => { setEnd(value); }}
61
61
  slotProps={{
62
62
  textField: {
63
63
  id: 'end-time',
frontend/src/calendar/eventEditor/form/TitleFormSection.tsx CHANGED
@@ -26,7 +26,7 @@ const TitleFormSection: React.FC<TitleFormSectionProps> = ({
26
26
  label='Title'
27
27
  variant='outlined'
28
28
  value={title}
29
- onChange={(event) => setTitle(event.target.value)}
29
+ onChange={(event) => { setTitle(event.target.value); }}
30
30
  error={Boolean(error)}
31
31
  helperText={error}
32
32
  sx={{ mt: 2 }}
frontend/src/calendar/eventEditor/useEventEditor.ts CHANGED
@@ -222,12 +222,12 @@ export default function useEventEditor(
222
222
  const [availabilityTypes, setAvailabilityTypes] = useState<
223
223
  Record<AvailabilityType, boolean>
224
224
  >(
225
- Object.values(AvailabilityType).reduce(
225
+ Object.values(AvailabilityType).reduce<Record<AvailabilityType, boolean>>(
226
226
  (map, type) => {
227
227
  map[type] = false;
228
228
  return map;
229
229
  },
230
- {} as Record<AvailabilityType, boolean>,
230
+ {},
231
231
  ),
232
232
  );
233
233
  const setAvailabilityType = useCallback(
@@ -243,13 +243,13 @@ export default function useEventEditor(
243
243
  const userCohortIndex = dojoCohorts.findIndex((c) => c === user.dojoCohort);
244
244
  const [allCohorts, setAllCohorts] = useState(false);
245
245
  const [cohorts, setCohorts] = useState<Record<string, boolean>>(
246
- dojoCohorts.reduce(
246
+ dojoCohorts.reduce<Record<string, boolean>>(
247
247
  (map, cohort, index) => {
248
248
  map[cohort] =
249
249
  userCohortIndex >= 0 && Math.abs(index - userCohortIndex) <= 1;
250
250
  return map;
251
251
  },
252
- {} as Record<string, boolean>,
252
+ {},
253
253
  ),
254
254
  );
255
255
  const setCohort = useCallback(
@@ -287,12 +287,12 @@ export default function useEventEditor(
287
287
 
288
288
  const onChangeEventType = useCallback(
289
289
  (value: EventType) => {
290
- const allFalseCohorts = dojoCohorts.reduce(
290
+ const allFalseCohorts = dojoCohorts.reduce<Record<string, boolean>>(
291
291
  (map, cohort) => {
292
292
  map[cohort] = false;
293
293
  return map;
294
294
  },
295
- {} as Record<string, boolean>,
295
+ {},
296
296
  );
297
297
 
298
298
  setType(value);
@@ -312,14 +312,14 @@ export default function useEventEditor(
312
312
  );
313
313
  } else {
314
314
  setCohorts(
315
- dojoCohorts.reduce(
315
+ dojoCohorts.reduce<Record<string, boolean>>(
316
316
  (map, cohort, index) => {
317
317
  map[cohort] =
318
318
  userCohortIndex >= 0 &&
319
319
  Math.abs(index - userCohortIndex) <= 1;
320
320
  return map;
321
321
  },
322
- {} as Record<string, boolean>,
322
+ {},
323
323
  ),
324
324
  );
325
325
  }
@@ -347,12 +347,12 @@ export default function useEventEditor(
347
347
 
348
348
  const originalCohorts: string[] = initialEvent?.cohorts || [];
349
349
  if (originalCohorts.length > 0) {
350
- const allFalseCohorts = dojoCohorts.reduce(
350
+ const allFalseCohorts = dojoCohorts.reduce<Record<string, boolean>>(
351
351
  (map, cohort) => {
352
352
  map[cohort] = false;
353
353
  return map;
354
354
  },
355
- {} as Record<string, boolean>,
355
+ {},
356
356
  );
357
357
  setCohorts(() =>
358
358
  originalCohorts.reduce(
frontend/src/calendar/eventViewer/CoachingViewer.tsx CHANGED
@@ -107,7 +107,7 @@ const CoachingViewer: React.FC<CoachingViewerProps> = ({ processedEvent }) => {
107
107
  {isOwner || isParticipant ? (
108
108
  <Button
109
109
  variant='contained'
110
- onClick={() => navigate(`/meeting/${event.id}`)}
110
+ onClick={() => { navigate(`/meeting/${event.id}`); }}
111
111
  >
112
112
  View Details
113
113
  </Button>
frontend/src/calendar/eventViewer/LigaTournamentViewer.tsx CHANGED
@@ -99,7 +99,7 @@ const LigaTournamentViewer: React.FC<LigaTournamentViewerProps> = ({
99
99
  fen: ligaTournament.fen.trim(),
100
100
  viewOnly: true,
101
101
  }}
102
- onInitialize={(board) => board.redrawAll()}
102
+ onInitialize={(board) => { board.redrawAll(); }}
103
103
  />
104
104
  )}
105
105
  </Box>
frontend/src/calendar/eventViewer/MeetingViewer.tsx CHANGED
@@ -44,7 +44,7 @@ const MeetingViewer: React.FC<MeetingViewerProps> = ({ processedEvent }) => {
44
44
  )}
45
45
  </Stack>
46
46
 
47
- <Button variant='contained' onClick={() => navigate(`/meeting/${event.id}`)}>
47
+ <Button variant='contained' onClick={() => { navigate(`/meeting/${event.id}`); }}>
48
48
  View Details
49
49
  </Button>
50
50
  </Stack>
frontend/src/calendar/filters/CalendarFilters.tsx CHANGED
@@ -82,44 +82,44 @@ export const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
82
82
  paddingLeft: theme.spacing(1),
83
83
  }));
84
84
 
85
- const initialFilterTypes = Object.values(AvailabilityType).reduce(
85
+ const initialFilterTypes = Object.values(AvailabilityType).reduce<Record<AvailabilityType, boolean>>(
86
86
  (map, type) => {
87
87
  map[type] = false;
88
88
  return map;
89
89
  },
90
- {} as Record<AvailabilityType, boolean>,
90
+ {},
91
91
  );
92
92
 
93
- const initialFilterCohorts = dojoCohorts.reduce(
93
+ const initialFilterCohorts = dojoCohorts.reduce<Record<string, boolean>>(
94
94
  (map, cohort) => {
95
95
  map[cohort] = false;
96
96
  return map;
97
97
  },
98
- {} as Record<string, boolean>,
98
+ {},
99
99
  );
100
100
 
101
- const initialFilterTournamentTypes = Object.values(TournamentType).reduce(
101
+ const initialFilterTournamentTypes = Object.values(TournamentType).reduce<Record<TournamentType, boolean>>(
102
102
  (map, type) => {
103
103
  map[type] = true;
104
104
  return map;
105
105
  },
106
- {} as Record<TournamentType, boolean>,
106
+ {},
107
107
  );
108
108
 
109
- const initialFilterTournamentTimeControls = Object.values(TimeControlType).reduce(
109
+ const initialFilterTournamentTimeControls = Object.values(TimeControlType).reduce<Record<TimeControlType, boolean>>(
110
110
  (map, type) => {
111
111
  map[type] = true;
112
112
  return map;
113
113
  },
114
- {} as Record<TimeControlType, boolean>,
114
+ {},
115
115
  );
116
116
 
117
- const initialFilterTournamentPositions = Object.values(PositionType).reduce(
117
+ const initialFilterTournamentPositions = Object.values(PositionType).reduce<Record<PositionType, boolean>>(
118
118
  (m, t) => {
119
119
  m[t] = true;
120
120
  return m;
121
121
  },
122
- {} as Record<PositionType, boolean>,
122
+ {},
123
123
  );
124
124
 
125
125
  export interface Filters {
@@ -442,7 +442,7 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
442
442
  <Checkbox
443
443
  checked={filters.availabilities}
444
444
  onChange={(event) =>
445
- filters.setAvailabilities(event.target.checked)
445
+ { filters.setAvailabilities(event.target.checked); }
446
446
  }
447
447
  />
448
448
  }
@@ -453,7 +453,7 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
453
453
  <Checkbox
454
454
  checked={filters.meetings}
455
455
  onChange={(event) =>
456
- filters.setMeetings(event.target.checked)
456
+ { filters.setMeetings(event.target.checked); }
457
457
  }
458
458
  />
459
459
  }
@@ -482,7 +482,7 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
482
482
  <Checkbox
483
483
  checked={filters.dojoEvents}
484
484
  onChange={(event) =>
485
- filters.setDojoEvents(event.target.checked)
485
+ { filters.setDojoEvents(event.target.checked); }
486
486
  }
487
487
  color='success'
488
488
  />
@@ -494,7 +494,7 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
494
494
  <Checkbox
495
495
  checked={filters.coaching}
496
496
  onChange={(event) =>
497
- filters.setCoaching(event.target.checked)
497
+ { filters.setCoaching(event.target.checked); }
498
498
  }
499
499
  color='coaching'
500
500
  />
@@ -525,10 +525,10 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
525
525
  filters.tournamentTimeControls[type]
526
526
  }
527
527
  onChange={(event) =>
528
- onChangeTournamentTimeControls(
528
+ { onChangeTournamentTimeControls(
529
529
  type,
530
530
  event.target.checked,
531
- )
531
+ ); }
532
532
  }
533
533
  disabled={!filters.dojoEvents}
534
534
  color='warning'
@@ -549,7 +549,7 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
549
549
  <Checkbox
550
550
  checked={filters.allTypes}
551
551
  onChange={(event) =>
552
- filters.setAllTypes(event.target.checked)
552
+ { filters.setAllTypes(event.target.checked); }
553
553
  }
554
554
  sx={{
555
555
  color: 'error.dark',
@@ -568,7 +568,7 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
568
568
  <Checkbox
569
569
  checked={filters.allTypes || filters.types[type]}
570
570
  onChange={(event) =>
571
- onChangeType(type, event.target.checked)
571
+ { onChangeType(type, event.target.checked); }
572
572
  }
573
573
  sx={{
574
574
  color: 'error.dark',
@@ -592,7 +592,7 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
592
592
  <Checkbox
593
593
  checked={filters.allCohorts}
594
594
  onChange={(event) =>
595
- filters.setAllCohorts(event.target.checked)
595
+ { filters.setAllCohorts(event.target.checked); }
596
596
  }
597
597
  sx={{
598
598
  color: 'error.dark',
@@ -613,7 +613,7 @@ export const CalendarFilters: React.FC<CalendarFiltersProps> = ({ filters }) =>
613
613
  filters.allCohorts || filters.cohorts[cohort]
614
614
  }
615
615
  onChange={(event) =>
616
- onChangeCohort(cohort, event.target.checked)
616
+ { onChangeCohort(cohort, event.target.checked); }
617
617
  }
618
618
  sx={{
619
619
  color: 'error.dark',
frontend/src/calendar/filters/TimezoneFilter.tsx CHANGED
@@ -68,8 +68,8 @@ const TimezoneFilter: React.FC<TimezoneFilterProps> = ({ filters }) => {
68
68
  const browserDefaultLabel =
69
69
  timezoneOffset > 0 ? `UTC-${timezoneOffset}` : `UTC+${Math.abs(timezoneOffset)}`;
70
70
 
71
- let minHourNum = minHour?.hour || 0;
72
- let maxHourNum = (maxHour?.hour || 23) + 1;
71
+ const minHourNum = minHour?.hour || 0;
72
+ const maxHourNum = (maxHour?.hour || 23) + 1;
73
73
 
74
74
  return (
75
75
  <Stack spacing={2.5}>
@@ -78,7 +78,7 @@ const TimezoneFilter: React.FC<TimezoneFilterProps> = ({ filters }) => {
78
78
  <RadioGroup
79
79
  row
80
80
  value={timeFormat}
81
- onChange={(e) => onChangeTimeFormat(e.target.value as TimeFormat)}
81
+ onChange={(e) => { onChangeTimeFormat(e.target.value as TimeFormat); }}
82
82
  >
83
83
  <FormControlLabel
84
84
  value={TimeFormat.TwelveHour}
@@ -108,7 +108,7 @@ const TimezoneFilter: React.FC<TimezoneFilterProps> = ({ filters }) => {
108
108
  select
109
109
  data-cy='timezone-selector'
110
110
  value={timezone}
111
- onChange={(e) => onChangeTimezone(e.target.value)}
111
+ onChange={(e) => { onChangeTimezone(e.target.value); }}
112
112
  size='small'
113
113
  >
114
114
  <MenuItem value={DefaultTimezone}>
@@ -121,7 +121,7 @@ const TimezoneFilter: React.FC<TimezoneFilterProps> = ({ filters }) => {
121
121
  label='Week Start'
122
122
  select
123
123
  value={weekStartOn}
124
- onChange={(e) => setWeekStartOn(parseInt(e.target.value) as WeekDays)}
124
+ onChange={(e) => { setWeekStartOn(parseInt(e.target.value) as WeekDays); }}
125
125
  size='small'
126
126
  >
127
127
  <MenuItem value={0}>Sunday</MenuItem>
@@ -138,7 +138,7 @@ const TimezoneFilter: React.FC<TimezoneFilterProps> = ({ filters }) => {
138
138
  views={['hours']}
139
139
  ampm={timeFormat === TimeFormat.TwelveHour}
140
140
  value={minHour}
141
- onChange={(v) => setMinHour(v)}
141
+ onChange={(v) => { setMinHour(v); }}
142
142
  maxTime={maxHour}
143
143
  slotProps={{
144
144
  textField: {
@@ -155,7 +155,7 @@ const TimezoneFilter: React.FC<TimezoneFilterProps> = ({ filters }) => {
155
155
  views={['hours']}
156
156
  ampm={timeFormat === TimeFormat.TwelveHour}
157
157
  value={maxHour}
158
- onChange={(v) => setMaxHour(v)}
158
+ onChange={(v) => { setMaxHour(v); }}
159
159
  minTime={minHour}
160
160
  slotProps={{
161
161
  textField: {
frontend/src/clubs/ClubDetailsPage.tsx CHANGED
@@ -33,9 +33,9 @@ import MemberCountChip from './MemberCountChip';
33
33
  import ScoreboardTab from './ScoreboardTab';
34
34
  import UrlChip from './UrlChip';
35
35
 
36
- export type ClubDetailsParams = {
36
+ export interface ClubDetailsParams {
37
37
  id: string;
38
- };
38
+ }
39
39
 
40
40
  const ClubDetailsPage = () => {
41
41
  const auth = useAuth();
@@ -144,14 +144,14 @@ const ClubDetailsPage = () => {
144
144
  <Snackbar
145
145
  open={Boolean(snackbarText)}
146
146
  autoHideDuration={5000}
147
- onClose={() => setSnackbarText('')}
147
+ onClose={() => { setSnackbarText(''); }}
148
148
  message={snackbarText}
149
149
  anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
150
150
  />
151
151
 
152
152
  <UpsellDialog
153
153
  open={Boolean(upsellAction)}
154
- onClose={() => setUpsellAction('')}
154
+ onClose={() => { setUpsellAction(''); }}
155
155
  currentAction={upsellAction}
156
156
  />
157
157
 
@@ -182,7 +182,7 @@ const ClubDetailsPage = () => {
182
182
  <Button
183
183
  variant='contained'
184
184
  onClick={() =>
185
- navigate(`/clubs/${club.id}/edit`)
185
+ { navigate(`/clubs/${club.id}/edit`); }
186
186
  }
187
187
  >
188
188
  Edit Settings
@@ -228,7 +228,7 @@ const ClubDetailsPage = () => {
228
228
  <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
229
229
  <Tabs
230
230
  value={searchParams.get('view')}
231
- onChange={(_, t) => setSearchParams({ view: t })}
231
+ onChange={(_, t) => { setSearchParams({ view: t }); }}
232
232
  aria-label='profile tabs'
233
233
  variant='scrollable'
234
234
  >
@@ -269,7 +269,7 @@ const ClubDetailsPage = () => {
269
269
  clubName={club.name}
270
270
  open={showJoinRequestDialog}
271
271
  onSuccess={onSuccessfulJoinRequest}
272
- onClose={() => setShowJoinRequestDialog(false)}
272
+ onClose={() => { setShowJoinRequestDialog(false); }}
273
273
  />
274
274
 
275
275
  <LeaveClubDialog
@@ -278,7 +278,7 @@ const ClubDetailsPage = () => {
278
278
  approvalRequired={club.approvalRequired}
279
279
  open={showLeaveDialog}
280
280
  onSuccess={onLeaveClubConfirm}
281
- onClose={() => setShowLeaveDialog(false)}
281
+ onClose={() => { setShowLeaveDialog(false); }}
282
282
  />
283
283
  </Stack>
284
284
  )}
frontend/src/clubs/ClubFilters.tsx CHANGED
@@ -102,7 +102,7 @@ export const ClubFilterEditor: React.FC<ClubFilterEditorProps> = ({ filters }) =
102
102
  <TextField
103
103
  label='Search...'
104
104
  value={filters.search}
105
- onChange={(e) => filters.setSearch(e.target.value)}
105
+ onChange={(e) => { filters.setSearch(e.target.value); }}
106
106
  sx={{ flexGrow: 1 }}
107
107
  />
108
108
 
@@ -112,7 +112,7 @@ export const ClubFilterEditor: React.FC<ClubFilterEditorProps> = ({ filters }) =
112
112
  label='Sort By'
113
113
  value={filters.sortMethod}
114
114
  onChange={(e) =>
115
- filters.setSortMethod(e.target.value as ClubSortMethod)
115
+ { filters.setSortMethod(e.target.value as ClubSortMethod); }
116
116
  }
117
117
  >
118
118
  {Object.values(ClubSortMethod).map((method) => (
@@ -127,7 +127,7 @@ export const ClubFilterEditor: React.FC<ClubFilterEditorProps> = ({ filters }) =
127
127
  <RadioGroup
128
128
  value={filters.sortDirection}
129
129
  onChange={(e) =>
130
- filters.setSortDirection(e.target.value as 'asc' | 'desc')
130
+ { filters.setSortDirection(e.target.value as 'asc' | 'desc'); }
131
131
  }
132
132
  row
133
133
  >
frontend/src/clubs/ClubGrid.tsx CHANGED
@@ -63,7 +63,7 @@ export const ListClubItem: React.FC<ListClubItemProps> = ({ club, sx }) => {
63
63
  alignItems: 'start',
64
64
  justifyContent: 'start',
65
65
  }}
66
- onClick={() => navigate(`/clubs/${club.id}`)}
66
+ onClick={() => { navigate(`/clubs/${club.id}`); }}
67
67
  >
68
68
  <CardHeader
69
69
  sx={{ pb: 1 }}
frontend/src/clubs/ClubJoinRequestDialog.tsx CHANGED
@@ -60,7 +60,7 @@ const ClubJoinRequestDialog: React.FC<ClubJoinRequestDialogProps> = ({
60
60
  <TextField
61
61
  label='Optionally explain your request'
62
62
  value={notes}
63
- onChange={(e) => setNotes(e.target.value)}
63
+ onChange={(e) => { setNotes(e.target.value); }}
64
64
  multiline
65
65
  minRows={3}
66
66
  fullWidth
frontend/src/clubs/CreateClubPage.tsx CHANGED
@@ -203,7 +203,7 @@ const CreateClubPage = () => {
203
203
  label='Name'
204
204
  required
205
205
  value={name}
206
- onChange={(e) => setName(e.target.value)}
206
+ onChange={(e) => { setName(e.target.value); }}
207
207
  error={Boolean(errors.name)}
208
208
  helperText={errors.name}
209
209
  />
@@ -215,7 +215,7 @@ const CreateClubPage = () => {
215
215
  multiline
216
216
  minRows={3}
217
217
  value={shortDescription}
218
- onChange={(e) => setShortDescription(e.target.value)}
218
+ onChange={(e) => { setShortDescription(e.target.value); }}
219
219
  error={
220
220
  Boolean(errors.shortDescription) ||
221
221
  shortDescription.length > 300
@@ -233,7 +233,7 @@ const CreateClubPage = () => {
233
233
  multiline
234
234
  minRows={5}
235
235
  value={description}
236
- onChange={(e) => setDescription(e.target.value)}
236
+ onChange={(e) => { setDescription(e.target.value); }}
237
237
  error={Boolean(errors.description)}
238
238
  helperText={
239
239
  errors.description ||
@@ -245,7 +245,7 @@ const CreateClubPage = () => {
245
245
  label='URL'
246
246
  helperText='Add this if you want to link to an external site'
247
247
  value={externalUrl}
248
- onChange={(e) => setExternalUrl(e.target.value)}
248
+ onChange={(e) => { setExternalUrl(e.target.value); }}
249
249
  />
250
250
 
251
251
  <Grid2 container columnSpacing={2} rowSpacing={3}>
@@ -254,7 +254,7 @@ const CreateClubPage = () => {
254
254
  label='City'
255
255
  fullWidth
256
256
  value={city}
257
- onChange={(e) => setCity(e.target.value)}
257
+ onChange={(e) => { setCity(e.target.value); }}
258
258
  />
259
259
  </Grid2>
260
260
  <Grid2 sm={4}>
@@ -262,7 +262,7 @@ const CreateClubPage = () => {
262
262
  label='State'
263
263
  fullWidth
264
264
  value={state}
265
- onChange={(e) => setState(e.target.value)}
265
+ onChange={(e) => { setState(e.target.value); }}
266
266
  />
267
267
  </Grid2>
268
268
  <Grid2 sm={4}>
@@ -270,7 +270,7 @@ const CreateClubPage = () => {
270
270
  label='Country'
271
271
  fullWidth
272
272
  value={country}
273
- onChange={(e) => setCountry(e.target.value)}
273
+ onChange={(e) => { setCountry(e.target.value); }}
274
274
  />
275
275
  </Grid2>
276
276
  </Grid2>
@@ -280,7 +280,7 @@ const CreateClubPage = () => {
280
280
  control={
281
281
  <Checkbox
282
282
  checked={!allowFreeTier}
283
- onChange={(_, checked) => setAllowFreeTier(!checked)}
283
+ onChange={(_, checked) => { setAllowFreeTier(!checked); }}
284
284
  />
285
285
  }
286
286
  label='Limit access to subscribers? If checked, free-tier users will not be able to join.'
@@ -289,7 +289,7 @@ const CreateClubPage = () => {
289
289
  control={
290
290
  <Checkbox
291
291
  checked={unlisted}
292
- onChange={(_, checked) => setUnlisted(checked)}
292
+ onChange={(_, checked) => { setUnlisted(checked); }}
293
293
  />
294
294
  }
295
295
  label='Unlisted? If checked, this club will not appear in the list and can only be shared by its URL.'
@@ -298,7 +298,7 @@ const CreateClubPage = () => {
298
298
  control={
299
299
  <Checkbox
300
300
  checked={approvalRequired}
301
- onChange={(_, checked) => setApprovalRequired(checked)}
301
+ onChange={(_, checked) => { setApprovalRequired(checked); }}
302
302
  />
303
303
  }
304
304
  label="Require approval to join? If checked, you must manually approve each user's request to join."
frontend/src/clubs/JoinRequestsTab.tsx CHANGED
@@ -159,7 +159,7 @@ const JoinRequest: React.FC<JoinRequestProps> = ({
159
159
  <Check
160
160
  color='success'
161
161
  onClick={() =>
162
- handleRequest(ClubJoinRequestStatus.Approved)
162
+ { handleRequest(ClubJoinRequestStatus.Approved); }
163
163
  }
164
164
  />
165
165
  </IconButton>
@@ -171,9 +171,9 @@ const JoinRequest: React.FC<JoinRequestProps> = ({
171
171
  <Block
172
172
  color='error'
173
173
  onClick={() =>
174
- handleRequest(
174
+ { handleRequest(
175
175
  ClubJoinRequestStatus.Rejected
176
- )
176
+ ); }
177
177
  }
178
178
  />
179
179
  </IconButton>
frontend/src/clubs/ListClubsPage.tsx CHANGED
@@ -44,7 +44,7 @@ const ListClubsPage = () => {
44
44
  <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
45
45
  <Tabs
46
46
  value={searchParams.get('view') || 'all'}
47
- onChange={(_, t) => setSearchParams({ view: t })}
47
+ onChange={(_, t) => { setSearchParams({ view: t }); }}
48
48
  variant='scrollable'
49
49
  >
50
50
  <Tab label='All Clubs' value='all' />
@@ -62,7 +62,7 @@ const ListClubsPage = () => {
62
62
 
63
63
  <UpsellDialog
64
64
  open={Boolean(upsellAction)}
65
- onClose={() => setUpsellAction('')}
65
+ onClose={() => { setUpsellAction(''); }}
66
66
  currentAction={upsellAction}
67
67
  />
68
68
  </Container>
frontend/src/coaching/coaches/courseEditor/CourseEditorPage.tsx CHANGED
@@ -81,10 +81,10 @@ interface CourseEditorErrors {
81
81
  };
82
82
  }
83
83
 
84
- type CourseEditorPageParams = {
84
+ interface CourseEditorPageParams {
85
85
  type: CourseType;
86
86
  id: string;
87
- };
87
+ }
88
88
 
89
89
  const CourseEditorPage = () => {
90
90
  const params = useParams<CourseEditorPageParams>();
@@ -341,7 +341,7 @@ const CourseEditorPage = () => {
341
341
  return (
342
342
  <PurchaseCoursePreview
343
343
  course={course}
344
- closePreview={() => setShowPreview(false)}
344
+ closePreview={() => { setShowPreview(false); }}
345
345
  />
346
346
  );
347
347
  }
@@ -437,7 +437,7 @@ const CourseEditorPage = () => {
437
437
  <Button
438
438
  size='small'
439
439
  variant='contained'
440
- onClick={() => setShowPreview(true)}
440
+ onClick={() => { setShowPreview(true); }}
441
441
  >
442
442
  Show Preview
443
443
  </Button>
@@ -459,7 +459,7 @@ const CourseEditorPage = () => {
459
459
  select
460
460
  label='Course Type'
461
461
  value={type}
462
- onChange={(e) => setType(e.target.value as CourseType)}
462
+ onChange={(e) => { setType(e.target.value as CourseType); }}
463
463
  helperText='Changing course type is not implemented yet'
464
464
  >
465
465
  {Object.values(CourseType).map((t) => (
@@ -472,7 +472,7 @@ const CourseEditorPage = () => {
472
472
  <TextField
473
473
  label='Name'
474
474
  value={name}
475
- onChange={(e) => setName(e.target.value)}
475
+ onChange={(e) => { setName(e.target.value); }}
476
476
  error={Boolean(errors.name)}
477
477
  helperText={errors.name}
478
478
  />
@@ -480,7 +480,7 @@ const CourseEditorPage = () => {
480
480
  <TextField
481
481
  label='Author'
482
482
  value={ownerDisplayName}
483
- onChange={(e) => setOwnerDisplayName(e.target.value)}
483
+ onChange={(e) => { setOwnerDisplayName(e.target.value); }}
484
484
  helperText='Defaults to your account display name if left blank'
485
485
  />
486
486
 
@@ -490,7 +490,7 @@ const CourseEditorPage = () => {
490
490
  minRows={3}
491
491
  maxRows={8}
492
492
  value={description}
493
- onChange={(e) => setDescription(e.target.value)}
493
+ onChange={(e) => { setDescription(e.target.value); }}
494
494
  error={Boolean(errors.description)}
495
495
  helperText={errors.description}
496
496
  />
@@ -520,7 +520,7 @@ const CourseEditorPage = () => {
520
520
  label='Bullet Point'
521
521
  value={item}
522
522
  onChange={(e) =>
523
- onChangeWhatsIncluded(idx, e.target.value)
523
+ { onChangeWhatsIncluded(idx, e.target.value); }
524
524
  }
525
525
  error={Boolean(errors.whatsIncluded?.[idx])}
526
526
  helperText={errors.whatsIncluded?.[idx]}
@@ -529,7 +529,7 @@ const CourseEditorPage = () => {
529
529
  <span>
530
530
  <IconButton
531
531
  disabled={idx === 0}
532
- onClick={() => onMoveUpWhatsIncluded(idx)}
532
+ onClick={() => { onMoveUpWhatsIncluded(idx); }}
533
533
  >
534
534
  <ArrowUpward />
535
535
  </IconButton>
@@ -537,7 +537,7 @@ const CourseEditorPage = () => {
537
537
  </Tooltip>
538
538
  <Tooltip title='Delete'>
539
539
  <IconButton
540
- onClick={() => onDeleteWhatsIncluded(idx)}
540
+ onClick={() => { onDeleteWhatsIncluded(idx); }}
541
541
  >
542
542
  <Delete />
543
543
  </IconButton>
@@ -546,7 +546,7 @@ const CourseEditorPage = () => {
546
546
  ))}
547
547
  <Button
548
548
  sx={{ alignSelf: 'start' }}
549
- onClick={() => setWhatsIncluded([...whatsIncluded, ''])}
549
+ onClick={() => { setWhatsIncluded([...whatsIncluded, '']); }}
550
550
  >
551
551
  Add Bullet Point
552
552
  </Button>
@@ -559,7 +559,7 @@ const CourseEditorPage = () => {
559
559
  <RadioGroup
560
560
  row
561
561
  value={color}
562
- onChange={(e) => setColor(e.target.value)}
562
+ onChange={(e) => { setColor(e.target.value); }}
563
563
  >
564
564
  <FormControlLabel label='None' value='None' control={<Radio />} />
565
565
  <FormControlLabel
@@ -588,7 +588,7 @@ const CourseEditorPage = () => {
588
588
  fullWidth
589
589
  label='Min Cohort'
590
590
  value={minCohort}
591
- onChange={(e) => setMinCohort(e.target.value)}
591
+ onChange={(e) => { setMinCohort(e.target.value); }}
592
592
  >
593
593
  {dojoCohorts.map((cohort) => (
594
594
  <MenuItem key={cohort} value={cohort}>
@@ -613,7 +613,7 @@ const CourseEditorPage = () => {
613
613
  fullWidth
614
614
  label='Max Cohort'
615
615
  value={maxCohort}
616
- onChange={(e) => setMaxCohort(e.target.value)}
616
+ onChange={(e) => { setMaxCohort(e.target.value); }}
617
617
  >
618
618
  {dojoCohorts.map((cohort, i) => (
619
619
  <MenuItem
@@ -634,7 +634,7 @@ const CourseEditorPage = () => {
634
634
  fullWidth
635
635
  label='Cohort Range Description'
636
636
  value={cohortRangeStr}
637
- onChange={(e) => setCohortRangeStr(e.target.value)}
637
+ onChange={(e) => { setCohortRangeStr(e.target.value); }}
638
638
  helperText={`Defaults to ${cohortRange} if left blank`}
639
639
  />
640
640
  </Grid2>
@@ -648,7 +648,7 @@ const CourseEditorPage = () => {
648
648
  <Checkbox
649
649
  checked={includedWithSubscription}
650
650
  onChange={(e) =>
651
- setIncludedWithSubscription(e.target.checked)
651
+ { setIncludedWithSubscription(e.target.checked); }
652
652
  }
653
653
  />
654
654
  }
@@ -659,7 +659,7 @@ const CourseEditorPage = () => {
659
659
  <Checkbox
660
660
  checked={availableForFreeUsers}
661
661
  onChange={(e) =>
662
- setAvailableForFreeUsers(e.target.checked)
662
+ { setAvailableForFreeUsers(e.target.checked); }
663
663
  }
664
664
  />
665
665
  }
@@ -698,7 +698,7 @@ const CourseEditorPage = () => {
698
698
  <IconButton
699
699
  disabled={idx === 0}
700
700
  onClick={() =>
701
- onMoveUpPurchaseOption(idx)
701
+ { onMoveUpPurchaseOption(idx); }
702
702
  }
703
703
  >
704
704
  <ArrowUpward />
@@ -707,7 +707,7 @@ const CourseEditorPage = () => {
707
707
  </Tooltip>
708
708
  <Tooltip title='Delete'>
709
709
  <IconButton
710
- onClick={() => onDeletePurchaseOption(idx)}
710
+ onClick={() => { onDeletePurchaseOption(idx); }}
711
711
  >
712
712
  <Delete />
713
713
  </IconButton>
@@ -719,11 +719,11 @@ const CourseEditorPage = () => {
719
719
  label='Name'
720
720
  value={option.name}
721
721
  onChange={(e) =>
722
- onChangePurchaseOption(
722
+ { onChangePurchaseOption(
723
723
  idx,
724
724
  'name',
725
725
  e.target.value,
726
- )
726
+ ); }
727
727
  }
728
728
  helperText='If left blank, it defaults to the course name. Generally set this only if you have multiple purchase options.'
729
729
  />
@@ -731,11 +731,11 @@ const CourseEditorPage = () => {
731
731
  label='Full Price'
732
732
  value={option.fullPrice}
733
733
  onChange={(e) =>
734
- onChangePurchaseOption(
734
+ { onChangePurchaseOption(
735
735
  idx,
736
736
  'fullPrice',
737
737
  e.target.value,
738
- )
738
+ ); }
739
739
  }
740
740
  InputProps={{
741
741
  startAdornment: (
@@ -756,11 +756,11 @@ const CourseEditorPage = () => {
756
756
  variant='outlined'
757
757
  value={option.currentPrice}
758
758
  onChange={(e) =>
759
- onChangePurchaseOption(
759
+ { onChangePurchaseOption(
760
760
  idx,
761
761
  'currentPrice',
762
762
  e.target.value,
763
- )
763
+ ); }
764
764
  }
765
765
  helperText={
766
766
  errors.purchaseOptions?.[idx]?.currentPrice ||
@@ -801,12 +801,12 @@ const CourseEditorPage = () => {
801
801
  label='Description'
802
802
  value={item.description}
803
803
  onChange={(e) =>
804
- onChangeSellingPoint(
804
+ { onChangeSellingPoint(
805
805
  idx,
806
806
  spIdx,
807
807
  'description',
808
808
  e.target.value,
809
- )
809
+ ); }
810
810
  }
811
811
  />
812
812
  <FormControlLabel
@@ -815,12 +815,12 @@ const CourseEditorPage = () => {
815
815
  <Checkbox
816
816
  checked={item.included}
817
817
  onChange={(e) =>
818
- onChangeSellingPoint(
818
+ { onChangeSellingPoint(
819
819
  idx,
820
820
  spIdx,
821
821
  'included',
822
822
  e.target.checked,
823
- )
823
+ ); }
824
824
  }
825
825
  />
826
826
  }
@@ -830,10 +830,10 @@ const CourseEditorPage = () => {
830
830
  <IconButton
831
831
  disabled={spIdx === 0}
832
832
  onClick={() =>
833
- onMoveUpSellingPoint(
833
+ { onMoveUpSellingPoint(
834
834
  idx,
835
835
  spIdx,
836
- )
836
+ ); }
837
837
  }
838
838
  >
839
839
  <ArrowUpward />
@@ -843,10 +843,10 @@ const CourseEditorPage = () => {
843
843
  <Tooltip title='Delete'>
844
844
  <IconButton
845
845
  onClick={() =>
846
- onDeleteSellingPoint(
846
+ { onDeleteSellingPoint(
847
847
  idx,
848
848
  spIdx,
849
- )
849
+ ); }
850
850
  }
851
851
  >
852
852
  <Delete />
@@ -856,7 +856,7 @@ const CourseEditorPage = () => {
856
856
  ))}
857
857
  <Button
858
858
  sx={{ alignSelf: 'start' }}
859
- onClick={() => onAddSellingPoint(idx)}
859
+ onClick={() => { onAddSellingPoint(idx); }}
860
860
  >
861
861
  Add Selling Point
862
862
  </Button>
frontend/src/coaching/coaches/courseEditor/PurchaseCoursePreview.tsx CHANGED
@@ -45,7 +45,7 @@ const PurchaseCoursePreview: React.FC<PurchaseCoursePreviewProps> = ({
45
45
  control={
46
46
  <Checkbox
47
47
  checked={isFreeTier}
48
- onChange={(e) => setIsFreeTier(e.target.checked)}
48
+ onChange={(e) => { setIsFreeTier(e.target.checked); }}
49
49
  />
50
50
  }
51
51
  label='Preview as free-tier user'
@@ -55,7 +55,7 @@ const PurchaseCoursePreview: React.FC<PurchaseCoursePreviewProps> = ({
55
55
  <TabContext value={previewTab}>
56
56
  <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
57
57
  <TabList
58
- onChange={(_, value) => setPreviewTab(value)}
58
+ onChange={(_, value) => { setPreviewTab(value); }}
59
59
  aria-label='lab API tabs example'
60
60
  >
61
61
  <Tab label='Course List Page' value='listCourses' />
frontend/src/coaching/customers/CoachesTab.tsx CHANGED
@@ -80,7 +80,7 @@ const CoachListItem: React.FC<{ coach: User }> = ({ coach }) => {
80
80
  event.preventDefault();
81
81
  event.stopPropagation();
82
82
 
83
- if (!currentUser || currentUser?.username === coach.username) {
83
+ if (!currentUser || currentUser.username === coach.username) {
84
84
  return;
85
85
  }
86
86
 
@@ -104,7 +104,7 @@ const CoachListItem: React.FC<{ coach: User }> = ({ coach }) => {
104
104
 
105
105
  return (
106
106
  <Card key={coach.username}>
107
- <CardActionArea onClick={() => navigate(`/profile/${coach.username}`)}>
107
+ <CardActionArea onClick={() => { navigate(`/profile/${coach.username}`); }}>
108
108
  <CardContent>
109
109
  <Stack spacing={4}>
110
110
  <Stack
frontend/src/coaching/customers/CoachingList.tsx CHANGED
@@ -136,7 +136,7 @@ const CoachingListItem: React.FC<{ event: Event }> = ({ event }) => {
136
136
  isOwner || isParticipant ? (
137
137
  <Button
138
138
  variant='contained'
139
- onClick={() => navigate(`/meeting/${event.id}`)}
139
+ onClick={() => { navigate(`/meeting/${event.id}`); }}
140
140
  >
141
141
  View Details
142
142
  </Button>
frontend/src/coaching/customers/CoachingPage.tsx CHANGED
@@ -14,7 +14,7 @@ const CoachingPage = () => {
14
14
  <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
15
15
  <Tabs
16
16
  value={searchParams.get('view') || 'coaches'}
17
- onChange={(_, t) => setSearchParams({ view: t })}
17
+ onChange={(_, t) => { setSearchParams({ view: t }); }}
18
18
  variant='scrollable'
19
19
  >
20
20
  <Tab label='Coaches' value='coaches' />
frontend/src/courses/list/CourseFilters.tsx CHANGED
@@ -111,10 +111,10 @@ export const CourseFilterEditor: React.FC<CourseFilterEditorProps> = ({ filters
111
111
  <Checkbox
112
112
  checked={filters.categories[category]}
113
113
  onChange={(event) =>
114
- onChangeCategories(
114
+ { onChangeCategories(
115
115
  category,
116
116
  event.target.checked,
117
- )
117
+ ); }
118
118
  }
119
119
  color={getCategoryColor(category)}
120
120
  />
@@ -142,7 +142,7 @@ export const CourseFilterEditor: React.FC<CourseFilterEditorProps> = ({ filters
142
142
  fullWidth
143
143
  label='Min Cohort'
144
144
  value={filters.minCohort}
145
- onChange={(e) => filters.setMinCohort(e.target.value)}
145
+ onChange={(e) => { filters.setMinCohort(e.target.value); }}
146
146
  >
147
147
  {dojoCohorts.map((cohort) => (
148
148
  <MenuItem key={cohort} value={cohort}>
@@ -167,7 +167,7 @@ export const CourseFilterEditor: React.FC<CourseFilterEditorProps> = ({ filters
167
167
  fullWidth
168
168
  label='Max Cohort'
169
169
  value={filters.maxCohort}
170
- onChange={(e) => filters.setMaxCohort(e.target.value)}
170
+ onChange={(e) => { filters.setMaxCohort(e.target.value); }}
171
171
  >
172
172
  {dojoCohorts.map((cohort, i) => (
173
173
  <MenuItem
@@ -212,7 +212,7 @@ export const CourseFilterEditor: React.FC<CourseFilterEditorProps> = ({ filters
212
212
  <Checkbox
213
213
  checked={filters.showAccessible}
214
214
  onChange={(event) =>
215
- filters.setShowAccessible(event.target.checked)
215
+ { filters.setShowAccessible(event.target.checked); }
216
216
  }
217
217
  />
218
218
  }
frontend/src/courses/list/CourseListItem.tsx CHANGED
@@ -115,7 +115,7 @@ const CourseListItem: React.FC<CourseListItemProps> = ({
115
115
  <Link
116
116
  component={RouterLink}
117
117
  to={`/profile/${course.owner}`}
118
- onClick={(e) => e.stopPropagation()}
118
+ onClick={(e) => { e.stopPropagation(); }}
119
119
  >
120
120
  {course.ownerDisplayName}
121
121
  </Link>
frontend/src/courses/localStorage.ts CHANGED
@@ -20,7 +20,7 @@ export function setCheckoutSessionId(courseId?: string, checkoutId?: string) {
20
20
  }
21
21
 
22
22
  const courseCheckoutStr = localStorage.getItem(COURSE_STORAGE_KEY);
23
- let checkoutSessionIds = courseCheckoutStr ? JSON.parse(courseCheckoutStr) : {};
23
+ const checkoutSessionIds = courseCheckoutStr ? JSON.parse(courseCheckoutStr) : {};
24
24
  checkoutSessionIds[courseId] = checkoutId;
25
25
  localStorage.setItem(COURSE_STORAGE_KEY, JSON.stringify(checkoutSessionIds));
26
26
  }
frontend/src/courses/view/CoursePage.tsx CHANGED
@@ -23,10 +23,10 @@ import PurchaseCoursePage from './PurchaseCoursePage';
23
23
  import { AuthStatus, useAuth, useFreeTier } from '../../auth/Auth';
24
24
  import { getCheckoutSessionId, setCheckoutSessionId } from '../localStorage';
25
25
 
26
- type CoursePageParams = {
26
+ interface CoursePageParams {
27
27
  type: CourseType;
28
28
  id: string;
29
- };
29
+ }
30
30
 
31
31
  const CoursePage = () => {
32
32
  const navigate = useNavigate();
@@ -107,7 +107,7 @@ const CoursePage = () => {
107
107
  sx={{ mb: 4 }}
108
108
  action={
109
109
  <Button
110
- onClick={() => navigate('/signup')}
110
+ onClick={() => { navigate('/signup'); }}
111
111
  size='small'
112
112
  color='inherit'
113
113
  >
@@ -144,10 +144,10 @@ const CoursePage = () => {
144
144
  <Button
145
145
  variant='contained'
146
146
  onClick={() =>
147
- setSearchParams({
147
+ { setSearchParams({
148
148
  chapter: prevModule.chapterIndex,
149
149
  module: prevModule.moduleIndex,
150
- })
150
+ }); }
151
151
  }
152
152
  >
153
153
  Previous: {prevModule.name}
@@ -158,10 +158,10 @@ const CoursePage = () => {
158
158
  <Button
159
159
  variant='contained'
160
160
  onClick={() =>
161
- setSearchParams({
161
+ { setSearchParams({
162
162
  chapter: nextModule.chapterIndex,
163
163
  module: nextModule.moduleIndex,
164
- })
164
+ }); }
165
165
  }
166
166
  >
167
167
  Next: {nextModule.name}
frontend/src/courses/view/ExercisesModule.tsx CHANGED
@@ -14,10 +14,7 @@ import { useApi } from '../../api/Api';
14
14
  function getCompleted(user: User | undefined, module: CourseModule): boolean[] {
15
15
  let exercises: boolean[] = [];
16
16
  if (
17
- user &&
18
- user.openingProgress &&
19
- user.openingProgress[module.id] &&
20
- user.openingProgress[module.id].exercises
17
+ user?.openingProgress?.[module.id]?.exercises
21
18
  ) {
22
19
  exercises = user.openingProgress[module.id].exercises!;
23
20
  }
frontend/src/courses/view/PgnSelector.tsx CHANGED
@@ -23,7 +23,7 @@ function getPgnName(header: Record<string, string> | PgnHeaders): string {
23
23
 
24
24
  interface PgnSelectorProps {
25
25
  pgns?: string[];
26
- headers?: Array<Record<string, string> | PgnHeaders>;
26
+ headers?: (Record<string, string> | PgnHeaders)[];
27
27
  selectedIndex: number;
28
28
  setSelectedIndex: (i: number) => void;
29
29
  completed?: boolean[];
@@ -42,7 +42,7 @@ const PgnSelector: React.FC<PgnSelectorProps> = ({
42
42
  hiddenCount,
43
43
  noCard,
44
44
  }) => {
45
- let selectedHeaders: Array<Record<string, string> | PgnHeaders> = [];
45
+ let selectedHeaders: (Record<string, string> | PgnHeaders)[] = [];
46
46
  if (headers) {
47
47
  selectedHeaders = headers;
48
48
  } else if (pgns) {
@@ -57,7 +57,7 @@ const PgnSelector: React.FC<PgnSelectorProps> = ({
57
57
  <ListItemButton
58
58
  sx={{ pl: 0 }}
59
59
  selected={selectedIndex === idx}
60
- onClick={() => setSelectedIndex(idx)}
60
+ onClick={() => { setSelectedIndex(idx); }}
61
61
  >
62
62
  <ListItemIcon sx={{ minWidth: '40px' }}>
63
63
  <Stack alignItems='center' width={1}>
frontend/src/database/club.ts CHANGED
@@ -44,14 +44,10 @@ export interface Club {
44
44
 
45
45
  export interface ClubDetails extends Club {
46
46
  /** The members of the club, mapped by their usernames. */
47
- members: {
48
- [username: string]: ClubMember;
49
- };
47
+ members: Record<string, ClubMember>;
50
48
 
51
49
  /** The pending requests to join the club, mapped by their usernames. */
52
- joinRequests: {
53
- [username: string]: ClubJoinRequest;
54
- };
50
+ joinRequests: Record<string, ClubJoinRequest>;
55
51
  }
56
52
 
57
53
  export interface ClubLocation {
frontend/src/database/graduation.ts CHANGED
@@ -11,9 +11,7 @@ export interface Graduation {
11
11
  startRating: number;
12
12
  currentRating: number;
13
13
  comments: string;
14
- progress: {
15
- [id: string]: RequirementProgress;
16
- };
14
+ progress: Record<string, RequirementProgress>;
17
15
  graduationCohorts: string[];
18
16
  startedAt: string;
19
17
  createdAt: string;
frontend/src/database/notification.ts CHANGED
@@ -52,9 +52,7 @@ export interface Notification {
52
52
  id: string;
53
53
 
54
54
  /** The headers of the Game. */
55
- headers: {
56
- [key: string]: string;
57
- };
55
+ headers: Record<string, string>;
58
56
  };
59
57
 
60
58
  /** Metadata for a game review Notification. */
@@ -66,9 +64,7 @@ export interface Notification {
66
64
  id: string;
67
65
 
68
66
  /** The headers of the Game. */
69
- headers: {
70
- [key: string]: string;
71
- };
67
+ headers: Record<string, string>;
72
68
 
73
69
  /** The reviewer of the Game. */
74
70
  reviewer: {
@@ -122,9 +118,7 @@ export interface Notification {
122
118
  result: string;
123
119
 
124
120
  /** The headers of the game. */
125
- headers: {
126
- [key: string]: string;
127
- };
121
+ headers: Record<string, string>;
128
122
  };
129
123
 
130
124
  /** Metadata for a club join request notification. */
@@ -140,16 +134,16 @@ export interface Notification {
140
134
  export function getTitle(notification: Notification): string {
141
135
  switch (notification.type) {
142
136
  case NotificationType.GameComment:
143
- return `${notification.gameCommentMetadata?.headers?.White} - ${notification.gameCommentMetadata?.headers?.Black}`;
137
+ return `${notification.gameCommentMetadata?.headers.White} - ${notification.gameCommentMetadata?.headers.Black}`;
144
138
  case NotificationType.GameReviewComplete:
145
- return `${notification.gameReviewMetadata?.headers?.White} - ${notification.gameReviewMetadata?.headers?.Black}`;
139
+ return `${notification.gameReviewMetadata?.headers.White} - ${notification.gameReviewMetadata?.headers.Black}`;
146
140
  case NotificationType.NewFollower:
147
141
  return 'You have a new follower';
148
142
  case NotificationType.TimelineComment:
149
143
  case NotificationType.TimelineReaction:
150
144
  return `${notification.timelineCommentMetadata?.name}`;
151
145
  case NotificationType.ExplorerGame:
152
- return `${notification.explorerGameMetadata?.headers?.White} - ${notification.explorerGameMetadata?.headers?.Black}`;
146
+ return `${notification.explorerGameMetadata?.headers.White} - ${notification.explorerGameMetadata?.headers.Black}`;
153
147
  case NotificationType.NewClubJoinRequest:
154
148
  return `${notification.clubMetadata?.name}`;
155
149
  case NotificationType.ClubJoinRequestApproved:
@@ -158,7 +152,7 @@ export function getTitle(notification: Notification): string {
158
152
  }
159
153
 
160
154
  export function getDescription(notification: Notification): string {
161
- let count = notification.count || 1;
155
+ const count = notification.count || 1;
162
156
 
163
157
  switch (notification.type) {
164
158
  case NotificationType.GameComment:
frontend/src/database/requirement.ts CHANGED
@@ -19,9 +19,7 @@ export interface CustomTask {
19
19
  owner: string;
20
20
  name: string;
21
21
  description: string;
22
- counts: {
23
- [cohort: string]: number;
24
- };
22
+ counts: Record<string, number>;
25
23
  scoreboardDisplay: ScoreboardDisplay;
26
24
  category: RequirementCategory;
27
25
  updatedAt: string;
@@ -54,15 +52,11 @@ export interface Requirement {
54
52
  name: string;
55
53
  description: string;
56
54
  freeDescription: string;
57
- counts: {
58
- [cohort: string]: number;
59
- };
55
+ counts: Record<string, number>;
60
56
  startCount: number;
61
57
  numberOfCohorts: number;
62
58
  unitScore: number;
63
- unitScoreOverride?: {
64
- [cohort: string]: number;
65
- };
59
+ unitScoreOverride?: Record<string, number>;
66
60
  totalScore: number;
67
61
  videoUrls?: string[];
68
62
  positions?: Position[];
@@ -82,12 +76,8 @@ export interface Requirement {
82
76
 
83
77
  export interface RequirementProgress {
84
78
  requirementId: string;
85
- counts: {
86
- [cohort: string]: number;
87
- };
88
- minutesSpent: {
89
- [cohort: string]: number;
90
- };
79
+ counts: Record<string, number>;
80
+ minutesSpent: Record<string, number>;
91
81
  updatedAt: string;
92
82
  }
93
83
 
@@ -244,7 +234,7 @@ export function getTotalScore(cohort: string | undefined, requirements: Requirem
244
234
  return sum + r.totalScore;
245
235
  }
246
236
  let unitScore = r.unitScore;
247
- if (r.unitScoreOverride && r.unitScoreOverride[cohort] !== undefined) {
237
+ if (r.unitScoreOverride?.[cohort] !== undefined) {
248
238
  unitScore = r.unitScoreOverride[cohort];
249
239
  }
250
240
  const count = r.counts[cohort] || 0;
@@ -306,8 +296,7 @@ export function getTotalCategoryScore(
306
296
 
307
297
  export function getUnitScore(cohort: string, requirement: Requirement): number {
308
298
  if (
309
- requirement.unitScoreOverride &&
310
- requirement.unitScoreOverride[cohort] !== undefined
299
+ requirement.unitScoreOverride?.[cohort] !== undefined
311
300
  ) {
312
301
  return requirement.unitScoreOverride[cohort];
313
302
  }
frontend/src/database/scoreboard.ts CHANGED
@@ -4,8 +4,7 @@ import { User } from './user';
4
4
  * Represents a single row of data in the summary scoreboards
5
5
  * (IE: full dojo and follower scoreboards).
6
6
  */
7
- export interface ScoreboardSummary
8
- extends Pick<
7
+ export type ScoreboardSummary = Pick<
9
8
  User,
10
9
  | 'username'
11
10
  | 'displayName'
@@ -16,7 +15,7 @@ export interface ScoreboardSummary
16
15
  | 'dojoCohort'
17
16
  | 'totalDojoScore'
18
17
  | 'minutesSpent'
19
- > {}
18
+ >
20
19
 
21
20
  /**
22
21
  * Returns true if the provided object is a ScoreboardSummary.
frontend/src/database/statistics.ts CHANGED
@@ -13,12 +13,8 @@ export interface CohortStatistics {
13
13
  activeRatingChanges: number;
14
14
  inactiveRatingChanges: number;
15
15
 
16
- activeRatingSystems: {
17
- [system: string]: number;
18
- };
19
- inactiveRatingSystems: {
20
- [system: string]: number;
21
- };
16
+ activeRatingSystems: Record<string, number>;
17
+ inactiveRatingSystems: Record<string, number>;
22
18
 
23
19
  activeMinutesSpent: number;
24
20
  inactiveMinutesSpent: number;
@@ -33,7 +29,5 @@ export interface CohortStatistics {
33
29
  }
34
30
 
35
31
  export interface UserStatistics {
36
- cohorts: {
37
- [cohort: string]: CohortStatistics;
38
- };
32
+ cohorts: Record<string, CohortStatistics>;
39
33
  }
frontend/src/database/user.ts CHANGED
@@ -101,7 +101,7 @@ export interface User {
101
101
  ratings: Partial<Record<RatingSystem, Rating>>;
102
102
  ratingHistories?: Record<RatingSystem, RatingHistory[]>;
103
103
 
104
- progress: { [requirementId: string]: RequirementProgress };
104
+ progress: Record<string, RequirementProgress>;
105
105
  disableBookingNotifications: boolean;
106
106
  disableCancellationNotifications: boolean;
107
107
  isAdmin: boolean;
@@ -124,11 +124,9 @@ export interface User {
124
124
 
125
125
  customTasks?: CustomTask[];
126
126
 
127
- openingProgress?: {
128
- [moduleId: string]: {
127
+ openingProgress?: Record<string, {
129
128
  exercises?: boolean[];
130
- };
131
- };
129
+ }>;
132
130
 
133
131
  tutorials?: Record<string, boolean>;
134
132
  minutesSpent?: Record<MinutesSpentKey, number>;
@@ -707,7 +705,7 @@ export function normalizeToFide(rating: number, ratingSystem: RatingSystem): num
707
705
  }
708
706
 
709
707
  export function shouldPromptGraduation(user?: User): boolean {
710
- if (!user || !user.dojoCohort || !user.ratingSystem) {
708
+ if (!user?.dojoCohort || !user.ratingSystem) {
711
709
  return false;
712
710
  }
713
711
  if (user.ratingSystem === RatingSystem.Custom) {
@@ -735,7 +733,7 @@ const THREE_MONTHS = 1000 * 60 * 60 * 24 * 90;
735
733
  * @returns True if the user should be prompted to demote.
736
734
  */
737
735
  export function shouldPromptDemotion(user?: User): boolean {
738
- if (!user || !user.dojoCohort || !user.ratingSystem) {
736
+ if (!user?.dojoCohort || !user.ratingSystem) {
739
737
  return false;
740
738
  }
741
739
  if (user.ratingSystem === RatingSystem.Custom) {
frontend/src/dojoDigest/UnsubscribePage.tsx CHANGED
@@ -62,7 +62,7 @@ const UnsubscribePage = () => {
62
62
  <TextField
63
63
  label='Email'
64
64
  value={email}
65
- onChange={(e) => setEmail(e.target.value)}
65
+ onChange={(e) => { setEmail(e.target.value); }}
66
66
  />
67
67
 
68
68
  <LoadingButton
frontend/src/games/edit/EditGamePage.tsx CHANGED
@@ -120,7 +120,7 @@ const EditGamePage = () => {
120
120
  {preflight && (
121
121
  <SubmitGamePreflight
122
122
  open={true}
123
- onClose={() => setPreflight(undefined)}
123
+ onClose={() => { setPreflight(undefined); }}
124
124
  initHeaders={preflight.headers}
125
125
  loading={request.isLoading()}
126
126
  onSubmit={onPreflight}
frontend/src/games/edit/GameSubmissionForm.tsx CHANGED
@@ -93,7 +93,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
93
93
  if (black.trim() === '') {
94
94
  errors.black = 'This field is required';
95
95
  }
96
- if (!date || !date.isValid) {
96
+ if (!date?.isValid) {
97
97
  errors.date = 'This field is required';
98
98
  }
99
99
  if (result.trim() === '') {
@@ -132,7 +132,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
132
132
  <FormControl>
133
133
  <RadioGroup
134
134
  value={type}
135
- onChange={(e, v) => setType(v as GameSubmissionType)}
135
+ onChange={(e, v) => { setType(v as GameSubmissionType); }}
136
136
  >
137
137
  <FormControlLabel
138
138
  value={GameSubmissionType.LichessChapter}
@@ -165,7 +165,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
165
165
  label='Lichess Chapter URL'
166
166
  placeholder='https://lichess.org/study/abcd1234/abcd1234'
167
167
  value={lichessUrl}
168
- onChange={(e) => setLichessUrl(e.target.value)}
168
+ onChange={(e) => { setLichessUrl(e.target.value); }}
169
169
  error={!!errors.lichessUrl}
170
170
  helperText={
171
171
  errors.lichessUrl ||
@@ -180,7 +180,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
180
180
  label='Lichess Study URL'
181
181
  placeholder='https://lichess.org/study/abcd1234'
182
182
  value={lichessUrl}
183
- onChange={(e) => setLichessUrl(e.target.value)}
183
+ onChange={(e) => { setLichessUrl(e.target.value); }}
184
184
  error={!!errors.lichessUrl}
185
185
  helperText={
186
186
  errors.lichessUrl ||
@@ -195,7 +195,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
195
195
  label='PGN Text'
196
196
  placeholder={pgnTextPlaceholder}
197
197
  value={pgnText}
198
- onChange={(e) => setPgnText(e.target.value)}
198
+ onChange={(e) => { setPgnText(e.target.value); }}
199
199
  multiline
200
200
  minRows={5}
201
201
  error={!!errors.pgnText}
@@ -215,7 +215,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
215
215
  data-cy={`white`}
216
216
  label='White'
217
217
  value={white}
218
- onChange={(e) => setWhite(e.target.value)}
218
+ onChange={(e) => { setWhite(e.target.value); }}
219
219
  error={!!errors.white}
220
220
  helperText={errors.white}
221
221
  />
@@ -225,7 +225,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
225
225
  data-cy={`black`}
226
226
  label='Black'
227
227
  value={black}
228
- onChange={(e) => setBlack(e.target.value)}
228
+ onChange={(e) => { setBlack(e.target.value); }}
229
229
  error={!!errors.black}
230
230
  helperText={errors.black}
231
231
  />
@@ -236,7 +236,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
236
236
  data-cy={`result`}
237
237
  label='Result'
238
238
  value={result}
239
- onChange={(e) => setResult(e.target.value)}
239
+ onChange={(e) => { setResult(e.target.value); }}
240
240
  error={!!errors.result}
241
241
  helperText={errors.result}
242
242
  >
@@ -249,7 +249,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
249
249
  label='Date'
250
250
  disableFuture
251
251
  value={date}
252
- onChange={(newValue) => setDate(newValue)}
252
+ onChange={(newValue) => { setDate(newValue); }}
253
253
  slotProps={{
254
254
  textField: {
255
255
  id: `date`,
@@ -271,7 +271,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
271
271
  ? 'unlisted'
272
272
  : visibility
273
273
  }
274
- onChange={(e) => setVisibility(e.target.value)}
274
+ onChange={(e) => { setVisibility(e.target.value); }}
275
275
  >
276
276
  <FormControlLabel
277
277
  value='public'
@@ -315,7 +315,7 @@ const GameSubmissionForm: React.FC<GameSubmissionFormProps> = ({
315
315
  <RadioGroup
316
316
  row
317
317
  value={orientation}
318
- onChange={(e, v) => setOrientation(v)}
318
+ onChange={(e, v) => { setOrientation(v); }}
319
319
  >
320
320
  <FormControlLabel
321
321
  value='white'
frontend/src/games/edit/SubmitGamePreflight.tsx CHANGED
@@ -96,7 +96,7 @@ const SubmitGamePreflight: React.FC<SubmitGamePreflightProps> = ({
96
96
  };
97
97
 
98
98
  const submit = () => {
99
- let errors: Record<number, FormError> = {};
99
+ const errors: Record<number, FormError> = {};
100
100
  headers.forEach((h, i) => {
101
101
  const error: FormError = { white: '', black: '', result: '', date: '' };
102
102
  if (h.white.trim() === '') {
@@ -112,7 +112,7 @@ const SubmitGamePreflight: React.FC<SubmitGamePreflightProps> = ({
112
112
  errors[i] = error;
113
113
  }
114
114
  console.log('h.date: ', h.date);
115
- if (h.date === null || !h.date.isValid) {
115
+ if (!h.date?.isValid) {
116
116
  error.date = 'This field is required';
117
117
  errors[i] = error;
118
118
  }
@@ -160,10 +160,10 @@ const SubmitGamePreflight: React.FC<SubmitGamePreflightProps> = ({
160
160
  label='White'
161
161
  value={h.white}
162
162
  onChange={(e) =>
163
- onChangeHeader(i, 'white', e.target.value)
163
+ { onChangeHeader(i, 'white', e.target.value); }
164
164
  }
165
- error={!!errors[i]?.white}
166
- helperText={errors[i]?.white}
165
+ error={!!errors[i].white}
166
+ helperText={errors[i].white}
167
167
  />
168
168
  </Grid2>
169
169
 
@@ -174,10 +174,10 @@ const SubmitGamePreflight: React.FC<SubmitGamePreflightProps> = ({
174
174
  label='Black'
175
175
  value={h.black}
176
176
  onChange={(e) =>
177
- onChangeHeader(i, 'black', e.target.value)
177
+ { onChangeHeader(i, 'black', e.target.value); }
178
178
  }
179
- error={!!errors[i]?.black}
180
- helperText={errors[i]?.black}
179
+ error={!!errors[i].black}
180
+ helperText={errors[i].black}
181
181
  />
182
182
  </Grid2>
183
183
 
@@ -188,10 +188,10 @@ const SubmitGamePreflight: React.FC<SubmitGamePreflightProps> = ({
188
188
  label='Result'
189
189
  value={h.result}
190
190
  onChange={(e) =>
191
- onChangeHeader(i, 'result', e.target.value)
191
+ { onChangeHeader(i, 'result', e.target.value); }
192
192
  }
193
- error={!!errors[i]?.result}
194
- helperText={errors[i]?.result}
193
+ error={!!errors[i].result}
194
+ helperText={errors[i].result}
195
195
  fullWidth
196
196
  >
197
197
  <MenuItem value='1-0'>White Won</MenuItem>
@@ -211,8 +211,8 @@ const SubmitGamePreflight: React.FC<SubmitGamePreflightProps> = ({
211
211
  slotProps={{
212
212
  textField: {
213
213
  id: `date-${i}`,
214
- error: !!errors[i]?.date,
215
- helperText: errors[i]?.date,
214
+ error: !!errors[i].date,
215
+ helperText: errors[i].date,
216
216
  fullWidth: true,
217
217
  },
218
218
  }}
frontend/src/games/list/ListGamesPage.tsx CHANGED
@@ -45,7 +45,7 @@ export const gameTableColumns: GridColDef<GameInfo>[] = [
45
45
  direction='row'
46
46
  spacing={1}
47
47
  alignItems='center'
48
- onClick={(e) => e.stopPropagation()}
48
+ onClick={(e) => { e.stopPropagation(); }}
49
49
  >
50
50
  <CohortIcon cohort={params.value} size={25} tooltip='' />
51
51
  <Typography variant='body2'>{params.value}</Typography>
@@ -67,7 +67,7 @@ export const gameTableColumns: GridColDef<GameInfo>[] = [
67
67
  direction='row'
68
68
  spacing={1}
69
69
  alignItems='center'
70
- onClick={(e) => e.stopPropagation()}
70
+ onClick={(e) => { e.stopPropagation(); }}
71
71
  >
72
72
  <Avatar
73
73
  username={params.row.owner}
@@ -269,8 +269,8 @@ const ListGamesPage = () => {
269
269
  pageSize={pageSize}
270
270
  count={rowCount}
271
271
  hasMore={hasMore}
272
- onPrevPage={() => setPage(page - 1)}
273
- onNextPage={() => setPage(page + 1)}
272
+ onPrevPage={() => { setPage(page - 1); }}
273
+ onNextPage={() => { setPage(page + 1); }}
274
274
  />
275
275
  ),
276
276
  }}
frontend/src/games/list/SearchFilters.tsx CHANGED
@@ -99,7 +99,7 @@ export const SearchByCohort: React.FC<SearchByCohortProps> = ({
99
99
  data-cy='cohort-select'
100
100
  value={cohort}
101
101
  label='Cohort'
102
- onChange={(e) => setCohort(e.target.value)}
102
+ onChange={(e) => { setCohort(e.target.value); }}
103
103
  >
104
104
  {dojoCohorts.map((c) => (
105
105
  <MenuItem key={c} value={c}>
@@ -259,7 +259,7 @@ const SearchByPlayer: React.FC<SearchByPlayerProps> = ({
259
259
  data-cy='player-name'
260
260
  label='Player Name'
261
261
  value={player}
262
- onChange={(e) => setPlayer(e.target.value)}
262
+ onChange={(e) => { setPlayer(e.target.value); }}
263
263
  error={!!errors.player}
264
264
  helperText={errors.player}
265
265
  />
@@ -268,7 +268,7 @@ const SearchByPlayer: React.FC<SearchByPlayerProps> = ({
268
268
  data-cy='color'
269
269
  value={color}
270
270
  label='Color'
271
- onChange={(e) => setColor(e.target.value)}
271
+ onChange={(e) => { setColor(e.target.value); }}
272
272
  >
273
273
  <MenuItem value='either'>Either</MenuItem>
274
274
  <MenuItem value='white'>White</MenuItem>
@@ -367,7 +367,7 @@ const SearchByOpening: React.FC<SearchByOpeningProps> = ({
367
367
  data-cy='opening-eco'
368
368
  value={eco}
369
369
  label='Opening ECO'
370
- onChange={(e) => setEco(e.target.value)}
370
+ onChange={(e) => { setEco(e.target.value); }}
371
371
  error={!!errors.eco}
372
372
  helperText={errors.eco}
373
373
  />
@@ -449,7 +449,7 @@ const SearchByPosition: React.FC<SearchByPositionProps> = ({
449
449
  data-cy='fen'
450
450
  value={fen}
451
451
  label='FEN'
452
- onChange={(e) => setFen(e.target.value)}
452
+ onChange={(e) => { setFen(e.target.value); }}
453
453
  error={!!errors.fen}
454
454
  helperText={errors.fen}
455
455
  />
@@ -555,7 +555,7 @@ const SearchFilters: React.FC<SearchFiltersProps> = ({ isLoading, onSearch }) =>
555
555
  let endDateStr: string | undefined = undefined;
556
556
  if (isValid(new Date(paramsStartDate || ''))) {
557
557
  startDateStr = new Date(paramsStartDate || '')
558
- ?.toISOString()
558
+ .toISOString()
559
559
  .substring(0, 10)
560
560
  .replaceAll('-', '.');
561
561
  }
@@ -569,7 +569,7 @@ const SearchFilters: React.FC<SearchFiltersProps> = ({ isLoading, onSearch }) =>
569
569
  // Functions that actually perform the search
570
570
  const searchByCohort = useCallback(
571
571
  (startKey: string) =>
572
- api.listGamesByCohort(cohort!, startKey, startDateStr, endDateStr),
572
+ api.listGamesByCohort(cohort, startKey, startDateStr, endDateStr),
573
573
  [cohort, api, startDateStr, endDateStr],
574
574
  );
575
575
 
@@ -580,8 +580,8 @@ const SearchFilters: React.FC<SearchFiltersProps> = ({ isLoading, onSearch }) =>
580
580
  startKey,
581
581
  startDateStr,
582
582
  endDateStr,
583
- player!,
584
- color!,
583
+ player,
584
+ color,
585
585
  ),
586
586
  [api, startDateStr, endDateStr, player, color],
587
587
  );
frontend/src/games/list/pagination.ts CHANGED
@@ -8,7 +8,7 @@ import { useSearchParams } from 'react-router-dom';
8
8
 
9
9
  export type SearchFunc = (
10
10
  startKey: string
11
- ) => Promise<AxiosResponse<ListGamesResponse, any>>;
11
+ ) => Promise<AxiosResponse<ListGamesResponse>>;
12
12
 
13
13
  export function usePagination(
14
14
  initialSearchFunc: SearchFunc | null,
@@ -109,7 +109,7 @@ export function usePagination(
109
109
  });
110
110
  }, [page, pageSize, games, startKey, searchFunc, request]);
111
111
 
112
- let rowCount = games.length;
112
+ const rowCount = games.length;
113
113
  // if (startKey !== undefined) {
114
114
  // rowCount += pageSize;
115
115
  // }
frontend/src/games/review/ReviewQueuePage.tsx CHANGED
@@ -42,7 +42,7 @@ const columns: GridColDef<GameInfo>[] = [
42
42
  direction='row'
43
43
  spacing={1}
44
44
  alignItems='center'
45
- onClick={(e) => e.stopPropagation()}
45
+ onClick={(e) => { e.stopPropagation(); }}
46
46
  >
47
47
  <Avatar
48
48
  username={params.row.owner}
@@ -101,7 +101,7 @@ const columns: GridColDef<GameInfo>[] = [
101
101
  align: 'right',
102
102
  headerAlign: 'right',
103
103
  valueFormatter: (params: GridValueFormatterParams<string>) => {
104
- return params.value?.split('T')[0].replaceAll('-', '.');
104
+ return params.value.split('T')[0].replaceAll('-', '.');
105
105
  },
106
106
  },
107
107
  {
@@ -213,8 +213,8 @@ const ReviewQueuePage = () => {
213
213
  pageSize={pageSize}
214
214
  count={rowCount}
215
215
  hasMore={hasMore}
216
- onPrevPage={() => setPage(page - 1)}
217
- onNextPage={() => setPage(page + 1)}
216
+ onPrevPage={() => { setPage(page - 1); }}
217
+ onNextPage={() => { setPage(page + 1); }}
218
218
  />
219
219
  ),
220
220
  }}
frontend/src/games/view/DeleteGameButton.tsx CHANGED
@@ -58,7 +58,7 @@ const DeleteGameButton: React.FC<DeleteGameButtonProps> = ({
58
58
  <Tooltip title='Delete Game'>
59
59
  <IconButton
60
60
  data-cy='delete-game-button'
61
- onClick={() => setShowDeleteModal(true)}
61
+ onClick={() => { setShowDeleteModal(true); }}
62
62
  >
63
63
  <DeleteIcon sx={{ color: 'text.secondary' }} />
64
64
  </IconButton>
@@ -67,7 +67,7 @@ const DeleteGameButton: React.FC<DeleteGameButtonProps> = ({
67
67
  <Button
68
68
  data-cy='delete-game-button'
69
69
  variant={variant}
70
- onClick={() => setShowDeleteModal(true)}
70
+ onClick={() => { setShowDeleteModal(true); }}
71
71
  color='error'
72
72
  >
73
73
  Delete Game
frontend/src/games/view/GamePage.tsx CHANGED
@@ -9,11 +9,11 @@ import { DefaultUnderboardTab } from '../../board/pgn/boardTools/underboard/Unde
9
9
  import { Game } from '../../database/game';
10
10
  import PgnErrorBoundary from './PgnErrorBoundary';
11
11
 
12
- type GameContextType = {
12
+ interface GameContextType {
13
13
  game?: Game;
14
14
  onUpdateGame?: (g: Game) => void;
15
15
  isOwner?: boolean;
16
- };
16
+ }
17
17
 
18
18
  const GameContext = createContext<GameContextType>({});
19
19
 
frontend/src/games/view/PgnErrorBoundary.tsx CHANGED
@@ -27,8 +27,7 @@ interface ErrorBoundaryState {
27
27
 
28
28
  class PgnErrorBoundary extends Component<
29
29
  React.PropsWithChildren<PgnErrorBoundaryNavigatorProps>,
30
- ErrorBoundaryState,
31
- any
30
+ ErrorBoundaryState
32
31
  > {
33
32
  constructor(props: PgnErrorBoundaryNavigatorProps) {
34
33
  super(props);
@@ -71,7 +70,7 @@ class PgnErrorBoundary extends Component<
71
70
  <Stack direction='row' spacing={2}>
72
71
  <Button
73
72
  variant='contained'
74
- onClick={() => this.props.navigate('./edit')}
73
+ onClick={() => { this.props.navigate('./edit'); }}
75
74
  >
76
75
  Resubmit PGN
77
76
  </Button>
frontend/src/help/AuthenticatedHelp.tsx CHANGED
@@ -384,7 +384,7 @@ const AuthenticatedHelp = () => {
384
384
  <Link
385
385
  key={section.title}
386
386
  href={`#${section.title}`}
387
- onClick={(e) => scrollToId(e, section.title)}
387
+ onClick={(e) => { scrollToId(e, section.title); }}
388
388
  >
389
389
  {section.title}
390
390
  </Link>
@@ -394,7 +394,7 @@ const AuthenticatedHelp = () => {
394
394
  <Link
395
395
  href={`#${item.title}`}
396
396
  onClick={(e) =>
397
- scrollToId(e, item.title)
397
+ { scrollToId(e, item.title); }
398
398
  }
399
399
  >
400
400
  {item.title}
@@ -406,7 +406,7 @@ const AuthenticatedHelp = () => {
406
406
  ))}
407
407
  <Link
408
408
  href='#support-ticket'
409
- onClick={(e) => scrollToId(e, 'support-ticket')}
409
+ onClick={(e) => { scrollToId(e, 'support-ticket'); }}
410
410
  >
411
411
  Open a Support Ticket
412
412
  </Link>
@@ -447,10 +447,10 @@ const AuthenticatedHelp = () => {
447
447
  <li>
448
448
  <Button
449
449
  onClick={() =>
450
- onTutorial(
450
+ { onTutorial(
451
451
  '/profile',
452
452
  TutorialName.ProfilePage,
453
- )
453
+ ); }
454
454
  }
455
455
  sx={{ textTransform: 'none' }}
456
456
  >
@@ -460,10 +460,10 @@ const AuthenticatedHelp = () => {
460
460
  <li>
461
461
  <Button
462
462
  onClick={() =>
463
- onTutorial(
463
+ { onTutorial(
464
464
  '/scoreboard',
465
465
  TutorialName.ScoreboardPage,
466
- )
466
+ ); }
467
467
  }
468
468
  sx={{ textTransform: 'none' }}
469
469
  >
@@ -473,10 +473,10 @@ const AuthenticatedHelp = () => {
473
473
  <li>
474
474
  <Button
475
475
  onClick={() =>
476
- onTutorial(
476
+ { onTutorial(
477
477
  '/calendar',
478
478
  TutorialName.CalendarPage,
479
- )
479
+ ); }
480
480
  }
481
481
  sx={{ textTransform: 'none' }}
482
482
  >
@@ -486,10 +486,10 @@ const AuthenticatedHelp = () => {
486
486
  <li>
487
487
  <Button
488
488
  onClick={() =>
489
- onTutorial(
489
+ { onTutorial(
490
490
  '/games',
491
491
  TutorialName.ListGamesPage,
492
- )
492
+ ); }
493
493
  }
494
494
  sx={{ textTransform: 'none' }}
495
495
  >
frontend/src/help/SupportTicket.tsx CHANGED
@@ -83,7 +83,7 @@ const SupportTicket = () => {
83
83
  data-cy='support-ticket-name'
84
84
  label='Full Name'
85
85
  value={name}
86
- onChange={(e) => setName(e.target.value)}
86
+ onChange={(e) => { setName(e.target.value); }}
87
87
  error={Boolean(errors.name)}
88
88
  helperText={errors.name}
89
89
  fullWidth
@@ -95,7 +95,7 @@ const SupportTicket = () => {
95
95
  data-cy='support-ticket-email'
96
96
  label='Email'
97
97
  value={email}
98
- onChange={(e) => setEmail(e.target.value)}
98
+ onChange={(e) => { setEmail(e.target.value); }}
99
99
  error={Boolean(errors.email)}
100
100
  helperText={errors.email}
101
101
  fullWidth
@@ -107,7 +107,7 @@ const SupportTicket = () => {
107
107
  data-cy='support-ticket-subject'
108
108
  label='Subject'
109
109
  value={subject}
110
- onChange={(e) => setSubject(e.target.value)}
110
+ onChange={(e) => { setSubject(e.target.value); }}
111
111
  error={Boolean(errors.subject)}
112
112
  helperText={errors.subject}
113
113
  fullWidth
@@ -119,7 +119,7 @@ const SupportTicket = () => {
119
119
  data-cy='support-ticket-message'
120
120
  label='Message'
121
121
  value={message}
122
- onChange={(e) => setMessage(e.target.value)}
122
+ onChange={(e) => { setMessage(e.target.value); }}
123
123
  error={Boolean(errors.message)}
124
124
  helperText={errors.message}
125
125
  fullWidth
frontend/src/help/UnauthenticatedHelp.tsx CHANGED
@@ -97,7 +97,7 @@ const UnauthenticatedHelp = () => {
97
97
  <React.Fragment key={section.title}>
98
98
  <Link
99
99
  href={`#${section.title}`}
100
- onClick={(e) => scrollToId(e, section.title)}
100
+ onClick={(e) => { scrollToId(e, section.title); }}
101
101
  >
102
102
  {section.title}
103
103
  </Link>
@@ -107,7 +107,7 @@ const UnauthenticatedHelp = () => {
107
107
  <Link
108
108
  href={`#${item.title}`}
109
109
  onClick={(e) =>
110
- scrollToId(e, item.title)
110
+ { scrollToId(e, item.title); }
111
111
  }
112
112
  >
113
113
  {item.title}
@@ -119,7 +119,7 @@ const UnauthenticatedHelp = () => {
119
119
  ))}
120
120
  <Link
121
121
  href='#support-ticket'
122
- onClick={(e) => scrollToId(e, 'support-ticket')}
122
+ onClick={(e) => { scrollToId(e, 'support-ticket'); }}
123
123
  >
124
124
  Open a Support Ticket
125
125
  </Link>
frontend/src/index.tsx CHANGED
@@ -8,7 +8,7 @@ import { ReportCallback } from 'web-vitals';
8
8
 
9
9
  ReactGA.initialize('G-9VPNTDELD2');
10
10
 
11
- const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
11
+ const root = ReactDOM.createRoot(document.getElementById('root')!);
12
12
  root.render(
13
13
  <React.StrictMode>
14
14
  <App />
frontend/src/landing/JoinToday.tsx CHANGED
@@ -29,8 +29,8 @@ const JoinToday = () => {
29
29
 
30
30
  <Grid2 container spacing={3} width={1}>
31
31
  <PriceMatrix
32
- onSubscribe={() => navigate('/signup', { state: locationState })}
33
- onFreeTier={() => navigate('/signup', { state: locationState })}
32
+ onSubscribe={() => { navigate('/signup', { state: locationState }); }}
33
+ onFreeTier={() => { navigate('/signup', { state: locationState }); }}
34
34
  />
35
35
  </Grid2>
36
36
  </Stack>
frontend/src/landing/LandingPage.tsx CHANGED
@@ -72,7 +72,7 @@ const LandingPage = () => {
72
72
  <Button
73
73
  variant='contained'
74
74
  onClick={() =>
75
- navigate('/signup', { state: locationState })
75
+ { navigate('/signup', { state: locationState }); }
76
76
  }
77
77
  sx={{
78
78
  fontSize: '1rem',
@@ -87,7 +87,7 @@ const LandingPage = () => {
87
87
  <Button
88
88
  variant='outlined'
89
89
  onClick={() =>
90
- navigate('/signin', { state: locationState })
90
+ { navigate('/signin', { state: locationState }); }
91
91
  }
92
92
  sx={{
93
93
  fontSize: '1rem',
frontend/src/landing/WhatsIncluded.tsx CHANGED
@@ -134,7 +134,7 @@ const WhatsIncluded = () => {
134
134
  <Tabs
135
135
  data-cy='whatsincluded-tab-list'
136
136
  value={value}
137
- onChange={(_, t) => setValue(t)}
137
+ onChange={(_, t) => { setValue(t); }}
138
138
  variant='scrollable'
139
139
  sx={{ justifyContent: 'center' }}
140
140
  >
frontend/src/material/MemorizeGamesPage.tsx CHANGED
@@ -107,7 +107,7 @@ const MemorizeGamesPage = () => {
107
107
 
108
108
  <FormControl>
109
109
  <FormLabel>Mode</FormLabel>
110
- <RadioGroup row value={mode} onChange={(e) => setMode(e.target.value)}>
110
+ <RadioGroup row value={mode} onChange={(e) => { setMode(e.target.value); }}>
111
111
  <FormControlLabel value='study' control={<Radio />} label='Study' />
112
112
  <FormControlLabel value='test' control={<Radio />} label='Test' />
113
113
  </RadioGroup>
frontend/src/material/ModelGamesPage.tsx CHANGED
@@ -105,7 +105,7 @@ const ModelGamesPage = () => {
105
105
  label='Cohort'
106
106
  value={cohort}
107
107
  onChange={(event) =>
108
- onChangeCohort(event.target.value)
108
+ { onChangeCohort(event.target.value); }
109
109
  }
110
110
  sx={{ mb: 3 }}
111
111
  fullWidth
frontend/src/meeting/CancelMeetingButton.tsx CHANGED
@@ -54,7 +54,7 @@ const CancelMeetingButton: React.FC<
54
54
  <Button
55
55
  variant='contained'
56
56
  color='error'
57
- onClick={() => setShowCancelDialog(true)}
57
+ onClick={() => { setShowCancelDialog(true); }}
58
58
  >
59
59
  {children}
60
60
  </Button>
@@ -63,7 +63,7 @@ const CancelMeetingButton: React.FC<
63
63
  onClose={
64
64
  cancelRequest.isLoading()
65
65
  ? undefined
66
- : () => setShowCancelDialog(false)
66
+ : () => { setShowCancelDialog(false); }
67
67
  }
68
68
  >
69
69
  <RequestSnackbar request={cancelRequest} />
@@ -71,7 +71,7 @@ const CancelMeetingButton: React.FC<
71
71
  {dialogTitle}
72
72
  <IconButton
73
73
  aria-label='close'
74
- onClick={() => setShowCancelDialog(false)}
74
+ onClick={() => { setShowCancelDialog(false); }}
75
75
  sx={{
76
76
  position: 'absolute',
77
77
  right: 10,
frontend/src/meeting/ListMeetingsPage.tsx CHANGED
@@ -50,7 +50,7 @@ const ListMeetingsPage = () => {
50
50
  Looks like you don't have any meetings. Go to the calendar and
51
51
  schedule one now!
52
52
  </Typography>
53
- <Button variant='contained' onClick={() => navigate('/calendar')}>
53
+ <Button variant='contained' onClick={() => { navigate('/calendar'); }}>
54
54
  Go to Calendar
55
55
  </Button>
56
56
  </>
frontend/src/meeting/MeetingMessages.tsx CHANGED
@@ -21,7 +21,7 @@ const MeetingMessages = () => {
21
21
  const messages = meeting?.messages;
22
22
 
23
23
  useEffect(() => {
24
- bottomRef.current?.scrollTo(0, bottomRef.current?.scrollHeight || 0);
24
+ bottomRef.current?.scrollTo(0, bottomRef.current.scrollHeight || 0);
25
25
  }, [messages]);
26
26
 
27
27
  if (!meeting) {
@@ -34,7 +34,7 @@ const MeetingMessages = () => {
34
34
  if (
35
35
  meeting.type === EventType.Coaching &&
36
36
  meeting.owner !== user.username &&
37
- !meeting.participants[user.username]?.hasPaid
37
+ !meeting.participants[user.username].hasPaid
38
38
  ) {
39
39
  return null;
40
40
  }
frontend/src/meeting/MeetingPage.tsx CHANGED
@@ -132,7 +132,7 @@ const MeetingPage = () => {
132
132
  <Container maxWidth='md' sx={{ py: 4 }}>
133
133
  <Typography>This meeting has not been booked yet.</Typography>
134
134
  <Button
135
- onClick={() => navigate('/calendar')}
135
+ onClick={() => { navigate('/calendar'); }}
136
136
  variant='contained'
137
137
  sx={{ mt: 2 }}
138
138
  >
frontend/src/navbar/NavbarMenu.tsx CHANGED
@@ -71,7 +71,7 @@ export const Logo = () => {
71
71
  cursor: 'pointer',
72
72
  }}
73
73
  alt=''
74
- onClick={() => navigate('/')}
74
+ onClick={() => { navigate('/'); }}
75
75
  />
76
76
  );
77
77
  };
@@ -96,118 +96,118 @@ function allStartItems(
96
96
  {
97
97
  name: 'Newsfeed',
98
98
  icon: <Feed />,
99
- onClick: () => navigate('/newsfeed'),
99
+ onClick: () => { navigate('/newsfeed'); },
100
100
  },
101
101
  {
102
102
  name: 'Training Plan',
103
103
  icon: <Checklist />,
104
- onClick: () => navigate('/profile?view=progress'),
104
+ onClick: () => { navigate('/profile?view=progress'); },
105
105
  },
106
106
  {
107
107
  name: 'Scoreboard',
108
108
  icon: <Scoreboard />,
109
- onClick: () => toggleExpansion('Scoreboard'),
109
+ onClick: () => { toggleExpansion('Scoreboard'); },
110
110
  children: [
111
111
  {
112
112
  name: 'My Cohort',
113
113
  icon: <GroupIcon />,
114
- onClick: () => navigate('/scoreboard'),
114
+ onClick: () => { navigate('/scoreboard'); },
115
115
  },
116
116
  {
117
117
  name: 'Full Dojo',
118
118
  icon: <LanguageIcon />,
119
- onClick: () => navigate('scoreboard/dojo'),
119
+ onClick: () => { navigate('scoreboard/dojo'); },
120
120
  },
121
121
  {
122
122
  name: 'Followers',
123
123
  icon: <ThumbUpIcon />,
124
- onClick: () => navigate('scoreboard/following'),
124
+ onClick: () => { navigate('scoreboard/following'); },
125
125
  },
126
126
  {
127
127
  name: 'Search Users',
128
128
  icon: <SearchIcon />,
129
- onClick: () => navigate('scoreboard/search'),
129
+ onClick: () => { navigate('scoreboard/search'); },
130
130
  },
131
131
  {
132
132
  name: 'Statistics',
133
133
  icon: <AutoGraphIcon />,
134
- onClick: () => navigate('scoreboard/stats'),
134
+ onClick: () => { navigate('scoreboard/stats'); },
135
135
  },
136
136
  ],
137
137
  },
138
138
  {
139
139
  name: 'Tournaments',
140
140
  icon: <Tournaments />,
141
- onClick: () => toggleExpansion('Tournaments'),
141
+ onClick: () => { toggleExpansion('Tournaments'); },
142
142
  children: [
143
143
  {
144
144
  name: 'DojoLiga',
145
145
  icon: <MilitaryTech />,
146
- onClick: () => navigate('/tournaments'),
146
+ onClick: () => { navigate('/tournaments'); },
147
147
  },
148
148
  {
149
149
  name: 'Open Classical',
150
150
  icon: <MilitaryTech />,
151
- onClick: () => navigate('/tournaments/open-classical'),
151
+ onClick: () => { navigate('/tournaments/open-classical'); },
152
152
  },
153
153
  ],
154
154
  },
155
155
  {
156
156
  name: 'Games',
157
157
  icon: <PawnIcon />,
158
- onClick: () => navigate('/games'),
158
+ onClick: () => { navigate('/games'); },
159
159
  },
160
160
  {
161
161
  name: 'Calendar',
162
162
  icon: <CalendarToday />,
163
- onClick: () => navigate('/calendar'),
163
+ onClick: () => { navigate('/calendar'); },
164
164
  },
165
165
  {
166
166
  name: 'Material',
167
167
  icon: <MenuBook />,
168
- onClick: () => toggleExpansion('Material'),
168
+ onClick: () => { toggleExpansion('Material'); },
169
169
  children: [
170
170
  {
171
171
  name: 'Courses',
172
172
  icon: <ImportContacts />,
173
- onClick: () => navigate('/courses'),
173
+ onClick: () => { navigate('/courses'); },
174
174
  },
175
175
  {
176
176
  name: 'Tactics Tests',
177
177
  icon: <Speed />,
178
- onClick: () => navigate('/tactics'),
178
+ onClick: () => { navigate('/tactics'); },
179
179
  },
180
180
  {
181
181
  name: 'Books',
182
182
  icon: <AutoStories />,
183
- onClick: () => navigate('/material/books'),
183
+ onClick: () => { navigate('/material/books'); },
184
184
  },
185
185
  {
186
186
  name: 'Sparring Positions',
187
187
  icon: <LocalFireDepartment />,
188
- onClick: () => navigate('/material/sparring'),
188
+ onClick: () => { navigate('/material/sparring'); },
189
189
  },
190
190
  {
191
191
  name: 'Model Annotations',
192
192
  icon: <BorderColor />,
193
- onClick: () => navigate('/material/modelgames'),
193
+ onClick: () => { navigate('/material/modelgames'); },
194
194
  },
195
195
  {
196
196
  name: 'Games to Memorize',
197
197
  icon: <Psychology />,
198
- onClick: () => navigate('/material/memorizegames'),
198
+ onClick: () => { navigate('/material/memorizegames'); },
199
199
  },
200
200
  {
201
201
  name: 'Rating Conversions',
202
202
  icon: <SignalCellularAlt />,
203
- onClick: () => navigate('/material/ratings'),
203
+ onClick: () => { navigate('/material/ratings'); },
204
204
  },
205
205
  ],
206
206
  },
207
207
  {
208
208
  name: 'Clubs',
209
209
  icon: <Groups />,
210
- onClick: () => navigate('/clubs'),
210
+ onClick: () => { navigate('/clubs'); },
211
211
  },
212
212
  {
213
213
  name: 'Blog',
@@ -217,17 +217,17 @@ function allStartItems(
217
217
  {
218
218
  name: 'Shop',
219
219
  icon: <Sell />,
220
- onClick: () => toggleExpansion('Shop'),
220
+ onClick: () => { toggleExpansion('Shop'); },
221
221
  children: [
222
222
  {
223
223
  name: 'Courses',
224
224
  icon: <ImportContacts />,
225
- onClick: () => navigate('/courses'),
225
+ onClick: () => { navigate('/courses'); },
226
226
  },
227
227
  {
228
228
  name: 'Coaching',
229
229
  icon: <RocketLaunch />,
230
- onClick: () => navigate('/coaching'),
230
+ onClick: () => { navigate('/coaching'); },
231
231
  },
232
232
  {
233
233
  name: 'Merch',
@@ -244,7 +244,7 @@ function helpItem(navigate: NavigateFunction): NavbarItem {
244
244
  return {
245
245
  name: 'Help',
246
246
  icon: <Help />,
247
- onClick: () => navigate('/help'),
247
+ onClick: () => { navigate('/help'); },
248
248
  };
249
249
  }
250
250
 
@@ -257,7 +257,7 @@ function NotificationsMenuItem({
257
257
  const navigate = useNavigate();
258
258
 
259
259
  return (
260
- <MenuItem onClick={handleClick(() => navigate('/notifications'))}>
260
+ <MenuItem onClick={handleClick(() => { navigate('/notifications'); })}>
261
261
  <ListItemIcon>
262
262
  <Badge
263
263
  badgeContent={notifications.length}
@@ -421,7 +421,7 @@ function HelpButton(navigate: NavigateFunction) {
421
421
  data-cy='Help'
422
422
  key='help'
423
423
  sx={{ color: 'white' }}
424
- onClick={() => navigate('/help')}
424
+ onClick={() => { navigate('/help'); }}
425
425
  >
426
426
  <Help />
427
427
  </IconButton>
@@ -450,7 +450,7 @@ function useNavbarItems(
450
450
  const showProfileDropdown = useMediaQuery('(min-width:542px)');
451
451
 
452
452
  const startItems = allStartItems(navigate, (item: string) =>
453
- setOpenItems((v) => ({ ...v, [item]: !(v[item] || false) })),
453
+ { setOpenItems((v) => ({ ...v, [item]: !(v[item] || false) })); },
454
454
  );
455
455
 
456
456
  let startItemCount = 0;
@@ -564,7 +564,7 @@ const LargeMenu: React.FC<MenuProps> = ({ meetingCount }) => {
564
564
  <Logo />
565
565
  <Stack spacing={1} direction='row' sx={{ flexGrow: 1 }}>
566
566
  <Button
567
- onClick={() => navigate('/profile')}
567
+ onClick={() => { navigate('/profile'); }}
568
568
  sx={{ color: 'white' }}
569
569
  startIcon={<Person2Icon />}
570
570
  >
@@ -572,7 +572,7 @@ const LargeMenu: React.FC<MenuProps> = ({ meetingCount }) => {
572
572
  </Button>
573
573
  </Stack>
574
574
 
575
- <Button onClick={() => navigate('/help')} sx={{ color: 'white' }}>
575
+ <Button onClick={() => { navigate('/help'); }} sx={{ color: 'white' }}>
576
576
  Help
577
577
  </Button>
578
578
 
@@ -625,7 +625,7 @@ const ExtraSmallMenu: React.FC<MenuProps> = ({ meetingCount }) => {
625
625
  const [openItems, setOpenItems] = useState<Record<string, boolean>>({});
626
626
 
627
627
  const startItems = allStartItems(navigate, (item: string) =>
628
- setOpenItems((v) => ({ ...v, [item]: !(v[item] || false) })),
628
+ { setOpenItems((v) => ({ ...v, [item]: !(v[item] || false) })); },
629
629
  );
630
630
 
631
631
  if (auth.status === AuthStatus.Unauthenticated) {
@@ -689,7 +689,7 @@ const ExtraSmallMenu: React.FC<MenuProps> = ({ meetingCount }) => {
689
689
  onClose={handleClose}
690
690
  >
691
691
  {!profileCreated && (
692
- <MenuItem onClick={handleClick(() => navigate('/profile'))}>
692
+ <MenuItem onClick={handleClick(() => { navigate('/profile'); })}>
693
693
  <ListItemIcon>
694
694
  <Person2Icon />
695
695
  </ListItemIcon>
@@ -699,7 +699,7 @@ const ExtraSmallMenu: React.FC<MenuProps> = ({ meetingCount }) => {
699
699
 
700
700
  {startItemsJsx}
701
701
 
702
- <MenuItem onClick={handleClick(() => navigate('/notifications'))}>
702
+ <MenuItem onClick={handleClick(() => { navigate('/notifications'); })}>
703
703
  <ListItemIcon>
704
704
  <Badge
705
705
  badgeContent={notifications.length}
@@ -712,7 +712,7 @@ const ExtraSmallMenu: React.FC<MenuProps> = ({ meetingCount }) => {
712
712
  <Typography textAlign='center'>Notifications</Typography>
713
713
  </MenuItem>
714
714
 
715
- <MenuItem onClick={handleClick(() => navigate('/help'))}>
715
+ <MenuItem onClick={handleClick(() => { navigate('/help'); })}>
716
716
  <ListItemIcon>
717
717
  <Help />
718
718
  </ListItemIcon>
frontend/src/navbar/PawnIcon.tsx CHANGED
@@ -2,9 +2,9 @@ import { faChessPawn } from '@fortawesome/free-solid-svg-icons';
2
2
  import { SvgIcon } from '@mui/material';
3
3
  import { forwardRef } from 'react';
4
4
 
5
- type FontAwesomeSvgIconProps = {
5
+ interface FontAwesomeSvgIconProps {
6
6
  icon: any;
7
- };
7
+ }
8
8
 
9
9
  const FontAwesomeSvgIcon = forwardRef<SVGSVGElement, FontAwesomeSvgIconProps>(
10
10
  (props, ref) => {
frontend/src/navbar/ProfileButton.tsx CHANGED
@@ -79,14 +79,14 @@ const ProfileButton = () => {
79
79
  horizontal: 'right',
80
80
  }}
81
81
  >
82
- <MenuItem onClick={handleClick(() => navigate('/profile?view=stats'))}>
82
+ <MenuItem onClick={handleClick(() => { navigate('/profile?view=stats'); })}>
83
83
  <ListItemIcon>
84
84
  <Person2Icon />
85
85
  </ListItemIcon>
86
86
  <Typography textAlign='center'>Profile</Typography>
87
87
  </MenuItem>
88
88
 
89
- <MenuItem onClick={handleClick(() => navigate('/profile/edit'))}>
89
+ <MenuItem onClick={handleClick(() => { navigate('/profile/edit'); })}>
90
90
  <ListItemIcon>
91
91
  <SettingsIcon />
92
92
  </ListItemIcon>
@@ -94,7 +94,7 @@ const ProfileButton = () => {
94
94
  </MenuItem>
95
95
 
96
96
  {user.isCoach && (
97
- <MenuItem onClick={handleClick(() => navigate('/coach'))}>
97
+ <MenuItem onClick={handleClick(() => { navigate('/coach'); })}>
98
98
  <ListItemIcon>
99
99
  <SportsIcon />
100
100
  </ListItemIcon>
frontend/src/navbar/UnauthenticatedMenu.tsx CHANGED
@@ -48,37 +48,37 @@ function unauthenticatedStartItems(
48
48
  {
49
49
  name: 'Tournaments',
50
50
  icon: <Tournaments />,
51
- onClick: () => toggleExpansion('Tournaments'),
51
+ onClick: () => { toggleExpansion('Tournaments'); },
52
52
  children: [
53
53
  {
54
54
  name: 'DojoLiga',
55
- onClick: () => navigate('/tournaments'),
55
+ onClick: () => { navigate('/tournaments'); },
56
56
  },
57
57
  {
58
58
  name: 'Open Classical',
59
- onClick: () => navigate('/tournaments/open-classical'),
59
+ onClick: () => { navigate('/tournaments/open-classical'); },
60
60
  },
61
61
  ],
62
62
  },
63
63
  {
64
64
  name: 'Material',
65
65
  icon: <MenuBook />,
66
- onClick: () => toggleExpansion('Material'),
66
+ onClick: () => { toggleExpansion('Material'); },
67
67
  children: [
68
68
  {
69
69
  name: 'Courses',
70
70
  icon: <ImportContacts />,
71
- onClick: () => navigate('/courses'),
71
+ onClick: () => { navigate('/courses'); },
72
72
  },
73
73
  {
74
74
  name: 'Books',
75
75
  icon: <AutoStories />,
76
- onClick: () => navigate('/material/books'),
76
+ onClick: () => { navigate('/material/books'); },
77
77
  },
78
78
  {
79
79
  name: 'Rating Conversions',
80
80
  icon: <SignalCellularAlt />,
81
- onClick: () => navigate('/material/ratings'),
81
+ onClick: () => { navigate('/material/ratings'); },
82
82
  },
83
83
  ],
84
84
  },
@@ -90,15 +90,15 @@ function unauthenticatedStartItems(
90
90
  {
91
91
  name: 'Shop',
92
92
  icon: <Sell />,
93
- onClick: () => toggleExpansion('Shop'),
93
+ onClick: () => { toggleExpansion('Shop'); },
94
94
  children: [
95
95
  {
96
96
  name: 'Courses',
97
- onClick: () => navigate('/courses'),
97
+ onClick: () => { navigate('/courses'); },
98
98
  },
99
99
  {
100
100
  name: 'Coaching',
101
- onClick: () => navigate('/coaching'),
101
+ onClick: () => { navigate('/coaching'); },
102
102
  },
103
103
  {
104
104
  name: 'Merch',
@@ -110,7 +110,7 @@ function unauthenticatedStartItems(
110
110
  {
111
111
  name: 'Contact Us',
112
112
  icon: <ContactSupport />,
113
- onClick: () => navigate('/help'),
113
+ onClick: () => { navigate('/help'); },
114
114
  },
115
115
  ];
116
116
  }
@@ -125,7 +125,7 @@ function useNavbarItems(handleClick: (func: () => void) => () => void) {
125
125
  const hide4 = useMediaQuery('(min-width:600px)');
126
126
 
127
127
  const startItems = unauthenticatedStartItems(navigate, (item: string) =>
128
- setOpenItems((v) => ({ ...v, [item]: !(v[item] || false) })),
128
+ { setOpenItems((v) => ({ ...v, [item]: !(v[item] || false) })); },
129
129
  );
130
130
 
131
131
  let startItemCount = 0;
@@ -214,10 +214,10 @@ export const LargeMenuUnauthenticated = () => {
214
214
  </Stack>
215
215
 
216
216
  <Stack spacing={1} direction='row'>
217
- <Button onClick={() => navigate('/signin')} sx={{ color: 'white' }}>
217
+ <Button onClick={() => { navigate('/signin'); }} sx={{ color: 'white' }}>
218
218
  Sign In
219
219
  </Button>
220
- <Button onClick={() => navigate('/signup')} sx={{ color: 'white' }}>
220
+ <Button onClick={() => { navigate('/signup'); }} sx={{ color: 'white' }}>
221
221
  Sign Up
222
222
  </Button>
223
223
  </Stack>
@@ -231,7 +231,7 @@ export const ExtraSmallMenuUnauthenticated = () => {
231
231
  const [openItems, setOpenItems] = useState<Record<string, boolean>>({});
232
232
 
233
233
  const startItems = unauthenticatedStartItems(navigate, (item: string) =>
234
- setOpenItems((v) => ({ ...v, [item]: !(v[item] || false) })),
234
+ { setOpenItems((v) => ({ ...v, [item]: !(v[item] || false) })); },
235
235
  );
236
236
 
237
237
  const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
@@ -275,10 +275,10 @@ export const ExtraSmallMenuUnauthenticated = () => {
275
275
  open={Boolean(anchorEl)}
276
276
  onClose={handleClose}
277
277
  >
278
- <MenuItem onClick={handleClick(() => navigate('/signin'))}>
278
+ <MenuItem onClick={handleClick(() => { navigate('/signin'); })}>
279
279
  <Typography textAlign='center'>Sign In</Typography>
280
280
  </MenuItem>
281
- <MenuItem onClick={handleClick(() => navigate('/signup'))}>
281
+ <MenuItem onClick={handleClick(() => { navigate('/signup'); })}>
282
282
  <Typography textAlign='center'>Sign Up</Typography>
283
283
  </MenuItem>
284
284
 
frontend/src/newsfeed/detail/CommentEditor.tsx CHANGED
@@ -57,7 +57,7 @@ function CommentEditor<T, CreateFunctionProps>(
57
57
  fullWidth
58
58
  multiline
59
59
  value={comment}
60
- onChange={(e) => setComment(e.target.value)}
60
+ onChange={(e) => { setComment(e.target.value); }}
61
61
  />
62
62
 
63
63
  {request.isLoading() ? (
frontend/src/newsfeed/detail/NewsfeedDetailPage.tsx CHANGED
@@ -8,10 +8,10 @@ import NewsfeedItem from './NewsfeedItem';
8
8
  import LoadingPage from '../../loading/LoadingPage';
9
9
  import NotFoundPage from '../../NotFoundPage';
10
10
 
11
- type NewsfeedDetailPageParams = {
11
+ interface NewsfeedDetailPageParams {
12
12
  owner: string;
13
13
  id: string;
14
- };
14
+ }
15
15
 
16
16
  const NewsfeedDetailPage = () => {
17
17
  const { owner, id } = useParams<NewsfeedDetailPageParams>();
frontend/src/newsfeed/detail/ReactionList.tsx CHANGED
@@ -202,7 +202,7 @@ const ReactionList: React.FC<ReactionListProps> = ({ owner, id, reactions, onEdi
202
202
  variant={
203
203
  isReactor(user, reactions, type) ? 'contained' : 'outlined'
204
204
  }
205
- onClick={() => onReact(type)}
205
+ onClick={() => { onReact(type); }}
206
206
  >
207
207
  <Paper
208
208
  elevation={2}
@@ -242,7 +242,7 @@ const ReactionList: React.FC<ReactionListProps> = ({ owner, id, reactions, onEdi
242
242
  <IconButton
243
243
  key={type}
244
244
  sx={{ width: '2.96875rem' }}
245
- onClick={() => onReact(type)}
245
+ onClick={() => { onReact(type); }}
246
246
  >
247
247
  <ReactionEmoji type={type} icon />
248
248
  </IconButton>
frontend/src/newsfeed/list/MultipleSelectChip.tsx CHANGED
@@ -22,7 +22,7 @@ const MenuProps = {
22
22
  function getStyles(value: string, selected: readonly string[], theme: Theme) {
23
23
  return {
24
24
  fontWeight:
25
- selected.indexOf(value) === -1
25
+ !selected.includes(value)
26
26
  ? theme.typography.fontWeightRegular
27
27
  : theme.typography.fontWeightMedium,
28
28
  };
frontend/src/newsfeed/list/NewsfeedList.tsx CHANGED
@@ -68,7 +68,7 @@ function useNewsfeedIds(initialNewsfeedIds: string[]): [string[], (v: string[])
68
68
  setNewsfeedIds(newValue);
69
69
 
70
70
  const removedIds = initialNewsfeedIds.filter(
71
- (id) => newValue.indexOf(id) === -1,
71
+ (id) => !newValue.includes(id),
72
72
  );
73
73
 
74
74
  for (const id of removedIds) {
@@ -222,7 +222,7 @@ const NewsfeedList: React.FC<NewsfeedListProps> = ({
222
222
  let shownEntries = data?.entries ?? [];
223
223
  if (showAdditionalFilters) {
224
224
  shownEntries = shownEntries.filter((entry) =>
225
- filters.some((filterKey) => Filters[filterKey]?.(entry)),
225
+ filters.some((filterKey) => Filters[filterKey](entry)),
226
226
  );
227
227
  }
228
228
 
frontend/src/profile/GamesTab.tsx CHANGED
@@ -138,8 +138,8 @@ const GamesTab: React.FC<GamesTabProps> = ({ user }) => {
138
138
  pageSize={pageSize}
139
139
  count={rowCount}
140
140
  hasMore={hasMore}
141
- onPrevPage={() => setPage(page - 1)}
142
- onNextPage={() => setPage(page + 1)}
141
+ onPrevPage={() => { setPage(page - 1); }}
142
+ onNextPage={() => { setPage(page + 1); }}
143
143
  />
144
144
  ),
145
145
  }}
frontend/src/profile/GraduationDialog.tsx CHANGED
@@ -87,7 +87,7 @@ const GraduationDialog = () => {
87
87
  <Dialog
88
88
  open={showGraduationDialog}
89
89
  onClose={
90
- request.isLoading() ? undefined : () => setShowGraduationDialog(false)
90
+ request.isLoading() ? undefined : () => { setShowGraduationDialog(false); }
91
91
  }
92
92
  fullWidth
93
93
  >
@@ -109,7 +109,7 @@ const GraduationDialog = () => {
109
109
  <TextField
110
110
  label='Comments'
111
111
  value={comments}
112
- onChange={(event) => setComments(event.target.value)}
112
+ onChange={(event) => { setComments(event.target.value); }}
113
113
  multiline
114
114
  minRows={3}
115
115
  maxRows={3}
@@ -119,7 +119,7 @@ const GraduationDialog = () => {
119
119
  </DialogContent>
120
120
  <DialogActions>
121
121
  <Button
122
- onClick={() => setShowGraduationDialog(false)}
122
+ onClick={() => { setShowGraduationDialog(false); }}
123
123
  disabled={request.isLoading()}
124
124
  >
125
125
  Cancel
frontend/src/profile/ProfilePage.tsx CHANGED
@@ -37,9 +37,9 @@ import ProgressTab from './progress/ProgressTab';
37
37
  import StatsTab from './stats/StatsTab';
38
38
  import ProfilePageTutorial from './tutorials/ProfilePageTutorial';
39
39
 
40
- export type ProfilePageProps = {
40
+ export interface ProfilePageProps {
41
41
  username: string;
42
- };
42
+ }
43
43
 
44
44
  const ProfilePage = () => {
45
45
  const { username } = useParams<ProfilePageProps>();
@@ -141,7 +141,7 @@ const ProfilePage = () => {
141
141
  id='edit-profile-button'
142
142
  variant='contained'
143
143
  startIcon={<Edit />}
144
- onClick={() => navigate('/profile/edit')}
144
+ onClick={() => { navigate('/profile/edit'); }}
145
145
  >
146
146
  Edit Profile
147
147
  </Button>
@@ -194,7 +194,7 @@ const ProfilePage = () => {
194
194
  <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
195
195
  <Tabs
196
196
  value={searchParams.get('view') || 'stats'}
197
- onChange={(_, t) => setSearchParams({ view: t })}
197
+ onChange={(_, t) => { setSearchParams({ view: t }); }}
198
198
  aria-label='profile tabs'
199
199
  variant='scrollable'
200
200
  >
frontend/src/profile/SwitchCohortPrompt.tsx CHANGED
@@ -35,8 +35,8 @@ export function SwitchCohortPrompt() {
35
35
  }
36
36
  onClose={
37
37
  showGraduation
38
- ? () => setHideGraduation(true)
39
- : () => setHideDemotion(true)
38
+ ? () => { setHideGraduation(true); }
39
+ : () => { setHideDemotion(true); }
40
40
  }
41
41
  autoHideDuration={showGraduation ? 6000 : 7000}
42
42
  >
@@ -48,7 +48,7 @@ export function SwitchCohortPrompt() {
48
48
  <Button
49
49
  color='inherit'
50
50
  size='small'
51
- onClick={() => navigate('/profile')}
51
+ onClick={() => { navigate('/profile'); }}
52
52
  endIcon={<NavigateNextIcon />}
53
53
  >
54
54
  Profile
@@ -57,7 +57,7 @@ export function SwitchCohortPrompt() {
57
57
  <Button
58
58
  color='inherit'
59
59
  size='small'
60
- onClick={() => navigate('/profile/edit')}
60
+ onClick={() => { navigate('/profile/edit'); }}
61
61
  endIcon={<NavigateNextIcon />}
62
62
  >
63
63
  Settings
frontend/src/profile/activity/ActivityPieChart.tsx CHANGED
@@ -169,7 +169,7 @@ const ActivityPieChart: React.FC<ActivityPieChartProps> = ({ user, timeline }) =
169
169
  label='Timeframe'
170
170
  value={timeframe}
171
171
  onChange={(event) =>
172
- onChangeTimeframe(event.target.value as Timeframe)
172
+ { onChangeTimeframe(event.target.value as Timeframe); }
173
173
  }
174
174
  sx={{ mb: 3, height: 1 }}
175
175
  fullWidth
@@ -202,7 +202,7 @@ const ActivityPieChart: React.FC<ActivityPieChartProps> = ({ user, timeline }) =
202
202
  {Math.round(score * 100) / 100}
203
203
  </Typography>
204
204
  {scoreChartCategory && (
205
- <Button onClick={() => setScoreChartCategory('')}>
205
+ <Button onClick={() => { setScoreChartCategory(''); }}>
206
206
  Back to Cohort
207
207
  </Button>
208
208
  )}
@@ -227,7 +227,7 @@ const ActivityPieChart: React.FC<ActivityPieChartProps> = ({ user, timeline }) =
227
227
  {getTimeDisplay(time)}
228
228
  </Typography>
229
229
  {timeChartCategory && (
230
- <Button onClick={() => setTimeChartCategory('')}>
230
+ <Button onClick={() => { setTimeChartCategory(''); }}>
231
231
  Back to Cohort
232
232
  </Button>
233
233
  )}
frontend/src/profile/activity/ActivityTimeline.tsx CHANGED
@@ -37,7 +37,7 @@ const CreatedAtItem: React.FC<{ user: User; viewer?: User }> = ({ user, viewer }
37
37
  requirementName: 'CreatedAt',
38
38
  requirementCategory: 'Welcome to the Dojo',
39
39
  cohort:
40
- user.graduationCohorts?.length > 0
40
+ user.graduationCohorts.length > 0
41
41
  ? user.graduationCohorts[0]
42
42
  : user.dojoCohort,
43
43
  };
@@ -97,7 +97,7 @@ const ActivityTimeline: React.FC<ActivityTimelineProps> = ({ user, timeline }) =
97
97
  <NewsfeedItem
98
98
  key={entry.id}
99
99
  entry={entry}
100
- onEdit={(e) => onEdit(i, e)}
100
+ onEdit={(e) => { onEdit(i, e); }}
101
101
  maxComments={3}
102
102
  />
103
103
  ))}
frontend/src/profile/activity/PieChart.tsx CHANGED
@@ -22,7 +22,7 @@ interface PieChartProps {
22
22
  data: PieChartData[];
23
23
  renderTotal: (value: number) => JSX.Element;
24
24
  getTooltip: (entry: PieChartData) => string;
25
- onClick: (event: React.MouseEvent<Element, MouseEvent>, dataIndex: number) => void;
25
+ onClick: (event: React.MouseEvent, dataIndex: number) => void;
26
26
  }
27
27
 
28
28
  const PieChart: React.FC<PieChartProps> = ({
frontend/src/profile/activity/activity.ts CHANGED
@@ -193,12 +193,12 @@ function getTimeframeScoreChartData(
193
193
 
194
194
  const data: Record<string, PieChartData> = {};
195
195
  const timeCutoff = timeframeToISO(timeframe);
196
- const requirementMap = requirements.reduce(
196
+ const requirementMap = requirements.reduce<Record<string, Requirement>>(
197
197
  (m, r) => {
198
198
  m[r.id] = r;
199
199
  return m;
200
200
  },
201
- {} as Record<string, Requirement>,
201
+ {},
202
202
  );
203
203
 
204
204
  for (const entry of timeline) {
@@ -270,12 +270,12 @@ function getCategoryScoreChartData(
270
270
 
271
271
  const data: Record<string, PieChartData> = {};
272
272
  const timeCutoff = timeframeToISO(timeframe);
273
- const requirementMap = requirements.reduce(
273
+ const requirementMap = requirements.reduce<Record<string, Requirement>>(
274
274
  (m, r) => {
275
275
  m[r.id] = r;
276
276
  return m;
277
277
  },
278
- {} as Record<string, Requirement>,
278
+ {},
279
279
  );
280
280
 
281
281
  for (const entry of timeline) {
@@ -507,12 +507,12 @@ function getAllTimeTimeChartData(
507
507
  requirements: Requirement[],
508
508
  ): PieChartData[] {
509
509
  const requirementMap =
510
- requirements.reduce(
510
+ requirements.reduce<Record<string, Requirement | CustomTask>>(
511
511
  (map, r) => {
512
512
  map[r.id] = r;
513
513
  return map;
514
514
  },
515
- {} as Record<string, Requirement | CustomTask>,
515
+ {},
516
516
  ) ?? {};
517
517
 
518
518
  user.customTasks?.forEach((t) => {
@@ -638,7 +638,7 @@ function getAllTimeCategoryTimeChartData(
638
638
  continue;
639
639
  }
640
640
  const progress = user.progress[requirement.id];
641
- if (!progress || !progress.minutesSpent) {
641
+ if (!progress.minutesSpent) {
642
642
  continue;
643
643
  }
644
644
 
@@ -674,11 +674,11 @@ function getAllTimeCategoryTimeChartData(
674
674
  if (category === 'Non-Dojo') {
675
675
  for (const task of user.customTasks || []) {
676
676
  const progress = user.progress[task.id];
677
- if (!progress || !progress.minutesSpent) {
677
+ if (!progress.minutesSpent) {
678
678
  continue;
679
679
  }
680
680
 
681
- let name = task.name;
681
+ const name = task.name;
682
682
 
683
683
  let reqCohorts = cohorts;
684
684
  if (cohorts.includes(ALL_COHORTS)) {
frontend/src/profile/creator/DiscordForm.tsx CHANGED
@@ -87,7 +87,7 @@ const DiscordForm: React.FC<ProfileCreatorFormProps> = ({
87
87
  <TextField
88
88
  label='Discord Username'
89
89
  value={discordUsername}
90
- onChange={(event) => setDiscordUsername(event.target.value)}
90
+ onChange={(event) => { setDiscordUsername(event.target.value); }}
91
91
  helperText={'Format as username#id for older-style Discord usernames'}
92
92
  />
93
93
 
@@ -97,7 +97,7 @@ const DiscordForm: React.FC<ProfileCreatorFormProps> = ({
97
97
  <Checkbox
98
98
  checked={!disableBookingNotifications}
99
99
  onChange={(event) =>
100
- setDisableBookingNotifications(!event.target.checked)
100
+ { setDisableBookingNotifications(!event.target.checked); }
101
101
  }
102
102
  />
103
103
  }
@@ -109,7 +109,7 @@ const DiscordForm: React.FC<ProfileCreatorFormProps> = ({
109
109
  <Checkbox
110
110
  checked={!disableCancellationNotifications}
111
111
  onChange={(event) =>
112
- setDisableCancellationNotifications(!event.target.checked)
112
+ { setDisableCancellationNotifications(!event.target.checked); }
113
113
  }
114
114
  />
115
115
  }
frontend/src/profile/creator/ExtraRatingSystemsForm.tsx CHANGED
@@ -56,17 +56,17 @@ const ExtraRatingSystemsForm: React.FC<ProfileCreatorFormProps> = ({
56
56
  const request = useRequest();
57
57
 
58
58
  const [usernames, setUsernames] = useState<Record<RatingSystem, string>>(
59
- Object.values(RatingSystems).reduce((map, rs) => {
59
+ Object.values(RatingSystems).reduce<Record<RatingSystem, string>>((map, rs) => {
60
60
  map[rs] = getRatingUsername(user, rs);
61
61
  return map;
62
- }, {} as Record<RatingSystem, string>)
62
+ }, {})
63
63
  );
64
64
 
65
65
  const [hideUsernames, setHideUsernames] = useState<Record<RatingSystem, boolean>>(
66
- Object.values(RatingSystems).reduce((map, rs) => {
66
+ Object.values(RatingSystems).reduce<Record<RatingSystem, boolean>>((map, rs) => {
67
67
  map[rs] = hideRatingUsername(user, rs);
68
68
  return map;
69
- }, {} as Record<RatingSystem, boolean>)
69
+ }, {})
70
70
  );
71
71
 
72
72
  const setUsername = (rs: RatingSystem, value: string) => {
@@ -123,7 +123,7 @@ const ExtraRatingSystemsForm: React.FC<ProfileCreatorFormProps> = ({
123
123
  label={getUsernameLabel(rs)}
124
124
  value={usernames[rs]}
125
125
  onChange={(event) =>
126
- setUsername(rs, event.target.value)
126
+ { setUsername(rs, event.target.value); }
127
127
  }
128
128
  helperText={getHelperText(rs)}
129
129
  fullWidth
@@ -136,7 +136,7 @@ const ExtraRatingSystemsForm: React.FC<ProfileCreatorFormProps> = ({
136
136
  <Checkbox
137
137
  checked={hideUsernames[rs]}
138
138
  onChange={(event) =>
139
- setHideUsername(rs, event.target.checked)
139
+ { setHideUsername(rs, event.target.checked); }
140
140
  }
141
141
  />
142
142
  }
frontend/src/profile/creator/PersonalInfoForm.tsx CHANGED
@@ -57,7 +57,7 @@ const PersonalInfoForm: React.FC<ProfileCreatorFormProps> = ({ user, onNextStep
57
57
  required
58
58
  label='Display Name'
59
59
  value={displayName}
60
- onChange={(event) => setDisplayName(event.target.value)}
60
+ onChange={(event) => { setDisplayName(event.target.value); }}
61
61
  helperText={'This is how other users will identify you'}
62
62
  />
63
63
 
@@ -67,14 +67,14 @@ const PersonalInfoForm: React.FC<ProfileCreatorFormProps> = ({ user, onNextStep
67
67
  minRows={3}
68
68
  maxRows={6}
69
69
  value={bio}
70
- onChange={(event) => setBio(event.target.value)}
70
+ onChange={(event) => { setBio(event.target.value); }}
71
71
  />
72
72
 
73
73
  <TextField
74
74
  select
75
75
  label='Timezone (Optional)'
76
76
  value={timezone}
77
- onChange={(e) => setTimezone(e.target.value)}
77
+ onChange={(e) => { setTimezone(e.target.value); }}
78
78
  >
79
79
  {getTimezoneOptions()}
80
80
  </TextField>
frontend/src/profile/creator/PreferredRatingSystemForm.tsx CHANGED
@@ -158,7 +158,7 @@ const PreferredRatingSystemForm: React.FC<ProfileCreatorFormProps> = ({
158
158
  select
159
159
  label='Preferred Rating System'
160
160
  value={ratingSystem}
161
- onChange={(event) => setRatingSystem(event.target.value as RatingSystem)}
161
+ onChange={(event) => { setRatingSystem(event.target.value as RatingSystem); }}
162
162
  helperText='Choose the rating system you play most often'
163
163
  >
164
164
  {Object.values(RatingSystems).map((option) => (
@@ -174,7 +174,7 @@ const PreferredRatingSystemForm: React.FC<ProfileCreatorFormProps> = ({
174
174
  required
175
175
  label={getUsernameLabel(ratingSystem)}
176
176
  value={username}
177
- onChange={(event) => setUsername(event.target.value)}
177
+ onChange={(event) => { setUsername(event.target.value); }}
178
178
  helperText={getHelperText(ratingSystem)}
179
179
  />
180
180
 
@@ -183,7 +183,7 @@ const PreferredRatingSystemForm: React.FC<ProfileCreatorFormProps> = ({
183
183
  <Checkbox
184
184
  checked={hideUsername}
185
185
  onChange={(event) =>
186
- setHideUsername(event.target.checked)
186
+ { setHideUsername(event.target.checked); }
187
187
  }
188
188
  />
189
189
  }
frontend/src/profile/creator/ProfileCreatorPage.tsx CHANGED
@@ -77,7 +77,7 @@ const ProfileCreatorPage = () => {
77
77
  user.subscriptionStatus !== SubscriptionStatus.Subscribed &&
78
78
  activeStep === 0
79
79
  ) {
80
- return <PricingPage onFreeTier={() => setShowPricingPage(false)} />;
80
+ return <PricingPage onFreeTier={() => { setShowPricingPage(false); }} />;
81
81
  }
82
82
 
83
83
  return (
@@ -101,8 +101,8 @@ const ProfileCreatorPage = () => {
101
101
  <Box mt={5}>
102
102
  <Form
103
103
  user={user}
104
- onNextStep={() => setActiveStep(activeStep + 1)}
105
- onPrevStep={() => setActiveStep(activeStep - 1)}
104
+ onNextStep={() => { setActiveStep(activeStep + 1); }}
105
+ onPrevStep={() => { setActiveStep(activeStep - 1); }}
106
106
  />
107
107
  </Box>
108
108
  </Container>
frontend/src/profile/creator/ReferralSourceForm.tsx CHANGED
@@ -82,7 +82,7 @@ const ReferralSourceForm: React.FC<ProfileCreatorFormProps> = ({ user, onPrevSte
82
82
  required
83
83
  label='Referral Source'
84
84
  value={referralSource}
85
- onChange={(e) => setReferralSource(e.target.value)}
85
+ onChange={(e) => { setReferralSource(e.target.value); }}
86
86
  error={!!errors.referralSource}
87
87
  helperText={errors.referralSource}
88
88
  >
@@ -100,7 +100,7 @@ const ReferralSourceForm: React.FC<ProfileCreatorFormProps> = ({ user, onPrevSte
100
100
  required
101
101
  label='Other (Please Specify)'
102
102
  value={otherSource}
103
- onChange={(e) => setOtherSource(e.target.value)}
103
+ onChange={(e) => { setOtherSource(e.target.value); }}
104
104
  error={!!errors.otherSource}
105
105
  helperText={errors.otherSource}
106
106
  />
frontend/src/profile/editor/NotificationSettingsEditor.tsx CHANGED
@@ -45,7 +45,7 @@ function setSettingValue(
45
45
 
46
46
  interface NotificationSettingsSection {
47
47
  label: string;
48
- settings: Array<{ label: string; path: string }>;
48
+ settings: { label: string; path: string }[];
49
49
  icon: React.ReactNode;
50
50
  }
51
51
 
@@ -155,13 +155,13 @@ const NotificationSettingsEditor: React.FC<NotificationSettingsEditorProps> = ({
155
155
  )
156
156
  }
157
157
  onChange={(e) =>
158
- setNotificationSettings(
158
+ { setNotificationSettings(
159
159
  setSettingValue(
160
160
  notificationSettings,
161
161
  setting.path,
162
162
  !e.target.checked,
163
163
  ),
164
- )
164
+ ); }
165
165
  }
166
166
  />
167
167
  }
frontend/src/profile/editor/ProfileEditorPage.tsx CHANGED
@@ -103,7 +103,7 @@ interface RatingEditor {
103
103
  function getRatingEditors(ratings: Partial<Record<RatingSystem, Rating>>) {
104
104
  const ratingEditors: Record<RatingSystem, RatingEditor> = Object.values(
105
105
  RatingSystem,
106
- ).reduce(
106
+ ).reduce<Record<RatingSystem, RatingEditor>>(
107
107
  (m, rs) => {
108
108
  m[rs] = {
109
109
  username: ratings[rs]?.username || '',
@@ -114,13 +114,13 @@ function getRatingEditors(ratings: Partial<Record<RatingSystem, Rating>>) {
114
114
  };
115
115
  return m;
116
116
  },
117
- {} as Record<RatingSystem, RatingEditor>,
117
+ {},
118
118
  );
119
119
  return ratingEditors;
120
120
  }
121
121
 
122
122
  function getRatingsFromEditors(ratingEditors: Record<RatingSystem, RatingEditor>) {
123
- const ratings: Record<RatingSystem, Rating> = Object.values(RatingSystem).reduce(
123
+ const ratings: Record<RatingSystem, Rating> = Object.values(RatingSystem).reduce<Record<RatingSystem, Rating>>(
124
124
  (m, rs) => {
125
125
  m[rs] = {
126
126
  username: ratingEditors[rs].username || '',
@@ -131,7 +131,7 @@ function getRatingsFromEditors(ratingEditors: Record<RatingSystem, RatingEditor>
131
131
  };
132
132
  return m;
133
133
  },
134
- {} as Record<RatingSystem, Rating>,
134
+ {},
135
135
  );
136
136
  return ratings;
137
137
  }
@@ -146,7 +146,7 @@ function parseRating(rating: string | undefined): number {
146
146
  return 0;
147
147
  }
148
148
  rating = rating.replace(/^0+/, '') || '0';
149
- let n = Math.floor(Number(rating));
149
+ const n = Math.floor(Number(rating));
150
150
  if (n === Infinity) {
151
151
  return -1;
152
152
  }
@@ -196,7 +196,7 @@ function getTimezoneOptions() {
196
196
 
197
197
  export function encodeFileToBase64(file: File): Promise<string> {
198
198
  return new Promise<string>((resolve, reject) => {
199
- let reader = new FileReader();
199
+ const reader = new FileReader();
200
200
  reader.onloadend = function () {
201
201
  const base64string = reader.result as string;
202
202
  console.log('Base 64 string: ', base64string);
@@ -354,7 +354,7 @@ const ProfileEditorPage = () => {
354
354
 
355
355
  if (
356
356
  ratingSystem !== RatingSystem.Custom &&
357
- !ratingEditors[ratingSystem]?.username.trim()
357
+ !ratingEditors[ratingSystem].username.trim()
358
358
  ) {
359
359
  newErrors[`${ratingSystem}Username`] =
360
360
  `This field is required when using ${formatRatingSystem(
@@ -363,17 +363,17 @@ const ProfileEditorPage = () => {
363
363
  }
364
364
 
365
365
  for (const rs of Object.keys(ratingEditors)) {
366
- if (parseRating(ratingEditors[rs as RatingSystem]?.startRating) < 0) {
366
+ if (parseRating(ratingEditors[rs as RatingSystem].startRating) < 0) {
367
367
  newErrors[`${rs}StartRating`] = 'Rating must be an integer >= 0';
368
368
  }
369
369
  }
370
370
 
371
371
  if (ratingSystem === RatingSystem.Custom) {
372
- if (parseRating(ratingEditors[RatingSystem.Custom]?.currentRating) <= 0) {
372
+ if (parseRating(ratingEditors[RatingSystem.Custom].currentRating) <= 0) {
373
373
  newErrors.currentCustomRating =
374
374
  'This field is required when using Custom rating system.';
375
375
  }
376
- if (parseRating(ratingEditors[RatingSystem.Custom]?.startRating) <= 0) {
376
+ if (parseRating(ratingEditors[RatingSystem.Custom].startRating) <= 0) {
377
377
  newErrors.startCustomRating =
378
378
  'This field is required when using Custom rating system.';
379
379
  }
@@ -410,12 +410,12 @@ const ProfileEditorPage = () => {
410
410
  required: ratingSystem === rsf.system,
411
411
  label: rsf.label,
412
412
  hideLabel: rsf.hideLabel,
413
- username: ratingEditors[rsf.system]?.username,
414
- setUsername: (value: string) => setUsername(rsf.system, value),
415
- startRating: ratingEditors[rsf.system]?.startRating,
416
- setStartRating: (value: string) => setStartRating(rsf.system, value),
417
- hidden: ratingEditors[rsf.system]?.hideUsername,
418
- setHidden: (value: boolean) => setHidden(rsf.system, value),
413
+ username: ratingEditors[rsf.system].username,
414
+ setUsername: (value: string) => { setUsername(rsf.system, value); },
415
+ startRating: ratingEditors[rsf.system].startRating,
416
+ setStartRating: (value: string) => { setStartRating(rsf.system, value); },
417
+ hidden: ratingEditors[rsf.system].hideUsername,
418
+ setHidden: (value: boolean) => { setHidden(rsf.system, value); },
419
419
  usernameError: errors[`${rsf.system}Username`],
420
420
  startRatingError: errors[`${rsf.system}StartRating`],
421
421
  }));
@@ -555,7 +555,7 @@ const ProfileEditorPage = () => {
555
555
  variant='contained'
556
556
  color='error'
557
557
  disableElevation
558
- onClick={() => navigate('..')}
558
+ onClick={() => { navigate('..'); }}
559
559
  startIcon={<NotInterestedIcon />}
560
560
  >
561
561
  Cancel
@@ -625,7 +625,7 @@ const ProfileEditorPage = () => {
625
625
  required
626
626
  label='Display Name'
627
627
  value={displayName}
628
- onChange={(event) => setDisplayName(event.target.value)}
628
+ onChange={(event) => { setDisplayName(event.target.value); }}
629
629
  error={!!errors.displayName}
630
630
  helperText={
631
631
  errors.displayName ||
@@ -637,7 +637,7 @@ const ProfileEditorPage = () => {
637
637
  label='Discord Username'
638
638
  value={discordUsername}
639
639
  onChange={(event) =>
640
- setDiscordUsername(event.target.value)
640
+ { setDiscordUsername(event.target.value); }
641
641
  }
642
642
  error={!!errors.discordUsername}
643
643
  helperText={
@@ -652,7 +652,7 @@ const ProfileEditorPage = () => {
652
652
  minRows={3}
653
653
  maxRows={6}
654
654
  value={bio}
655
- onChange={(event) => setBio(event.target.value)}
655
+ onChange={(event) => { setBio(event.target.value); }}
656
656
  error={!!errors.bio}
657
657
  helperText={
658
658
  errors.bio ||
@@ -667,7 +667,7 @@ const ProfileEditorPage = () => {
667
667
  minRows={3}
668
668
  maxRows={6}
669
669
  value={coachBio}
670
- onChange={(event) => setCoachBio(event.target.value)}
670
+ onChange={(event) => { setCoachBio(event.target.value); }}
671
671
  helperText='An optional coaching-specific bio. If included, it will be displayed on the coaching page and on the coach tab on your profile. If not included, the coaching page will use your regular bio and the coach tab on your profile will not have an additional bio.'
672
672
  />
673
673
  )}
@@ -676,7 +676,7 @@ const ProfileEditorPage = () => {
676
676
  select
677
677
  label='Timezone'
678
678
  value={timezone}
679
- onChange={(e) => setTimezone(e.target.value)}
679
+ onChange={(e) => { setTimezone(e.target.value); }}
680
680
  >
681
681
  {getTimezoneOptions()}
682
682
  </TextField>
@@ -706,7 +706,7 @@ const ProfileEditorPage = () => {
706
706
  select
707
707
  label='ChessDojo Cohort'
708
708
  value={dojoCohort}
709
- onChange={(event) => setDojoCohort(event.target.value)}
709
+ onChange={(event) => { setDojoCohort(event.target.value); }}
710
710
  error={!!errors.dojoCohort}
711
711
  helperText={errors.dojoCohort}
712
712
  >
@@ -723,7 +723,7 @@ const ProfileEditorPage = () => {
723
723
  label='Preferred Rating System'
724
724
  value={ratingSystem}
725
725
  onChange={(event) =>
726
- setRatingSystem(event.target.value as RatingSystem)
726
+ { setRatingSystem(event.target.value as RatingSystem); }
727
727
  }
728
728
  error={!!errors.ratingSystem}
729
729
  helperText={errors.ratingSystem}
@@ -748,7 +748,7 @@ const ProfileEditorPage = () => {
748
748
  label={rs.label}
749
749
  value={rs.username}
750
750
  onChange={(event) =>
751
- rs.setUsername(event.target.value)
751
+ { rs.setUsername(event.target.value); }
752
752
  }
753
753
  error={!!rs.usernameError}
754
754
  helperText={
@@ -776,7 +776,7 @@ const ProfileEditorPage = () => {
776
776
  label='Start Rating'
777
777
  value={rs.startRating}
778
778
  onChange={(event) =>
779
- rs.setStartRating(event.target.value)
779
+ { rs.setStartRating(event.target.value); }
780
780
  }
781
781
  error={!!rs.startRatingError}
782
782
  helperText={
@@ -793,7 +793,7 @@ const ProfileEditorPage = () => {
793
793
  <Checkbox
794
794
  checked={rs.hidden}
795
795
  onChange={(event) =>
796
- rs.setHidden(event.target.checked)
796
+ { rs.setHidden(event.target.checked); }
797
797
  }
798
798
  />
799
799
  }
@@ -810,13 +810,13 @@ const ProfileEditorPage = () => {
810
810
  label='Current Rating (Custom)'
811
811
  value={
812
812
  ratingEditors[RatingSystem.Custom]
813
- ?.currentRating
813
+ .currentRating
814
814
  }
815
815
  onChange={(event) =>
816
- setCurrentRating(
816
+ { setCurrentRating(
817
817
  RatingSystem.Custom,
818
818
  event.target.value,
819
- )
819
+ ); }
820
820
  }
821
821
  error={!!errors.currentCustomRating}
822
822
  helperText={
@@ -833,13 +833,13 @@ const ProfileEditorPage = () => {
833
833
  label='Start Rating (Custom)'
834
834
  value={
835
835
  ratingEditors[RatingSystem.Custom]
836
- ?.startRating
836
+ .startRating
837
837
  }
838
838
  onChange={(event) =>
839
- setStartRating(
839
+ { setStartRating(
840
840
  RatingSystem.Custom,
841
841
  event.target.value,
842
- )
842
+ ); }
843
843
  }
844
844
  error={!!errors.startCustomRating}
845
845
  helperText={
@@ -853,12 +853,12 @@ const ProfileEditorPage = () => {
853
853
  <Grid item xs>
854
854
  <TextField
855
855
  label='Custom Rating Name'
856
- value={ratingEditors[RatingSystem.Custom]?.name}
856
+ value={ratingEditors[RatingSystem.Custom].name}
857
857
  onChange={(event) =>
858
- setRatingName(
858
+ { setRatingName(
859
859
  RatingSystem.Custom,
860
860
  event.target.value,
861
- )
861
+ ); }
862
862
  }
863
863
  sx={{ width: 1 }}
864
864
  helperText=' '
@@ -897,7 +897,7 @@ const ProfileEditorPage = () => {
897
897
  <Checkbox
898
898
  checked={enableLightMode}
899
899
  onChange={(event) =>
900
- setEnableLightMode(event.target.checked)
900
+ { setEnableLightMode(event.target.checked); }
901
901
  }
902
902
  />
903
903
  }
frontend/src/profile/editor/SubscriptionManager.tsx CHANGED
@@ -54,7 +54,7 @@ const SubscriptionManager: React.FC<SubscriptionManagerProps> = ({ user }) => {
54
54
  {isFreeTier ? (
55
55
  <>
56
56
  <Typography>Subscription Status: Free Tier</Typography>
57
- <Button variant='contained' onClick={() => navigate('/prices')}>
57
+ <Button variant='contained' onClick={() => { navigate('/prices'); }}>
58
58
  View Prices
59
59
  </Button>
60
60
  </>
@@ -62,7 +62,7 @@ const SubscriptionManager: React.FC<SubscriptionManagerProps> = ({ user }) => {
62
62
  <>
63
63
  <Typography>Subscription Status: Subscribed</Typography>
64
64
 
65
- {paymentInfo && paymentInfo.customerId ? (
65
+ {paymentInfo?.customerId ? (
66
66
  <LoadingButton
67
67
  loading={request.isLoading()}
68
68
  onClick={onManageSubscription}
frontend/src/profile/followers/FollowersList.tsx CHANGED
@@ -41,7 +41,7 @@ const FollowersList = () => {
41
41
  return <LoadingPage />;
42
42
  }
43
43
 
44
- if (!request.data || !request.data.followers || request.data.followers.length === 0) {
44
+ if (!request.data?.followers || request.data.followers.length === 0) {
45
45
  return (
46
46
  <>
47
47
  <RequestSnackbar request={request} />
frontend/src/profile/info/CoachChip.tsx CHANGED
@@ -8,7 +8,7 @@ interface CoachChipProps {
8
8
  }
9
9
 
10
10
  const CoachChip: React.FC<CoachChipProps> = ({ user }) => {
11
- if (!user || !user.isCoach) {
11
+ if (!user?.isCoach) {
12
12
  return null;
13
13
  }
14
14
 
frontend/src/profile/info/DiscordChip.tsx CHANGED
@@ -2,9 +2,9 @@ import { faDiscord } from '@fortawesome/free-brands-svg-icons';
2
2
  import { Chip, SvgIcon, Tooltip } from '@mui/material';
3
3
  import { forwardRef } from 'react';
4
4
 
5
- type FontAwesomeSvgIconProps = {
5
+ interface FontAwesomeSvgIconProps {
6
6
  icon: any;
7
- };
7
+ }
8
8
 
9
9
  export const FontAwesomeSvgIcon = forwardRef<SVGSVGElement, FontAwesomeSvgIconProps>(
10
10
  (props, ref) => {
frontend/src/profile/progress/CustomTaskEditor.tsx CHANGED
@@ -41,12 +41,12 @@ const CustomTaskEditor: React.FC<CustomTaskEditorProps> = ({ task, open, onClose
41
41
  task ? Object.values(task.counts).length === dojoCohorts.length : true,
42
42
  );
43
43
  const [cohorts, setCohorts] = useState<Record<string, boolean>>(
44
- dojoCohorts.reduce(
44
+ dojoCohorts.reduce<Record<string, boolean>>(
45
45
  (map, cohort) => {
46
46
  map[cohort] = task?.counts[cohort] !== undefined || false;
47
47
  return map;
48
48
  },
49
- {} as Record<string, boolean>,
49
+ {},
50
50
  ),
51
51
  );
52
52
  const [errors, setErrors] = useState<Record<string, string>>({});
@@ -75,12 +75,12 @@ const CustomTaskEditor: React.FC<CustomTaskEditorProps> = ({ task, open, onClose
75
75
  const includedCohorts = allCohorts
76
76
  ? dojoCohorts
77
77
  : Object.keys(cohorts).filter((c) => cohorts[c]);
78
- const newCounts = includedCohorts.reduce(
78
+ const newCounts = includedCohorts.reduce<Record<string, number>>(
79
79
  (map, c) => {
80
80
  map[c] = 1;
81
81
  return map;
82
82
  },
83
- {} as Record<string, number>,
83
+ {},
84
84
  );
85
85
 
86
86
  const newTask = {
@@ -152,7 +152,7 @@ const CustomTaskEditor: React.FC<CustomTaskEditorProps> = ({ task, open, onClose
152
152
  label='Activity Name'
153
153
  required
154
154
  value={name}
155
- onChange={(e) => setName(e.target.value)}
155
+ onChange={(e) => { setName(e.target.value); }}
156
156
  error={!!errors.name}
157
157
  helperText={errors.name}
158
158
  fullWidth
@@ -164,7 +164,7 @@ const CustomTaskEditor: React.FC<CustomTaskEditorProps> = ({ task, open, onClose
164
164
  minRows={3}
165
165
  maxRows={3}
166
166
  value={description}
167
- onChange={(e) => setDescription(e.target.value)}
167
+ onChange={(e) => { setDescription(e.target.value); }}
168
168
  fullWidth
169
169
  />
170
170
 
@@ -179,7 +179,7 @@ const CustomTaskEditor: React.FC<CustomTaskEditorProps> = ({ task, open, onClose
179
179
  <Checkbox
180
180
  checked={allCohorts}
181
181
  onChange={(event) =>
182
- setAllCohorts(event.target.checked)
182
+ { setAllCohorts(event.target.checked); }
183
183
  }
184
184
  />
185
185
  }
@@ -199,10 +199,10 @@ const CustomTaskEditor: React.FC<CustomTaskEditorProps> = ({ task, open, onClose
199
199
  allCohorts || cohorts[cohort]
200
200
  }
201
201
  onChange={(event) =>
202
- onChangeCohort(
202
+ { onChangeCohort(
203
203
  cohort,
204
204
  event.target.checked,
205
- )
205
+ ); }
206
206
  }
207
207
  />
208
208
  }
frontend/src/profile/progress/CustomTaskProgressItem.tsx CHANGED
@@ -40,7 +40,7 @@ const CustomTaskProgressItem: React.FC<CustomTaskProgressItemProps> = ({
40
40
  <Grid
41
41
  item
42
42
  xs={9}
43
- onClick={() => setShowReqModal(true)}
43
+ onClick={() => { setShowReqModal(true); }}
44
44
  sx={{ cursor: 'pointer', position: 'relative' }}
45
45
  >
46
46
  <Typography>{task.name}</Typography>
@@ -79,7 +79,7 @@ const CustomTaskProgressItem: React.FC<CustomTaskProgressItemProps> = ({
79
79
 
80
80
  <IconButton
81
81
  aria-label={`Update ${task.name}`}
82
- onClick={() => setShowUpdateDialog(true)}
82
+ onClick={() => { setShowUpdateDialog(true); }}
83
83
  >
84
84
  <EditIcon />
85
85
  </IconButton>
@@ -91,7 +91,7 @@ const CustomTaskProgressItem: React.FC<CustomTaskProgressItemProps> = ({
91
91
  {showReqModal && (
92
92
  <RequirementModal
93
93
  open={showReqModal}
94
- onClose={() => setShowReqModal(false)}
94
+ onClose={() => { setShowReqModal(false); }}
95
95
  requirement={task}
96
96
  />
97
97
  )}
@@ -99,7 +99,7 @@ const CustomTaskProgressItem: React.FC<CustomTaskProgressItemProps> = ({
99
99
  {showUpdateDialog && (
100
100
  <ProgressDialog
101
101
  open={showUpdateDialog}
102
- onClose={() => setShowUpdateDialog(false)}
102
+ onClose={() => { setShowUpdateDialog(false); }}
103
103
  requirement={task}
104
104
  cohort={cohort}
105
105
  progress={progress}
frontend/src/profile/progress/ProgressCategory.tsx CHANGED
@@ -19,7 +19,7 @@ import ProgressItem from './ProgressItem';
19
19
 
20
20
  export interface Category {
21
21
  name: string;
22
- requirements: Array<Requirement | CustomTask>;
22
+ requirements: (Requirement | CustomTask)[];
23
23
  totalComplete: number;
24
24
  }
25
25
 
@@ -67,7 +67,7 @@ const DefaultProgressCategory: React.FC<ProgressCategoryProps> = ({
67
67
  <Accordion
68
68
  key={c.name}
69
69
  expanded={expanded}
70
- onChange={() => toggleExpand(c.name)}
70
+ onChange={() => { toggleExpand(c.name); }}
71
71
  sx={{ width: 1 }}
72
72
  >
73
73
  <AccordionSummary
@@ -118,7 +118,7 @@ const DefaultProgressCategory: React.FC<ProgressCategoryProps> = ({
118
118
  })}
119
119
 
120
120
  {!isFreeTier && c.name === 'Non-Dojo' && isCurrentUser && (
121
- <Button sx={{ mt: 2 }} onClick={() => setShowCustomTaskEditor(true)}>
121
+ <Button sx={{ mt: 2 }} onClick={() => { setShowCustomTaskEditor(true); }}>
122
122
  Add Custom Activity
123
123
  </Button>
124
124
  )}
frontend/src/profile/progress/ProgressDialog.tsx CHANGED
@@ -68,7 +68,7 @@ const ProgressDialog: React.FC<ProgressDialogProps> = ({
68
68
  select
69
69
  label='Cohort'
70
70
  value={selectedCohort}
71
- onChange={(event) => setSelectedCohort(event.target.value)}
71
+ onChange={(event) => { setSelectedCohort(event.target.value); }}
72
72
  sx={{ mt: 1 }}
73
73
  fullWidth
74
74
  >
@@ -85,7 +85,7 @@ const ProgressDialog: React.FC<ProgressDialogProps> = ({
85
85
  <ProgressHistory
86
86
  requirement={requirement}
87
87
  onClose={onClose}
88
- toggleView={() => setView(ProgressDialogView.Updater)}
88
+ toggleView={() => { setView(ProgressDialogView.Updater); }}
89
89
  cohort={selectedCohort}
90
90
  />
91
91
  )}
@@ -95,7 +95,7 @@ const ProgressDialog: React.FC<ProgressDialogProps> = ({
95
95
  progress={progress}
96
96
  cohort={selectedCohort}
97
97
  onClose={onClose}
98
- toggleView={() => setView(ProgressDialogView.History)}
98
+ toggleView={() => { setView(ProgressDialogView.History); }}
99
99
  />
100
100
  )}
101
101
  </Dialog>
frontend/src/profile/progress/ProgressHistory.tsx CHANGED
@@ -140,7 +140,7 @@ const ProgressHistoryItem: React.FC<ProgressHistoryItemProps> = ({
140
140
  <TextField
141
141
  label='Count'
142
142
  value={count}
143
- onChange={(event) => onChangeCount(event.target.value)}
143
+ onChange={(event) => { onChangeCount(event.target.value); }}
144
144
  sx={{ maxWidth: '100px' }}
145
145
  error={!!error.count}
146
146
  helperText={error.count}
@@ -154,7 +154,7 @@ const ProgressHistoryItem: React.FC<ProgressHistoryItemProps> = ({
154
154
  inputMode: 'numeric',
155
155
  pattern: '[0-9]*',
156
156
  }}
157
- onChange={(event) => onChangeHours(event.target.value)}
157
+ onChange={(event) => { onChangeHours(event.target.value); }}
158
158
  sx={{ maxWidth: '100px' }}
159
159
  error={!!error.hours}
160
160
  helperText={error.hours}
@@ -167,7 +167,7 @@ const ProgressHistoryItem: React.FC<ProgressHistoryItemProps> = ({
167
167
  inputMode: 'numeric',
168
168
  pattern: '[0-9]*',
169
169
  }}
170
- onChange={(event) => onChangeMinutes(event.target.value)}
170
+ onChange={(event) => { onChangeMinutes(event.target.value); }}
171
171
  sx={{ maxWidth: '100px' }}
172
172
  error={!!error.minutes}
173
173
  helperText={error.minutes}
@@ -201,7 +201,7 @@ function getTimelineUpdate(items: HistoryItem[]): {
201
201
  return;
202
202
  }
203
203
 
204
- let itemErrors: HistoryItemError = {};
204
+ const itemErrors: HistoryItemError = {};
205
205
  if (item.date === null) {
206
206
  itemErrors.date = 'This field is required';
207
207
  }
@@ -357,20 +357,20 @@ const ProgressHistory: React.FC<ProgressHistoryProps> = ({
357
357
 
358
358
  const getUpdateItem = useCallback(
359
359
  (idx: number) => (item: HistoryItem) =>
360
- setItems((items) => [...items.slice(0, idx), item, ...items.slice(idx + 1)]),
360
+ { setItems((items) => [...items.slice(0, idx), item, ...items.slice(idx + 1)]); },
361
361
  [setItems],
362
362
  );
363
363
 
364
364
  const getDeleteItem = useCallback(
365
365
  (idx: number) => () =>
366
- setItems((items) => [
366
+ { setItems((items) => [
367
367
  ...items.slice(0, idx),
368
368
  {
369
369
  ...items[idx],
370
370
  deleted: true,
371
371
  },
372
372
  ...items.slice(idx + 1),
373
- ]),
373
+ ]); },
374
374
  [setItems],
375
375
  );
376
376
 
frontend/src/profile/progress/ProgressItem.tsx CHANGED
@@ -95,12 +95,12 @@ const RequirementProgressItem: React.FC<RequirementProgressItemProps> = ({
95
95
  return { isBlocked: false };
96
96
  }
97
97
 
98
- const requirementMap = requirements.reduce(
98
+ const requirementMap = requirements.reduce<Record<string, Requirement>>(
99
99
  (acc, r) => {
100
100
  acc[r.id] = r;
101
101
  return acc;
102
102
  },
103
- {} as Record<string, Requirement>,
103
+ {},
104
104
  );
105
105
  for (const blockerId of requirement.blockers) {
106
106
  const blocker = requirementMap[blockerId];
@@ -129,7 +129,7 @@ const RequirementProgressItem: React.FC<RequirementProgressItemProps> = ({
129
129
  <Checkbox
130
130
  aria-label={`Checkbox ${requirement.name}`}
131
131
  checked={currentCount >= totalCount}
132
- onClick={() => setShowUpdateDialog(true)}
132
+ onClick={() => { setShowUpdateDialog(true); }}
133
133
  disabled={!isCurrentUser}
134
134
  />
135
135
  );
@@ -149,11 +149,11 @@ const RequirementProgressItem: React.FC<RequirementProgressItemProps> = ({
149
149
  );
150
150
  UpdateElement =
151
151
  currentCount >= totalCount ? (
152
- <Checkbox checked onClick={() => setShowUpdateDialog(true)} />
152
+ <Checkbox checked onClick={() => { setShowUpdateDialog(true); }} />
153
153
  ) : !isCurrentUser ? null : (
154
154
  <IconButton
155
155
  aria-label={`Update ${requirement.name}`}
156
- onClick={() => setShowUpdateDialog(true)}
156
+ onClick={() => { setShowUpdateDialog(true); }}
157
157
  >
158
158
  <EditIcon />
159
159
  </IconButton>
@@ -164,7 +164,7 @@ const RequirementProgressItem: React.FC<RequirementProgressItemProps> = ({
164
164
  UpdateElement = (
165
165
  <IconButton
166
166
  aria-label={`Update ${requirement.name}`}
167
- onClick={() => setShowUpdateDialog(true)}
167
+ onClick={() => { setShowUpdateDialog(true); }}
168
168
  >
169
169
  <EditIcon />
170
170
  </IconButton>
@@ -187,7 +187,7 @@ const RequirementProgressItem: React.FC<RequirementProgressItemProps> = ({
187
187
  {showUpdateDialog && (
188
188
  <ProgressDialog
189
189
  open={showUpdateDialog}
190
- onClose={() => setShowUpdateDialog(false)}
190
+ onClose={() => { setShowUpdateDialog(false); }}
191
191
  requirement={requirement}
192
192
  cohort={cohort}
193
193
  progress={progress}
@@ -208,7 +208,7 @@ const RequirementProgressItem: React.FC<RequirementProgressItemProps> = ({
208
208
  ? 9
209
209
  : 10
210
210
  }
211
- onClick={() => setShowReqModal(true)}
211
+ onClick={() => { setShowReqModal(true); }}
212
212
  sx={{ cursor: 'pointer', position: 'relative' }}
213
213
  id='task-details'
214
214
  >
@@ -292,7 +292,7 @@ const RequirementProgressItem: React.FC<RequirementProgressItemProps> = ({
292
292
  {showReqModal && (
293
293
  <RequirementModal
294
294
  open={showReqModal}
295
- onClose={() => setShowReqModal(false)}
295
+ onClose={() => { setShowReqModal(false); }}
296
296
  requirement={requirement}
297
297
  />
298
298
  )}
frontend/src/profile/progress/ProgressTab.tsx CHANGED
@@ -32,7 +32,7 @@ function useHideCompleted(isCurrentUser: boolean) {
32
32
 
33
33
  interface Category {
34
34
  name: string;
35
- requirements: Array<Requirement | CustomTask>;
35
+ requirements: (Requirement | CustomTask)[];
36
36
  totalComplete: number;
37
37
  }
38
38
 
@@ -63,7 +63,7 @@ const ProgressTab: React.FC<ProgressTabProps> = ({ user, isCurrentUser }) => {
63
63
 
64
64
  const categories = useMemo(() => {
65
65
  const categories: Category[] = [];
66
- requirements?.forEach((r) => {
66
+ requirements.forEach((r) => {
67
67
  const c = categories.find((c) => c.name === r.category);
68
68
  const complete = isComplete(cohort, r, user.progress[r.id]);
69
69
  if (complete && hideCompleted === 'true') {
@@ -148,7 +148,7 @@ const ProgressTab: React.FC<ProgressTabProps> = ({ user, isCurrentUser }) => {
148
148
  select
149
149
  label='Cohort'
150
150
  value={cohort}
151
- onChange={(event) => onChangeCohort(event.target.value)}
151
+ onChange={(event) => { onChangeCohort(event.target.value); }}
152
152
  sx={{ mb: 3 }}
153
153
  fullWidth
154
154
  >
@@ -180,7 +180,7 @@ const ProgressTab: React.FC<ProgressTabProps> = ({ user, isCurrentUser }) => {
180
180
  <Checkbox
181
181
  size='small'
182
182
  checked={hideCompleted === 'true'}
183
- onChange={(e) => setHideCompleted(`${e.target.checked}`)}
183
+ onChange={(e) => { setHideCompleted(`${e.target.checked}`); }}
184
184
  />
185
185
  }
186
186
  label='Hide Completed Tasks'
@@ -214,7 +214,7 @@ const ProgressTab: React.FC<ProgressTabProps> = ({ user, isCurrentUser }) => {
214
214
 
215
215
  <CustomTaskEditor
216
216
  open={showCustomTaskEditor}
217
- onClose={() => setShowCustomTaskEditor(false)}
217
+ onClose={() => { setShowCustomTaskEditor(false); }}
218
218
  />
219
219
  </Stack>
220
220
  );
frontend/src/profile/progress/ProgressUpdater.tsx CHANGED
@@ -113,8 +113,8 @@ const ProgressUpdater: React.FC<ProgressUpdaterProps> = ({
113
113
  const isNonDojo = requirement.scoreboardDisplay === ScoreboardDisplay.NonDojo;
114
114
  const isMinutes = requirement.scoreboardDisplay === ScoreboardDisplay.Minutes;
115
115
 
116
- let hoursInt = parseInt(hours) || 0;
117
- let minutesInt = parseInt(minutes) || 0;
116
+ const hoursInt = parseInt(hours) || 0;
117
+ const minutesInt = parseInt(minutes) || 0;
118
118
  const totalTime = 60 * hoursInt + minutesInt + (progress?.minutesSpent[cohort] ?? 0);
119
119
  const addedTime = 60 * hoursInt + minutesInt;
120
120
 
@@ -198,7 +198,7 @@ const ProgressUpdater: React.FC<ProgressUpdaterProps> = ({
198
198
  <Checkbox
199
199
  checked={markComplete}
200
200
  onChange={(event) =>
201
- setMarkComplete(event.target.checked)
201
+ { setMarkComplete(event.target.checked); }
202
202
  }
203
203
  />
204
204
  }
@@ -212,7 +212,7 @@ const ProgressUpdater: React.FC<ProgressUpdaterProps> = ({
212
212
  multiline={true}
213
213
  maxRows={3}
214
214
  value={notes}
215
- onChange={(e) => setNotes(e.target.value)}
215
+ onChange={(e) => { setNotes(e.target.value); }}
216
216
  />
217
217
 
218
218
  <Stack spacing={2}>
@@ -238,7 +238,7 @@ const ProgressUpdater: React.FC<ProgressUpdaterProps> = ({
238
238
  inputMode: 'numeric',
239
239
  pattern: '[0-9]*',
240
240
  }}
241
- onChange={(event) => setHours(event.target.value)}
241
+ onChange={(event) => { setHours(event.target.value); }}
242
242
  error={!!errors.hours}
243
243
  helperText={errors.hours}
244
244
  fullWidth
@@ -252,7 +252,7 @@ const ProgressUpdater: React.FC<ProgressUpdaterProps> = ({
252
252
  inputMode: 'numeric',
253
253
  pattern: '[0-9]*',
254
254
  }}
255
- onChange={(event) => setMinutes(event.target.value)}
255
+ onChange={(event) => { setMinutes(event.target.value); }}
256
256
  error={!!errors.minutes}
257
257
  helperText={errors.minutes}
258
258
  fullWidth
frontend/src/profile/stats/RatingCard.tsx CHANGED
@@ -77,7 +77,7 @@ export function getChartData(
77
77
  let data = [];
78
78
 
79
79
  if (dates.length === ratingHistory.length) {
80
- data = ratingHistory?.map((r) => ({
80
+ data = ratingHistory.map((r) => ({
81
81
  date: new Date(r.date),
82
82
  rating: r.rating,
83
83
  }));
@@ -151,7 +151,7 @@ export const primaryAxis: AxisOptions<Datum> = {
151
151
  getValue: (datum) => datum.date,
152
152
  };
153
153
 
154
- export const secondaryAxes: Array<AxisOptions<Datum>> = [
154
+ export const secondaryAxes: AxisOptions<Datum>[] = [
155
155
  {
156
156
  scaleType: 'linear',
157
157
  getValue: (datum) => datum.rating,
frontend/src/profile/yearReview/DojoPointSection.tsx CHANGED
@@ -19,7 +19,7 @@ export const primaryAxis: AxisOptions<Datum> = {
19
19
  getValue: (datum) => datum.primary,
20
20
  };
21
21
 
22
- export const secondaryAxes: Array<AxisOptions<Datum>> = [
22
+ export const secondaryAxes: AxisOptions<Datum>[] = [
23
23
  {
24
24
  position: 'bottom',
25
25
  getValue: (datum) => datum.secondary,
@@ -42,7 +42,7 @@ export function getCategoryData(
42
42
  label,
43
43
  data: categories.reverse().map((category) => ({
44
44
  primary: category,
45
- secondary: data.byCategory?.[category] || 0,
45
+ secondary: data.byCategory[category] || 0,
46
46
  })),
47
47
  },
48
48
  ];
@@ -71,7 +71,7 @@ export function getMonthData(label: string, data: YearReviewDataSection) {
71
71
  .sort((lhs, rhs) => rhs[1].localeCompare(lhs[1]))
72
72
  .map((month) => ({
73
73
  primary: month[0],
74
- secondary: data.byPeriod?.[month[1]] || 0,
74
+ secondary: data.byPeriod[month[1]] || 0,
75
75
  })),
76
76
  },
77
77
  ];
frontend/src/profile/yearReview/TimeSection.tsx CHANGED
@@ -16,7 +16,7 @@ import { formatTime } from '../../database/requirement';
16
16
  import { CategoryColors } from '../activity/activity';
17
17
  import Percentiles from './Percentiles';
18
18
 
19
- const secondaryAxes: Array<AxisOptions<Datum>> = [
19
+ const secondaryAxes: AxisOptions<Datum>[] = [
20
20
  {
21
21
  position: 'bottom',
22
22
  getValue: (datum) => datum.secondary,
frontend/src/profile/yearReview/YearReviewPage.tsx CHANGED
@@ -19,10 +19,10 @@ export interface SectionProps {
19
19
  review: YearReview;
20
20
  }
21
21
 
22
- type YearReviewPageParams = {
22
+ interface YearReviewPageParams {
23
23
  username: string;
24
24
  year: string;
25
- };
25
+ }
26
26
 
27
27
  const YearReviewPage = () => {
28
28
  const { username, year } = useParams<YearReviewPageParams>();
frontend/src/react-app-env.d.ts CHANGED
@@ -51,17 +51,17 @@ declare module "*.svg" {
51
51
  }
52
52
 
53
53
  declare module "*.module.css" {
54
- const classes: { readonly [key: string]: string };
54
+ const classes: Readonly<Record<string, string>>;
55
55
  export default classes;
56
56
  }
57
57
 
58
58
  declare module "*.module.scss" {
59
- const classes: { readonly [key: string]: string };
59
+ const classes: Readonly<Record<string, string>>;
60
60
  export default classes;
61
61
  }
62
62
 
63
63
  declare module "*.module.sass" {
64
- const classes: { readonly [key: string]: string };
64
+ const classes: Readonly<Record<string, string>>;
65
65
  export default classes;
66
66
  }
67
67
 
frontend/src/recent/FeaturedGames.tsx CHANGED
@@ -18,7 +18,7 @@ const FeaturedGames = () => {
18
18
  if (!request.isSent()) {
19
19
  request.onStart();
20
20
  api.listFeaturedGames()
21
- .then((games) => request.onSuccess(games))
21
+ .then((games) => { request.onSuccess(games); })
22
22
  .catch((err) => {
23
23
  console.error('listFeaturedGames: ', err);
24
24
  request.onFailure(err);
frontend/src/recent/RecentGraduates.tsx CHANGED
@@ -54,7 +54,7 @@ function getTimeframeOptions() {
54
54
  const options: TimeframeOption[] = [];
55
55
 
56
56
  for (let i = 0; i < numberOfOptions; i++) {
57
- let prevGraduation = new Date(currGraduation);
57
+ const prevGraduation = new Date(currGraduation);
58
58
  prevGraduation.setUTCDate(prevGraduation.getUTCDate() - 7);
59
59
 
60
60
  options.push({
@@ -186,7 +186,7 @@ const RecentGraduates = () => {
186
186
  if (!request.isSent()) {
187
187
  request.onStart();
188
188
  api.listGraduationsByDate()
189
- .then((graduations) => request.onSuccess(graduations))
189
+ .then((graduations) => { request.onSuccess(graduations); })
190
190
  .catch((err) => {
191
191
  console.error('listGraduationsByDate: ', err);
192
192
  request.onFailure(err);
@@ -218,7 +218,7 @@ const RecentGraduates = () => {
218
218
  >
219
219
  <Select
220
220
  value={timeframe}
221
- onChange={(e) => setTimeframe(e.target.value as Timeframe)}
221
+ onChange={(e) => { setTimeframe(e.target.value as Timeframe); }}
222
222
  sx={{
223
223
  '::before': {
224
224
  border: 'none !important',
frontend/src/requirements/CustomTaskDisplay.tsx CHANGED
@@ -37,14 +37,14 @@ const CustomTaskDisplay: React.FC<CustomTaskDisplayProps> = ({ task, onClose })
37
37
  <Stack direction='row' spacing={2}>
38
38
  <Button
39
39
  variant='contained'
40
- onClick={() => setShowEditor(true)}
40
+ onClick={() => { setShowEditor(true); }}
41
41
  >
42
42
  Edit Task
43
43
  </Button>
44
44
  <Button
45
45
  variant='contained'
46
46
  color='error'
47
- onClick={() => setShowDeleter(true)}
47
+ onClick={() => { setShowDeleter(true); }}
48
48
  >
49
49
  Delete Task
50
50
  </Button>
@@ -59,14 +59,14 @@ const CustomTaskDisplay: React.FC<CustomTaskDisplayProps> = ({ task, onClose })
59
59
 
60
60
  <CustomTaskEditor
61
61
  open={showEditor}
62
- onClose={() => setShowEditor(false)}
62
+ onClose={() => { setShowEditor(false); }}
63
63
  task={task}
64
64
  />
65
65
 
66
66
  <DeleteCustomTaskModal
67
67
  task={task}
68
68
  open={showDeleter}
69
- onCancel={() => setShowDeleter(false)}
69
+ onCancel={() => { setShowDeleter(false); }}
70
70
  onDelete={onClose}
71
71
  />
72
72
  </>
frontend/src/requirements/Position.tsx CHANGED
@@ -59,7 +59,7 @@ const Position: React.FC<PositionProps> = ({ position, orientation }) => {
59
59
  'clock.limit': position.limitSeconds,
60
60
  'clock.increment': position.incrementSeconds,
61
61
  fen: position.fen.trim(),
62
- name: `${position.title}`,
62
+ name: position.title,
63
63
  })
64
64
  .then((resp) => {
65
65
  console.log('Generate Lichess URL: ', resp);
frontend/src/requirements/RequirementDisplay.tsx CHANGED
@@ -43,7 +43,7 @@ function dojoPointDescription(requirement: Requirement, cohort: string) {
43
43
  unit = 'percentage';
44
44
  } else if (requirement.progressBarSuffix) {
45
45
  unit = requirement.progressBarSuffix.toLowerCase();
46
- if (unit[unit.length - 1] === 's') {
46
+ if (unit.endsWith('s')) {
47
47
  unit = unit.substring(0, unit.length - 1);
48
48
  }
49
49
  }
@@ -133,12 +133,12 @@ const RepeatChip: React.FC<{ requirement: Requirement }> = ({ requirement }) =>
133
133
  const BlockerChips: React.FC<{ requirement: Requirement }> = ({ requirement }) => {
134
134
  const { requirements } = useRequirements(ALL_COHORTS, false);
135
135
  const requirementMap = useMemo(() => {
136
- return requirements.reduce(
136
+ return requirements.reduce<Record<string, Requirement>>(
137
137
  (acc, r) => {
138
138
  acc[r.id] = r;
139
139
  return acc;
140
140
  },
141
- {} as Record<string, Requirement>,
141
+ {},
142
142
  );
143
143
  }, [requirements]);
144
144
 
@@ -203,12 +203,12 @@ const RequirementDisplay: React.FC<RequirementDisplayProps> = ({
203
203
  return { isBlocked: false };
204
204
  }
205
205
 
206
- const requirementMap = requirements.reduce(
206
+ const requirementMap = requirements.reduce<Record<string, Requirement>>(
207
207
  (acc, r) => {
208
208
  acc[r.id] = r;
209
209
  return acc;
210
210
  },
211
- {} as Record<string, Requirement>,
211
+ {},
212
212
  );
213
213
  for (const blockerId of requirement.blockers) {
214
214
  const blocker = requirementMap[blockerId];
@@ -229,7 +229,7 @@ const RequirementDisplay: React.FC<RequirementDisplayProps> = ({
229
229
  const progress = user.progress[requirement.id];
230
230
 
231
231
  const totalCount = requirement.counts[cohort] || requirement.counts[ALL_COHORTS];
232
- const currentCount = progress?.counts[cohort] || progress?.counts[ALL_COHORTS] || 0;
232
+ const currentCount = progress.counts[cohort] || progress.counts[ALL_COHORTS] || 0;
233
233
  const isCompleted = currentCount >= totalCount;
234
234
 
235
235
  let requirementName = requirement.name;
@@ -263,12 +263,12 @@ const RequirementDisplay: React.FC<RequirementDisplayProps> = ({
263
263
  icon={<CheckIcon />}
264
264
  label='Completed'
265
265
  color='success'
266
- onClick={() => setShowUpdateDialog(true)}
266
+ onClick={() => { setShowUpdateDialog(true); }}
267
267
  />
268
268
  ) : (
269
269
  <Button
270
270
  variant='contained'
271
- onClick={() => setShowUpdateDialog(true)}
271
+ onClick={() => { setShowUpdateDialog(true); }}
272
272
  >
273
273
  Update Progress
274
274
  </Button>
@@ -303,8 +303,7 @@ const RequirementDisplay: React.FC<RequirementDisplayProps> = ({
303
303
  </Grid>
304
304
  )}
305
305
 
306
- {requirement.videoUrls &&
307
- requirement.videoUrls.map((url, idx) => (
306
+ {requirement.videoUrls?.map((url, idx) => (
308
307
  <Box sx={{ mt: 3, width: 1, aspectRatio: '1.77' }} key={url}>
309
308
  <iframe
310
309
  src={url}
@@ -320,7 +319,7 @@ const RequirementDisplay: React.FC<RequirementDisplayProps> = ({
320
319
 
321
320
  <ProgressDialog
322
321
  open={showUpdateDialog}
323
- onClose={() => setShowUpdateDialog(false)}
322
+ onClose={() => { setShowUpdateDialog(false); }}
324
323
  requirement={requirement}
325
324
  cohort={user.dojoCohort}
326
325
  progress={progress}
frontend/src/requirements/RequirementPage.tsx CHANGED
@@ -9,9 +9,9 @@ import LoadingPage from '../loading/LoadingPage';
9
9
  import NotFoundPage from '../NotFoundPage';
10
10
  import RequirementDisplay from './RequirementDisplay';
11
11
 
12
- type RequirementPageProps = {
12
+ interface RequirementPageProps {
13
13
  id: string;
14
- };
14
+ }
15
15
 
16
16
  const RequirementPage = () => {
17
17
  const { id } = useParams<RequirementPageProps>();
frontend/src/scoreboard/Scoreboard.tsx CHANGED
@@ -316,9 +316,9 @@ function getActionColumns(
316
316
  </Tooltip>
317
317
  }
318
318
  onClick={() =>
319
- setPinnedRowIds((prevPinnedRowIds) => {
319
+ { setPinnedRowIds((prevPinnedRowIds) => {
320
320
  return prevPinnedRowIds.filter((rowId) => rowId !== id);
321
- })
321
+ }); }
322
322
  }
323
323
  />,
324
324
  ];
@@ -332,7 +332,7 @@ function getActionColumns(
332
332
  }
333
333
  label='Pin Row'
334
334
  onClick={() =>
335
- setPinnedRowIds((prevPinnedRowIds) => [...prevPinnedRowIds, id])
335
+ { setPinnedRowIds((prevPinnedRowIds) => [...prevPinnedRowIds, id]); }
336
336
  }
337
337
  />,
338
338
  ];
frontend/src/scoreboard/ScoreboardCheck.tsx CHANGED
@@ -26,7 +26,7 @@ const ScoreboardCheck: React.FC<ScoreboardCheckProps> = ({
26
26
  const user = useAuth().user!;
27
27
 
28
28
  const canUpdate = requirement && user.username === username;
29
- const onClick = canUpdate ? () => setShowUpdateDialog(true) : undefined;
29
+ const onClick = canUpdate ? () => { setShowUpdateDialog(true); } : undefined;
30
30
 
31
31
  return (
32
32
  <>
@@ -43,7 +43,7 @@ const ScoreboardCheck: React.FC<ScoreboardCheckProps> = ({
43
43
  {canUpdate && showUpdateDialog && (
44
44
  <ProgressDialog
45
45
  open={showUpdateDialog}
46
- onClose={() => setShowUpdateDialog(false)}
46
+ onClose={() => { setShowUpdateDialog(false); }}
47
47
  requirement={requirement}
48
48
  cohort={cohort}
49
49
  progress={user.progress[requirement.id]}
frontend/src/scoreboard/ScoreboardPage.tsx CHANGED
@@ -16,9 +16,9 @@ import { ScoreboardRow } from './scoreboardData';
16
16
  import ScoreboardTutorial from './ScoreboardTutorial';
17
17
  import ScoreboardViewSelector from './ScoreboardViewSelector';
18
18
 
19
- type ScoreboardPageParams = {
19
+ interface ScoreboardPageParams {
20
20
  type: string;
21
- };
21
+ }
22
22
 
23
23
  const ScoreboardPage = () => {
24
24
  const user = useAuth().user!;
frontend/src/scoreboard/ScoreboardProgress.tsx CHANGED
@@ -34,7 +34,7 @@ const ScoreboardProgress: React.FC<LinearProgressProps & ScoreboardProgressProps
34
34
  const user = useAuth().user!;
35
35
 
36
36
  const canUpdate = requirement && cohort && user.username === username;
37
- const onClick = canUpdate ? () => setShowUpdateDialog(true) : undefined;
37
+ const onClick = canUpdate ? () => { setShowUpdateDialog(true); } : undefined;
38
38
 
39
39
  const normalized = ((value - min) * 100) / (max - min);
40
40
  const displayValue = Math.min(Math.max(normalized, 0), 100);
@@ -71,7 +71,7 @@ const ScoreboardProgress: React.FC<LinearProgressProps & ScoreboardProgressProps
71
71
  {canUpdate && showUpdateDialog && (
72
72
  <ProgressDialog
73
73
  open={showUpdateDialog}
74
- onClose={() => setShowUpdateDialog(false)}
74
+ onClose={() => { setShowUpdateDialog(false); }}
75
75
  requirement={requirement}
76
76
  cohort={cohort}
77
77
  progress={user.progress[requirement.id]}
frontend/src/scoreboard/ScoreboardViewSelector.tsx CHANGED
@@ -49,9 +49,9 @@ const ScoreboardViewSelector: React.FC<ScoreboardViewSelectorProps> = ({
49
49
  label='View'
50
50
  value={value}
51
51
  onChange={(event) =>
52
- onChange
52
+ { onChange
53
53
  ? onChange(event.target.value)
54
- : defaultOnChange(event.target.value)
54
+ : defaultOnChange(event.target.value); }
55
55
  }
56
56
  sx={{ mb: 3 }}
57
57
  fullWidth
frontend/src/scoreboard/club/ClubScoreboardPage.tsx CHANGED
@@ -10,9 +10,9 @@ import LoadingPage from '../../loading/LoadingPage';
10
10
  import Scoreboard from '../Scoreboard';
11
11
  import ScoreboardViewSelector from '../ScoreboardViewSelector';
12
12
 
13
- export type ClubScoreboardPageParams = {
13
+ export interface ClubScoreboardPageParams {
14
14
  id: string;
15
- };
15
+ }
16
16
 
17
17
  const ClubScoreboardPage = () => {
18
18
  const { id } = useParams<ClubScoreboardPageParams>();
frontend/src/scoreboard/scoreboardData.tsx CHANGED
@@ -45,10 +45,10 @@ const Header: React.FC<HeaderProps> = ({ requirement, cohort }) => {
45
45
 
46
46
  return (
47
47
  <>
48
- <div onClick={() => setShowReqModal(true)}>{headerName}</div>
48
+ <div onClick={() => { setShowReqModal(true); }}>{headerName}</div>
49
49
  <RequirementModal
50
50
  open={showReqModal}
51
- onClose={() => setShowReqModal(false)}
51
+ onClose={() => { setShowReqModal(false); }}
52
52
  requirement={requirement}
53
53
  />
54
54
  </>
@@ -228,7 +228,7 @@ export function getRatingSystem(params: GridValueGetterParams<ScoreboardRow>) {
228
228
  !isGraduation(params.row) &&
229
229
  params.row.ratings[RatingSystem.Custom]?.name
230
230
  ) {
231
- return `Custom (${params.row.ratings[RatingSystem.Custom]?.name})`;
231
+ return `Custom (${params.row.ratings[RatingSystem.Custom].name})`;
232
232
  }
233
233
  return formatRatingSystem(params.row.ratingSystem);
234
234
  }
frontend/src/scoreboard/search/SeachPage.tsx CHANGED
@@ -28,13 +28,13 @@ const AllColumns: GridColDef[] = [
28
28
  {
29
29
  field: 'dojoCohort',
30
30
  headerName: 'Cohort',
31
- valueGetter: (params: GridValueGetterParams<User, any>) => params.row.dojoCohort,
31
+ valueGetter: (params: GridValueGetterParams<User>) => params.row.dojoCohort,
32
32
  minWidth: 125,
33
33
  },
34
34
  {
35
35
  field: 'display',
36
36
  headerName: 'Display Name',
37
- valueGetter: (params: GridValueGetterParams<User, any>) => params.row.displayName,
37
+ valueGetter: (params: GridValueGetterParams<User>) => params.row.displayName,
38
38
  renderCell: (params: GridRenderCellParams<User, string>) => {
39
39
  return (
40
40
  <Stack direction='row' spacing={1} alignItems='center'>
@@ -55,14 +55,14 @@ const AllColumns: GridColDef[] = [
55
55
  {
56
56
  field: 'discord',
57
57
  headerName: 'Discord Username',
58
- valueGetter: (params: GridValueGetterParams<User, any>) =>
58
+ valueGetter: (params: GridValueGetterParams<User>) =>
59
59
  params.row.discordUsername,
60
60
  flex: 1,
61
61
  },
62
62
  {
63
63
  field: RatingSystem.Chesscom,
64
64
  headerName: 'Chess.com Username',
65
- valueGetter: (params: GridValueGetterParams<User, any>) =>
65
+ valueGetter: (params: GridValueGetterParams<User>) =>
66
66
  params.row.ratings[RatingSystem.Chesscom]?.username,
67
67
  flex: 1,
68
68
  minWidth: 175,
@@ -70,49 +70,49 @@ const AllColumns: GridColDef[] = [
70
70
  {
71
71
  field: RatingSystem.Lichess,
72
72
  headerName: 'Lichess Username',
73
- valueGetter: (params: GridValueGetterParams<User, any>) =>
73
+ valueGetter: (params: GridValueGetterParams<User>) =>
74
74
  params.row.ratings[RatingSystem.Lichess]?.username,
75
75
  flex: 1,
76
76
  },
77
77
  {
78
78
  field: RatingSystem.Fide,
79
79
  headerName: 'FIDE ID',
80
- valueGetter: (params: GridValueGetterParams<User, any>) =>
80
+ valueGetter: (params: GridValueGetterParams<User>) =>
81
81
  params.row.ratings[RatingSystem.Fide]?.username,
82
82
  flex: 1,
83
83
  },
84
84
  {
85
85
  field: RatingSystem.Uscf,
86
86
  headerName: 'USCF ID',
87
- valueGetter: (params: GridValueGetterParams<User, any>) =>
87
+ valueGetter: (params: GridValueGetterParams<User>) =>
88
88
  params.row.ratings[RatingSystem.Uscf]?.username,
89
89
  flex: 1,
90
90
  },
91
91
  {
92
92
  field: RatingSystem.Cfc,
93
93
  headerName: 'CFC ID',
94
- valueGetter: (params: GridValueGetterParams<User, any>) =>
94
+ valueGetter: (params: GridValueGetterParams<User>) =>
95
95
  params.row.ratings[RatingSystem.Cfc]?.username,
96
96
  flex: 1,
97
97
  },
98
98
  {
99
99
  field: RatingSystem.Ecf,
100
100
  headerName: 'ECF ID',
101
- valueGetter: (params: GridValueGetterParams<User, any>) =>
101
+ valueGetter: (params: GridValueGetterParams<User>) =>
102
102
  params.row.ratings[RatingSystem.Ecf]?.username,
103
103
  flex: 1,
104
104
  },
105
105
  {
106
106
  field: RatingSystem.Dwz,
107
107
  headerName: 'DWZ ID',
108
- valueGetter: (params: GridValueGetterParams<User, any>) =>
108
+ valueGetter: (params: GridValueGetterParams<User>) =>
109
109
  params.row.ratings[RatingSystem.Dwz]?.username,
110
110
  flex: 1,
111
111
  },
112
112
  {
113
113
  field: RatingSystem.Acf,
114
114
  headerName: 'ACF ID',
115
- valueGetter: (params: GridValueGetterParams<User, any>) =>
115
+ valueGetter: (params: GridValueGetterParams<User>) =>
116
116
  params.row.ratings[RatingSystem.Acf]?.username,
117
117
  flex: 1,
118
118
  },
@@ -155,7 +155,7 @@ function useDebounce(
155
155
 
156
156
  useEffect(() => {
157
157
  const timeout = setTimeout(callback, delay);
158
- return () => clearTimeout(timeout);
158
+ return () => { clearTimeout(timeout); };
159
159
  }, [callback, delay]);
160
160
  }
161
161
 
@@ -166,10 +166,10 @@ const SearchPage = () => {
166
166
  const [query, setQuery] = useState('');
167
167
  const [allFields, setAllFields] = useState(true);
168
168
  const [fields, setFields] = useState<Record<string, boolean>>(
169
- SearchFields.reduce((map, field) => {
169
+ SearchFields.reduce<Record<string, boolean>>((map, field) => {
170
170
  map[field] = false;
171
171
  return map;
172
- }, {} as Record<string, boolean>)
172
+ }, {})
173
173
  );
174
174
  const [errors, setErrors] = useState<Record<string, string>>({});
175
175
  const [columns, setColumns] = useState(AllColumns);
@@ -237,7 +237,7 @@ const SearchPage = () => {
237
237
  data-cy='search-query'
238
238
  label='Search Query'
239
239
  value={query}
240
- onChange={(e) => setQuery(e.target.value)}
240
+ onChange={(e) => { setQuery(e.target.value); }}
241
241
  fullWidth
242
242
  error={!!errors.query}
243
243
  helperText={errors.query}
@@ -254,7 +254,7 @@ const SearchPage = () => {
254
254
  <Checkbox
255
255
  checked={allFields}
256
256
  onChange={(event) =>
257
- setAllFields(event.target.checked)
257
+ { setAllFields(event.target.checked); }
258
258
  }
259
259
  />
260
260
  }
@@ -276,10 +276,10 @@ const SearchPage = () => {
276
276
  <Checkbox
277
277
  checked={allFields || fields[field]}
278
278
  onChange={(event) =>
279
- onChangeField(
279
+ { onChangeField(
280
280
  field,
281
281
  event.target.checked
282
- )
282
+ ); }
283
283
  }
284
284
  />
285
285
  }
frontend/src/style/style.ts CHANGED
@@ -20,13 +20,13 @@ function stringToColor(string: string) {
20
20
  return color;
21
21
  }
22
22
 
23
- export type SxSize = {
23
+ export interface SxSize {
24
24
  xs?: string;
25
25
  sm?: string;
26
26
  md?: string;
27
27
  lg?: string;
28
28
  xl?: string;
29
- };
29
+ }
30
30
 
31
31
  /**
32
32
  * Returns the props for a MUI Avatar with the given size and name.
@@ -37,12 +37,12 @@ export type SxSize = {
37
37
  export function avatarProps(name: string, size: number | SxSize = 74, fontSize?: SxSize) {
38
38
  let uppercaseLetters = name.replace(/[a-z]/g, '').slice(0, 3);
39
39
 
40
- let tokens = name.split(' ');
40
+ const tokens = name.split(' ');
41
41
  if (tokens.length > 1) {
42
42
  uppercaseLetters = tokens
43
43
  .slice(0, 3)
44
44
  .map((v) => v[0])
45
- .map((v) => v?.toLocaleUpperCase() || '')
45
+ .map((v) => v.toLocaleUpperCase() || '')
46
46
  .join('');
47
47
  }
48
48
 
frontend/src/tactics/CompletedTacticsExamPgnSelector.tsx CHANGED
@@ -59,7 +59,7 @@ const CompletedTacticsExamPgnSelector: React.FC<CompletedTacticsExamPgnSelectorP
59
59
  <TextField
60
60
  select
61
61
  value={attempt}
62
- onChange={(e) => selectAttempt(parseInt(e.target.value))}
62
+ onChange={(e) => { selectAttempt(parseInt(e.target.value)); }}
63
63
  fullWidth
64
64
  size='small'
65
65
  sx={{ mb: 1 }}
@@ -84,7 +84,7 @@ const CompletedTacticsExamPgnSelector: React.FC<CompletedTacticsExamPgnSelectorP
84
84
  <ListItem key={i} disablePadding>
85
85
  <ListItemButton
86
86
  selected={i === selected}
87
- onClick={() => onSelect(i)}
87
+ onClick={() => { onSelect(i); }}
88
88
  >
89
89
  <ListItemIcon sx={{ minWidth: '40px' }}>
90
90
  <Stack alignItems='center' width={1}>
frontend/src/tactics/ExamStatistics.tsx CHANGED
@@ -61,7 +61,7 @@ const ExamStatistics: React.FC<ExamStatisticsProps> = ({ exam }) => {
61
61
  valueFormatter: (value) => `Score: ${value.x}, Rating: ${value.y}`,
62
62
  color: cohortColors[answer.cohort],
63
63
  };
64
- series.data?.push({ x: answer.score, y: answer.rating, id: username });
64
+ series.data.push({ x: answer.score, y: answer.rating, id: username });
65
65
  cohortToSeries[answer.cohort] = series;
66
66
  });
67
67
 
@@ -119,7 +119,7 @@ const ExamStatistics: React.FC<ExamStatisticsProps> = ({ exam }) => {
119
119
  });
120
120
  observer.observe(ref.current);
121
121
 
122
- return () => observer.disconnect();
122
+ return () => { observer.disconnect(); };
123
123
  }, [ref, series, setLegendMargin]);
124
124
 
125
125
  const onChangeCohort = (newCohorts: string[]) => {
frontend/src/tactics/TacticsExamPage.tsx CHANGED
@@ -84,7 +84,7 @@ const TacticsExamPage = () => {
84
84
  const [showRetakeDialog, setShowRetakeDialog] = useState(false);
85
85
  const [showLatestAttempt, setShowLatestAttempt] = useState(false);
86
86
 
87
- const hasTakenExam = Boolean(exam?.answers[user.username]);
87
+ const hasTakenExam = Boolean(exam.answers[user.username]);
88
88
 
89
89
  useEffect(() => {
90
90
  if (!answerRequest.isSent() && exam && hasTakenExam) {
@@ -128,13 +128,13 @@ const TacticsExamPage = () => {
128
128
  <CompletedTacticsExam
129
129
  exam={exam}
130
130
  answerRequest={answerRequest}
131
- onReset={() => setShowRetakeDialog(true)}
131
+ onReset={() => { setShowRetakeDialog(true); }}
132
132
  resetLabel='Retake Test'
133
133
  showLatestAttempt={showLatestAttempt}
134
134
  />
135
135
  <Dialog
136
136
  open={showRetakeDialog}
137
- onClose={() => setShowRetakeDialog(false)}
137
+ onClose={() => { setShowRetakeDialog(false); }}
138
138
  >
139
139
  <DialogTitle>Retake this test?</DialogTitle>
140
140
  <DialogContent>
@@ -145,7 +145,7 @@ const TacticsExamPage = () => {
145
145
  </DialogContentText>
146
146
  </DialogContent>
147
147
  <DialogActions>
148
- <Button onClick={() => setShowRetakeDialog(false)}>Cancel</Button>
148
+ <Button onClick={() => { setShowRetakeDialog(false); }}>Cancel</Button>
149
149
  <Button onClick={onRetake}>Retake</Button>
150
150
  </DialogActions>
151
151
  </Dialog>
@@ -186,7 +186,7 @@ export const InProgressTacticsExam: React.FC<InProgressTacticsExamProps> = ({
186
186
  const api = useApi();
187
187
  const pgnApi = useRef<PgnBoardApi>(null);
188
188
  const [selectedProblem, setSelectedProblem] = useState(0);
189
- const answerPgns = useRef<string[]>((exam?.pgns || []).map(() => ''));
189
+ const answerPgns = useRef<string[]>((exam.pgns || []).map(() => ''));
190
190
  const [isTimeOver, setIsTimeOver] = useState(false);
191
191
  const [problemStatus, setProblemStatus] = useState<Record<number, ProblemStatus>>({});
192
192
 
@@ -198,9 +198,9 @@ export const InProgressTacticsExam: React.FC<InProgressTacticsExamProps> = ({
198
198
  isPlaying: !disableClock,
199
199
  size: 80,
200
200
  strokeWidth: 6,
201
- duration: exam?.timeLimitSeconds || 3600,
201
+ duration: exam.timeLimitSeconds || 3600,
202
202
  colors: ['#66bb6a', '#29b6f6', '#ce93d8', '#ffa726', '#f44336'],
203
- colorsTime: getColorsTime(exam?.timeLimitSeconds),
203
+ colorsTime: getColorsTime(exam.timeLimitSeconds),
204
204
  trailColor: 'rgba(0,0,0,0)',
205
205
  onComplete: onCountdownComplete,
206
206
  });
frontend/src/tactics/TacticsExamPgnSelector.tsx CHANGED
@@ -93,7 +93,7 @@ const TacticsExamPgnSelector: React.FC<TacticsExamPgnSelectorProps> = ({
93
93
  justifyContent='center'
94
94
  >
95
95
  <CountdownTimer {...countdown} />
96
- <Button variant='contained' onClick={() => setIsFinishEarly(true)}>
96
+ <Button variant='contained' onClick={() => { setIsFinishEarly(true); }}>
97
97
  Finish Early
98
98
  </Button>
99
99
  </Stack>
@@ -103,8 +103,8 @@ const TacticsExamPgnSelector: React.FC<TacticsExamPgnSelectorProps> = ({
103
103
  <ListItem key={i} disablePadding>
104
104
  <ListItemButton
105
105
  selected={i === selected}
106
- onClick={() => onSelect(i)}
107
- onContextMenu={(e) => handleOpenStatusMenu(i, e)}
106
+ onClick={() => { onSelect(i); }}
107
+ onContextMenu={(e) => { handleOpenStatusMenu(i, e); }}
108
108
  >
109
109
  <ListItemIcon sx={{ minWidth: '40px' }}>
110
110
  <Stack alignItems='center' width={1}>
@@ -151,7 +151,7 @@ const TacticsExamPgnSelector: React.FC<TacticsExamPgnSelectorProps> = ({
151
151
 
152
152
  <Dialog
153
153
  open={isFinishEarly}
154
- onClose={() => setIsFinishEarly(false)}
154
+ onClose={() => { setIsFinishEarly(false); }}
155
155
  classes={{
156
156
  container: BlockBoardKeyboardShortcuts,
157
157
  }}
@@ -166,7 +166,7 @@ const TacticsExamPgnSelector: React.FC<TacticsExamPgnSelectorProps> = ({
166
166
  </DialogContentText>
167
167
  </DialogContent>
168
168
  <DialogActions>
169
- <Button onClick={() => setIsFinishEarly(false)}>Cancel</Button>
169
+ <Button onClick={() => { setIsFinishEarly(false); }}>Cancel</Button>
170
170
  <Button onClick={onComplete}>Finish</Button>
171
171
  </DialogActions>
172
172
  </Dialog>
@@ -185,7 +185,7 @@ const TacticsExamPgnSelector: React.FC<TacticsExamPgnSelectorProps> = ({
185
185
  }}
186
186
  >
187
187
  <MenuItem
188
- onClick={() => markStatus(ProblemStatus.Complete)}
188
+ onClick={() => { markStatus(ProblemStatus.Complete); }}
189
189
  disabled={
190
190
  problemStatus?.[openStatusProblem] === ProblemStatus.Complete
191
191
  }
@@ -193,7 +193,7 @@ const TacticsExamPgnSelector: React.FC<TacticsExamPgnSelectorProps> = ({
193
193
  Mark as Completed
194
194
  </MenuItem>
195
195
  <MenuItem
196
- onClick={() => markStatus(ProblemStatus.NeedsReview)}
196
+ onClick={() => { markStatus(ProblemStatus.NeedsReview); }}
197
197
  disabled={
198
198
  problemStatus?.[openStatusProblem] === ProblemStatus.NeedsReview
199
199
  }
@@ -201,7 +201,7 @@ const TacticsExamPgnSelector: React.FC<TacticsExamPgnSelectorProps> = ({
201
201
  Mark as Needs Review
202
202
  </MenuItem>
203
203
  <MenuItem
204
- onClick={() => markStatus(ProblemStatus.Unknown)}
204
+ onClick={() => { markStatus(ProblemStatus.Unknown); }}
205
205
  disabled={!problemStatus?.[openStatusProblem]}
206
206
  >
207
207
  Clear Status
frontend/src/tactics/instructions/TacticsInstructionsPage.tsx CHANGED
@@ -14,7 +14,7 @@ const TacticsInstructionsPage = () => {
14
14
  navigate('/tactics/exam', { state: locationState });
15
15
  };
16
16
 
17
- if (!locationState || !locationState.exam) {
17
+ if (!locationState?.exam) {
18
18
  return <Navigate to='/tactics/' />;
19
19
  }
20
20
 
frontend/src/tactics/list/ExamsTable.tsx CHANGED
@@ -148,7 +148,7 @@ const ExamsTable = ({ exams }: { exams: Exam[] }) => {
148
148
  if (
149
149
  !hasAnswered &&
150
150
  i >= 1 &&
151
- !Boolean(exams[i - 1].answers[user?.username || ''])
151
+ !exams[i - 1].answers[user?.username || '']
152
152
  ) {
153
153
  return (
154
154
  <Tooltip title='This exam is locked until you complete the previous exam'>
@@ -255,7 +255,7 @@ const ExamsTable = ({ exams }: { exams: Exam[] }) => {
255
255
  }
256
256
 
257
257
  const i = exams.findIndex((e) => e.id === params.row.id);
258
- if (i >= 1 && !Boolean(exams[i - 1].answers[user?.username || ''])) {
258
+ if (i >= 1 && !exams[i - 1].answers[user?.username || '']) {
259
259
  setSnackbarOpen(true);
260
260
  } else if (i >= 1 && isFreeTier) {
261
261
  setUpsellOpen(true);
@@ -296,7 +296,7 @@ const ExamsTable = ({ exams }: { exams: Exam[] }) => {
296
296
  </Snackbar>
297
297
  <UpsellDialog
298
298
  open={upsellOpen}
299
- onClose={() => setUpsellOpen(false)}
299
+ onClose={() => { setUpsellOpen(false); }}
300
300
  currentAction={RestrictedAction.TacticsExams}
301
301
  />
302
302
  </>
frontend/src/tactics/list/ListTacticsExamsPage.tsx CHANGED
@@ -113,7 +113,7 @@ const ExamListSection: React.FC<ExamListSectionProps> = ({
113
113
  <Stack>
114
114
  <Stack spacing={1} direction='row' alignItems='center'>
115
115
  <Tooltip title={expanded ? 'Collapse Section' : 'Expand Section'}>
116
- <IconButton onClick={() => setExpanded(!expanded)}>
116
+ <IconButton onClick={() => { setExpanded(!expanded); }}>
117
117
  {expanded ? <ExpandLess /> : <ExpandMore />}
118
118
  </IconButton>
119
119
  </Tooltip>
frontend/src/tactics/tactics.ts CHANGED
@@ -113,10 +113,10 @@ export function getSolutionScore(
113
113
  ): number {
114
114
  let score = 0;
115
115
 
116
- for (let move of solution) {
116
+ for (const move of solution) {
117
117
  // Recursively check variations
118
118
  if (move.variations.length > 0) {
119
- for (let variation of move.variations) {
119
+ for (const variation of move.variations) {
120
120
  score += getSolutionScore(playAs, variation, chess, isUnscored);
121
121
  }
122
122
  }
@@ -182,12 +182,12 @@ export function scoreVariation(
182
182
  let score = 0;
183
183
  let altFound = false;
184
184
 
185
- for (let move of solution) {
185
+ for (const move of solution) {
186
186
  // The user may not have found the mainline solution,
187
187
  // but may have found a variation, which can also have a score associated, or can be an alternate solution
188
188
  // for this move
189
189
  if (move.variations.length > 0) {
190
- for (let variation of move.variations) {
190
+ for (const variation of move.variations) {
191
191
  const [variationScore, alt] = scoreVariation(
192
192
  playAs,
193
193
  variation,
@@ -248,9 +248,9 @@ export function addExtraVariation(
248
248
  currentSolutionMove: Move | null,
249
249
  solution: Chess,
250
250
  ) {
251
- for (let move of answer) {
251
+ for (const move of answer) {
252
252
  if (move.variations.length > 0) {
253
- for (let variation of move.variations) {
253
+ for (const variation of move.variations) {
254
254
  addExtraVariation(variation, currentSolutionMove, solution);
255
255
  }
256
256
  }
frontend/src/tournaments/LeaderboardTab.tsx CHANGED
@@ -102,7 +102,7 @@ const LeaderboardTab = () => {
102
102
  select
103
103
  label='Site'
104
104
  value={site}
105
- onChange={(e) => setSite(e.target.value as LeaderboardSite)}
105
+ onChange={(e) => { setSite(e.target.value as LeaderboardSite); }}
106
106
  >
107
107
  <MenuItem value={LeaderboardSite.Lichess}>Lichess</MenuItem>
108
108
  <MenuItem value={LeaderboardSite.Chesscom}>Chess.com</MenuItem>
@@ -114,7 +114,7 @@ const LeaderboardTab = () => {
114
114
  select
115
115
  label='Time Control'
116
116
  value={timeControl}
117
- onChange={(e) => setTimeControl(e.target.value as TimeControl)}
117
+ onChange={(e) => { setTimeControl(e.target.value as TimeControl); }}
118
118
  >
119
119
  <MenuItem value={'blitz'}>Blitz</MenuItem>
120
120
  <MenuItem value={'rapid'}>Rapid</MenuItem>
@@ -128,7 +128,7 @@ const LeaderboardTab = () => {
128
128
  label='Tournament Type'
129
129
  value={tournamentType}
130
130
  onChange={(e) =>
131
- setTournamentType(e.target.value as TournamentType)
131
+ { setTournamentType(e.target.value as TournamentType); }
132
132
  }
133
133
  >
134
134
  <MenuItem value={TournamentType.Arena}>Arena</MenuItem>
@@ -159,13 +159,13 @@ const LeaderboardTab = () => {
159
159
 
160
160
  <Button
161
161
  color={timePeriod === 'monthly' ? 'primary' : 'inherit'}
162
- onClick={() => setTimePeriod('monthly')}
162
+ onClick={() => { setTimePeriod('monthly'); }}
163
163
  >
164
164
  Monthly
165
165
  </Button>
166
166
  <Button
167
167
  color={timePeriod === 'yearly' ? 'primary' : 'inherit'}
168
- onClick={() => setTimePeriod('yearly')}
168
+ onClick={() => { setTimePeriod('yearly'); }}
169
169
  >
170
170
  Yearly
171
171
  </Button>
frontend/src/tournaments/TournamentCalendarFilters.tsx CHANGED
@@ -106,10 +106,10 @@ export const TournamentCalendarFilters: React.FC<TournamentCalendarFiltersProps>
106
106
  <Checkbox
107
107
  checked={filters.tournamentTypes[type]}
108
108
  onChange={(event) =>
109
- onChangeTournamentType(
109
+ { onChangeTournamentType(
110
110
  type,
111
111
  event.target.checked,
112
- )
112
+ ); }
113
113
  }
114
114
  />
115
115
  }
@@ -138,10 +138,10 @@ export const TournamentCalendarFilters: React.FC<TournamentCalendarFiltersProps>
138
138
  <Checkbox
139
139
  checked={filters.tournamentTimeControls[type]}
140
140
  onChange={(event) =>
141
- onChangeTournamentTimeControl(
141
+ { onChangeTournamentTimeControl(
142
142
  type,
143
143
  event.target.checked,
144
- )
144
+ ); }
145
145
  }
146
146
  color={getColor(type)}
147
147
  />
@@ -171,10 +171,10 @@ export const TournamentCalendarFilters: React.FC<TournamentCalendarFiltersProps>
171
171
  <Checkbox
172
172
  checked={filters.tournamentPositions[type]}
173
173
  onChange={(event) =>
174
- onChangeTournamentPositions(
174
+ { onChangeTournamentPositions(
175
175
  type,
176
176
  event.target.checked,
177
- )
177
+ ); }
178
178
  }
179
179
  />
180
180
  }
frontend/src/tournaments/TournamentsPage.tsx CHANGED
@@ -17,7 +17,7 @@ const TournamentsPage = () => {
17
17
  <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
18
18
  <TabList
19
19
  data-cy='tournaments-tab-list'
20
- onChange={(_, t) => setSearchParams({ type: t })}
20
+ onChange={(_, t) => { setSearchParams({ type: t }); }}
21
21
  variant='scrollable'
22
22
  >
23
23
  <Tab label='Calendar' value='calendar' />
frontend/src/tournaments/openClassical/DetailsPage.tsx CHANGED
@@ -81,7 +81,7 @@ const DetailsPage = () => {
81
81
  </Stack>
82
82
 
83
83
  {(user?.isAdmin || user?.isTournamentAdmin) && (
84
- <Button variant='contained' onClick={() => navigate('./admin')}>
84
+ <Button variant='contained' onClick={() => { navigate('./admin'); }}>
85
85
  Admin Portal
86
86
  </Button>
87
87
  )}
@@ -113,7 +113,7 @@ const Details: React.FC<DetailsProps> = ({ openClassical }) => {
113
113
  const view = searchParams.get('view') || 'standings';
114
114
 
115
115
  const maxRound =
116
- openClassical.sections[`${region}_${ratingRange}`]?.rounds.length || 0;
116
+ openClassical.sections[`${region}_${ratingRange}`].rounds.length || 0;
117
117
 
118
118
  const updateSearchParams = (key: string, value: string) => {
119
119
  const updatedParams = new URLSearchParams(searchParams.toString());
@@ -167,7 +167,7 @@ const Details: React.FC<DetailsProps> = ({ openClassical }) => {
167
167
  label='Region'
168
168
  select
169
169
  value={region}
170
- onChange={(e) => updateSearchParams('region', e.target.value)}
170
+ onChange={(e) => { updateSearchParams('region', e.target.value); }}
171
171
  sx={{
172
172
  flexGrow: 1,
173
173
  }}
@@ -181,7 +181,7 @@ const Details: React.FC<DetailsProps> = ({ openClassical }) => {
181
181
  label='Section'
182
182
  select
183
183
  value={ratingRange}
184
- onChange={(e) => updateSearchParams('ratingRange', e.target.value)}
184
+ onChange={(e) => { updateSearchParams('ratingRange', e.target.value); }}
185
185
  sx={{
186
186
  flexGrow: 1,
187
187
  }}
@@ -195,7 +195,7 @@ const Details: React.FC<DetailsProps> = ({ openClassical }) => {
195
195
  label='View'
196
196
  select
197
197
  value={view}
198
- onChange={(e) => updateSearchParams('view', e.target.value)}
198
+ onChange={(e) => { updateSearchParams('view', e.target.value); }}
199
199
  sx={{
200
200
  flexGrow: 1,
201
201
  }}
frontend/src/tournaments/openClassical/PairingsTable.tsx CHANGED
@@ -94,7 +94,7 @@ const PairingsTable: React.FC<PairingsTableProps> = ({
94
94
  round,
95
95
  }) => {
96
96
  const pairings =
97
- openClassical.sections[`${region}_${ratingRange}`]?.rounds[round - 1]?.pairings ??
97
+ openClassical.sections[`${region}_${ratingRange}`].rounds[round - 1]?.pairings ??
98
98
  [];
99
99
 
100
100
  return (
frontend/src/tournaments/openClassical/RegistrationPage.tsx CHANGED
@@ -33,7 +33,7 @@ const RegistrationPage = () => {
33
33
 
34
34
  const [email, setEmail] = useState('');
35
35
  const [lichessUsername, setLichessUsername] = useState(
36
- user?.ratings?.LICHESS?.username || '',
36
+ user?.ratings.LICHESS?.username || '',
37
37
  );
38
38
  const [discordUsername, setDiscordUsername] = useState(user?.discordUsername || '');
39
39
  const [title, setTitle] = useState('');
@@ -53,7 +53,7 @@ const RegistrationPage = () => {
53
53
  const request = useRequest();
54
54
 
55
55
  useEffect(() => {
56
- setLichessUsername(user?.ratings?.LICHESS?.username || '');
56
+ setLichessUsername(user?.ratings.LICHESS?.username || '');
57
57
  setDiscordUsername(user?.discordUsername || '');
58
58
  }, [user]);
59
59
 
@@ -130,7 +130,7 @@ const RegistrationPage = () => {
130
130
  <TextField
131
131
  label='Email'
132
132
  value={email}
133
- onChange={(e) => setEmail(e.target.value)}
133
+ onChange={(e) => { setEmail(e.target.value); }}
134
134
  required
135
135
  fullWidth
136
136
  error={Boolean(errors.email)}
@@ -141,9 +141,9 @@ const RegistrationPage = () => {
141
141
  <TextField
142
142
  label='Lichess Username'
143
143
  value={lichessUsername}
144
- onChange={(e) => setLichessUsername(e.target.value)}
145
- disabled={Boolean(user?.ratings?.LICHESS?.username)}
146
- required={!Boolean(user?.ratings?.LICHESS?.username)}
144
+ onChange={(e) => { setLichessUsername(e.target.value); }}
145
+ disabled={Boolean(user?.ratings.LICHESS?.username)}
146
+ required={!user?.ratings.LICHESS?.username}
147
147
  fullWidth
148
148
  error={Boolean(errors.lichessUsername)}
149
149
  helperText={errors.lichessUsername}
@@ -152,9 +152,9 @@ const RegistrationPage = () => {
152
152
  <TextField
153
153
  label='Discord Username'
154
154
  value={discordUsername}
155
- onChange={(e) => setDiscordUsername(e.target.value)}
155
+ onChange={(e) => { setDiscordUsername(e.target.value); }}
156
156
  disabled={Boolean(user?.discordUsername)}
157
- required={!Boolean(user?.discordUsername)}
157
+ required={!user?.discordUsername}
158
158
  fullWidth
159
159
  error={Boolean(errors.discordUsername)}
160
160
  helperText={errors.discordUsername}
@@ -163,7 +163,7 @@ const RegistrationPage = () => {
163
163
  <TextField
164
164
  label='Title'
165
165
  value={title}
166
- onChange={(e) => setTitle(e.target.value)}
166
+ onChange={(e) => { setTitle(e.target.value); }}
167
167
  select
168
168
  fullWidth
169
169
  >
@@ -184,7 +184,7 @@ const RegistrationPage = () => {
184
184
  select
185
185
  required
186
186
  value={region}
187
- onChange={(e) => setRegion(e.target.value)}
187
+ onChange={(e) => { setRegion(e.target.value); }}
188
188
  error={Boolean(errors.region)}
189
189
  helperText={errors.region}
190
190
  fullWidth
@@ -199,7 +199,7 @@ const RegistrationPage = () => {
199
199
  select
200
200
  required
201
201
  value={section}
202
- onChange={(e) => setSection(e.target.value)}
202
+ onChange={(e) => { setSection(e.target.value); }}
203
203
  error={Boolean(errors.section)}
204
204
  helperText={errors.section}
205
205
  fullWidth
@@ -218,7 +218,7 @@ const RegistrationPage = () => {
218
218
  <Checkbox
219
219
  checked={byeRequests[i]}
220
220
  onChange={(event) =>
221
- onSetByeRequest(i, event.target.checked)
221
+ { onSetByeRequest(i, event.target.checked); }
222
222
  }
223
223
  />
224
224
  }
@@ -281,9 +281,9 @@ const RegistrationPage = () => {
281
281
  <DialogActions>
282
282
  <Button
283
283
  onClick={() =>
284
- navigate(
284
+ { navigate(
285
285
  `/tournaments/open-classical?region=${region}&ratingRange=${section}`,
286
- )
286
+ ); }
287
287
  }
288
288
  >
289
289
  Done
frontend/src/tournaments/openClassical/SubmitResultsPage.tsx CHANGED
@@ -128,7 +128,7 @@ const SubmitResultsPage = () => {
128
128
  console.log('submitResultsForOpenClassical: ', resp);
129
129
  request.onSuccess();
130
130
  const round =
131
- resp.data.sections?.[`${region}_${section}`]?.rounds?.length ||
131
+ resp.data.sections[`${region}_${section}`].rounds.length ||
132
132
  'standings';
133
133
  navigate(
134
134
  `/tournaments/open-classical?region=${region}&ratingRange=${section}&view=${round}`,
@@ -161,7 +161,7 @@ const SubmitResultsPage = () => {
161
161
  label='Email'
162
162
  required
163
163
  value={email}
164
- onChange={(e) => setEmail(e.target.value)}
164
+ onChange={(e) => { setEmail(e.target.value); }}
165
165
  error={Boolean(errors.email)}
166
166
  helperText={
167
167
  errors.email ||
@@ -176,7 +176,7 @@ const SubmitResultsPage = () => {
176
176
  select
177
177
  required
178
178
  value={region}
179
- onChange={(e) => setRegion(e.target.value)}
179
+ onChange={(e) => { setRegion(e.target.value); }}
180
180
  error={Boolean(errors.region)}
181
181
  helperText={errors.region}
182
182
  >
@@ -190,7 +190,7 @@ const SubmitResultsPage = () => {
190
190
  select
191
191
  required
192
192
  value={section}
193
- onChange={(e) => setSection(e.target.value)}
193
+ onChange={(e) => { setSection(e.target.value); }}
194
194
  error={Boolean(errors.section)}
195
195
  helperText={errors.section}
196
196
  >
@@ -202,7 +202,7 @@ const SubmitResultsPage = () => {
202
202
  data-cy='game-url'
203
203
  label='Game URL'
204
204
  value={gameUrl}
205
- onChange={(e) => setGameUrl(e.target.value)}
205
+ onChange={(e) => { setGameUrl(e.target.value); }}
206
206
  onBlur={onBlurGameUrl}
207
207
  error={Boolean(errors.gameUrl)}
208
208
  helperText={errors.gameUrl || 'Please provide a link to the game'}
@@ -213,7 +213,7 @@ const SubmitResultsPage = () => {
213
213
  label='White'
214
214
  required
215
215
  value={white}
216
- onChange={(e) => setWhite(e.target.value)}
216
+ onChange={(e) => { setWhite(e.target.value); }}
217
217
  error={Boolean(errors.white)}
218
218
  helperText={
219
219
  errors.white ||
@@ -225,7 +225,7 @@ const SubmitResultsPage = () => {
225
225
  label='Black'
226
226
  required
227
227
  value={black}
228
- onChange={(e) => setBlack(e.target.value)}
228
+ onChange={(e) => { setBlack(e.target.value); }}
229
229
  error={Boolean(errors.black)}
230
230
  helperText={
231
231
  errors.black ||
@@ -239,7 +239,7 @@ const SubmitResultsPage = () => {
239
239
  select
240
240
  required
241
241
  value={result}
242
- onChange={(e) => setResult(e.target.value)}
242
+ onChange={(e) => { setResult(e.target.value); }}
243
243
  error={Boolean(errors.result)}
244
244
  helperText={errors.result}
245
245
  >
@@ -258,7 +258,7 @@ const SubmitResultsPage = () => {
258
258
  <Checkbox
259
259
  checked={reportOpponent}
260
260
  onChange={(event) =>
261
- setReportOpponent(event.target.checked)
261
+ { setReportOpponent(event.target.checked); }
262
262
  }
263
263
  />
264
264
  }
@@ -272,7 +272,7 @@ const SubmitResultsPage = () => {
272
272
  multiline
273
273
  minRows={3}
274
274
  value={notes}
275
- onChange={(e) => setNotes(e.target.value)}
275
+ onChange={(e) => { setNotes(e.target.value); }}
276
276
  />
277
277
 
278
278
  <LoadingButton
frontend/src/tournaments/openClassical/admin/AdminPage.tsx CHANGED
@@ -64,7 +64,7 @@ const AdminPage = () => {
64
64
  {request.data && (
65
65
  <TabContext value={tab}>
66
66
  <TabList
67
- onChange={(_, value) => setTab(value)}
67
+ onChange={(_, value) => { setTab(value); }}
68
68
  sx={{ borderBottom: 1, borderColor: 'divider' }}
69
69
  >
70
70
  <Tab label='Active Players' value='players' />
frontend/src/tournaments/openClassical/admin/BannedPlayersTab.tsx CHANGED
@@ -42,7 +42,7 @@ const BannedPlayersTab: React.FC<BannedPlayersTabProps> = ({
42
42
  <GridActionsCellItem
43
43
  icon={<Check color='success' />}
44
44
  label='Unban Player'
45
- onClick={() => setUnbanPlayer(params.row.lichessUsername)}
45
+ onClick={() => { setUnbanPlayer(params.row.lichessUsername); }}
46
46
  />
47
47
  </Tooltip>,
48
48
  ],
@@ -90,7 +90,7 @@ const BannedPlayersTab: React.FC<BannedPlayersTabProps> = ({
90
90
 
91
91
  <Dialog
92
92
  open={Boolean(unbanPlayer)}
93
- onClose={unbanRequest.isLoading() ? undefined : () => setUnbanPlayer('')}
93
+ onClose={unbanRequest.isLoading() ? undefined : () => { setUnbanPlayer(''); }}
94
94
  maxWidth='sm'
95
95
  fullWidth
96
96
  >
@@ -103,7 +103,7 @@ const BannedPlayersTab: React.FC<BannedPlayersTabProps> = ({
103
103
  </DialogContent>
104
104
  <DialogActions>
105
105
  <Button
106
- onClick={() => setUnbanPlayer('')}
106
+ onClick={() => { setUnbanPlayer(''); }}
107
107
  disabled={unbanRequest.isLoading()}
108
108
  >
109
109
  Cancel
frontend/src/tournaments/openClassical/admin/CompleteTournament.tsx CHANGED
@@ -30,7 +30,7 @@ const CompleteTournament: React.FC<CompleteTournamentProps> = ({
30
30
  const api = useApi();
31
31
 
32
32
  const onComplete = () => {
33
- if (!date || !date.isValid) {
33
+ if (!date?.isValid) {
34
34
  return;
35
35
  }
36
36
 
@@ -54,12 +54,12 @@ const CompleteTournament: React.FC<CompleteTournamentProps> = ({
54
54
 
55
55
  return (
56
56
  <>
57
- <Button variant='contained' color='error' onClick={() => setOpen(true)}>
57
+ <Button variant='contained' color='error' onClick={() => { setOpen(true); }}>
58
58
  Complete Tournament
59
59
  </Button>
60
60
  <Dialog
61
61
  open={open}
62
- onClose={request.isLoading() ? undefined : () => setOpen(false)}
62
+ onClose={request.isLoading() ? undefined : () => { setOpen(false); }}
63
63
  maxWidth='sm'
64
64
  fullWidth
65
65
  >
@@ -75,12 +75,12 @@ const CompleteTournament: React.FC<CompleteTournamentProps> = ({
75
75
  <DatePicker
76
76
  label='Next Tournament Start Date'
77
77
  value={date}
78
- onChange={(newValue) => setDate(newValue)}
78
+ onChange={(newValue) => { setDate(newValue); }}
79
79
  />
80
80
  </Stack>
81
81
  </DialogContent>
82
82
  <DialogActions>
83
- <Button onClick={() => setOpen(false)} disabled={request.isLoading()}>
83
+ <Button onClick={() => { setOpen(false); }} disabled={request.isLoading()}>
84
84
  Cancel
85
85
  </Button>
86
86
  <LoadingButton
frontend/src/tournaments/openClassical/admin/Editor.tsx CHANGED
@@ -102,7 +102,7 @@ const Editor: React.FC<EditorProps> = ({ openClassical, onSuccess }) => {
102
102
  if (openClassical.acceptingRegistrations) {
103
103
  return (
104
104
  <>
105
- <Button variant='contained' onClick={() => setOpen(true)}>
105
+ <Button variant='contained' onClick={() => { setOpen(true); }}>
106
106
  Edit Pairings
107
107
  </Button>
108
108
  <Dialog open={open} onClose={handleClose} maxWidth='sm' fullWidth>
@@ -129,7 +129,7 @@ const Editor: React.FC<EditorProps> = ({ openClassical, onSuccess }) => {
129
129
 
130
130
  return (
131
131
  <>
132
- <Button variant='contained' onClick={() => setOpen(true)}>
132
+ <Button variant='contained' onClick={() => { setOpen(true); }}>
133
133
  Edit Pairings
134
134
  </Button>
135
135
  <Dialog open={open} onClose={handleClose} maxWidth='sm' fullWidth>
@@ -142,7 +142,7 @@ const Editor: React.FC<EditorProps> = ({ openClassical, onSuccess }) => {
142
142
  select
143
143
  required
144
144
  value={region}
145
- onChange={(e) => setRegion(e.target.value)}
145
+ onChange={(e) => { setRegion(e.target.value); }}
146
146
  error={Boolean(errors.region)}
147
147
  helperText={errors.region}
148
148
  >
@@ -158,7 +158,7 @@ const Editor: React.FC<EditorProps> = ({ openClassical, onSuccess }) => {
158
158
  select
159
159
  required
160
160
  value={section}
161
- onChange={(e) => setSection(e.target.value)}
161
+ onChange={(e) => { setSection(e.target.value); }}
162
162
  error={Boolean(errors.section)}
163
163
  helperText={errors.section}
164
164
  >
@@ -171,7 +171,7 @@ const Editor: React.FC<EditorProps> = ({ openClassical, onSuccess }) => {
171
171
  label='Round'
172
172
  fullWidth
173
173
  value={round}
174
- onChange={(e) => setRound(parseInt(e.target.value))}
174
+ onChange={(e) => { setRound(parseInt(e.target.value)); }}
175
175
  error={!!errors.round}
176
176
  helperText={errors.round}
177
177
  >
@@ -190,7 +190,7 @@ const Editor: React.FC<EditorProps> = ({ openClassical, onSuccess }) => {
190
190
  minRows={3}
191
191
  maxRows={15}
192
192
  value={csvData}
193
- onChange={(e) => setCsvData(e.target.value)}
193
+ onChange={(e) => { setCsvData(e.target.value); }}
194
194
  error={!!errors.csvData}
195
195
  helperText={errors.csvData}
196
196
  />
frontend/src/tournaments/openClassical/admin/EmailPairingsButton.tsx CHANGED
@@ -58,7 +58,7 @@ const EmailPairingsButton: React.FC<EmailPairingsButtonProps> = ({
58
58
  variant='contained'
59
59
  color='warning'
60
60
  disabled={emailsSent || maxRound !== currentRound}
61
- onClick={() => setOpen(true)}
61
+ onClick={() => { setOpen(true); }}
62
62
  >
63
63
  Send Pairing Emails
64
64
  </Button>
frontend/src/tournaments/openClassical/admin/PairingsTab.tsx CHANGED
@@ -44,10 +44,10 @@ const PairingsTab: React.FC<PairingsTabProps> = ({ openClassical, onUpdate }) =>
44
44
  const view = searchParams.get('view') || '1';
45
45
 
46
46
  const round =
47
- openClassical.sections[`${region}_${ratingRange}`]?.rounds[parseInt(view) - 1];
47
+ openClassical.sections[`${region}_${ratingRange}`].rounds[parseInt(view) - 1];
48
48
 
49
49
  const maxRound =
50
- openClassical.sections[`${region}_${ratingRange}`]?.rounds.length ?? 1;
50
+ openClassical.sections[`${region}_${ratingRange}`].rounds.length ?? 1;
51
51
 
52
52
  return (
53
53
  <Stack spacing={3}>
@@ -56,7 +56,7 @@ const PairingsTab: React.FC<PairingsTabProps> = ({ openClassical, onUpdate }) =>
56
56
  <EmailPairingsButton
57
57
  maxRound={maxRound}
58
58
  currentRound={parseInt(view)}
59
- emailsSent={round?.pairingEmailsSent}
59
+ emailsSent={round.pairingEmailsSent}
60
60
  onSuccess={onUpdate}
61
61
  />
62
62
  </Stack>
@@ -66,7 +66,7 @@ const PairingsTab: React.FC<PairingsTabProps> = ({ openClassical, onUpdate }) =>
66
66
  label='Region'
67
67
  select
68
68
  value={region}
69
- onChange={(e) => updateSearchParams('region', e.target.value)}
69
+ onChange={(e) => { updateSearchParams('region', e.target.value); }}
70
70
  sx={{
71
71
  flexGrow: 1,
72
72
  }}
@@ -80,7 +80,7 @@ const PairingsTab: React.FC<PairingsTabProps> = ({ openClassical, onUpdate }) =>
80
80
  label='Section'
81
81
  select
82
82
  value={ratingRange}
83
- onChange={(e) => updateSearchParams('ratingRange', e.target.value)}
83
+ onChange={(e) => { updateSearchParams('ratingRange', e.target.value); }}
84
84
  sx={{
85
85
  flexGrow: 1,
86
86
  }}
@@ -93,7 +93,7 @@ const PairingsTab: React.FC<PairingsTabProps> = ({ openClassical, onUpdate }) =>
93
93
  label='Round'
94
94
  select
95
95
  value={view}
96
- onChange={(e) => updateSearchParams('view', e.target.value)}
96
+ onChange={(e) => { updateSearchParams('view', e.target.value); }}
97
97
  sx={{
98
98
  flexGrow: 1,
99
99
  }}
@@ -173,7 +173,7 @@ const AdminPairingsTable: React.FC<AdminPairingsTableProps> = ({
173
173
  }, [setUpdatePairing]);
174
174
 
175
175
  const pairings =
176
- openClassical.sections[`${region}_${ratingRange}`]?.rounds[round - 1]?.pairings ??
176
+ openClassical.sections[`${region}_${ratingRange}`].rounds[round - 1]?.pairings ??
177
177
  [];
178
178
 
179
179
  const onConfirmUpdate = () => {
@@ -228,7 +228,7 @@ const AdminPairingsTable: React.FC<AdminPairingsTableProps> = ({
228
228
  onClose={
229
229
  updateRequest.isLoading()
230
230
  ? undefined
231
- : () => setUpdatePairing(undefined)
231
+ : () => { setUpdatePairing(undefined); }
232
232
  }
233
233
  maxWidth='sm'
234
234
  fullWidth
@@ -249,7 +249,7 @@ const AdminPairingsTable: React.FC<AdminPairingsTableProps> = ({
249
249
  select
250
250
  required
251
251
  value={updateResult}
252
- onChange={(e) => setUpdateResult(e.target.value)}
252
+ onChange={(e) => { setUpdateResult(e.target.value); }}
253
253
  sx={{ mt: 3, mb: 1, width: 1 }}
254
254
  >
255
255
  <MenuItem value='1-0'>White Wins (1-0)</MenuItem>
@@ -263,7 +263,7 @@ const AdminPairingsTable: React.FC<AdminPairingsTableProps> = ({
263
263
  </DialogContent>
264
264
  <DialogActions>
265
265
  <Button
266
- onClick={() => setUpdatePairing(undefined)}
266
+ onClick={() => { setUpdatePairing(undefined); }}
267
267
  disabled={updateRequest.isLoading()}
268
268
  >
269
269
  Cancel
frontend/src/tournaments/openClassical/admin/PlayersTab.tsx CHANGED
@@ -140,7 +140,7 @@ const PlayersTab: React.FC<PlayersTabProps> = ({ openClassical, onUpdate }) => {
140
140
  const players = useMemo(
141
141
  () =>
142
142
  Object.values(
143
- openClassical.sections[`${region}_${ratingRange}`]?.players || {},
143
+ openClassical.sections[`${region}_${ratingRange}`].players || {},
144
144
  ).filter((player) => player.lichessUsername !== 'No Opponent'),
145
145
  [openClassical, region, ratingRange],
146
146
  );
@@ -204,7 +204,7 @@ const PlayersTab: React.FC<PlayersTabProps> = ({ openClassical, onUpdate }) => {
204
204
  label='Region'
205
205
  select
206
206
  value={region}
207
- onChange={(e) => updateSearchParams('region', e.target.value)}
207
+ onChange={(e) => { updateSearchParams('region', e.target.value); }}
208
208
  sx={{
209
209
  flexGrow: 1,
210
210
  }}
@@ -218,7 +218,7 @@ const PlayersTab: React.FC<PlayersTabProps> = ({ openClassical, onUpdate }) => {
218
218
  label='Section'
219
219
  select
220
220
  value={ratingRange}
221
- onChange={(e) => updateSearchParams('ratingRange', e.target.value)}
221
+ onChange={(e) => { updateSearchParams('ratingRange', e.target.value); }}
222
222
  sx={{
223
223
  flexGrow: 1,
224
224
  }}
@@ -252,7 +252,7 @@ const PlayersTab: React.FC<PlayersTabProps> = ({ openClassical, onUpdate }) => {
252
252
  <Dialog
253
253
  open={Boolean(updatePlayer)}
254
254
  onClose={
255
- updateRequest.isLoading() ? undefined : () => setUpdatePlayer('')
255
+ updateRequest.isLoading() ? undefined : () => { setUpdatePlayer(''); }
256
256
  }
257
257
  maxWidth='sm'
258
258
  fullWidth
@@ -269,7 +269,7 @@ const PlayersTab: React.FC<PlayersTabProps> = ({ openClassical, onUpdate }) => {
269
269
  </DialogContent>
270
270
  <DialogActions>
271
271
  <Button
272
- onClick={() => setUpdatePlayer('')}
272
+ onClick={() => { setUpdatePlayer(''); }}
273
273
  disabled={updateRequest.isLoading()}
274
274
  >
275
275
  Cancel
frontend/src/tutorial/Tutorial.tsx CHANGED
@@ -21,7 +21,7 @@ const Tutorial: React.FC<TutorialProps> = ({ name, steps, zIndex }) => {
21
21
 
22
22
  useEffect(() => {
23
23
  if (
24
- (!user.tutorials || !user.tutorials[name]) &&
24
+ (!user.tutorials?.[name]) &&
25
25
  tutorialState.activeTutorial !== name
26
26
  ) {
27
27
  console.log('Starting tutorial: ', name);
@@ -41,7 +41,7 @@ const Tutorial: React.FC<TutorialProps> = ({ name, steps, zIndex }) => {
41
41
  .then(() => {
42
42
  setTutorialState({});
43
43
  })
44
- .catch((err) => console.error('completeTutorial: ', err));
44
+ .catch((err) => { console.error('completeTutorial: ', err); });
45
45
  }
46
46
  },
47
47
  [setTutorialState, api, user.tutorials, name]
frontend/src/upsell/PriceMatrix.tsx CHANGED
@@ -116,7 +116,7 @@ const PriceMatrix: React.FC<PriceMatrixProps> = ({
116
116
  fullWidth
117
117
  loading={request?.isLoading() && interval === 'month'}
118
118
  disabled={request?.isLoading() && interval !== 'month'}
119
- onClick={() => onSubscribe('month')}
119
+ onClick={() => { onSubscribe('month'); }}
120
120
  color='subscribe'
121
121
  >
122
122
  Subscribe
@@ -159,7 +159,7 @@ const PriceMatrix: React.FC<PriceMatrixProps> = ({
159
159
  fullWidth
160
160
  loading={request?.isLoading() && interval === 'year'}
161
161
  disabled={request?.isLoading() && interval !== 'year'}
162
- onClick={() => onSubscribe('year')}
162
+ onClick={() => { onSubscribe('year'); }}
163
163
  color='subscribe'
164
164
  >
165
165
  Subscribe
frontend/src/upsell/UpsellDialog.tsx CHANGED
@@ -78,7 +78,7 @@ const UpsellDialog: React.FC<UpsellDialogProps> = ({
78
78
  maxWidth='sm'
79
79
  fullWidth
80
80
  open={open}
81
- onClose={() => onClose(false)}
81
+ onClose={() => { onClose(false); }}
82
82
  >
83
83
  <DialogTitle>Upgrade to a Full Account</DialogTitle>
84
84
  <DialogContent>
@@ -100,7 +100,7 @@ const UpsellDialog: React.FC<UpsellDialogProps> = ({
100
100
  </DialogContentText>
101
101
  </DialogContent>
102
102
  <DialogActions>
103
- <Button onClick={() => onClose(false)}>Cancel</Button>
103
+ <Button onClick={() => { onClose(false); }}>Cancel</Button>
104
104
  <Button onClick={onViewPrices} href='/prices'>
105
105
  View Prices
106
106
  </Button>
frontend/src/upsell/UpsellPage.tsx CHANGED
@@ -12,7 +12,7 @@ const UpsellPage: React.FC<UpsellPageProps> = ({ redirectTo, ...props }) => {
12
12
 
13
13
  return (
14
14
  <Container maxWidth='lg' sx={{ pt: 5 }}>
15
- <UpsellDialog open={true} onClose={() => navigate(redirectTo)} {...props} />
15
+ <UpsellDialog open={true} onClose={() => { navigate(redirectTo); }} {...props} />
16
16
  </Container>
17
17
  );
18
18
  };