-
Notifications
You must be signed in to change notification settings - Fork 152
fix: search & filter mismatch with pagination on Tracker pageagination #362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,7 +55,6 @@ const Home: React.FC = () => { | |
| setUsername, | ||
| token, | ||
| setToken, | ||
| error: authError, | ||
| getOctokit, | ||
| } = useGitHubAuth(); | ||
|
|
||
|
|
@@ -82,14 +81,26 @@ const Home: React.FC = () => { | |
| // Fetch data when username, tab, or page changes | ||
| useEffect(() => { | ||
| if (username) { | ||
| fetchData(username, page + 1, ROWS_PER_PAGE); | ||
| fetchData(username, page + 1, ROWS_PER_PAGE, tab === 0 ? "issue" : "pr", { | ||
| search: searchTitle, | ||
| repo: selectedRepo, | ||
| startDate, | ||
| endDate, | ||
| state: tab === 0 ? issueFilter : prFilter, | ||
| }); | ||
| } | ||
| }, [tab, page]); | ||
| }, [tab, page, issueFilter, prFilter]); | ||
|
|
||
| const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => { | ||
| e.preventDefault(); | ||
| setPage(0); | ||
| fetchData(username, 1, ROWS_PER_PAGE); | ||
| fetchData(username, 1, ROWS_PER_PAGE, tab === 0 ? "issue" : "pr", { | ||
| search: searchTitle, | ||
| repo: selectedRepo, | ||
| startDate, | ||
| endDate, | ||
| state: tab === 0 ? issueFilter : prFilter, | ||
| }); | ||
| }; | ||
|
Comment on lines
94
to
104
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential double fetch when submitting from a non-first page.
♻️ Suggested fix — let the effect own the fetch const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
- setPage(0);
- fetchData(username, 1, ROWS_PER_PAGE, tab === 0 ? "issue" : "pr", {
- search: searchTitle,
- repo: selectedRepo,
- startDate,
- endDate,
- state: tab === 0 ? issueFilter : prFilter,
- });
+ if (page !== 0) {
+ setPage(0); // effect will refetch with page=0
+ } else {
+ fetchData(username, 1, ROWS_PER_PAGE, tab === 0 ? "issue" : "pr", {
+ search: searchTitle,
+ repo: selectedRepo,
+ startDate,
+ endDate,
+ state: tab === 0 ? issueFilter : prFilter,
+ });
+ }
};🤖 Prompt for AI Agents |
||
|
|
||
| const handlePageChange = (_: unknown, newPage: number) => { | ||
|
|
@@ -99,45 +110,6 @@ const Home: React.FC = () => { | |
| const formatDate = (dateString: string): string => | ||
| new Date(dateString).toLocaleDateString(); | ||
|
|
||
| const filterData = (data: GitHubItem[], filterType: string): GitHubItem[] => { | ||
| let filtered = [...data]; | ||
| if (["open", "closed", "merged"].includes(filterType)) { | ||
| filtered = filtered.filter((item) => { | ||
| if (filterType === "merged") { | ||
| return !!item.pull_request?.merged_at | ||
| } | ||
| else if (filterType === "closed") { | ||
| return item.state === "closed" && !item.pull_request?.merged_at | ||
| } | ||
| else { | ||
| //open | ||
| return item.state === "open" | ||
| } | ||
| }); | ||
| } | ||
| if (searchTitle) { | ||
| filtered = filtered.filter((item) => | ||
| item.title.toLowerCase().includes(searchTitle.toLowerCase()) | ||
| ); | ||
| } | ||
| if (selectedRepo) { | ||
| filtered = filtered.filter((item) => | ||
| item.repository_url.includes(selectedRepo) | ||
| ); | ||
| } | ||
| if (startDate) { | ||
| filtered = filtered.filter( | ||
| (item) => new Date(item.created_at) >= new Date(startDate) | ||
| ); | ||
| } | ||
| if (endDate) { | ||
| filtered = filtered.filter( | ||
| (item) => new Date(item.created_at) <= new Date(endDate) | ||
| ); | ||
| } | ||
| return filtered; | ||
| }; | ||
|
|
||
| const getStatusIcon = (item: GitHubItem) => { | ||
|
|
||
| if (item.pull_request) { | ||
|
|
@@ -158,16 +130,15 @@ const Home: React.FC = () => { | |
| }; | ||
|
|
||
|
|
||
| // Current data and filtered data according to tab and filters | ||
| const currentRawData = tab === 0 ? issues : prs; | ||
| const currentFilteredData = filterData(currentRawData, tab === 0 ? issueFilter : prFilter); | ||
| // Current data according to tab | ||
| const currentFilteredData = tab === 0 ? issues : prs; | ||
| const totalCount = tab === 0 ? totalIssues : totalPrs; | ||
|
|
||
| return ( | ||
| <Container maxWidth="lg" sx={{ mt: 4, minHeight: "80vh", color: theme.palette.text.primary }}> | ||
| {/* Auth Form */} | ||
| <Paper elevation={1} sx={{ p: 2, mb: 4, backgroundColor: theme.palette.background.paper }}> | ||
| <form onSubmit={handleSubmit}> | ||
| <form onSubmit={handleSubmit}> | ||
| {/* Auth Form */} | ||
| <Paper elevation={1} sx={{ p: 2, mb: 4, backgroundColor: theme.palette.background.paper }}> | ||
| <Box sx={{ display: "flex", gap: 2, flexWrap: "wrap" }}> | ||
| <TextField | ||
| label="GitHub Username" | ||
|
|
@@ -236,43 +207,43 @@ const Home: React.FC = () => { | |
| alignSelf: "flex-start", | ||
| }} | ||
| > | ||
| Fetch Data | ||
| Fetch Data / Apply Filters | ||
| </Button> | ||
| </Box> | ||
| </form> | ||
| </Paper> | ||
|
|
||
| {/* Filters */} | ||
| <Box sx={{ mb: 2, display: "flex", flexWrap: "wrap", gap: 2 }}> | ||
| <TextField | ||
| label="Search Title" | ||
| value={searchTitle} | ||
| onChange={(e) => setSearchTitle(e.target.value)} | ||
| sx={{ minWidth: 200 }} | ||
| /> | ||
| <TextField | ||
| label="Repository" | ||
| value={selectedRepo} | ||
| onChange={(e) => setSelectedRepo(e.target.value)} | ||
| sx={{ minWidth: 200 }} | ||
| /> | ||
| <TextField | ||
| label="Start Date" | ||
| type="date" | ||
| value={startDate} | ||
| onChange={(e) => setStartDate(e.target.value)} | ||
| InputLabelProps={{ shrink: true }} | ||
| sx={{ minWidth: 150 }} | ||
| /> | ||
| <TextField | ||
| label="End Date" | ||
| type="date" | ||
| value={endDate} | ||
| onChange={(e) => setEndDate(e.target.value)} | ||
| InputLabelProps={{ shrink: true }} | ||
| sx={{ minWidth: 150 }} | ||
| /> | ||
| </Box> | ||
| </Paper> | ||
|
|
||
| {/* Filters */} | ||
| <Box sx={{ mb: 2, display: "flex", flexWrap: "wrap", gap: 2 }}> | ||
| <TextField | ||
| label="Search Title" | ||
| value={searchTitle} | ||
| onChange={(e) => setSearchTitle(e.target.value)} | ||
| sx={{ minWidth: 200 }} | ||
| /> | ||
| <TextField | ||
| label="Repository" | ||
| value={selectedRepo} | ||
| onChange={(e) => setSelectedRepo(e.target.value)} | ||
| sx={{ minWidth: 200 }} | ||
| /> | ||
| <TextField | ||
| label="Start Date" | ||
| type="date" | ||
| value={startDate} | ||
| onChange={(e) => setStartDate(e.target.value)} | ||
| InputLabelProps={{ shrink: true }} | ||
| sx={{ minWidth: 150 }} | ||
| /> | ||
| <TextField | ||
| label="End Date" | ||
| type="date" | ||
| value={endDate} | ||
| onChange={(e) => setEndDate(e.target.value)} | ||
| InputLabelProps={{ shrink: true }} | ||
| sx={{ minWidth: 150 }} | ||
| /> | ||
| </Box> | ||
| </form> | ||
|
|
||
| {/* Tabs + State Filter */} | ||
| <Box | ||
|
|
@@ -300,11 +271,14 @@ const Home: React.FC = () => { | |
| <InputLabel sx={{ fontSize: "14px" }}>State</InputLabel> | ||
| <Select | ||
| value={tab === 0 ? issueFilter : prFilter} | ||
| onChange={(e) => | ||
| tab === 0 | ||
| ? setIssueFilter(e.target.value) | ||
| : setPrFilter(e.target.value) | ||
| } | ||
| onChange={(e) => { | ||
| setPage(0); | ||
| if (tab === 0) { | ||
| setIssueFilter(e.target.value); | ||
| } else { | ||
| setPrFilter(e.target.value); | ||
| } | ||
| }} | ||
| label="State" | ||
| sx={{ | ||
| backgroundColor: theme.palette.background.paper, | ||
|
|
@@ -324,9 +298,9 @@ const Home: React.FC = () => { | |
| </FormControl> | ||
| </Box> | ||
|
|
||
| {(authError || dataError) && ( | ||
| {dataError && ( | ||
| <Alert severity="error" sx={{ mb: 3 }}> | ||
| {authError || dataError} | ||
| {dataError} | ||
| </Alert> | ||
| )} | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 1662
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 115
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 19844
🏁 Script executed:
Repository: GitMetricsLab/github_tracker
Length of output: 54
Avoid duplicate network requests when applying filters + fix deps lint intent
handleSubmitsetspageto0and also callsfetchDatadirectly; when the currentpageis not0, theuseEffect(which depends onpage) fires and triggers the same fetch again → duplicate requests.useEffectusesusername(andfetchData) but they’re intentionally not in deps; sincefetchDatais already memoized viauseCallback, keep the current “auth changes require Submit” behavior by suppressingreact-hooks/exhaustive-depswith a brief comment (or include them only if you want auto-fetch on auth changes).mergedstate is already handled inuseGitHubDataby translatingfilters.state === 'merged'tois:mergedfor PR searches—no change needed there.authErrorexists inuseGitHubAuth;dataErrorfromuseGitHubDatais what’s displayed.🤖 Prompt for AI Agents