Skip to content

Commit 9c0deeb

Browse files
elliotBraemitexpert120coderabbitai[bot]
authored
Upgrade staging (#206)
* add feed conflicts and lowercase id * Adds "my feeds" on profile (#204) * feat: update my feeds tab on profile page * feat: update tabs to be routes * fix: double scrollbar * comment unused files in overview tab * fix: revert rsbuild * fmt * fix: add routegen to prettier ignore * clean up, server side --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com> * Resolve shot's comments (#205) * feat: add name and tag validation to create * feat: add no activity data in activity tab * add coming soon sections for profile page * feat: clicking on leaderboard feed hashtag will redirect to that feed * fix: keeps name on start when disable feed names collapse * fix: rsbuild * fix: add routegen to prettier ignore * fix: add ability to navigate to collapsed feeds in leaderboard * add ability to expand or collapse all * fix: rsbuild * adjustments * nitpicks --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com> * feat: new feed edit (#198) * feat: update the feed editor * feat: improve performance, and fix bugs * feat: revert local development change * add routegen to pretttier ignore * fix: resolve code rabbit comments * fix some nitpick comments * fix prettier and build config * formats * merge --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com> * debounce * nitpicks * adds processing step plan * fix auth * Feat: recent content (#208) * Feat: add recent feeds * feat: add recent content to the main feed page * fmt * Update apps/app/src/hooks/use-rss-feed.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * nitpicks --------- Co-authored-by: Elliot Braem <elliot@everything.dev> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Elliot Braem <elliot@ejlbraem.com> * atuh flow * delete old requests --------- Co-authored-by: Zeeshan Ahmad <itexpert120@outlook.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent b724990 commit 9c0deeb

File tree

87 files changed

+7030
-1646
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+7030
-1646
lines changed

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ node_modules
44
.turbo
55
.next
66
.docusaurus
7-
packages/shared-db/migrations
7+
apps/app/src/routeTree.gen.ts
8+
packages/shared-db/migrations

apps/api/src/routes/api/processing.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,117 @@ const StepIdParamSchema = z.object({
230230
stepId: z.string(),
231231
});
232232

233+
// Reprocess a job with the latest feed config
234+
processingRoutes.post(
235+
"/jobs/:jobId/reprocess",
236+
zValidator("param", JobIdParamSchema),
237+
async (c) => {
238+
const { jobId } = c.req.valid("param");
239+
const sp = c.var.sp;
240+
241+
try {
242+
const processingService = sp.getProcessingService();
243+
const newJob = await processingService.reprocessWithLatestConfig(jobId);
244+
245+
return c.json(
246+
ProcessingJobRetryResponseSchema.parse({
247+
statusCode: 200,
248+
success: true,
249+
data: {
250+
job: newJob,
251+
message: "Job reprocessing initiated successfully.",
252+
},
253+
}),
254+
);
255+
} catch (error: unknown) {
256+
sp.getLogger().error(
257+
{ error, jobId },
258+
"Error in processingRoutes.post('/jobs/:jobId/reprocess')",
259+
);
260+
261+
if (error instanceof NotFoundError || error instanceof ServiceError) {
262+
return c.json(
263+
ApiErrorResponseSchema.parse({
264+
statusCode: error.statusCode as ContentfulStatusCode,
265+
success: false,
266+
error: { message: error.message },
267+
}),
268+
error.statusCode as ContentfulStatusCode,
269+
);
270+
}
271+
272+
return c.json(
273+
ApiErrorResponseSchema.parse({
274+
statusCode: 500,
275+
success: false,
276+
error: { message: "Failed to reprocess job" },
277+
}),
278+
500,
279+
);
280+
}
281+
},
282+
);
283+
284+
// Tweak a step's input and reprocess from that point
285+
const TweakStepBodySchema = z.object({
286+
newInput: z.string(),
287+
});
288+
289+
processingRoutes.post(
290+
"/steps/:stepId/tweak",
291+
zValidator("param", StepIdParamSchema),
292+
zValidator("json", TweakStepBodySchema),
293+
async (c) => {
294+
const { stepId } = c.req.valid("param");
295+
const { newInput } = c.req.valid("json");
296+
const sp = c.var.sp;
297+
298+
try {
299+
const processingService = sp.getProcessingService();
300+
const newJob = await processingService.tweakAndReprocessStep(
301+
stepId,
302+
newInput,
303+
);
304+
305+
return c.json(
306+
ProcessingJobRetryResponseSchema.parse({
307+
statusCode: 200,
308+
success: true,
309+
data: {
310+
job: newJob,
311+
message: "Step tweak and reprocess initiated successfully.",
312+
},
313+
}),
314+
);
315+
} catch (error: unknown) {
316+
sp.getLogger().error(
317+
{ error, stepId },
318+
"Error in processingRoutes.post('/steps/:stepId/tweak')",
319+
);
320+
321+
if (error instanceof NotFoundError || error instanceof ServiceError) {
322+
return c.json(
323+
ApiErrorResponseSchema.parse({
324+
statusCode: error.statusCode as ContentfulStatusCode,
325+
success: false,
326+
error: { message: error.message },
327+
}),
328+
error.statusCode as ContentfulStatusCode,
329+
);
330+
}
331+
332+
return c.json(
333+
ApiErrorResponseSchema.parse({
334+
statusCode: 500,
335+
success: false,
336+
error: { message: "Failed to tweak and reprocess step" },
337+
}),
338+
500,
339+
);
340+
}
341+
},
342+
);
343+
233344
// Retry processing from a specific failed step
234345
processingRoutes.post(
235346
"/steps/:stepId/retry",

apps/api/src/routes/api/users.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { FeedService } from "@curatedotfun/core-services";
12
import {
23
ApiErrorResponseSchema,
34
CreateUserRequestSchema,
5+
FeedsWrappedResponseSchema,
46
UpdateUserRequestSchema,
57
UserDeletedWrappedResponseSchema,
68
UserNearAccountIdParamSchema,
@@ -333,4 +335,41 @@ usersRoutes.get(
333335
},
334336
);
335337

338+
usersRoutes.get(
339+
"/:nearAccountId/feeds",
340+
zValidator("param", UserNearAccountIdParamSchema),
341+
async (c) => {
342+
const { nearAccountId } = c.req.valid("param");
343+
const sp = c.var.sp;
344+
345+
try {
346+
const feedService: FeedService = sp.getFeedService();
347+
const feeds = await feedService.getFeedsByCreator(nearAccountId);
348+
349+
return c.json(
350+
FeedsWrappedResponseSchema.parse({
351+
statusCode: 200,
352+
success: true,
353+
data: feeds.map((feed) => ({
354+
...feed,
355+
config: feed.config,
356+
})),
357+
}),
358+
);
359+
} catch (error) {
360+
c.var.sp
361+
.getLogger()
362+
.error({ error }, `Error fetching feeds for ${nearAccountId}`);
363+
return c.json(
364+
ApiErrorResponseSchema.parse({
365+
statusCode: 500,
366+
success: false,
367+
error: { message: "Failed to fetch feeds" },
368+
}),
369+
500,
370+
);
371+
}
372+
},
373+
);
374+
336375
export { usersRoutes };

apps/app/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@tanstack/react-query": "^5.81.2",
3535
"@tanstack/react-router": "^1.121.34",
3636
"@tanstack/react-table": "^8.21.3",
37+
"@tanstack/react-virtual": "^3.13.12",
3738
"@tanstack/zod-form-adapter": "^0.42.1",
3839
"autoprefixer": "^10.4.21",
3940
"class-variance-authority": "^0.7.1",
@@ -42,7 +43,7 @@
4243
"date-fns": "^4.1.0",
4344
"fastintear": "latest",
4445
"immer": "^10.1.1",
45-
"lodash": "^4.17.21",
46+
"lodash-es": "^4.17.21",
4647
"lucide-react": "^0.483.0",
4748
"near-sign-verify": "^0.4.1",
4849
"pinata-web3": "^0.5.4",
@@ -66,7 +67,7 @@
6667
"@rsbuild/plugin-react": "1.1.0",
6768
"@tanstack/router-devtools": "1.97.23",
6869
"@tanstack/router-plugin": "^1.121.34",
69-
"@types/lodash": "^4.17.18",
70+
"@types/lodash-es": "^4.17.12",
7071
"@types/react": "^18.3.23",
7172
"@types/react-dom": "^18.3.7",
7273
"@vitejs/plugin-react": "^4.6.0",

apps/app/src/components/FilterControls.tsx

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Filter, Search } from "lucide-react";
33
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
44
import { SortOrderType, StatusFilterType } from "../lib/api";
55
import { Button } from "./ui/button";
6+
import { debounce } from "lodash-es";
67
import { Input } from "./ui/input";
78
import {
89
Select,
@@ -36,7 +37,6 @@ const FilterControls: React.FC<FilterControlsProps> = ({
3637
initialSortOrder || "newest",
3738
);
3839
const [showFiltersDropdown, setShowFiltersDropdown] = useState(false);
39-
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
4040

4141
type TargetSearchSchema =
4242
FileRouteTypes["fileRoutesById"][typeof parentRouteId]["preLoaderRoute"]["validateSearch"]["_output"];
@@ -48,31 +48,27 @@ const FilterControls: React.FC<FilterControlsProps> = ({
4848
setSortOrder(initialSortOrder || "newest");
4949
}, [initialQ, initialStatus, initialSortOrder]);
5050

51-
const handleSearchChange = (event: ChangeEvent<HTMLInputElement>) => {
52-
const newValue = event.target.value;
53-
setSearchQuery(newValue);
54-
55-
if (debounceTimerRef.current) {
56-
clearTimeout(debounceTimerRef.current);
57-
}
58-
59-
debounceTimerRef.current = setTimeout(() => {
51+
const debouncedNavigate = useRef(
52+
debounce((newValue) => {
6053
navigate({
6154
// @ts-expect-error tanstack router types are hard for a dynamic route
6255
search: (prev: TargetSearchSchema) => ({
6356
...prev,
6457
q: newValue || undefined,
65-
// page: 1, // Optional: Reset page on filter change
6658
}),
6759
replace: true,
6860
});
69-
}, 300);
61+
}, 300),
62+
).current;
63+
64+
const handleSearchChange = (event: ChangeEvent<HTMLInputElement>) => {
65+
const newValue = event.target.value;
66+
setSearchQuery(newValue);
67+
debouncedNavigate(newValue);
7068
};
7169

7270
const handleApplyFiltersClick = () => {
73-
if (debounceTimerRef.current) {
74-
clearTimeout(debounceTimerRef.current);
75-
}
71+
debouncedNavigate.cancel();
7672
console.log("status", status);
7773
navigate({
7874
// @ts-expect-error tanstack router types are hard for a dynamic route
@@ -91,9 +87,7 @@ const FilterControls: React.FC<FilterControlsProps> = ({
9187
useEffect(() => {
9288
// Cleanup debounce timer
9389
return () => {
94-
if (debounceTimerRef.current) {
95-
clearTimeout(debounceTimerRef.current);
96-
}
90+
debouncedNavigate.cancel();
9791
};
9892
}, []);
9993

apps/app/src/components/Leaderboard.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from "react";
2+
import { ChevronDown, ChevronUp } from "lucide-react";
23
import { LeaderboardEntry } from "../lib/api";
34
import { Container } from "./Container";
45
import { Hero } from "./Hero";
@@ -33,6 +34,8 @@ export default React.memo(function Leaderboard({
3334
handleTimeDropdownToggle,
3435
handleFeedDropdownClose,
3536
handleTimeDropdownClose,
37+
expandAllRows,
38+
collapseAllRows,
3639
feedDropdownRef,
3740
timeDropdownRef,
3841
table,
@@ -63,6 +66,25 @@ export default React.memo(function Leaderboard({
6366
timeDropdownRef={timeDropdownRef}
6467
/>
6568

69+
{hasData && (
70+
<div className="flex justify-end gap-2 mb-4">
71+
<button
72+
onClick={expandAllRows}
73+
className="flex items-center gap-1.5 px-3 py-2 text-sm border border-neutral-300 rounded-md bg-white hover:bg-neutral-50 transition-colors text-[#111111]"
74+
>
75+
<ChevronDown className="h-4 w-4" />
76+
Expand All
77+
</button>
78+
<button
79+
onClick={collapseAllRows}
80+
className="flex items-center gap-1.5 px-3 py-2 text-sm border border-neutral-300 rounded-md bg-white hover:bg-neutral-50 transition-colors text-[#111111]"
81+
>
82+
<ChevronUp className="h-4 w-4" />
83+
Collapse All
84+
</button>
85+
</div>
86+
)}
87+
6688
<LeaderboardTable
6789
table={table}
6890
isLoading={isLoading}

apps/app/src/components/UserMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export default function UserMenu({ className }: UserMenuProps) {
7676
) : (
7777
<ProfileImage size="small" />
7878
)}
79-
<p className="text-sm font-medium leading-6 hidden sm:block">
79+
<p className="text-sm font-medium leading-6">
8080
{getUserDisplayName()}
8181
</p>
8282
<ChevronDown
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Clock, Sparkles } from "lucide-react";
2+
import { Card } from "./ui/card";
3+
import { Badge } from "./ui/badge";
4+
5+
interface ComingSoonProps {
6+
title: string;
7+
description?: string;
8+
features?: string[];
9+
}
10+
11+
export function ComingSoon({ title, description, features }: ComingSoonProps) {
12+
return (
13+
<Card className="p-6 space-y-6">
14+
<div className="text-center space-y-4">
15+
<div className="flex items-center justify-center space-x-2">
16+
<Sparkles className="h-8 w-8" />
17+
<h2 className="text-2xl font-semibold">{title}</h2>
18+
</div>
19+
20+
<Badge
21+
variant="secondary"
22+
className="flex items-center space-x-1 w-fit mx-auto"
23+
>
24+
<Clock className="h-3 w-3" />
25+
<span>Coming Soon</span>
26+
</Badge>
27+
28+
{description && (
29+
<p className="text-gray-600 dark:text-gray-400 max-w-md mx-auto">
30+
{description}
31+
</p>
32+
)}
33+
</div>
34+
35+
{features && features.length > 0 && (
36+
<div className="space-y-3">
37+
<h3 className="text-lg font-medium text-center">What to expect:</h3>
38+
<ul className="space-y-2 max-w-md mx-auto">
39+
{features.map((feature, index) => (
40+
<li
41+
key={index}
42+
className="flex items-center space-x-2 text-sm text-gray-600 dark:text-gray-400"
43+
>
44+
<div className="w-1.5 h-1.5 bg-blue-500 rounded-full flex-shrink-0" />
45+
<span>{feature}</span>
46+
</li>
47+
))}
48+
</ul>
49+
</div>
50+
)}
51+
</Card>
52+
);
53+
}

0 commit comments

Comments
 (0)