feat(admin-ui): Phase B frontend — notifications preferences route + UI

This commit is contained in:
Александр Зимин
2026-05-15 14:56:48 +00:00
parent 300fd12d6c
commit e2fc3bcbe0
7 changed files with 332 additions and 4 deletions
+41
View File
@@ -12,6 +12,7 @@ import {
type DecisionRequest,
type DictionaryDetail,
type DraftResponse,
type NotificationPreferences,
type PublishSchemaDraftRequest,
type PublishSchemaDraftResult,
type RecordResponse,
@@ -700,3 +701,43 @@ export const useMarkAllNotificationsRead = () => {
},
})
}
/**
* Phase B — update per-channel notification preferences. PUT upserts all 3
* каналов атомарно (backend @Transactional). Optimistic update — UI flips
* toggle instantly, onError refetch restores original state.
*
* <p>Cache key 'notifications/me/preferences' — separate from feed query
* (которая использует 'notifications/me'). Invalidate prefs alone не
* triggers feed refetch.
*/
export const useUpdateNotificationPreferences = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: async (prefs: NotificationPreferences): Promise<NotificationPreferences> => {
const { data } = await apiClient.put<NotificationPreferences>(
'/me/notifications/preferences',
prefs,
)
return data
},
onMutate: async (next) => {
await qc.cancelQueries({ queryKey: ['notifications', 'me', 'preferences'] })
const prev = qc.getQueryData<NotificationPreferences>([
'notifications',
'me',
'preferences',
])
qc.setQueryData(['notifications', 'me', 'preferences'], next)
return { prev }
},
onError: (_err, _next, ctx) => {
if (ctx?.prev) {
qc.setQueryData(['notifications', 'me', 'preferences'], ctx.prev)
}
},
onSettled: () => {
qc.invalidateQueries({ queryKey: ['notifications', 'me', 'preferences'] })
},
})
}