Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.4",
"@tanstack/react-query": "^5.90.6",
"@vercel/analytics": "^2.0.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
Expand Down
8 changes: 7 additions & 1 deletion src/app/DisplayEntities.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { track } from "@vercel/analytics";
import Link from "next/link";
import { useState } from "react";
import { EntityBadge } from "@/components/Badges";
Expand Down Expand Up @@ -89,7 +90,12 @@ function EntityItem(props: {
entity: { id: string; name: string; subtype: string };
}) {
return (
<Item size="sm" className="w-full px-2 py-1" asChild>
<Item
onClick={() => track("global search", { action: "entity clicked" })}
size="sm"
className="w-full px-2 py-1"
asChild
>
<Link href={`/${props.entity.id}`} prefetch={false} className="size-full">
<ItemContent className="gap-0">
<ItemTitle>{props.entity.name}</ItemTitle>
Expand Down
4 changes: 3 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import "./globals.css";
import { Analytics } from "@vercel/analytics/next";
import Script from "next/script";
import LoadingIndicator from "@/components/LoadingIndicator";
import { barlow } from "@/lib/fonts";
Expand Down Expand Up @@ -30,9 +31,10 @@ export default function RootLayout({
<Footer />
<LoadingIndicator />
</Providers>
<Analytics />
<Script id="feedback-button">
{`(function(){window.onUsersnapCXLoad=function(e){e.init()};var e=document.createElement("script");e.defer=1,e.src="https://widget.usersnap.com/global/load/b4393e90-ec13-4338-b299-7b6f122b7de3?onload=onUsersnapCXLoad",document.getElementsByTagName("head")[0].appendChild(e)})();`}
</Script>{" "}
</Script>
</body>
</html>
);
Expand Down
37 changes: 27 additions & 10 deletions src/components/ActionButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { track } from "@vercel/analytics";
import { Info } from "lucide-react";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
Expand Down Expand Up @@ -70,6 +71,14 @@ function FilterByRegistrationYear(props: { entity: Entity }) {
(y) => y.id === searchParams.get(SEARCH_PARAMETERS.REGISTRATION_YEAR),
) || null;

function onItemClick() {
track("filters", {
on: "registration year",
action: "filter clicked",
});
setOpen(false);
}

return (
<Combobox
items={data?.registrationYears ?? []}
Expand Down Expand Up @@ -105,11 +114,7 @@ function FilterByRegistrationYear(props: { entity: Entity }) {
const href = `/${props.entity.id}?${params.toString()}`;

return (
<ComboboxItem
key={item.id}
value={item.id}
onClick={() => setOpen(false)}
>
<ComboboxItem key={item.id} value={item.id} onClick={onItemClick}>
<Link href={href} prefetch className="size-full">
<Item size="sm" className="px-0 py-0.5">
<ItemContent className="gap-0">
Expand Down Expand Up @@ -139,6 +144,14 @@ function FilterByResourceType(props: { entity: Entity }) {
(rt) => rt.id === searchParams.get(SEARCH_PARAMETERS.RESOURCE_TYPE),
) || null;

function onItemClick() {
track("filters", {
on: "resource type",
action: "filter clicked",
});
setOpen(false);
}

return (
<Combobox
items={data?.resourceTypeData ?? []}
Expand Down Expand Up @@ -172,11 +185,7 @@ function FilterByResourceType(props: { entity: Entity }) {
const href = `/${props.entity.id}?${params.toString()}`;

return (
<ComboboxItem
key={item.id}
value={item.id}
onClick={() => setOpen(false)}
>
<ComboboxItem key={item.id} value={item.id} onClick={onItemClick}>
<Link href={href} prefetch className="size-full">
<Item size="sm" className="px-0 py-0.5">
<ItemContent className="gap-0">
Expand Down Expand Up @@ -207,9 +216,16 @@ function FilterByQuery(props: { entity: Entity }) {

const disabled = !query.trim();

const trackQuery = () =>
track("filters", {
on: "query",
action: "query submitted",
});

function onKeyDown(e: KeyboardEvent<HTMLInputElement>) {
if (e.key === "Enter") {
e.preventDefault();
trackQuery();
router.push(href);
}
Comment on lines 225 to 230
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Enter path currently lacks disabled guard in this handler.
rg -n -C3 'const disabled|function onKeyDown|if \(e.key === "Enter"\)|trackQuery\(\)|router.push\(href\)' src/components/ActionButtons.tsx

Repository: datacite/puli

Length of output: 549


Guard Enter handling when query is empty.

The onKeyDown handler at line 226 submits on Enter regardless of the disabled state, allowing empty or whitespace-only input to trigger "query submitted" tracking and navigation, while the button itself prevents this via the disabled prop.

🔧 Proposed fix
 function onKeyDown(e: KeyboardEvent<HTMLInputElement>) {
-  if (e.key === "Enter") {
-    e.preventDefault();
-    trackQuery();
-    router.push(href);
-  }
+  if (e.key !== "Enter" || disabled) return;
+  e.preventDefault();
+  trackQuery();
+  router.push(href);
 }
📝 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
function onKeyDown(e: KeyboardEvent<HTMLInputElement>) {
if (e.key === "Enter") {
e.preventDefault();
trackQuery();
router.push(href);
}
function onKeyDown(e: KeyboardEvent<HTMLInputElement>) {
if (e.key !== "Enter" || disabled) return;
e.preventDefault();
trackQuery();
router.push(href);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ActionButtons.tsx` around lines 225 - 230, The onKeyDown
handler currently submits on Enter regardless of input state; update the
onKeyDown(KeyboardEvent<HTMLInputElement>) function to first check the same
condition the button uses (e.g., !disabled and query.trim().length > 0 or the
component's isDisabled flag) and only then call trackQuery() and
router.push(href); ensure you mirror the exact validation used by the submit
button so Enter does nothing for empty/whitespace inputs.

}
Expand All @@ -234,6 +250,7 @@ function FilterByQuery(props: { entity: Entity }) {
)}
disabled={disabled}
asChild={!disabled}
onClick={trackQuery}
>
<Link href={href} prefetch>
Filter by Query
Expand Down
23 changes: 18 additions & 5 deletions src/components/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { track } from "@vercel/analytics";
import { ChevronsUpDown, Home, Slash } from "lucide-react";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
Expand Down Expand Up @@ -86,6 +87,11 @@ function BreadcrumbContent(props: {
}) {
const className = `flex flex-row items-center ${props.entity.id === props.active.id ? "bg-black/0 font-semibold" : ""}`;

function onClick(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
track("breadcrumbs", { on: "breadcrumb item", action: "entity clicked" });
e.stopPropagation();
}

const BreadcrumbPageLink = (wrapperProps: { children: ReactNode }) =>
props.entity.id === props.active.id ? (
<BreadcrumbPage {...wrapperProps} className={className} />
Expand All @@ -94,7 +100,7 @@ function BreadcrumbContent(props: {
{...wrapperProps}
href={`/${props.entity.id}`}
className={className}
onMouseDown={(e) => e.stopPropagation()}
onMouseDown={onClick}
/>
);

Expand Down Expand Up @@ -161,15 +167,22 @@ function SiblingSelect(props: {
/>
<ComboboxEmpty>
No{" "}
{props.parent?.type === "provider"
? "repositories"
: "organizations"}{" "}
{props.parent?.type === "provider" ? "repositories" : "organizations"}{" "}
found.
</ComboboxEmpty>
<ComboboxList>
{(item: Entity) => {
return (
<ComboboxItem value={item} key={item.id}>
<ComboboxItem
onClick={() =>
track("breadcrumbs", {
on: "dropdown",
action: "entity clicked",
})
}
value={item}
key={item.id}
>
<Link
href={`/${item.id}?${searchParams.toString()}`}
prefetch
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,11 @@
dependencies:
csstype "^3.0.2"

"@vercel/analytics@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@vercel/analytics/-/analytics-2.0.1.tgz#d7d7bd37af7cfbeea95db5f132ae4889f4d26407"
integrity sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==

aria-hidden@^1.2.4:
version "1.2.6"
resolved "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz"
Expand Down
Loading