diff --git a/.gitignore b/.gitignore
index 45b1552b..53e04a08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,4 +23,7 @@ tmp/*
.local/
# Coverage files
-coverage.*
\ No newline at end of file
+coverage.*
+
+# Debug files
+__debug_bin*
\ No newline at end of file
diff --git a/internal/persistence/local/dag_store.go b/internal/persistence/local/dag_store.go
index 3831ec3a..29a9df96 100644
--- a/internal/persistence/local/dag_store.go
+++ b/internal/persistence/local/dag_store.go
@@ -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 {
diff --git a/ui/package.json b/ui/package.json
index 08812cbe..b94ed47d 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -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",
diff --git a/ui/src/App.tsx b/ui/src/App.tsx
index 977def8e..56084e69 100644
--- a/ui/src/App.tsx
+++ b/ui/src/App.tsx
@@ -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;
@@ -38,18 +39,20 @@ function App({ config }: Props) {
setTitle,
}}
>
-
-
-
- } />
- } />
- } />
- } />
- } />
- } />
-
-
-
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
);
diff --git a/ui/src/components/molecules/DAGPagination.tsx b/ui/src/components/molecules/DAGPagination.tsx
index 0745a843..5e272ef2 100644
--- a/ui/src/components/molecules/DAGPagination.tsx
+++ b/ui/src/components/molecules/DAGPagination.tsx
@@ -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, value: number) => {
- pageChange(value);
- };
-
- return (
-
-
-
- );
-}
-
-export default DAGPagination;
\ No newline at end of file
+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, value: number) => {
+ pageChange(value);
+ };
+
+ const handleLimitChange = (event: React.ChangeEvent) => {
+ 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) => {
+ if (event.key === 'Enter') {
+ commitChange();
+ event.preventDefault();
+ (event.target as HTMLInputElement).blur(); // Remove focus after Enter
+ }
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+export default DAGPagination;
diff --git a/ui/src/contexts/UserPreference.tsx b/ui/src/contexts/UserPreference.tsx
new file mode 100644
index 00000000..9179333d
--- /dev/null
+++ b/ui/src/contexts/UserPreference.tsx
@@ -0,0 +1,47 @@
+import React, { createContext, useCallback, useContext, useState } from 'react';
+
+export type UserPreferences = {
+ pageLimit: number;
+}
+
+const UserPreferencesContext = createContext<{
+ preferences: UserPreferences;
+ updatePreference: (
+ key: K,
+ value: UserPreferences[K]
+ ) => void;
+}>(null!);
+
+
+export function UserPreferencesProvider({ children }: { children: React.ReactNode }) {
+ const [preferences, setPreferences] = useState(() => {
+ 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((
+ key: K,
+ value: UserPreferences[K]
+ ) => {
+ setPreferences(prev => {
+ const next = { ...prev, [key]: value };
+ localStorage.setItem('user_preferences', JSON.stringify(next));
+ return next;
+ });
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+
+}
+
+export function useUserPreferences() {
+ return useContext(UserPreferencesContext);
+}
\ No newline at end of file
diff --git a/ui/src/pages/dags/index.tsx b/ui/src/pages/dags/index.tsx
index 5a0c0d1a..66c04597 100644
--- a/ui/src/pages/dags/index.tsx
+++ b/ui/src/pages/dags/index.tsx
@@ -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);
@@ -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(endPoint, null, {
refreshInterval: 10000,
revalidateIfStale: false,
@@ -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);
@@ -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 (
-
+
)}
diff --git a/ui/yarn.lock b/ui/yarn.lock
index cd1d2f46..5d3a42d8 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -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"