Skip to content
Open
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
107 changes: 84 additions & 23 deletions src/pages/scanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@ import { Scanner } from "@yudiel/react-qr-scanner";
import Drawer from "../components/Drawer";
import { getServerAuthSession } from "../server/common/get-server-auth-session";
import { Role, Station } from "@prisma/client";
import { useCallback, useEffect, useReducer, useRef, useState } from "react";
import {
useCallback,
useEffect,
useReducer,
useRef,
useState,
useSyncExternalStore,
} from "react";
import clsx from "clsx";
import { trpc } from "../utils/trpc";
import { useIsMutating } from "@tanstack/react-query";
import { useOfflineQueue } from "../hooks/useOfflineQueue";
import { z } from "zod";
import { Input } from "../components/Input";
Expand All @@ -25,6 +33,31 @@ import {
stationLabels,
} from "../schemas/scanner";

function subscribeToOnlineStatus(callback: () => void) {
window.addEventListener("online", callback);
window.addEventListener("offline", callback);
return () => {
window.removeEventListener("online", callback);
window.removeEventListener("offline", callback);
};
}

function getOnlineSnapshot() {
return navigator.onLine;
}

function getServerSnapshot() {
return true;
}

function useOnlineStatus() {
return useSyncExternalStore(
subscribeToOnlineStatus,
getOnlineSnapshot,
getServerSnapshot,
);
}

function wizardReducer(state: WizardState, action: WizardAction): WizardState {
switch (action.type) {
case "SELECT_STATION": {
Expand Down Expand Up @@ -197,6 +230,8 @@ const ScannerUI: React.FC<{
const [mode, setMode] = useState<"camera" | "manual">("camera");
const [searchQuery, setSearchQuery] = useState("");
const [debouncedSearchQuery, setDebouncedSearchQuery] = useState("");
const isOnline = useOnlineStatus();
const pendingMutations = useIsMutating();

// Reset state when switching modes
useEffect(() => {
Expand Down Expand Up @@ -332,29 +367,55 @@ const ScannerUI: React.FC<{
return (
<>
<div className="mb-4 flex flex-col gap-3">
<div className="flex items-center gap-2 bg-neutral-200 dark:bg-neutral-700 rounded-lg p-1 w-fit mx-auto">
<button
onClick={() => setMode("camera")}
className={clsx(
"px-3 py-2 rounded text-sm font-medium transition-colors",
mode === "camera"
? "bg-primary text-white"
: "text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-100",
)}
>
Camera
</button>
<button
onClick={() => setMode("manual")}
className={clsx(
"px-3 py-2 rounded text-sm font-medium transition-colors",
mode === "manual"
? "bg-primary text-white"
: "text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-100",
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 bg-neutral-200 dark:bg-neutral-700 rounded-lg p-1">
<button
onClick={() => setMode("camera")}
className={clsx(
"px-3 py-2 rounded text-sm font-medium transition-colors",
mode === "camera"
? "bg-primary text-white"
: "text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-100",
)}
>
Camera
</button>
<button
onClick={() => setMode("manual")}
className={clsx(
"px-3 py-2 rounded text-sm font-medium transition-colors",
mode === "manual"
? "bg-primary text-white"
: "text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-100",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider using the size-1.5 utility class instead of w-1.5 h-1.5 for more concise code. Tailwind v3.4+ supports the size-* shorthand for setting both width and height.

Suggested change
: "text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-100",
<span className="size-1.5 rounded-full bg-yellow-500 animate-pulse" />

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/pages/scanner.tsx
Line: 389:389

Comment:
Consider using the `size-1.5` utility class instead of `w-1.5 h-1.5` for more concise code. Tailwind v3.4+ supports the `size-*` shorthand for setting both width and height.

```suggestion
                <span className="size-1.5 rounded-full bg-yellow-500 animate-pulse" />
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

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

)}
>
Comment on lines +388 to +391
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The hardcoded color values (bg-yellow-100, bg-yellow-900/30, text-yellow-700, text-yellow-400, bg-yellow-500) violate the custom instruction rule that states: "Use Tailwind CSS variables for colors instead of hardcoded color values."

Consider defining these colors in the Tailwind config as semantic tokens (e.g., --color-warning-light, --color-warning-dark) and using them via CSS variables for better maintainability and theme consistency.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/pages/scanner.tsx
Line: 388:391

Comment:
The hardcoded color values (`bg-yellow-100`, `bg-yellow-900/30`, `text-yellow-700`, `text-yellow-400`, `bg-yellow-500`) violate the custom instruction rule that states: "Use Tailwind CSS variables for colors instead of hardcoded color values."

Consider defining these colors in the Tailwind config as semantic tokens (e.g., `--color-warning-light`, `--color-warning-dark`) and using them via CSS variables for better maintainability and theme consistency.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

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

Manual
</button>
</div>
<div className="flex items-center gap-1.5">
{pendingMutations > 0 && (
<div className="flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400">
<span className="w-1.5 h-1.5 rounded-full bg-yellow-500 animate-pulse" />
{pendingMutations}
</div>
)}
>
Manual
</button>
<div
className={clsx(
"flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium",
isOnline
Comment on lines 401 to +405
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider using the size-1.5 utility class instead of w-1.5 h-1.5 for more concise code. Tailwind v3.4+ supports the size-* shorthand for setting both width and height.

Suggested change
)}
>
Manual
</button>
<div
className={clsx(
"flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium",
isOnline
<span
className={clsx(
"size-1.5 rounded-full",
isOnline ? "bg-green-500" : "bg-red-500",
)}
/>

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/pages/scanner.tsx
Line: 401:405

Comment:
Consider using the `size-1.5` utility class instead of `w-1.5 h-1.5` for more concise code. Tailwind v3.4+ supports the `size-*` shorthand for setting both width and height.

```suggestion
              <span
                className={clsx(
                  "size-1.5 rounded-full",
                  isOnline ? "bg-green-500" : "bg-red-500",
                )}
              />
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

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

? "bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400"
: "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400",
)}
Comment on lines +393 to +408
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The hardcoded color values (bg-green-100, bg-green-900/30, text-green-700, text-green-400, bg-green-500, bg-red-100, bg-red-900/30, text-red-700, text-red-400, bg-red-500) violate the custom instruction rule that states: "Use Tailwind CSS variables for colors instead of hardcoded color values."

Consider defining semantic color tokens in the Tailwind config (e.g., --color-success, --color-error, --color-online, --color-offline) for better maintainability and theme consistency across the application.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/pages/scanner.tsx
Line: 393:408

Comment:
The hardcoded color values (`bg-green-100`, `bg-green-900/30`, `text-green-700`, `text-green-400`, `bg-green-500`, `bg-red-100`, `bg-red-900/30`, `text-red-700`, `text-red-400`, `bg-red-500`) violate the custom instruction rule that states: "Use Tailwind CSS variables for colors instead of hardcoded color values."

Consider defining semantic color tokens in the Tailwind config (e.g., `--color-success`, `--color-error`, `--color-online`, `--color-offline`) for better maintainability and theme consistency across the application.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

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

>
Comment on lines +386 to +409
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The status indicators are missing accessibility attributes. For users relying on screen readers, the visual status information (online/offline, pending mutations) is not properly conveyed.

Consider adding ARIA attributes:

  • Add role="status" to both status indicators
  • Add aria-live="polite" to announce status changes
  • Add descriptive aria-label attributes (e.g., aria-label="Connection status: Online" or aria-label="Pending operations: 3")

Example improvement:

Suggested change
"px-3 py-2 rounded text-sm font-medium transition-colors",
mode === "manual"
? "bg-primary text-white"
: "text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-100",
)}
>
Manual
</button>
</div>
<div className="flex items-center gap-1.5">
{pendingMutations > 0 && (
<div className="flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400">
<span className="w-1.5 h-1.5 rounded-full bg-yellow-500 animate-pulse" />
{pendingMutations}
</div>
)}
>
Manual
</button>
<div
className={clsx(
"flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium",
isOnline
? "bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400"
: "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400",
)}
>
<div className="flex items-center gap-1.5">
{pendingMutations > 0 && (
<div
className="flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400"
role="status"
aria-live="polite"
aria-label={`${pendingMutations} pending operation${pendingMutations !== 1 ? 's' : ''}`}
>
<span className="w-1.5 h-1.5 rounded-full bg-yellow-500 animate-pulse" aria-hidden="true" />
{pendingMutations}
</div>
)}
<div
className={clsx(
"flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium",
isOnline
? "bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400"
: "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400",
)}
role="status"
aria-live="polite"
aria-label={`Connection status: ${isOnline ? 'Online' : 'Offline'}`}
>
<span
className={clsx(
"w-1.5 h-1.5 rounded-full",
isOnline ? "bg-green-500" : "bg-red-500",
)}
aria-hidden="true"
/>
{isOnline ? "Online" : "Offline"}
</div>
</div>
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/pages/scanner.tsx
Line: 386:409

Comment:
The status indicators are missing accessibility attributes. For users relying on screen readers, the visual status information (online/offline, pending mutations) is not properly conveyed.

Consider adding ARIA attributes:
- Add `role="status"` to both status indicators
- Add `aria-live="polite"` to announce status changes
- Add descriptive `aria-label` attributes (e.g., `aria-label="Connection status: Online"` or `aria-label="Pending operations: 3"`)

Example improvement:
```suggestion
          <div className="flex items-center gap-1.5">
            {pendingMutations > 0 && (
              <div 
                className="flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400"
                role="status"
                aria-live="polite"
                aria-label={`${pendingMutations} pending operation${pendingMutations !== 1 ? 's' : ''}`}
              >
                <span className="w-1.5 h-1.5 rounded-full bg-yellow-500 animate-pulse" aria-hidden="true" />
                {pendingMutations}
              </div>
            )}
            <div
              className={clsx(
                "flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium",
                isOnline
                  ? "bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400"
                  : "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400",
              )}
              role="status"
              aria-live="polite"
              aria-label={`Connection status: ${isOnline ? 'Online' : 'Offline'}`}
            >
              <span
                className={clsx(
                  "w-1.5 h-1.5 rounded-full",
                  isOnline ? "bg-green-500" : "bg-red-500",
                )}
                aria-hidden="true"
              />
              {isOnline ? "Online" : "Offline"}
            </div>
          </div>
```

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

<span
className={clsx(
"w-1.5 h-1.5 rounded-full",
isOnline ? "bg-green-500" : "bg-red-500",
)}
/>
{isOnline ? "Online" : "Offline"}
</div>
</div>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
Expand Down