Skip to content

Commit

Permalink
[#700] Add Page Limit Input and Improve Case-Insensitive DAG Search (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
yohamta authored Nov 8, 2024
1 parent 6801654 commit 7582f5f
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 72 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@ tmp/*
.local/

# Coverage files
coverage.*
coverage.*

# Debug files
__debug_bin*
5 changes: 2 additions & 3 deletions internal/persistence/local/dag_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,9 @@ func (d *dagStoreImpl) searchName(fileName string, searchText *string) bool {
if searchText == nil {
return true
}

fileName = strings.TrimSuffix(fileName, path.Ext(fileName))

return strings.Contains(fileName, *searchText)
ret := strings.Contains(strings.ToLower(fileName), strings.ToLower(*searchText))
return ret
}

func (d *dagStoreImpl) searchTags(tags []string, searchTag *string) bool {
Expand Down
2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"monaco-react": "^1.1.0",
"monaco-yaml": "^4.0.4",
"prism": "^4.1.2",
"react": "^18.1.0",
"react": "^18.3.0",
"react-cookie": "^4.1.1",
"react-dom": "^18.1.0",
"react-monaco-editor": "^0.54.0",
Expand Down
27 changes: 15 additions & 12 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AppBarContext } from './contexts/AppBarContext';
import { SWRConfig } from 'swr';
import fetchJson from './lib/fetchJson';
import Search from './pages/search';
import { UserPreferencesProvider } from './contexts/UserPreference';

export type Config = {
apiURL: string;
Expand Down Expand Up @@ -38,18 +39,20 @@ function App({ config }: Props) {
setTitle,
}}
>
<BrowserRouter>
<Layout {...config}>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/dags/" element={<DAGs />} />
<Route path="/dags/:name/:tab" element={<DAGDetails />} />
<Route path="/dags/:name/" element={<DAGDetails />} />
<Route path="/search/" element={<Search />} />
</Routes>
</Layout>
</BrowserRouter>
<UserPreferencesProvider>
<BrowserRouter>
<Layout {...config}>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/dags/" element={<DAGs />} />
<Route path="/dags/:name/:tab" element={<DAGDetails />} />
<Route path="/dags/:name/" element={<DAGDetails />} />
<Route path="/search/" element={<Search />} />
</Routes>
</Layout>
</BrowserRouter>
</UserPreferencesProvider>
</AppBarContext.Provider>
</SWRConfig>
);
Expand Down
109 changes: 79 additions & 30 deletions ui/src/components/molecules/DAGPagination.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,83 @@
import { Box, Pagination } from "@mui/material";
import React from "react";
import { Box, Pagination, TextField } from '@mui/material';
import React from 'react';

type DAGPaginationProps = {
totalPages: number;
page: number;
pageChange: (page: number) => void;
totalPages: number;
page: number;
pageLimit: number;
pageChange: (page: number) => void;
onPageLimitChange: (pageLimit: number) => void;
};

const DAGPagination = ({ totalPages, page, pageChange }: DAGPaginationProps) => {
const handleChange = (event: React.ChangeEvent<unknown>, value: number) => {
pageChange(value);
};

return (
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
mt: 2,
}}
>
<Pagination
count={totalPages}
page={page}
onChange={handleChange}
/>
</Box>
);
}

export default DAGPagination;
const DAGPagination = ({
totalPages,
page,
pageChange,
pageLimit,
onPageLimitChange,
}: DAGPaginationProps) => {
const [inputValue, setInputValue] = React.useState(pageLimit.toString());

React.useEffect(() => {
setInputValue(pageLimit.toString());
}, [pageLimit]);

const handleChange = (event: React.ChangeEvent<unknown>, value: number) => {
pageChange(value);
};

const handleLimitChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
setInputValue(value);
};

const commitChange = () => {
const numValue = parseInt(inputValue);
if (!isNaN(numValue) && numValue > 0) {
onPageLimitChange(numValue);
} else {
setInputValue(pageLimit.toString());
}
};

const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
commitChange();
event.preventDefault();
(event.target as HTMLInputElement).blur(); // Remove focus after Enter
}
};

return (
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 3,
mt: 2,
}}
>
<TextField
size="small"
label="Items per page"
value={inputValue}
onChange={handleLimitChange}
onBlur={commitChange}
onKeyDown={handleKeyDown}
inputProps={{
type: 'number',
min: '1',
style: {
width: '100px',
textAlign: 'left',
},
}}
/>
<Pagination count={totalPages} page={page} onChange={handleChange} />
</Box>
);
};

export default DAGPagination;
47 changes: 47 additions & 0 deletions ui/src/contexts/UserPreference.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { createContext, useCallback, useContext, useState } from 'react';

export type UserPreferences = {
pageLimit: number;
}

const UserPreferencesContext = createContext<{
preferences: UserPreferences;
updatePreference: <K extends keyof UserPreferences>(
key: K,
value: UserPreferences[K]
) => void;
}>(null!);


export function UserPreferencesProvider({ children }: { children: React.ReactNode }) {
const [preferences, setPreferences] = useState<UserPreferences>(() => {
try {
const saved = localStorage.getItem('user_preferences');
return saved ? JSON.parse(saved) : { pageLimit: 50, theme: 'light' };
} catch {
return { pageLimit: 50, theme: 'light' };
}
});

const updatePreference = useCallback(<K extends keyof UserPreferences>(
key: K,
value: UserPreferences[K]
) => {
setPreferences(prev => {
const next = { ...prev, [key]: value };
localStorage.setItem('user_preferences', JSON.stringify(next));
return next;
});
}, []);

return (
<UserPreferencesContext.Provider value={{ preferences, updatePreference }}>
{children}
</UserPreferencesContext.Provider>
);

}

export function useUserPreferences() {
return useContext(UserPreferencesContext);
}
69 changes: 48 additions & 21 deletions ui/src/pages/dags/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AppBarContext } from '../../contexts/AppBarContext';
import useSWR, { useSWRConfig } from 'swr';
import DAGPagination from '../../components/molecules/DAGPagination';
import { debounce } from 'lodash';
import { useUserPreferences } from '../../contexts/UserPreference';

function DAGs() {
const useQuery = () => new URLSearchParams(useLocation().search);
Expand All @@ -21,18 +22,26 @@ function DAGs() {
const [searchText, setSearchText] = React.useState(query.get('search') || '');
const [searchTag, setSearchTag] = React.useState(query.get('tag') || '');
const [page, setPage] = React.useState(parseInt(query.get('page') || '1'));
const [apiSearchText, setAPISearchText] = React.useState(query.get('search') || '');
const [apiSearchTag, setAPISearchTag] = React.useState(query.get('tag') || '');
const [apiSearchText, setAPISearchText] = React.useState(
query.get('search') || ''
);
const [apiSearchTag, setAPISearchTag] = React.useState(
query.get('tag') || ''
);

const { preferences, updatePreference } = useUserPreferences();
// Use preferences.pageLimit instead of local state
const handlePageLimitChange = (newLimit: number) => {
updatePreference('pageLimit', newLimit);
};

const { cache, mutate } = useSWRConfig();
const endPoint =`/dags?${new URLSearchParams(
{
page: page.toString(),
limit: '50',
searchName: apiSearchText,
searchTag: apiSearchTag,
}
).toString()}`
const endPoint = `/dags?${new URLSearchParams({
page: page.toString(),
limit: preferences.pageLimit.toString(),
searchName: apiSearchText,
searchTag: apiSearchTag,
}).toString()}`;
const { data } = useSWR<ListWorkflowsResponse>(endPoint, null, {
refreshInterval: 10000,
revalidateIfStale: false,
Expand All @@ -41,8 +50,12 @@ function DAGs() {
const addSearchParam = (key: string, value: string) => {
const locationQuery = new URLSearchParams(window.location.search);
locationQuery.set(key, value);
window.history.pushState({}, '', `${window.location.pathname}?${locationQuery.toString()}`);
}
window.history.pushState(
{},
'',
`${window.location.pathname}?${locationQuery.toString()}`
);
};

const refreshFn = React.useCallback(() => {
setTimeout(() => mutate(endPoint), 500);
Expand Down Expand Up @@ -73,27 +86,35 @@ function DAGs() {
setPage(page);
};

const debouncedAPISearchText = React.useMemo(() => debounce((searchText: string) => {
setAPISearchText(searchText);
}, 500), []);
const debouncedAPISearchText = React.useMemo(
() =>
debounce((searchText: string) => {
setAPISearchText(searchText);
}, 500),
[]
);

const debouncedAPISearchTag = React.useMemo(() => debounce((searchTag: string) => {
setAPISearchTag(searchTag);
}, 500), []);
const debouncedAPISearchTag = React.useMemo(
() =>
debounce((searchTag: string) => {
setAPISearchTag(searchTag);
}, 500),
[]
);

const searchTextChange = (searchText: string) => {
addSearchParam('search', searchText);
setSearchText(searchText);
setPage(1);
debouncedAPISearchText(searchText);
}
};

const searchTagChange = (searchTag: string) => {
addSearchParam('tag', searchTag);
setSearchTag(searchTag);
setPage(1);
debouncedAPISearchTag(searchTag);
}
};

return (
<Box
Expand Down Expand Up @@ -134,7 +155,13 @@ function DAGs() {
searchTag={searchTag}
handleSearchTagChange={searchTagChange}
></DAGTable>
<DAGPagination totalPages={data.PageCount} page={page} pageChange={pageChange} />
<DAGPagination
totalPages={data.PageCount}
page={page}
pageChange={pageChange}
onPageLimitChange={handlePageLimitChange}
pageLimit={preferences.pageLimit}
/>
</React.Fragment>
)}
</WithLoading>
Expand Down
8 changes: 4 additions & 4 deletions ui/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4103,10 +4103,10 @@ react@^16.3.2:
object-assign "^4.1.1"
prop-types "^15.6.2"

react@^18.1.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
react@^18.3.0:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
dependencies:
loose-envify "^1.1.0"

Expand Down

0 comments on commit 7582f5f

Please sign in to comment.