Enhance dialog components by adding smooth scroll to top functionality upon opening; update ConfirmDialog, ClearSubmissionsDialog, and DuplicateChainDialog for improved user experience. Remove unused ConfirmDialog from ChainsListPage and TasksListPage, streamlining code.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
DialogRoot,
|
||||
@@ -29,6 +29,13 @@ export const ClearSubmissionsDialog: React.FC<ClearSubmissionsDialogProps> = ({
|
||||
const { t } = useTranslation()
|
||||
const [clearSubmissions, { isLoading }] = useClearChainSubmissionsMutation()
|
||||
|
||||
// Прокручиваем страницу к началу при открытии диалога
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (!chain) return
|
||||
|
||||
@@ -52,7 +59,7 @@ export const ClearSubmissionsDialog: React.FC<ClearSubmissionsDialogProps> = ({
|
||||
if (!chain) return null
|
||||
|
||||
return (
|
||||
<DialogRoot open={isOpen} onOpenChange={(e) => !e.open && onClose()}>
|
||||
<DialogRoot open={isOpen} onOpenChange={(e) => !e.open && onClose()} scrollBehavior="inside">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('challenge.admin.chains.clear.submissions.dialog.title')}</DialogTitle>
|
||||
@@ -77,3 +84,4 @@ export const ClearSubmissionsDialog: React.FC<ClearSubmissionsDialogProps> = ({
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
DialogRoot,
|
||||
@@ -36,8 +36,16 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
|
||||
|
||||
const confirm = confirmLabel || t('challenge.admin.common.confirm')
|
||||
const cancel = cancelLabel || t('challenge.admin.common.cancel')
|
||||
|
||||
// Прокручиваем страницу к началу при открытии диалога
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
return (
|
||||
<DialogRoot open={isOpen} onOpenChange={(e) => !e.open && onClose()}>
|
||||
<DialogRoot open={isOpen} onOpenChange={(e) => !e.open && onClose()} scrollBehavior="inside">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
DialogRoot,
|
||||
@@ -33,6 +33,13 @@ export const DuplicateChainDialog: React.FC<DuplicateChainDialogProps> = ({
|
||||
const [name, setName] = useState('')
|
||||
const [duplicateChain, { isLoading }] = useDuplicateChainMutation()
|
||||
|
||||
// Прокручиваем страницу к началу при открытии диалога
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const handleClose = () => {
|
||||
setName('')
|
||||
onClose()
|
||||
@@ -68,7 +75,7 @@ export const DuplicateChainDialog: React.FC<DuplicateChainDialogProps> = ({
|
||||
})
|
||||
|
||||
return (
|
||||
<DialogRoot open={isOpen} onOpenChange={(e) => !e.open && handleClose()}>
|
||||
<DialogRoot open={isOpen} onOpenChange={(e) => !e.open && handleClose()} scrollBehavior="inside">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('challenge.admin.chains.duplicate.dialog.title')}</DialogTitle>
|
||||
@@ -106,3 +113,4 @@ export const DuplicateChainDialog: React.FC<DuplicateChainDialogProps> = ({
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import { URLs } from '../../__data__/urls'
|
||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||
import { ErrorAlert } from '../../components/ErrorAlert'
|
||||
import { EmptyState } from '../../components/EmptyState'
|
||||
import { ConfirmDialog } from '../../components/ConfirmDialog'
|
||||
import { DuplicateChainDialog } from '../../components/DuplicateChainDialog'
|
||||
import { ClearSubmissionsDialog } from '../../components/ClearSubmissionsDialog'
|
||||
import type { ChallengeChain } from '../../types/challenge'
|
||||
@@ -27,26 +26,28 @@ export const ChainsListPage: React.FC = () => {
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation()
|
||||
const { data: chains, isLoading, error, refetch } = useGetChainsQuery()
|
||||
const [deleteChain, { isLoading: isDeleting }] = useDeleteChainMutation()
|
||||
const [deleteChain] = useDeleteChainMutation()
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [chainToDelete, setChainToDelete] = useState<ChallengeChain | null>(null)
|
||||
const [chainToDuplicate, setChainToDuplicate] = useState<ChallengeChain | null>(null)
|
||||
const [chainToClearSubmissions, setChainToClearSubmissions] = useState<ChallengeChain | null>(null)
|
||||
const [updatingChainId, setUpdatingChainId] = useState<string | null>(null)
|
||||
const [updateChain] = useUpdateChainMutation()
|
||||
|
||||
const handleDeleteChain = async () => {
|
||||
if (!chainToDelete) return
|
||||
const handleDeleteChain = async (chain: ChallengeChain) => {
|
||||
const confirmed = window.confirm(
|
||||
t('challenge.admin.chains.delete.confirm.message', { name: chain.name })
|
||||
)
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
await deleteChain(chainToDelete.id).unwrap()
|
||||
await deleteChain(chain.id).unwrap()
|
||||
toaster.create({
|
||||
title: t('challenge.admin.common.success'),
|
||||
description: t('challenge.admin.chains.deleted'),
|
||||
type: 'success',
|
||||
})
|
||||
setChainToDelete(null)
|
||||
} catch (err) {
|
||||
toaster.create({
|
||||
title: t('challenge.admin.common.error'),
|
||||
@@ -205,7 +206,7 @@ export const ChainsListPage: React.FC = () => {
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
colorPalette="red"
|
||||
onClick={() => setChainToDelete(chain)}
|
||||
onClick={() => handleDeleteChain(chain)}
|
||||
>
|
||||
{t('challenge.admin.chains.list.button.delete')}
|
||||
</Button>
|
||||
@@ -218,16 +219,6 @@ export const ChainsListPage: React.FC = () => {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<ConfirmDialog
|
||||
isOpen={!!chainToDelete}
|
||||
onClose={() => setChainToDelete(null)}
|
||||
onConfirm={handleDeleteChain}
|
||||
title={t('challenge.admin.chains.delete.confirm.title')}
|
||||
message={t('challenge.admin.chains.delete.confirm.message', { name: chainToDelete?.name })}
|
||||
confirmLabel={t('challenge.admin.chains.delete.confirm.button')}
|
||||
isLoading={isDeleting}
|
||||
/>
|
||||
|
||||
<DuplicateChainDialog
|
||||
isOpen={!!chainToDuplicate}
|
||||
onClose={() => setChainToDuplicate(null)}
|
||||
|
||||
@@ -17,7 +17,6 @@ import { URLs } from '../../__data__/urls'
|
||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||
import { ErrorAlert } from '../../components/ErrorAlert'
|
||||
import { EmptyState } from '../../components/EmptyState'
|
||||
import { ConfirmDialog } from '../../components/ConfirmDialog'
|
||||
import type { ChallengeTask } from '../../types/challenge'
|
||||
import { toaster } from '../../components/ui/toaster'
|
||||
|
||||
@@ -25,22 +24,24 @@ export const TasksListPage: React.FC = () => {
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation()
|
||||
const { data: tasks, isLoading, error, refetch } = useGetTasksQuery()
|
||||
const [deleteTask, { isLoading: isDeleting }] = useDeleteTaskMutation()
|
||||
const [deleteTask] = useDeleteTaskMutation()
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [taskToDelete, setTaskToDelete] = useState<ChallengeTask | null>(null)
|
||||
|
||||
const handleDeleteTask = async () => {
|
||||
if (!taskToDelete) return
|
||||
const handleDeleteTask = async (task: ChallengeTask) => {
|
||||
const confirmed = window.confirm(
|
||||
t('challenge.admin.tasks.delete.confirm.message', { title: task.title })
|
||||
)
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
await deleteTask(taskToDelete.id).unwrap()
|
||||
await deleteTask(task.id).unwrap()
|
||||
toaster.create({
|
||||
title: t('challenge.admin.common.success'),
|
||||
description: t('challenge.admin.tasks.deleted'),
|
||||
type: 'success',
|
||||
})
|
||||
setTaskToDelete(null)
|
||||
} catch (_err) {
|
||||
toaster.create({
|
||||
title: t('challenge.admin.common.error'),
|
||||
@@ -152,7 +153,7 @@ export const TasksListPage: React.FC = () => {
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
colorPalette="red"
|
||||
onClick={() => setTaskToDelete(task)}
|
||||
onClick={() => handleDeleteTask(task)}
|
||||
>
|
||||
{t('challenge.admin.tasks.list.button.delete')}
|
||||
</Button>
|
||||
@@ -164,16 +165,6 @@ export const TasksListPage: React.FC = () => {
|
||||
</Table.Root>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<ConfirmDialog
|
||||
isOpen={!!taskToDelete}
|
||||
onClose={() => setTaskToDelete(null)}
|
||||
onConfirm={handleDeleteTask}
|
||||
title={t('challenge.admin.tasks.delete.confirm.title')}
|
||||
message={t('challenge.admin.tasks.delete.confirm.message', { title: taskToDelete?.title })}
|
||||
confirmLabel={t('challenge.admin.tasks.delete.confirm.button')}
|
||||
isLoading={isDeleting}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user