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
90 changes: 89 additions & 1 deletion apps/frontend/app/routes/_dashboard._index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ import {
DailyUserActivitiesResponseGroupedBy,
DashboardElementLot,
GraphqlSortOrder,
MediaLot,
MediaSortBy,
MinimalUserAnalyticsDocument,
TrendingMetadataDocument,
UserMetadataListDocument,
type UserMetadataListInput,
UserMetadataRecommendationsDocument,
type UserUpcomingCalendarEventInput,
UserUpcomingCalendarEventsDocument,
Expand All @@ -31,7 +35,7 @@ import {
} from "@tabler/icons-react";
import { skipToken, useQuery } from "@tanstack/react-query";
import CryptoJS from "crypto-js";
import { type ReactNode, useMemo } from "react";
import { type ReactNode, useMemo, useState } from "react";
import { redirect } from "react-router";
import { ClientOnly } from "remix-utils/client-only";
import { match } from "ts-pattern";
Expand All @@ -43,6 +47,7 @@ import {
SkeletonLoader,
} from "~/components/common";
import { ApplicationGrid } from "~/components/common/layout";
import { DebouncedSearchInput } from "~/components/common/filters";
Comment on lines 49 to +50

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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Import out of alphabetical order.

~/components/common/filters should precede ~/components/common/layout. This ordering is tooling-enforced and may fail lint/CI.

♻️ Proposed fix
-import { ApplicationGrid } from "~/components/common/layout";
 import { DebouncedSearchInput } from "~/components/common/filters";
+import { ApplicationGrid } from "~/components/common/layout";

As per coding guidelines: "Import statements should follow alphabetical ordering as enforced by tooling".

📝 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
import { ApplicationGrid } from "~/components/common/layout";
import { DebouncedSearchInput } from "~/components/common/filters";
import { DebouncedSearchInput } from "~/components/common/filters";
import { ApplicationGrid } from "~/components/common/layout";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/frontend/app/routes/_dashboard._index.tsx` around lines 49 - 50, Imports
are out of alphabetical order; move the import of DebouncedSearchInput (from
~/components/common/filters) so it appears before the import of ApplicationGrid
(from ~/components/common/layout). Update the two import lines so they are
alphabetically sorted by module path (filters before layout) to satisfy
tooling/linters and CI.

import { DisplaySummarySection } from "~/components/common/summary";
import { MetadataDisplayItem } from "~/components/media/display-items";
import { dayjsLib } from "~/lib/shared/date-utils";
Expand Down Expand Up @@ -86,6 +91,10 @@ export default function Page() {
const userDetails = useUserDetails();
const userCollections = useUserCollections();
const userPreferences = useUserPreferences();
const [dashboardSearchLot, setDashboardSearchLot] = useState<MediaLot | "ALL">(
"ALL",
);
const [dashboardSearchQuery, setDashboardSearchQuery] = useState("");
Comment on lines +94 to +97

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.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Order the new hooks by ascending line length.

dashboardSearchQuery (single line) should be declared before the multi-line dashboardSearchLot.

♻️ Proposed fix
+	const [dashboardSearchQuery, setDashboardSearchQuery] = useState("");
 	const [dashboardSearchLot, setDashboardSearchLot] = useState<MediaLot | "ALL">(
 		"ALL",
 	);
-	const [dashboardSearchQuery, setDashboardSearchQuery] = useState("");

As per coding guidelines: "When declaring multiple variables in sequence (particularly React hooks), order them by ascending line length".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/frontend/app/routes/_dashboard._index.tsx` around lines 94 - 97, Swap
the two React hook declarations so the shorter single-line state hook
dashboardSearchQuery (useState("")) is declared before the multi-line
dashboardSearchLot (useState<MediaLot | "ALL">("ALL")), i.e., reorder the
declarations of dashboardSearchQuery and dashboardSearchLot to follow ascending
line length while keeping useState types and initializers unchanged.

const { startOnboardingTour } = useOnboardingTour();
const isOnboardingTourCompleted = useIsOnboardingTourCompleted();

Expand Down Expand Up @@ -155,6 +164,28 @@ export default function Page() {
}),
});

const dashboardSearchInput: UserMetadataListInput = useMemo(
() => ({
lot: dashboardSearchLot === "ALL" ? undefined : dashboardSearchLot,
search: {
page: 1,
take: 24,
query: dashboardSearchQuery || undefined,
},
sort: { by: MediaSortBy.LastUpdated, order: GraphqlSortOrder.Desc },
}),
[dashboardSearchLot, dashboardSearchQuery],
);

const dashboardSearchResultsQuery = useQuery({
enabled: dashboardSearchQuery.length > 0,
queryKey: queryFactory.media.userMetadataList(dashboardSearchInput).queryKey,
queryFn: () =>
clientGqlService.request(UserMetadataListDocument, {
input: dashboardSearchInput,
}),
});

const latestUserSummary =
userAnalyticsQuery.data?.userAnalytics.response.activities.items.at(0);

Expand Down Expand Up @@ -187,6 +218,63 @@ export default function Page() {
</>
)}
</ClientOnly>
<Stack gap="sm">
<SectionTitle text="Discover your library" />
<DebouncedSearchInput
value={dashboardSearchQuery}
onChange={setDashboardSearchQuery}
placeholder="Search across all media..."
/>
<Group gap="xs" wrap="nowrap" style={{ overflowX: "auto" }}>
{(
[
{ label: "All", value: "ALL" as const },
{ label: "Anime", value: MediaLot.Anime },
{ label: "Books", value: MediaLot.Book },
{ label: "Manga", value: MediaLot.Manga },
{ label: "Movies", value: MediaLot.Movie },
{ label: "Music", value: MediaLot.Music },
{ label: "Shows", value: MediaLot.Show },
{ label: "Podcasts", value: MediaLot.Podcast },
{ label: "Games", value: MediaLot.VideoGame },
{ label: "Comics", value: MediaLot.ComicBook },
{ label: "Audiobooks", value: MediaLot.AudioBook },
{ label: "Visual Novels", value: MediaLot.VisualNovel },
] as const
).map((o) => (
<Button
key={o.value}
size="compact-sm"
variant={dashboardSearchLot === o.value ? "light" : "default"}
onClick={() => setDashboardSearchLot(o.value)}
>
{o.label}
</Button>
))}
</Group>
{dashboardSearchQuery.length > 0 ? (
dashboardSearchResultsQuery.data ? (
dashboardSearchResultsQuery.data.userMetadataList.response.items
.length > 0 ? (
<ApplicationGrid>
{dashboardSearchResultsQuery.data.userMetadataList.response.items.map(
(m) => (
<MetadataDisplayItem
key={m}
metadataId={m}
shouldHighlightNameIfInteracted
/>
),
)}
</ApplicationGrid>
) : (
<Text c="dimmed">No media found matching your query.</Text>
)
) : (
<SkeletonLoader />
)
) : null}
</Stack>
{userPreferences.general.dashboard.map((de) =>
match([de.section, de.hidden])
.with([DashboardElementLot.Upcoming, false], ([v, _]) => (
Expand Down