Skip to content

Commit 7c5c20c

Browse files
committed
Enhance error handling in PackageSearch component and PackageSearch route
1 parent 5ac0d36 commit 7c5c20c

File tree

3 files changed

+384
-180
lines changed

3 files changed

+384
-180
lines changed

apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx

Lines changed: 122 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -10,97 +10,135 @@ import { type OutletContextShape } from "~/root";
1010
import { DapperTs } from "@thunderstore/dapper-ts";
1111

1212
import type { Route } from "./+types/PackageSearch";
13+
import { throwUserFacingPayloadResponse } from "cyberstorm/utils/errors/userFacingErrorResponse";
14+
import { handleLoaderError } from "cyberstorm/utils/errors/handleLoaderError";
15+
import { createNotFoundMapping } from "cyberstorm/utils/errors/loaderMappings";
16+
import { NimbusDefaultRouteErrorBoundary } from "cyberstorm/utils/errors/NimbusErrorBoundary";
17+
import { getLoaderTools } from "cyberstorm/utils/getLoaderTools";
18+
import { parseIntegerSearchParam } from "cyberstorm/utils/searchParamsUtils";
19+
20+
interface PackageSearchQuery {
21+
ordering: string;
22+
page: number | undefined;
23+
search: string;
24+
includedCategories: string[] | undefined;
25+
excludedCategories: string[] | undefined;
26+
section: string;
27+
nsfw: boolean;
28+
deprecated: boolean;
29+
}
30+
31+
const communityNotFoundMappings = [
32+
createNotFoundMapping(
33+
"Community not found.",
34+
"We could not find the requested community."
35+
),
36+
];
37+
38+
function resolvePackageSearchQuery(request: Request): PackageSearchQuery {
39+
const searchParams = new URL(request.url).searchParams;
40+
const ordering = searchParams.get("ordering") ?? PackageOrderOptions.Updated;
41+
const page = parseIntegerSearchParam(searchParams.get("page"));
42+
const search = searchParams.get("search") ?? "";
43+
const included = searchParams.get("includedCategories");
44+
const excluded = searchParams.get("excludedCategories");
45+
const sectionParam = searchParams.get("section");
46+
const section = sectionParam
47+
? sectionParam === "all"
48+
? ""
49+
: sectionParam
50+
: "";
51+
const nsfw = searchParams.get("nsfw") === "true";
52+
const deprecated = searchParams.get("deprecated") === "true";
53+
54+
return {
55+
ordering,
56+
page,
57+
search,
58+
includedCategories: included?.split(",") ?? undefined,
59+
excludedCategories: excluded?.split(",") ?? undefined,
60+
section,
61+
nsfw,
62+
deprecated,
63+
};
64+
}
1365

1466
export async function loader({ params, request }: Route.LoaderArgs) {
1567
if (params.communityId) {
16-
const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]);
17-
const dapper = new DapperTs(() => {
68+
const { dapper } = getLoaderTools();
69+
try {
70+
const query = resolvePackageSearchQuery(request);
71+
1872
return {
19-
apiHost: publicEnvVariables.VITE_API_URL,
20-
sessionId: undefined,
73+
filters: await dapper.getCommunityFilters(params.communityId),
74+
listings: await dapper.getPackageListings(
75+
{
76+
kind: "community",
77+
communityId: params.communityId,
78+
},
79+
query.ordering ?? "",
80+
query.page,
81+
query.search,
82+
query.includedCategories,
83+
query.excludedCategories,
84+
query.section,
85+
query.nsfw,
86+
query.deprecated
87+
),
2188
};
22-
});
23-
const searchParams = new URL(request.url).searchParams;
24-
const ordering =
25-
searchParams.get("ordering") ?? PackageOrderOptions.Updated;
26-
const page = searchParams.get("page");
27-
const search = searchParams.get("search");
28-
const includedCategories = searchParams.get("includedCategories");
29-
const excludedCategories = searchParams.get("excludedCategories");
30-
const section = searchParams.get("section");
31-
const nsfw = searchParams.get("nsfw");
32-
const deprecated = searchParams.get("deprecated");
33-
const filters = await dapper.getCommunityFilters(params.communityId);
34-
35-
return {
36-
filters: filters,
37-
listings: await dapper.getPackageListings(
38-
{
39-
kind: "community",
40-
communityId: params.communityId,
41-
},
42-
ordering ?? "",
43-
page === null ? undefined : Number(page),
44-
search ?? "",
45-
includedCategories?.split(",") ?? undefined,
46-
excludedCategories?.split(",") ?? undefined,
47-
section ? (section === "all" ? "" : section) : "",
48-
nsfw === "true" ? true : false,
49-
deprecated === "true" ? true : false
50-
),
51-
};
89+
} catch (error) {
90+
handleLoaderError(error, { mappings: communityNotFoundMappings });
91+
}
5292
}
53-
throw new Response("Community not found", { status: 404 });
93+
throwUserFacingPayloadResponse({
94+
headline: "Community not found.",
95+
description: "We could not find the requested community.",
96+
category: "not_found",
97+
status: 404,
98+
});
5499
}
55100

56101
export async function clientLoader({
57102
request,
58103
params,
59104
}: Route.ClientLoaderArgs) {
60105
if (params.communityId) {
61-
const tools = getSessionTools();
62-
const dapper = new DapperTs(() => {
63-
return {
64-
apiHost: tools?.getConfig().apiHost,
65-
sessionId: tools?.getConfig().sessionId,
66-
};
67-
});
68-
const searchParams = new URL(request.url).searchParams;
69-
const ordering =
70-
searchParams.get("ordering") ?? PackageOrderOptions.Updated;
71-
const page = searchParams.get("page");
72-
const search = searchParams.get("search");
73-
const includedCategories = searchParams.get("includedCategories");
74-
const excludedCategories = searchParams.get("excludedCategories");
75-
const section = searchParams.get("section");
76-
const nsfw = searchParams.get("nsfw");
77-
const deprecated = searchParams.get("deprecated");
78-
const filters = dapper.getCommunityFilters(params.communityId);
106+
const { dapper } = getLoaderTools();
107+
const query = resolvePackageSearchQuery(request);
79108
return {
80-
filters: filters,
81-
listings: dapper.getPackageListings(
82-
{
83-
kind: "community",
84-
communityId: params.communityId,
85-
},
86-
ordering ?? "",
87-
page === null ? undefined : Number(page),
88-
search ?? "",
89-
includedCategories?.split(",") ?? undefined,
90-
excludedCategories?.split(",") ?? undefined,
91-
section ? (section === "all" ? "" : section) : "",
92-
nsfw === "true" ? true : false,
93-
deprecated === "true" ? true : false
94-
),
109+
filters: dapper
110+
.getCommunityFilters(params.communityId)
111+
.catch((error) =>
112+
handleLoaderError(error, { mappings: communityNotFoundMappings })
113+
),
114+
listings: dapper
115+
.getPackageListings(
116+
{
117+
kind: "community",
118+
communityId: params.communityId,
119+
},
120+
query.ordering ?? "",
121+
query.page,
122+
query.search,
123+
query.includedCategories,
124+
query.excludedCategories,
125+
query.section,
126+
query.nsfw,
127+
query.deprecated
128+
)
129+
.catch((error) =>
130+
handleLoaderError(error, { mappings: communityNotFoundMappings })
131+
),
95132
};
96133
}
97-
throw new Response("Community not found", { status: 404 });
134+
throwUserFacingPayloadResponse({
135+
headline: "Community not found.",
136+
description: "We could not find the requested community.",
137+
category: "not_found",
138+
status: 404,
139+
});
98140
}
99141

100-
// function shouldRevalidate(arg: ShouldRevalidateFunctionArgs) {
101-
// return true; // false
102-
// }
103-
104142
export default function CommunityPackageSearch() {
105143
const { filters, listings } = useLoaderData<
106144
typeof loader | typeof clientLoader
@@ -109,14 +147,16 @@ export default function CommunityPackageSearch() {
109147
const outletContext = useOutletContext() as OutletContextShape;
110148

111149
return (
112-
<>
113-
<PackageSearch
114-
listings={listings}
115-
filters={filters}
116-
config={outletContext.requestConfig}
117-
currentUser={outletContext.currentUser}
118-
dapper={outletContext.dapper}
119-
/>
120-
</>
150+
<PackageSearch
151+
listings={listings}
152+
filters={filters}
153+
config={outletContext.requestConfig}
154+
currentUser={outletContext.currentUser}
155+
dapper={outletContext.dapper}
156+
/>
121157
);
122158
}
159+
160+
export function ErrorBoundary() {
161+
return <NimbusDefaultRouteErrorBoundary />;
162+
}

apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@
127127
align-self: stretch;
128128
}
129129

130+
.package-search__error {
131+
display: flex;
132+
flex-direction: column;
133+
gap: 1rem;
134+
align-items: flex-start;
135+
align-items: center;
136+
align-self: stretch;
137+
padding: 1rem;
138+
}
139+
130140
.package-search__grid {
131141
display: grid;
132142
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));

0 commit comments

Comments
 (0)