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
+
+
+ >
+ )}
+
+
+ ))}
+ >
+ );
+};