Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/app/services/logsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { api } from './api';

export type LogEntry = any;
Copy link

Choose a reason for hiding this comment

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

Untyped LogEntry defeats type safety category Design

Tell me more
What is the issue?

The LogEntry type is defined as 'any', which eliminates type safety for log entries throughout the application.

Why this matters

Using 'any' defeats the purpose of TypeScript's type system, making it impossible to catch type-related errors at compile time and reducing code maintainability and developer experience.

Suggested change ∙ Feature Preview

Define a proper interface for LogEntry based on the actual log data structure. For example:

export interface LogEntry {
  id: string;
  timestamp: number;
  type: string;
  username: string;
  // Add other expected properties based on actual log structure
}
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Replace any with a safer type.

Avoid any to keep type-safety.

Apply this diff:

-export type LogEntry = any;
+export type LogEntry = unknown; // TODO: narrow to the server schema
📝 Committable suggestion

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

Suggested change
export type LogEntry = any;
export type LogEntry = unknown; // TODO: narrow to the server schema
🤖 Prompt for AI Agents
In src/app/services/logsApi.ts around line 3 replace the unsafe "any" with a
concrete LogEntry type: define and export an interface/alias that captures the
expected shape (e.g. timestamp as string|number|Date, level as a union of log
levels or string, message as string, and an optional metadata/fields property
typed as Record<string, unknown>), or if the shape is truly dynamic use
Record<string, unknown> instead of any; update all usages to accept that type
and narrow/validate fields where needed.


export type LogsResponse = {
data: LogEntry[];
next?: string | null;
prev?: string | null;
};

export type GetLogsQuery = {
username?: string;
startDate?: number; // seconds since epoch
endDate?: number; // seconds since epoch
Comment on lines +13 to +14
Copy link

Choose a reason for hiding this comment

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

Ambiguous Date Format Type category Readability

Tell me more
What is the issue?

Using raw numbers for dates with only comments to indicate the format is error-prone and unclear.

Why this matters

Developers may misinterpret the date format or forget to convert to seconds, leading to confusion and potential bugs.

Suggested change ∙ Feature Preview

Create a more descriptive type alias:

type UnixTimestamp = number;

export type GetLogsQuery = {
    username?: string;
    startDate?: UnixTimestamp;
    endDate?: UnixTimestamp;
    // ...
};
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

type?: string; // comma-separated list of types
Copy link

Choose a reason for hiding this comment

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

Unsafe Log Type String category Readability

Tell me more
What is the issue?

Using string type for a comma-separated list of predefined types is not type-safe and relies on comments for clarity.

Why this matters

Without type safety, developers might pass invalid log types or format the string incorrectly.

Suggested change ∙ Feature Preview

Define an enum or union type for log types:

export type LogType = 'OOO' | 'ACTIVE' | 'OTHER'; // Add actual log types
export type GetLogsQuery = {
    // ...
    type?: LogType[];
    // ...
};
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Support multiple types without manual CSV handling at call sites.

Accept string[] and join here.

Apply this diff:

-    type?: string; // comma-separated list of types
+    type?: string | string[]; // accepts array; joined as CSV
-                if (type) queryParams.set('type', type);
+                if (type) queryParams.set('type', Array.isArray(type) ? type.join(',') : type);

Also applies to: 39-39

🤖 Prompt for AI Agents
In src/app/services/logsApi.ts around lines 15 and 39, the type field is
currently declared as a string (expected to be a comma-separated list) which
forces callers to manually CSV their arrays; update the declaration to accept
string | string[] and, where the parameter is sent (line ~39), detect if the
value is an array and join it with commas before sending (otherwise leave the
string as-is), ensuring callers can pass either a string or an array without
extra handling.

format?: 'feed';
dev?: boolean;
nextLink?: string;
};

export const logsApi = api.injectEndpoints({
endpoints: (build) => ({
getLogs: build.query<LogsResponse, GetLogsQuery>({
query: ({
username,
startDate,
endDate,
type,
format = 'feed',
dev = true,
nextLink,
}) => {
if (nextLink) {
return nextLink;
}
Comment on lines +33 to +35
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

nextLink passthrough can fetch cross‑origin URLs. Constrain to same‑origin.

Returning arbitrary nextLink can bypass baseUrl and hit external origins (CORS/observability risk).

Apply this diff:

-                if (nextLink) {
-                    return nextLink;
-                }
+                if (nextLink) {
+                    // Allow only same-origin relative URLs; strip origin if absolute.
+                    if (/^https?:\/\//i.test(nextLink)) {
+                        const u = new URL(nextLink);
+                        return `${u.pathname}${u.search}`;
+                    }
+                    return nextLink.startsWith('/') ? nextLink : `/${nextLink}`;
+                }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/app/services/logsApi.ts around lines 33 to 35, returning nextLink
directly allows cross-origin URLs; instead parse/resolve nextLink against the
configured baseUrl and only return it if its origin matches baseUrl.origin (and
optionally its pathname starts with baseUrl.pathname/base path); otherwise
ignore or return null/undefined. Validate using a URL constructor (with baseUrl
as base) or compare origins, and ensure any returned nextLink is same-origin to
avoid CORS/observability leaks.

const queryParams = new URLSearchParams();
if (dev) queryParams.set('dev', 'true');
if (format) queryParams.set('format', format);
if (type) queryParams.set('type', type);
if (username) queryParams.set('username', username);
if (startDate) queryParams.set('startDate', String(startDate));
if (endDate) queryParams.set('endDate', String(endDate));
Comment on lines +36 to +42
Copy link

Choose a reason for hiding this comment

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

Inefficient query parameter construction category Performance

Tell me more
What is the issue?

Multiple individual URLSearchParams.set() calls create unnecessary overhead when building query strings.

Why this matters

Each set() call triggers internal operations and potential string manipulations. For APIs that may be called frequently, this creates cumulative performance overhead that could be avoided with batch construction.

Suggested change ∙ Feature Preview

Use URLSearchParams constructor with an object or build the params object first, then construct URLSearchParams once:

const params: Record<string, string> = {};
if (dev) params.dev = 'true';
if (format) params.format = format;
if (type) params.type = type;
if (username) params.username = username;
if (startDate) params.startDate = String(startDate);
if (endDate) params.endDate = String(endDate);
const queryParams = new URLSearchParams(params);
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

return `/logs?${queryParams.toString()}`;
Comment on lines +36 to +43
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Falsy checks drop 0 timestamps. Use explicit number checks.

0 (Unix epoch) is valid but currently omitted.

Apply this diff:

-                if (dev) queryParams.set('dev', 'true');
+                if (dev) queryParams.set('dev', 'true');
                 if (format) queryParams.set('format', format);
-                if (type) queryParams.set('type', type);
+                if (type) queryParams.set('type', type);
                 if (username) queryParams.set('username', username);
-                if (startDate) queryParams.set('startDate', String(startDate));
-                if (endDate) queryParams.set('endDate', String(endDate));
+                if (typeof startDate === 'number') queryParams.set('startDate', String(startDate));
+                if (typeof endDate === 'number') queryParams.set('endDate', String(endDate));
📝 Committable suggestion

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

Suggested change
const queryParams = new URLSearchParams();
if (dev) queryParams.set('dev', 'true');
if (format) queryParams.set('format', format);
if (type) queryParams.set('type', type);
if (username) queryParams.set('username', username);
if (startDate) queryParams.set('startDate', String(startDate));
if (endDate) queryParams.set('endDate', String(endDate));
return `/logs?${queryParams.toString()}`;
const queryParams = new URLSearchParams();
if (dev) queryParams.set('dev', 'true');
if (format) queryParams.set('format', format);
if (type) queryParams.set('type', type);
if (username) queryParams.set('username', username);
if (typeof startDate === 'number') queryParams.set('startDate', String(startDate));
if (typeof endDate === 'number') queryParams.set('endDate', String(endDate));
return `/logs?${queryParams.toString()}`;
🤖 Prompt for AI Agents
In src/app/services/logsApi.ts around lines 36 to 43, the current falsy checks
for startDate and endDate drop valid numeric timestamps like 0; change the
guards to explicit presence checks (e.g. startDate !== undefined && startDate
!== null) or a typeof/Number.isFinite check so that numeric 0 is included, then
set queryParams.set('startDate', String(startDate)) and similarly for endDate
only when the explicit check passes.

},
Comment on lines +43 to +44
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Avoid trailing ? when no params.

Minor cleanliness improvement for the request URL.

Apply this diff:

-                return `/logs?${queryParams.toString()}`;
+                const search = queryParams.toString();
+                return search ? `/logs?${search}` : '/logs';
📝 Committable suggestion

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

Suggested change
return `/logs?${queryParams.toString()}`;
},
const search = queryParams.toString();
return search ? `/logs?${search}` : '/logs';
},
🤖 Prompt for AI Agents
In src/app/services/logsApi.ts around lines 43-44, the function always appends a
'?' even when queryParams.toString() is empty; change the return to
conditionally append the query string: compute const qs = queryParams.toString()
and return qs ? `/logs?${qs}` : `/logs` so no trailing '?' appears when there
are no params.

providesTags: ['Status'],
}),
Comment on lines +45 to +46
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Use keyed tags for better cache invalidation granularity.

Current static tag may over‑invalidate.

Apply this diff:

-            providesTags: ['Status'],
+            providesTags: (result, error, arg) =>
+                arg?.username ? [{ type: 'Status' as const, id: arg.username }, 'Status'] : ['Status'],
📝 Committable suggestion

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

Suggested change
providesTags: ['Status'],
}),
providesTags: (result, error, arg) =>
arg?.username ? [{ type: 'Status' as const, id: arg.username }, 'Status'] : ['Status'],
}),
🤖 Prompt for AI Agents
In src/app/services/logsApi.ts around lines 45-46, the endpoint currently
returns a static providesTags: ['Status'] which over-invalidates; change it to a
function that returns keyed tags based on the response (e.g., for single-object
responses return [{ type: 'Status', id: response.id }] and for list responses
map each item to { type: 'Status', id: item.id } ), and keep a fallback to the
static tag when response is undefined to preserve behavior during
errors/loading.

}),
});

export const { useLazyGetLogsQuery } = logsApi;
3 changes: 2 additions & 1 deletion src/app/services/tasksApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const tasksApi = api.injectEndpoints({
),
next: response.next,
prev: response.prev,
};
} as TasksResponseType;
},
}),

Expand Down Expand Up @@ -138,6 +138,7 @@ export const tasksApi = api.injectEndpoints({

export const {
useGetAllTasksQuery,
useLazyGetAllTasksQuery,
useGetMineTasksQuery,
useAddTaskMutation,
useUpdateTaskMutation,
Expand Down
126 changes: 108 additions & 18 deletions src/components/Calendar/UserSearchField.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useState, useEffect, ChangeEvent, useRef } from 'react';
import classNames from './UserSearchField.module.scss';
import { useGetAllUsersQuery } from '@/app/services/usersApi';
import { logs } from '@/constants/calendar';
import { userDataType } from '@/interfaces/user.type';
import { useOutsideAlerter } from '@/hooks/useOutsideAlerter';
import { useLazyGetLogsQuery } from '@/app/services/logsApi';
import { useLazyGetAllTasksQuery } from '@/app/services/tasksApi';

type SearchFieldProps = {
onSearchTextSubmitted: (user: userDataType | undefined, data: any) => void;
Expand All @@ -22,37 +23,122 @@ const SearchField = ({ onSearchTextSubmitted, loading }: SearchFieldProps) => {
filterUser(e.target.value);
};

const handleOnSubmit = (e: React.FormEvent) => {
const { data: userData, isLoading: isUsersLoading } = useGetAllUsersQuery();
const [usersList, setUsersList] = useState<userDataType[]>([]);
const [displayList, setDisplayList] = useState<userDataType[]>([]);

const [triggerGetLogs, { isFetching: isLogsFetching }] =
useLazyGetLogsQuery();
const [triggerGetTasks, { isFetching: isTasksFetching }] =
useLazyGetAllTasksQuery();

const toMs = (value?: number | string) => {
if (typeof value === 'string') {
const parsed = Date.parse(value);
return isNaN(parsed) ? undefined : parsed;
}
if (typeof value !== 'number') return undefined as unknown as number;
return value >= 1e12 ? value : value * 1000;
};
Comment on lines +35 to +42
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix toMs return type; avoid undefined as number.

Return number | undefined and drop the unsafe cast.

-    const toMs = (value?: number | string) => {
+    const toMs = (value?: number | string): number | undefined => {
         if (typeof value === 'string') {
             const parsed = Date.parse(value);
             return isNaN(parsed) ? undefined : parsed;
         }
-        if (typeof value !== 'number') return undefined as unknown as number;
-        return value >= 1e12 ? value : value * 1000;
+        if (typeof value !== 'number') return undefined;
+        return value >= 1e12 ? value : value * 1000;
     };
📝 Committable suggestion

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

Suggested change
const toMs = (value?: number | string) => {
if (typeof value === 'string') {
const parsed = Date.parse(value);
return isNaN(parsed) ? undefined : parsed;
}
if (typeof value !== 'number') return undefined as unknown as number;
return value >= 1e12 ? value : value * 1000;
};
const toMs = (value?: number | string): number | undefined => {
if (typeof value === 'string') {
const parsed = Date.parse(value);
return isNaN(parsed) ? undefined : parsed;
}
if (typeof value !== 'number') return undefined;
return value >= 1e12 ? value : value * 1000;
};
🤖 Prompt for AI Agents
In src/components/Calendar/UserSearchField.tsx around lines 35 to 42, the toMs
function currently forces an undefined value into a number with "undefined as
unknown as number"; change the function signature to return number | undefined,
remove the unsafe cast, and return undefined directly in
non-number/non-parseable branches; ensure the final return is a number
(milliseconds) or undefined so callers handle the optional value.


const handleOnSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setDisplayList([]);
const user = usersList.find(
(user: userDataType) => user.username === searchText
);
onSearchTextSubmitted(user, data);
};

const { data: userData, isError, isLoading } = useGetAllUsersQuery();
const [usersList, setUsersList] = useState<userDataType[]>([]);
const [displayList, setDisplayList] = useState<userDataType[]>([]);
const [data, setData] = useState([]);
if (!user) {
onSearchTextSubmitted(undefined, []);
return;
}

// Fetch logs (events) and tasks (continuous ranges). Try tasks by both userId and username.
Copy link

Choose a reason for hiding this comment

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

Unclear rationale for dual task fetching category Documentation

Tell me more
What is the issue?

The inline comment doesn't explain why we need to fetch tasks by both userId and username.

Why this matters

Future developers might remove this seemingly redundant double fetch without understanding its necessity.

Suggested change ∙ Feature Preview

// Fetch logs (events) and tasks (continuous ranges). Tasks are fetched by both userId and username
// because legacy data might be linked to either identifier

Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

const [logsResult, tasksByIdResult, tasksByUsernameResult] =
await Promise.all([
triggerGetLogs({
username: user.username || undefined,
type: ['task', 'REQUEST_CREATED'].join(','),
format: 'feed',
dev: true,
}),
triggerGetTasks({ assignee: user.id }),
triggerGetTasks({ assignee: user.username || '' }),
]);
Comment on lines +66 to +67
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Avoid querying tasks with empty assignee.

Skip the username-based query when user.username is falsy.

-                triggerGetTasks({ assignee: user.username || '' }),
+                user.username
+                    ? triggerGetTasks({ assignee: user.username })
+                    : Promise.resolve({ data: { tasks: [] } as any }),
📝 Committable suggestion

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

Suggested change
triggerGetTasks({ assignee: user.username || '' }),
]);
user.username
? triggerGetTasks({ assignee: user.username })
: Promise.resolve({ data: { tasks: [] } as any }),
]);
🤖 Prompt for AI Agents
In src/components/Calendar/UserSearchField.tsx around lines 66-67, the code
always triggers a tasks query with assignee set to user.username || '', which
causes an unwanted query when username is falsy; change the logic to only
include or call triggerGetTasks with an assignee when user.username is truthy
(e.g., conditionally push the trigger into the array or guard the call so no
empty-string assignee query is made).


const logsResponse = logsResult.data;
const tasksById = tasksByIdResult.data?.tasks || [];
const tasksByUsername = tasksByUsernameResult.data?.tasks || [];

// Merge and de-duplicate tasks by id
const taskMap: Record<string, any> = {};
[...tasksById, ...tasksByUsername].forEach((t: any) => {
if (t?.id) taskMap[t.id] = t;
});
const mergedTasks = Object.values(taskMap);

const eventEntries = (logsResponse?.data || [])
.filter((log: any) => !!log)
.map((log: any) => {
// OOO ranges
if (log.type === 'REQUEST_CREATED' && log.from && log.until) {
return {
startTime: toMs(log.from),
endTime: toMs(log.until),
status: 'OOO',
};
}
// Task events => single-day ACTIVE
if (log.type === 'task') {
const ts = toMs(log.timestamp);
return {
startTime: ts,
endTime: ts,
status: 'ACTIVE',
taskTitle: log.taskTitle,
};
}
return null;
})
.filter(Boolean);

Comment on lines +80 to +104
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Filter out log entries with invalid timestamps.

Prevents pushing { startTime: undefined } into downstream processing.

-        const eventEntries = (logsResponse?.data || [])
+        const eventEntries = (logsResponse?.data || [])
             .filter((log: any) => !!log)
             .map((log: any) => {
...
                 if (log.type === 'task') {
                     const ts = toMs(log.timestamp);
                     return {
                         startTime: ts,
                         endTime: ts,
                         status: 'ACTIVE',
                         taskTitle: log.taskTitle,
                     };
                 }
                 return null;
             })
-            .filter(Boolean);
+            .filter((e: any) => e && e.startTime);
📝 Committable suggestion

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

Suggested change
const eventEntries = (logsResponse?.data || [])
.filter((log: any) => !!log)
.map((log: any) => {
// OOO ranges
if (log.type === 'REQUEST_CREATED' && log.from && log.until) {
return {
startTime: toMs(log.from),
endTime: toMs(log.until),
status: 'OOO',
};
}
// Task events => single-day ACTIVE
if (log.type === 'task') {
const ts = toMs(log.timestamp);
return {
startTime: ts,
endTime: ts,
status: 'ACTIVE',
taskTitle: log.taskTitle,
};
}
return null;
})
.filter(Boolean);
const eventEntries = (logsResponse?.data || [])
.filter((log: any) => !!log)
.map((log: any) => {
// OOO ranges
if (log.type === 'REQUEST_CREATED' && log.from && log.until) {
return {
startTime: toMs(log.from),
endTime: toMs(log.until),
status: 'OOO',
};
}
// Task events => single-day ACTIVE
if (log.type === 'task') {
const ts = toMs(log.timestamp);
return {
startTime: ts,
endTime: ts,
status: 'ACTIVE',
taskTitle: log.taskTitle,
};
}
return null;
})
.filter((e: any) => e && e.startTime);
🤖 Prompt for AI Agents
In src/components/Calendar/UserSearchField.tsx around lines 80 to 104, the
mapping can produce entries with undefined/invalid startTime or endTime which
later breaks downstream processing; update the logic to validate timestamps
before creating entries (either check log.from/log.until/log.timestamp exist and
that toMs(...) returns a finite number) and return null for any log where the
computed startTime or endTime is not a valid finite number, or alternatively
filter the resulting array to only keep entries whose startTime and endTime are
finite numbers.

const taskRangeEntries = mergedTasks
.filter((t: any) => t)
.map((t: any) => {
const start = toMs(t.startedOn);
const end = toMs(t.endsOn);
const taskUrl = t.github?.issue?.html_url || undefined;
return {
startTime: start,
endTime: end ?? start,
status: 'ACTIVE',
taskTitle: t.title,
taskUrl,
};
})
.filter((e: any) => e.startTime);

const calendarDataForUser = [...eventEntries, ...taskRangeEntries];

const mapped = [
{
userId: user.id,
data: calendarDataForUser,
},
];

onSearchTextSubmitted(user, mapped);
};

useEffect(() => {
if (userData?.users) {
const users: userDataType[] = userData.users;
const filteredUsers: userDataType[] = users.filter(
(user: userDataType) => !user.incompleteUserDetails
);
const logData: any = filteredUsers.map((user: userDataType) => {
const log = logs[Math.floor(Math.random() * 4)];
return {
data: log,
userId: user.id,
};
});
setData(logData);
setUsersList(filteredUsers);
}
}, [isLoading, userData]);
}, [isUsersLoading, userData]);

const isValidUsername = () => {
const usernames = usersList.map((user: userDataType) => user.username);
Expand Down Expand Up @@ -112,7 +198,11 @@ const SearchField = ({ onSearchTextSubmitted, loading }: SearchFieldProps) => {
<button
className={classNames.userSearchSubmitButton}
disabled={
loading || !(searchText ?? '').trim() || !isValidUsername()
loading ||
isLogsFetching ||
isTasksFetching ||
!(searchText ?? '').trim() ||
!isValidUsername()
}
>
Submit
Expand Down
15 changes: 11 additions & 4 deletions src/pages/calendar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ const UserStatusCalendar: FC = () => {

const setTileClassName = ({ activeStartDate, date, view }: any) => {
Copy link

Choose a reason for hiding this comment

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

Untyped Calendar Tile Properties category Readability

Tell me more
What is the issue?

The function parameters are typed as 'any' instead of using the proper Calendar tile properties type.

Why this matters

Without proper typing, it's unclear what properties are available in the tile object, and unused parameters (activeStartDate, view) are not flagged.

Suggested change ∙ Feature Preview
import { CalendarTileProperties } from 'react-calendar';

const setTileClassName = ({ date }: CalendarTileProperties) => {
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

if (date.getDay() === 0) return 'sunday';
return processedData[0] ? processedData[0][date.getTime()] : null;
const time = date.getTime();
// If there is a task on this date, mark as ACTIVE (green)
if (processedData[1] && processedData[1][time]) return 'ACTIVE';
Copy link

Choose a reason for hiding this comment

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

Hardcoded CSS class name may not exist category Functionality

Tell me more
What is the issue?

The setTileClassName function returns 'ACTIVE' as a CSS class name when tasks exist, but this hardcoded string may not correspond to actual CSS classes defined in the stylesheet.

Why this matters

If the CSS class 'ACTIVE' doesn't exist or is incorrectly styled, the calendar tiles won't display the intended visual styling for active days, leading to inconsistent or broken UI appearance.

Suggested change ∙ Feature Preview

Verify that the 'ACTIVE' CSS class is properly defined in the stylesheet, or use a constant/enum to ensure consistency between the returned class name and actual CSS definitions:

const CSS_CLASSES = {
    ACTIVE: 'active',
    OOO: 'OOO',
    IDLE: 'IDLE',
    SUNDAY: 'sunday'
} as const;

// Then use:
if (processedData[1] && processedData[1][time]) return CSS_CLASSES.ACTIVE;
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

// Otherwise fall back to status classes like OOO/IDLE
return processedData[0] ? processedData[0][time] : null;
Comment on lines +22 to +26
Copy link

Choose a reason for hiding this comment

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

Redundant date.getTime() calls in tile rendering category Performance

Tell me more
What is the issue?

The setTileClassName function calls date.getTime() for every tile rendered in the calendar, which is an unnecessary computation that gets repeated for each calendar tile on every render.

Why this matters

This creates O(n) time complexity for each calendar render where n is the number of visible tiles. For a monthly view with ~42 tiles, this means 42 redundant getTime() calls per render, which can impact performance during frequent re-renders or when users navigate between months.

Suggested change ∙ Feature Preview

Memoize the time calculation or pre-compute timestamps when processing data. Consider using useMemo to cache the time value or modify the data structure to use date strings as keys instead of timestamps to avoid repeated getTime() calls.

Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

Comment on lines +22 to +26
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Normalize keys to start-of-day to match processData.

getDatesInRange uses midnight timestamps; date.getTime() may diverge under TZ/DST.

-import { processData } from '@/utils/userStatusCalendar';
+import { processData, getStartOfDay } from '@/utils/userStatusCalendar';
...
-        const time = date.getTime();
+        const time = getStartOfDay(date).getTime();
         // If there is a task on this date, mark as ACTIVE (green)
         if (processedData[1] && processedData[1][time]) return 'ACTIVE';
         // Otherwise fall back to status classes like OOO/IDLE
         return processedData[0] ? processedData[0][time] : null;
📝 Committable suggestion

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

Suggested change
const time = date.getTime();
// If there is a task on this date, mark as ACTIVE (green)
if (processedData[1] && processedData[1][time]) return 'ACTIVE';
// Otherwise fall back to status classes like OOO/IDLE
return processedData[0] ? processedData[0][time] : null;
import { processData, getStartOfDay } from '@/utils/userStatusCalendar';
...
const time = getStartOfDay(date).getTime();
// If there is a task on this date, mark as ACTIVE (green)
if (processedData[1] && processedData[1][time]) return 'ACTIVE';
// Otherwise fall back to status classes like OOO/IDLE
return processedData[0] ? processedData[0][time] : null;
🤖 Prompt for AI Agents
In src/pages/calendar/index.tsx around lines 22-26, the code uses date.getTime()
which can differ from the midnight-based keys produced by getDatesInRange due to
TZ/DST; normalize the date to the start of day before computing the key (e.g.
create a copy of date, call setHours(0,0,0,0) and use that .getTime()) so the
timestamp matches the processedData keys and lookups succeed.

};

const handleDayClick = (value: Date, event: any) => {
Expand Down Expand Up @@ -48,12 +52,15 @@ const UserStatusCalendar: FC = () => {
return;
}
if (processedData[1] && processedData[1][value.getTime()]) {
const title = processedData[1][value.getTime()];
const url = processedData[2]
? processedData[2][value.getTime()]
: undefined;
const linkText = url ? ` (Link: ${url})` : '';
Comment on lines +55 to +59
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Same normalization on click handler.

-            const title = processedData[1][value.getTime()];
-            const url = processedData[2]
-                ? processedData[2][value.getTime()]
+            const key = getStartOfDay(value).getTime();
+            const title = processedData[1][key];
+            const url = processedData[2]
+                ? processedData[2][key]
                 : undefined;
📝 Committable suggestion

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

Suggested change
const title = processedData[1][value.getTime()];
const url = processedData[2]
? processedData[2][value.getTime()]
: undefined;
const linkText = url ? ` (Link: ${url})` : '';
const key = getStartOfDay(value).getTime();
const title = processedData[1][key];
const url = processedData[2]
? processedData[2][key]
: undefined;
const linkText = url ? ` (Link: ${url})` : '';
🤖 Prompt for AI Agents
In src/pages/calendar/index.tsx around lines 55 to 59, the code uses
value.getTime() to index processedData but the click handler elsewhere uses a
normalized date (zeroed time) — apply the same normalization before calling
getTime() so both reads use the identical key. Compute the normalized date from
value (using the same normalize/zero-time function or logic used in the click
handler) and use normalizedDate.getTime() when accessing processedData[1],
processedData[2] and when building linkText.

setMessage(
`${selectedUser.username} is ACTIVE on ${value.getDate()}-${
MONTHS[value.getMonth()]
}-${value.getFullYear()} having task with title - ${
processedData[1][value.getTime()]
}`
}-${value.getFullYear()} having task with title - ${title}${linkText}`
);
return;
}
Expand Down
5 changes: 5 additions & 0 deletions src/styles/calendar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
color: #000000;
}

.react-calendar .ACTIVE {
background: #22c55e; /* green-500 */
color: #ffffff;
}
Comment on lines +16 to +19
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

🧩 Analysis chain

A11y + specificity: ACTIVE contrast likely fails AA; also increase selector specificity.

White on green-500 (#22c55e) is marginal for normal text, and .react-calendar .ACTIVE may lose to library tile styles. Use a darker green and target the tile element.

-.react-calendar .ACTIVE {
-    background: #22c55e; /* green-500 */
-    color: #ffffff;
-}
+.react-calendar .react-calendar__tile.ACTIVE {
+    background: #16a34a; /* green-600 */
+    color: #ffffff;
+}
+/* Keyboard focus */
+.react-calendar .react-calendar__tile.ACTIVE:focus-visible {
+    outline: 2px solid #111827;
+    outline-offset: 2px;
+}

Run to verify contrast ratio:


🏁 Script executed:

#!/bin/bash
python - <<'PY'
def rel(c):
  c=[int(c[i:i+2],16)/255 for i in (1,3,5)]
  def lin(u): return u/12.92 if u<=0.03928 else ((u+0.055)/1.055)**2.4
  r,g,b=[lin(u) for u in c]
  return 0.2126*r+0.7152*g+0.0722*b
def ratio(fg,bg):
  L1=max(rel(fg),rel(bg)); L2=min(rel(fg),rel(bg))
  return (L1+0.05)/(L2+0.05)
print("contrast(#ffffff on #22c55e) =", ratio("ffffff","22c55e"))
print("contrast(#ffffff on #16a34a) =", ratio("ffffff","16a34a"))
PY

Length of output: 97


Contrast OK (8.11:1); increase selector specificity and add keyboard focus

White (#ffffff) on #22c55e = 8.11:1 (passes WCAG AA and AAA). Target the tile element to avoid being overridden (e.g. .react-calendar .react-calendar__tile.ACTIVE) and add a :focus-visible outline for keyboard users in src/styles/calendar.scss (≈ lines 16–19).

🤖 Prompt for AI Agents
In src/styles/calendar.scss around lines 16 to 19, the .react-calendar .ACTIVE
rule is too generic and lacks a keyboard focus style; update the selector to
target the tile element (e.g. .react-calendar .react-calendar__tile.ACTIVE) to
increase specificity so it won't be overridden, and add a matching
:focus-visible rule (e.g. .react-calendar .react-calendar__tile:focus-visible or
.react-calendar .react-calendar__tile.ACTIVE:focus-visible) that defines a
visible outline or ring for keyboard users; ensure the color/background values
remain the same and the focus outline contrasts and is clearly visible.


.calendar-container {
padding: 20px;
max-width: 750px;
Expand Down
23 changes: 15 additions & 8 deletions src/utils/userStatusCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ interface LOG_DATA {
status: string;
startTime: number;
endTime: number;
taskTitle: string;
taskTitle?: string;
taskUrl?: string;
}

export const getStartOfDay = (date: Date): Date => {
Expand All @@ -22,13 +23,13 @@ export const getStartOfDay = (date: Date): Date => {

export const getDatesInRange = (startDate: Date, endDate: Date) => {
const date = getStartOfDay(startDate);
const dates = [];
const dates: number[] = [];

if (
!(startDate instanceof Date && !isNaN(startDate.getTime())) ||
!(endDate instanceof Date && !isNaN(endDate.getTime()))
)
return [];
return [] as number[];

while (date <= getStartOfDay(endDate)) {
dates.push(getStartOfDay(date).getTime());
Expand All @@ -41,31 +42,37 @@ export const getDatesInRange = (startDate: Date, endDate: Date) => {
export const processData = (
itemId: string | null,
data: []
): [object, object] => {
): [object, object, object] => {
Comment on lines 41 to +45
Copy link

Choose a reason for hiding this comment

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

Non-specific return type category Design

Tell me more
What is the issue?

The return type [object, object, object] is too generic and loses type safety. The function actually returns specific record types.

Why this matters

Using generic object types masks the actual structure of the returned data, making it harder to use correctly and more prone to runtime errors.

Suggested change ∙ Feature Preview
type ProcessDataReturn = [Record<number, string>, Record<number, string>, Record<number, string>];

export const processData = (
    itemId: string | null,
    data: []
): ProcessDataReturn
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

if (!itemId) {
Comment on lines +45 to 46
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Type the API surface precisely (no []/object).

Use concrete types for inputs and return to prevent misuse and enable tooling.

-export const processData = (
-    itemId: string | null,
-    data: []
-): [object, object, object] => {
+type StatusMap = Record<number, string>;
+type TaskTitleMap = Record<number, string>;
+type TaskUrlMap = Record<number, string>;
+
+export const processData = (
+    itemId: string | null,
+    data: LOG_TYPE[]
+): [StatusMap, TaskTitleMap, TaskUrlMap] => {
📝 Committable suggestion

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

Suggested change
): [object, object, object] => {
if (!itemId) {
type StatusMap = Record<number, string>;
type TaskTitleMap = Record<number, string>;
type TaskUrlMap = Record<number, string>;
export const processData = (
itemId: string | null,
data: LOG_TYPE[]
): [StatusMap, TaskTitleMap, TaskUrlMap] => {
if (!itemId) {
🤖 Prompt for AI Agents
In src/utils/userStatusCalendar.ts around lines 45-46, the function signature
currently returns a vague [object, object, object]; replace that with a precise
tuple of concrete types (e.g., [EventSummary, UserAvailability,
CalendarMetadata]) or whatever three specific interfaces match each returned
value, and explicitly type the function parameters (e.g., itemId: string |
undefined, userId: string). Add or import the corresponding interfaces/types
near the top of the file, update the return statements to ensure they conform to
the new types, and adjust any callers if needed.

return [{}, {}];
return [{}, {}, {}];
} else {
const log: any = data.find((log: LOG_TYPE) => {
return log.userId === itemId;
});
if (!log || log.data?.length == 0) return [{}, {}];
if (!log || log.data?.length == 0) return [{}, {}, {}];
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Use strict equality.

-        if (!log || log.data?.length == 0) return [{}, {}, {}];
+        if (!log || log.data?.length === 0) return [{}, {}, {}];
📝 Committable suggestion

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

Suggested change
if (!log || log.data?.length == 0) return [{}, {}, {}];
if (!log || log.data?.length === 0) return [{}, {}, {}];
🤖 Prompt for AI Agents
In src/utils/userStatusCalendar.ts around line 52, the conditional uses loose
equality (==) when checking log.data?.length; replace it with strict equality
(===) so the line reads to check for length === 0, i.e., use if (!log ||
log.data?.length === 0) return [{}, {}, {}]; to ensure type-safe comparison.

const dictWithStatus: Record<number, string> = {};
const dictWithTask: Record<number, string> = {};
const dictWithTaskUrl: Record<number, string> = {};
log.data.forEach((logData: LOG_DATA) => {
const dates = getDatesInRange(
new Date(logData.startTime),
new Date(logData.endTime)
);
if (logData.status === 'ACTIVE') {
dates.forEach((dateTimestamp) => {
dictWithTask[dateTimestamp] = logData.taskTitle;
if (logData.taskTitle) {
dictWithTask[dateTimestamp] = logData.taskTitle;
}
if (logData.taskUrl) {
dictWithTaskUrl[dateTimestamp] = logData.taskUrl;
}
});
Comment on lines +63 to 69
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick

Optionally also record 'ACTIVE' in status map for completeness.

If other consumers read only dictWithStatus, they’ll miss ACTIVE days.

             if (logData.status === 'ACTIVE') {
                 dates.forEach((dateTimestamp) => {
+                    dictWithStatus[dateTimestamp] = 'ACTIVE';
                     if (logData.taskTitle) {
                         dictWithTask[dateTimestamp] = logData.taskTitle;
                     }
                     if (logData.taskUrl) {
                         dictWithTaskUrl[dateTimestamp] = logData.taskUrl;
                     }
                 });
📝 Committable suggestion

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

Suggested change
if (logData.taskTitle) {
dictWithTask[dateTimestamp] = logData.taskTitle;
}
if (logData.taskUrl) {
dictWithTaskUrl[dateTimestamp] = logData.taskUrl;
}
});
if (logData.status === 'ACTIVE') {
dates.forEach((dateTimestamp) => {
dictWithStatus[dateTimestamp] = 'ACTIVE';
if (logData.taskTitle) {
dictWithTask[dateTimestamp] = logData.taskTitle;
}
if (logData.taskUrl) {
dictWithTaskUrl[dateTimestamp] = logData.taskUrl;
}
});
🤖 Prompt for AI Agents
In src/utils/userStatusCalendar.ts around lines 63 to 69, when populating
dictWithTask and dictWithTaskUrl for a given dateTimestamp, also mark
dictWithStatus[dateTimestamp] = 'ACTIVE' so consumers that only read
dictWithStatus see those days; implement this assignment alongside the existing
task/title/url assignments and optionally guard it to avoid overwriting a more
specific status if one already exists for that date.

} else {
dates.forEach((dateTimestamp) => {
dictWithStatus[dateTimestamp] = logData.status;
});
}
});
return [dictWithStatus, dictWithTask];
return [dictWithStatus, dictWithTask, dictWithTaskUrl];
}
};
Loading