diff --git a/src/renderer/features/context-menu/actions/delete-playlist-action.tsx b/src/renderer/features/context-menu/actions/delete-playlist-action.tsx
index ef8ab53af..ac0f20008 100644
--- a/src/renderer/features/context-menu/actions/delete-playlist-action.tsx
+++ b/src/renderer/features/context-menu/actions/delete-playlist-action.tsx
@@ -8,6 +8,7 @@ import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServerId } from '/@/renderer/store';
import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
import { ConfirmModal } from '/@/shared/components/modal/modal';
+import { Text } from '/@/shared/components/text/text';
import { toast } from '/@/shared/components/toast/toast';
import { Playlist } from '/@/shared/types/domain-types';
@@ -21,31 +22,30 @@ export const DeletePlaylistAction = ({ items }: DeletePlaylistActionProps) => {
const serverId = useCurrentServerId();
const deletePlaylistMutation = useDeletePlaylist({});
- const handleDeletePlaylist = useCallback(() => {
+ const handleDeletePlaylist = useCallback(async () => {
if (items.length === 0 || !serverId) return;
- const playlist = items[0];
+ try {
+ await Promise.all(
+ items.map((playlist) =>
+ deletePlaylistMutation.mutateAsync({
+ apiClientProps: { serverId },
+ query: { id: playlist.id },
+ }),
+ ),
+ );
+
+ navigate(AppRoute.PLAYLISTS, { replace: true });
+ toast.success({
+ message: t('action.deletePlaylist', { postProcess: 'sentenceCase' }),
+ });
+ } catch (err: any) {
+ toast.error({
+ message: err.message,
+ title: t('error.genericError', { postProcess: 'sentenceCase' }),
+ });
+ }
- deletePlaylistMutation.mutate(
- {
- apiClientProps: { serverId },
- query: { id: playlist.id },
- },
- {
- onError: (err) => {
- toast.error({
- message: err.message,
- title: t('error.genericError', { postProcess: 'sentenceCase' }),
- });
- },
- onSuccess: () => {
- navigate(AppRoute.PLAYLISTS, { replace: true });
- toast.success({
- message: t('action.deletePlaylist', { postProcess: 'sentenceCase' }),
- });
- },
- },
- );
closeAllModals();
}, [deletePlaylistMutation, items, navigate, serverId, t]);
@@ -55,7 +55,7 @@ export const DeletePlaylistAction = ({ items }: DeletePlaylistActionProps) => {
openModal({
children: (
- {t('common.areYouSure', { postProcess: 'sentenceCase' })}
+ {t('common.areYouSure', { postProcess: 'sentenceCase' })}
),
title: t('form.deletePlaylist.title', { postProcess: 'sentenceCase' }),
diff --git a/src/renderer/features/genres/components/genre-list-header-filters.tsx b/src/renderer/features/genres/components/genre-list-header-filters.tsx
index 7ea7d791b..28e7592c2 100644
--- a/src/renderer/features/genres/components/genre-list-header-filters.tsx
+++ b/src/renderer/features/genres/components/genre-list-header-filters.tsx
@@ -4,7 +4,6 @@ import { ListFilters } from '/@/renderer/features/shared/components/list-filters
import { ListRefreshButton } from '/@/renderer/features/shared/components/list-refresh-button';
import { ListSortByDropdown } from '/@/renderer/features/shared/components/list-sort-by-dropdown';
import { ListSortOrderToggleButton } from '/@/renderer/features/shared/components/list-sort-order-toggle-button';
-import { useContainerQuery } from '/@/renderer/hooks';
import { Divider } from '/@/shared/components/divider/divider';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
@@ -12,11 +11,9 @@ import { GenreListSort, LibraryItem, SortOrder } from '/@/shared/types/domain-ty
import { ItemListKey } from '/@/shared/types/types';
export const GenreListHeaderFilters = () => {
- const { ref, ...cq } = useContainerQuery();
-
return (
-
+
{
},
});
const [isSmartPlaylist, setIsSmartPlaylist] = useState(false);
+ const [step, setStep] = useState<1 | 2>(1);
const handleSubmit = form.onSubmit((values) => {
- if (isSmartPlaylist) {
- values._custom!.navidrome = {
- ...values._custom?.navidrome,
- rules: queryBuilderRef.current?.getFilters(),
- };
+ if (!server) return;
+
+ // If creating a smart playlist and we're on the first step, advance to step 2
+ // to configure the query instead of submitting immediately.
+ if (isSmartPlaylist && step === 1) {
+ setStep(2);
+ return;
}
const smartPlaylist = queryBuilderRef.current?.getFilters();
- if (!server) return;
+ const rules =
+ isSmartPlaylist && smartPlaylist?.filters
+ ? {
+ ...convertQueryGroupToNDQuery(smartPlaylist.filters),
+ limit: smartPlaylist.extraFilters.limit,
+ order: smartPlaylist.extraFilters.sortOrder,
+ sort: smartPlaylist.extraFilters.sortBy,
+ }
+ : undefined;
mutation.mutate(
{
@@ -64,16 +75,10 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
_custom: {
navidrome: {
...values._custom?.navidrome,
- rules:
- isSmartPlaylist && smartPlaylist?.filters
- ? {
- ...convertQueryGroupToNDQuery(smartPlaylist.filters),
- limit: smartPlaylist.extraFilters.limit,
- order: smartPlaylist.extraFilters.sortOrder,
- sort: smartPlaylist.extraFilters.sortBy,
- }
- : undefined,
+ rules,
},
+ // Top-level rules field is what Navidrome expects for smart playlists.
+ ...(rules ? { rules } : {}),
},
},
},
@@ -100,51 +105,63 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
return (