diff --git a/pkg/services/sqlstore/playlist.go b/pkg/services/sqlstore/playlist.go index 9abfc9f3eb2..f8e1a730d09 100644 --- a/pkg/services/sqlstore/playlist.go +++ b/pkg/services/sqlstore/playlist.go @@ -133,7 +133,7 @@ func SearchPlaylists(query *models.GetPlaylistsQuery) error { sess := x.Limit(query.Limit) if query.Name != "" { - sess.Where("name LIKE ?", query.Name) + sess.Where("name LIKE ?", "%"+query.Name+"%") } sess.Where("org_id = ?", query.OrgId) diff --git a/public/app/features/playlist/EmptyQueryListBanner.tsx b/public/app/features/playlist/EmptyQueryListBanner.tsx new file mode 100644 index 00000000000..6a098c21d30 --- /dev/null +++ b/public/app/features/playlist/EmptyQueryListBanner.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { useStyles2 } from '@grafana/ui'; +import { GrafanaTheme2 } from '@grafana/data'; +import { css } from '@emotion/css'; + +export const EmptyQueryListBanner = () => { + const styles = useStyles2(getStyles); + return
No playlist found!
; +}; + +const getStyles = (theme: GrafanaTheme2) => { + return { + noResult: css` + padding: ${theme.spacing(2)}; + background: ${theme.colors.secondary.main}; + font-style: italic; + margin-top: ${theme.spacing(2)}; + `, + }; +}; diff --git a/public/app/features/playlist/PlaylistPage.tsx b/public/app/features/playlist/PlaylistPage.tsx index 69a11f8321f..a2790e9a77e 100644 --- a/public/app/features/playlist/PlaylistPage.tsx +++ b/public/app/features/playlist/PlaylistPage.tsx @@ -5,30 +5,44 @@ import Page from 'app/core/components/Page/Page'; import { StoreState } from 'app/types'; import { GrafanaRouteComponentProps } from '../../core/navigation/types'; import { getNavModel } from 'app/core/selectors/navModel'; -import { useAsync } from 'react-use'; +import { useDebounce } from 'react-use'; import { PlaylistDTO } from './types'; -import { Button, Card, ConfirmModal, LinkButton } from '@grafana/ui'; -import { contextSrv } from 'app/core/services/context_srv'; +import { ConfirmModal } from '@grafana/ui'; import PageActionBar from 'app/core/components/PageActionBar/PageActionBar'; import EmptyListCTA from '../../core/components/EmptyListCTA/EmptyListCTA'; import { deletePlaylist, getAllPlaylist } from './api'; import { StartModal } from './StartModal'; +import { PlaylistPageList } from './PlaylistPageList'; +import { EmptyQueryListBanner } from './EmptyQueryListBanner'; interface ConnectedProps { navModel: NavModel; } - export interface PlaylistPageProps extends ConnectedProps, GrafanaRouteComponentProps {} export const PlaylistPage: FC = ({ navModel }) => { const [searchQuery, setSearchQuery] = useState(''); + const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(searchQuery); + const [hasFetched, setHasFetched] = useState(false); const [startPlaylist, setStartPlaylist] = useState(); const [playlistToDelete, setPlaylistToDelete] = useState(); const [forcePlaylistsFetch, setForcePlaylistsFetch] = useState(0); - const { value: playlists, loading } = useAsync(async () => { - return getAllPlaylist(searchQuery) as Promise; - }, [forcePlaylistsFetch]); + const [playlists, setPlaylists] = useState([]); + + useDebounce( + async () => { + const playlists = await getAllPlaylist(searchQuery); + if (!hasFetched) { + setHasFetched(true); + } + setPlaylists(playlists); + setDebouncedSearchQuery(searchQuery); + }, + 350, + [forcePlaylistsFetch, searchQuery] + ); + const hasPlaylists = playlists && playlists.length > 0; const onDismissDelete = () => setPlaylistToDelete(undefined); const onDeletePlaylist = () => { @@ -41,7 +55,7 @@ export const PlaylistPage: FC = ({ navModel }) => { }); }; - let content = ( + const emptyListBanner = ( = ({ navModel }) => { /> ); - if (hasPlaylists) { - content = ( - <> - {playlists!.map((playlist) => ( - - - - {contextSrv.isEditor && ( - <> - - Edit playlist - - - - )} - - - ))} - - ); - } + const showSearch = playlists.length > 0 || searchQuery.length > 0 || debouncedSearchQuery.length > 0; + return ( - - {hasPlaylists && ( + + {showSearch && ( )} - {content} + + {!hasPlaylists && searchQuery ? ( + + ) : ( + + )} + {!showSearch && emptyListBanner} {playlistToDelete && ( void; + setPlaylistToDelete: (playlistItem: PlaylistDTO) => void; + playlists: PlaylistDTO[] | undefined; +} + +export const PlaylistPageList = ({ playlists, setStartPlaylist, setPlaylistToDelete }: Props) => { + return ( + <> + {playlists!.map((playlist: PlaylistDTO) => ( + + + + {contextSrv.isEditor && ( + <> + + Edit playlist + + + + )} + + + ))} + + ); +};