Skip to content
Merged
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
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
10 changes: 9 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,14 @@ 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", { on: "result", action: "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: "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: "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: "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 entity", action: "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 entity",
action: "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