refactor: update modal management to use centralized modal store methods

This commit is contained in:
kastov 2025-10-25 03:34:44 +03:00
parent a57139783e
commit 3bc541d1bf
No known key found for this signature in database
GPG key ID: 1B27BE29057F4C90
47 changed files with 290 additions and 481 deletions

5
.gitignore vendored
View file

@ -142,4 +142,7 @@ build.info.json
public/wasm_exec.js
public/xray.schema.json
public/main.wasm
public/xray.schema.cn.json
public/xray.schema.cn.json
scripts
scripts/**

View file

@ -1,4 +1,3 @@
import { UpdateHostCommand } from '@remnawave/backend-contract'
import { devtools } from 'zustand/middleware'
import { create } from '@shared/hocs/store-wrapper'
@ -10,15 +9,6 @@ const initialState: IState = {
configProfileUuid: null,
inboundUuid: null,
hostTag: null
},
editModal: {
isOpen: false,
host: null,
isLoading: false
},
createModal: {
isOpen: false,
isLoading: false
}
}
@ -27,31 +17,6 @@ export const useHostsStore = create<IActions & IState>()(
(set) => ({
...initialState,
actions: {
toggleEditModal: (isOpen: boolean) => {
set((state) => ({
editModal: { ...state.editModal, isOpen }
}))
if (!isOpen) {
set((state) => ({
editModal: { ...state.editModal, host: null, isLoading: false }
}))
}
},
toggleCreateModal: (isOpen: boolean) => {
set((state) => ({
createModal: { ...state.createModal, isOpen }
}))
if (!isOpen) {
set((state) => ({
createModal: { ...state.createModal, isLoading: false }
}))
}
},
setHost: (host: UpdateHostCommand.Response['response']) => {
set((state) => ({
editModal: { ...state.editModal, host }
}))
},
getInitialState: () => {
return initialState
},
@ -95,15 +60,3 @@ export const useHostsStoreConfigProfileFilter = () =>
export const useHostsStoreInboundFilter = () => useHostsStore((state) => state.filters.inboundUuid)
export const useHostsStoreHostTagFilter = () => useHostsStore((state) => state.filters.hostTag)
// Edit Modal
export const useHostsStoreEditModalIsOpen = () => useHostsStore((state) => state.editModal.isOpen)
export const useHostsStoreEditModalHost = () => useHostsStore((state) => state.editModal.host)
export const useHostsStoreEditModalIsLoading = () =>
useHostsStore((state) => state.editModal.isLoading)
// Create Modal
export const useHostsStoreCreateModalIsOpen = () =>
useHostsStore((state) => state.createModal.isOpen)
export const useHostsStoreCreateModalIsLoading = () =>
useHostsStore((state) => state.createModal.isLoading)

View file

@ -1,5 +1,3 @@
import { UpdateHostCommand } from '@remnawave/backend-contract'
import { IState } from './state.interface'
export interface IActions {
@ -8,10 +6,7 @@ export interface IActions {
resetFilters: () => void
resetState: () => Promise<void>
setConfigProfileFilter: (configProfileUuid: null | string) => void
setHost: (host: UpdateHostCommand.Response['response']) => void
setHostTagFilter: (hostTag: null | string) => void
setInboundFilter: (inboundUuid: null | string) => void
toggleCreateModal: (isOpen: boolean) => void
toggleEditModal: (isOpen: boolean) => void
}
}

View file

@ -1,7 +0,0 @@
import { UpdateHostCommand } from '@remnawave/backend-contract'
export interface IEditModal {
host: null | UpdateHostCommand.Response['response']
isLoading: boolean
isOpen: boolean
}

View file

@ -1,3 +1,2 @@
export * from './action.interface'
export * from './edit-modal.interface'
export * from './state.interface'

View file

@ -1,5 +1,3 @@
import { IEditModal } from './edit-modal.interface'
export interface IFilters {
configProfileUuid: null | string
hostTag: null | string
@ -7,10 +5,5 @@ export interface IFilters {
}
export interface IState {
createModal: {
isLoading: boolean
isOpen: boolean
}
editModal: IEditModal
filters: IFilters
}

View file

@ -2,7 +2,8 @@ import {
GetConfigProfilesCommand,
GetExternalSquadsCommand,
GetInfraProvidersCommand,
GetInternalSquadsCommand
GetInternalSquadsCommand,
UpdateHostCommand
} from '@remnawave/backend-contract'
export const MODALS = {
@ -21,16 +22,20 @@ export const MODALS = {
RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL: 'RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL',
INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER: 'INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER',
CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER: 'CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER',
CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER: 'CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER'
CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER: 'CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER',
EDIT_HOST_MODAL: 'EDIT_HOST_MODAL',
CREATE_HOST_MODAL: 'CREATE_HOST_MODAL'
} as const
export interface ModalInternalStates {
CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER: GetConfigProfilesCommand.Response['response']['configProfiles'][number]
CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER: undefined
CONFIG_PROFILES_SHOW_ACTIVE_NODE: GetConfigProfilesCommand.Response['response']['configProfiles'][number]['nodes']
CREATE_HOST_MODAL: undefined
CREATE_INFRA_BILLING_NODE_MODAL: undefined
CREATE_INFRA_BILLING_RECORD_DRAWER: undefined
CREATE_INFRA_PROVIDER_DRAWER: undefined
EDIT_HOST_MODAL: UpdateHostCommand.Response['response']
EDIT_NODE_BY_UUID_MODAL: {
nodeUuid: string
}

View file

@ -1,80 +1,3 @@
// import { create, StateCreator } from 'zustand'
// export const MODALS = {
// RANDOM_MODAL_NAME: 'RANDOM_MODAL_NAME',
// RANDOM_MODAL_NAME_2: 'RANDOM_MODAL_NAME_2'
// } as const
// export interface ModalsSlice {
// close: (modalKey: MODALS) => void
// modals: ModalState
// open: (modalKey: MODALS) => void
// resetModalToDefault: (modalKey: MODALS) => void
// setInternalData: (payload: { internalState: unknown; modalKey: MODALS }) => void
// }
// interface BaseModalState {
// internalState?: unknown
// isOpen: boolean
// }
// type MODALS = (typeof MODALS)[keyof typeof MODALS]
// type ModalState = {
// [K in keyof typeof MODALS]: BaseModalState
// }
// const createModals = (keys: (keyof typeof MODALS)[]): ModalState => {
// const initialModalsState: Partial<ModalState> = {}
// keys.forEach((key) => {
// const modalEntry: { [K in typeof key]?: BaseModalState } = {
// [key]: {
// isOpen: false,
// internalState: {}
// }
// }
// Object.assign(initialModalsState, modalEntry)
// })
// return initialModalsState as ModalState
// }
// export const modalsInitialState = createModals(Object.keys(MODALS) as (keyof typeof MODALS)[])
// const createModalsSlice: StateCreator<ModalsSlice, [], [], ModalsSlice> = (set) => ({
// modals: { ...modalsInitialState },
// open: (modalKey: MODALS) =>
// set((state) => ({
// modals: {
// ...state.modals,
// [modalKey]: { ...state.modals[modalKey], isOpen: true }
// }
// })),
// close: (modalKey: MODALS) =>
// set((state) => ({
// modals: {
// ...state.modals,
// [modalKey]: { ...state.modals[modalKey], isOpen: false }
// }
// })),
// setInternalData: (payload: { internalState: unknown; modalKey: MODALS }) =>
// set((state) => ({
// modals: {
// ...state.modals,
// [payload.modalKey]: {
// ...state.modals[payload.modalKey],
// internalState: payload.internalState
// }
// }
// })),
// resetModalToDefault: (modalKey: MODALS) =>
// set((state) => ({
// modals: { ...state.modals, [modalKey]: { isOpen: false } }
// }))
// })
// export const useModalsStore = create<ModalsSlice>()((...a) => ({
// ...createModalsSlice(...a)
// }))
import { create, StateCreator } from 'zustand'
import { ModalInternalStates, MODALS } from './modal-states'
@ -155,3 +78,40 @@ const createModalsSlice: StateCreator<ModalsSlice, [], [], ModalsSlice> = (set)
export const useModalsStore = create<ModalsSlice>()((...a) => ({
...createModalsSlice(...a)
}))
export const useModalsStoreOpen = () => useModalsStore((state) => state.open)
export const useModalsStoreSetInternalData = () => useModalsStore((state) => state.setInternalData)
export const useModalsStoreOpenWithData = () => {
const open = useModalsStore((state) => state.open)
const setInternalData = useModalsStore((state) => state.setInternalData)
return <K extends ModalKeys>(modalKey: K, internalState: ModalInternalStates[K]) => {
setInternalData({ modalKey, internalState })
open(modalKey)
}
}
export const useModalClose = (modalKey: ModalKeys) => {
const close = useModalsStore((state) => state.close)
return () => close(modalKey)
}
export const useModalState = <K extends ModalKeys>(modalKey: K) => {
return useModalsStore((state) => state.modals[modalKey])
}
export const useModalIsOpen = (modalKey: ModalKeys) => {
return useModalsStore((state) => state.modals[modalKey].isOpen)
}
// export const useModalOpenWithData = <K extends ModalKeys>(modalKey: K) => {
// const open = useModalsStore((state) => state.open)
// const setInternalData = useModalsStore((state) => state.setInternalData)
// return (internalState: ModalInternalStates[K]) => {
// setInternalData({ modalKey, internalState })
// open(modalKey)
// }
// }
// const openInbounds = useModalOpenWithData(MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS)

View file

@ -17,14 +17,21 @@ import { useTranslation } from 'react-i18next'
import { modals } from '@mantine/modals'
import consola from 'consola/browser'
import {
MODALS,
useModalClose,
useModalsStoreOpenWithData,
useModalState
} from '@entities/dashboard/modal-store'
import { KeypairGeneratorWidget } from '@widgets/dashboard/config-profiles/keypair-generator/keypair-generator.widget'
import { useDownloadTemplate } from '@shared/ui/load-templates/use-download-template'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { QueryKeys, useUpdateConfigProfile } from '@shared/api/hooks'
import { queryClient } from '@shared/api'
import { Props } from './interfaces'
const MODAL_KEY = MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER
export function ConfigEditorActionsFeature(props: Props) {
const {
editorRef,
@ -42,14 +49,12 @@ export function ConfigEditorActionsFeature(props: Props) {
const isMobile = useMediaQuery('(max-width: 48em)')
const clipboard = useClipboard({ timeout: 500 })
const modalsStore = useModalsStore()
const { open, close, setInternalData } = modalsStore
const { isOpen } = useModalState(MODAL_KEY)
const close = useModalClose(MODAL_KEY)
const openWithData = useModalsStoreOpenWithData()
const [opened, handlers] = useDisclosure(false)
const isSnippetsOpen =
modalsStore.modals[MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER]?.isOpen || false
const { mutate: updateConfig, isPending: isUpdating } = useUpdateConfigProfile({
mutationFns: {
onSuccess: async (
@ -323,14 +328,10 @@ export function ConfigEditorActionsFeature(props: Props) {
<ActionIcon
onClick={() => {
if (isSnippetsOpen) {
close(MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER)
if (isOpen) {
close()
} else {
setInternalData({
internalState: undefined,
modalKey: MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER
})
open(MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER)
openWithData(MODAL_KEY, undefined)
}
}}
size={36}
@ -338,7 +339,7 @@ export function ConfigEditorActionsFeature(props: Props) {
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0
}}
variant={isSnippetsOpen ? 'filled' : 'default'}
variant={isOpen ? 'filled' : 'default'}
>
<TbBraces size={20} />
</ActionIcon>

View file

@ -15,14 +15,14 @@ import { useTranslation } from 'react-i18next'
import { useField } from '@mantine/form'
import { QueryKeys, useCreateExternalSquad, useGetExternalSquads } from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { queryClient } from '@shared/api'
export const ExternalSquadsHeaderActionButtonsFeature = () => {
const { isFetching } = useGetExternalSquads()
const { t } = useTranslation()
const { open: openModal, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const [opened, { open, close }] = useDisclosure(false)
@ -48,11 +48,7 @@ export const ExternalSquadsHeaderActionButtonsFeature = () => {
nameField.reset()
handleUpdate()
setInternalData({
internalState: data,
modalKey: MODALS.EXTERNAL_SQUAD_DRAWER
})
openModal(MODALS.EXTERNAL_SQUAD_DRAWER)
openModalWithData(MODALS.EXTERNAL_SQUAD_DRAWER, data)
},
onError: (error) => {
nameField.setError(error.message)

View file

@ -2,19 +2,19 @@ import { ActionIcon, Tooltip } from '@mantine/core'
import { useTranslation } from 'react-i18next'
import { TbTrash } from 'react-icons/tb'
import { useHostsStoreActions, useHostsStoreEditModalHost } from '@entities/dashboard'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { useDeleteHost } from '@shared/api/hooks'
export function DeleteHostFeature() {
const { t } = useTranslation()
const actions = useHostsStoreActions()
const host = useHostsStoreEditModalHost()
const { internalState: host } = useModalState(MODALS.EDIT_HOST_MODAL)
const close = useModalClose(MODALS.EDIT_HOST_MODAL)
const { mutate: deleteHost, isPending: isDeleteHostPending } = useDeleteHost({
mutationFns: {
onSuccess: () => {
actions.toggleEditModal(false)
close()
}
}
})

View file

@ -2,19 +2,19 @@ import { ActionIcon, ActionIconGroup, Group, Tooltip } from '@mantine/core'
import { TbPlus, TbRefresh } from 'react-icons/tb'
import { useTranslation } from 'react-i18next'
import { useHostsStoreActions } from '@entities/dashboard'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { QueryKeys, useGetHosts } from '@shared/api/hooks'
import { queryClient } from '@shared/api'
export const HeaderActionButtonsFeature = () => {
const { t } = useTranslation()
const actions = useHostsStoreActions()
const openModalWithData = useModalsStoreOpenWithData()
const { isFetching } = useGetHosts()
const handleCreate = () => {
actions.toggleCreateModal(true)
openModalWithData(MODALS.CREATE_HOST_MODAL, undefined)
}
const handleUpdate = async () => {

View file

@ -16,14 +16,14 @@ import { useTranslation } from 'react-i18next'
import { useField } from '@mantine/form'
import { QueryKeys, useCreateInternalSquad, useGetInternalSquads } from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { queryClient } from '@shared/api'
export const InternalSquadsHeaderActionButtonsFeature = () => {
const { t } = useTranslation()
const { isFetching } = useGetInternalSquads()
const { open: openModal, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const [opened, { open, close }] = useDisclosure(false)
@ -50,12 +50,9 @@ export const InternalSquadsHeaderActionButtonsFeature = () => {
nameField.reset()
handleUpdate()
setInternalData({
internalState: data,
modalKey: MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS
})
openModal(MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS)
openModalWithData(MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS, data)
},
onError: (error) => {
nameField.setError(error.message)
}

View file

@ -3,7 +3,7 @@ import { TbServerCog } from 'react-icons/tb'
import { Menu } from '@mantine/core'
import { memo } from 'react'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { IProps } from './interfaces'
@ -11,19 +11,15 @@ const GetNodeLinkedHostsFeatureComponent = (props: IProps) => {
const { nodeUuid } = props
const { t } = useTranslation()
const { open: openModal, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
return (
<Menu.Item
leftSection={<TbServerCog size="16px" />}
onClick={() => {
setInternalData({
internalState: {
nodeUuid
},
modalKey: MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER
openModalWithData(MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER, {
nodeUuid
})
openModal(MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER)
}}
>
{t('get-node-linked-hosts.feature.linked-hosts')}

View file

@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
import { Menu } from '@mantine/core'
import { memo } from 'react'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { IProps } from './interfaces'
@ -11,20 +11,16 @@ const GetNodeUsersUsageFeatureComponent = (props: IProps) => {
const { nodeUuid } = props
const { t } = useTranslation()
const { open: openModal, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
return (
<Menu.Item
color="grape"
leftSection={<PiChartBarDuotone size="16px" />}
onClick={() => {
setInternalData({
internalState: {
nodeUuid
},
modalKey: MODALS.SHOW_NODE_USERS_USAGE_DRAWER
openModalWithData(MODALS.SHOW_NODE_USERS_USAGE_DRAWER, {
nodeUuid
})
openModal(MODALS.SHOW_NODE_USERS_USAGE_DRAWER)
}}
>
{t('get-user-usage.feature.show-usage')}

View file

@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
import { ConfigEditorWidget } from '@widgets/dashboard/config-profiles/config-editor/config-editor.widget'
import { SnippetsDrawerWidget } from '@widgets/dashboard/config-profiles/snippets-drawer'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalIsOpen } from '@entities/dashboard/modal-store'
import { ROUTES } from '@shared/constants'
import { PageHeader } from '@shared/ui'
import { Page } from '@shared/ui/page'
@ -21,10 +21,8 @@ export const ConfigProfileByUuidPageComponent = (props: Props) => {
const { t } = useTranslation()
const isMobile = useMediaQuery('(max-width: 1200px)')
const { isOpen } = useModalsStore(
(state) => state.modals[MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER]
)
const { close } = useModalsStore()
const isOpen = useModalIsOpen(MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER)
const close = useModalClose(MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER)
return (
<>
@ -48,7 +46,7 @@ export const ConfigProfileByUuidPageComponent = (props: Props) => {
<Drawer
keepMounted={false}
onClose={() => close(MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding={0}

View file

@ -7,11 +7,8 @@ import {
useGetHostTags,
useGetNodes
} from '@shared/api/hooks'
import {
useHostsStoreCreateModalIsOpen,
useHostsStoreEditModalIsOpen
} from '@entities/dashboard/hosts/hosts-store'
import HostsPageComponent from '@pages/dashboard/hosts/ui/components/hosts.page.component'
import { MODALS, useModalIsOpen } from '@entities/dashboard/modal-store'
import { queryClient } from '@shared/api'
export function HostsPageConnector() {
@ -20,8 +17,8 @@ export function HostsPageConnector() {
const { data: hostTags, isLoading: isHostTagsLoading } = useGetHostTags()
const { isLoading: isNodesLoading } = useGetNodes()
const isCreateModalOpen = useHostsStoreCreateModalIsOpen()
const isEditModalOpen = useHostsStoreEditModalIsOpen()
const isCreateModalOpen = useModalIsOpen(MODALS.CREATE_HOST_MODAL)
const isEditModalOpen = useModalIsOpen(MODALS.EDIT_HOST_MODAL)
useEffect(() => {
if (isCreateModalOpen || isEditModalOpen) return

View file

@ -5,6 +5,7 @@ import { Grid } from '@mantine/core'
import { InternalSquadAccessibleNodesModalWidget } from '@widgets/dashboard/internal-squads/internal-squad-accessible-nodes-modal/internal-squad-accessible-nodes.modal.widget'
import { InternalSquadsHeaderWidget } from '@widgets/dashboard/internal-squads/internal-squads-header-widget/internal-squads-header.widget'
import { InternalSquadsGridWidget } from '@widgets/dashboard/internal-squads/internal-squads-grid/internal-squads-grid.widget'
import { InternalSquadsDrawerWithStore } from '@widgets/dashboard/users/internal-squads-drawer-with-store'
import { RenameModalShared } from '@shared/ui/modals/rename-modal.shared'
import { ROUTES } from '@shared/constants'
import { PageHeader } from '@shared/ui'
@ -45,6 +46,7 @@ export const InternalSquadsPageComponent = (props: Props) => {
</Grid>
<RenameModalShared key="rename-internal-squad-modal" renameFrom="internalSquad" />
<InternalSquadAccessibleNodesModalWidget key="internal-squad-accessible-nodes-modal" />
<InternalSquadsDrawerWithStore key="internal-squads-drawer-with-store" />
</Page>
)
}

View file

@ -13,19 +13,18 @@ import { PiCheck, PiCopy, PiCpu } from 'react-icons/pi'
import ReactCountryFlag from 'react-country-flag'
import { useTranslation } from 'react-i18next'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
export const ActiveNodesListModalWithStoreShared = () => {
const { isOpen, internalState: nodes } = useModalsStore(
(state) => state.modals[MODALS.CONFIG_PROFILES_SHOW_ACTIVE_NODE]
)
const { close } = useModalsStore()
const { isOpen, internalState: nodes } = useModalState(MODALS.CONFIG_PROFILES_SHOW_ACTIVE_NODE)
const close = useModalClose(MODALS.CONFIG_PROFILES_SHOW_ACTIVE_NODE)
const { t } = useTranslation()
return (
<Modal
centered
onClose={() => close(MODALS.CONFIG_PROFILES_SHOW_ACTIVE_NODE)}
onClose={close}
opened={isOpen}
size="lg"
title={t('active-nodes-list-with-store.modal.shared.active-nodes')}

View file

@ -16,7 +16,7 @@ import {
useUpdateInternalSquad,
useUpdateSubscriptionTemplate
} from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { queryClient } from '@shared/api/query-client'
type RenameType = 'configProfile' | 'externalSquad' | 'internalSquad' | 'template'
@ -28,10 +28,8 @@ interface IProps {
export function RenameModalShared({ renameFrom }: IProps) {
const { t } = useTranslation()
const { isOpen, internalState } = useModalsStore(
(state) => state.modals[MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL]
)
const { close } = useModalsStore()
const { isOpen, internalState } = useModalState(MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL)
const close = useModalClose(MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL)
const nameField = useField<string>({
mode: 'controlled',
@ -68,7 +66,7 @@ export function RenameModalShared({ renameFrom }: IProps) {
})
const handleModalClose = () => {
close(MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL)
close()
}
const { mutate: updateInternalSquad, isPending: isUpdatingInternalSquad } =

View file

@ -22,7 +22,7 @@ import { modals } from '@mantine/modals'
import { motion } from 'framer-motion'
import clsx from 'clsx'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { formatInt } from '@shared/utils/misc'
import { XrayLogo } from '@shared/ui/logos'
import { ROUTES } from '@shared/constants'
@ -42,7 +42,8 @@ export function ConfigProfileCardWidget(props: IProps) {
const [opened, handlers] = useDisclosure(false)
const { open, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const navigate = useNavigate()
const nodesCount = configProfile.nodes.length
@ -117,12 +118,10 @@ export function ConfigProfileCardWidget(props: IProps) {
color="blue"
leftSection={<PiTag size={12} />}
onClick={() => {
setInternalData({
internalState: configProfile,
modalKey:
MODALS.CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER
})
open(MODALS.CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER)
openModalWithData(
MODALS.CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER,
configProfile
)
}}
size="lg"
style={{ cursor: 'pointer' }}
@ -139,12 +138,10 @@ export function ConfigProfileCardWidget(props: IProps) {
color={isActive ? 'teal' : 'gray'}
leftSection={<PiCpu size={12} />}
onClick={() => {
setInternalData({
internalState: configProfile.nodes,
modalKey:
MODALS.CONFIG_PROFILES_SHOW_ACTIVE_NODE
})
open(MODALS.CONFIG_PROFILES_SHOW_ACTIVE_NODE)
openModalWithData(
MODALS.CONFIG_PROFILES_SHOW_ACTIVE_NODE,
configProfile.nodes
)
}}
size="lg"
style={{
@ -274,14 +271,13 @@ export function ConfigProfileCardWidget(props: IProps) {
<Menu.Item
leftSection={<PiPencil size={18} />}
onClick={() => {
setInternalData({
internalState: {
openModalWithData(
MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL,
{
name: configProfile.name,
uuid: configProfile.uuid
},
modalKey: MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL
})
open(MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL)
}
)
}}
>
{t('common.rename')}

View file

@ -20,15 +20,14 @@ import { modals } from '@mantine/modals'
import ColorHash from 'color-hash'
import { InternalSquadsListSimpleWidgetShared } from '@shared/ui/internal-squads/internal-squads-list-simple'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { useGetConfigProfileInbounds, useGetInternalSquads } from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
export const ConfigProfileInboundsDrawerWidget = () => {
const { t } = useTranslation()
const { isOpen, internalState } = useModalsStore(
(state) => state.modals[MODALS.CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER]
)
const { close } = useModalsStore()
const { isOpen, internalState } = useModalState(MODALS.CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER)
const close = useModalClose(MODALS.CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER)
const { data: configProfileInbounds, isLoading } = useGetConfigProfileInbounds({
route: {
@ -173,7 +172,7 @@ export const ConfigProfileInboundsDrawerWidget = () => {
return (
<Drawer
keepMounted={false}
onClose={() => close(MODALS.CONFIG_PROFILE_SHOW_INBOUNDS_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"

View file

@ -39,8 +39,8 @@ import {
useUpdateSnippet
} from '@shared/api/hooks'
import { MonacoSetupSnippetsFeature } from '@features/dashboard/config-profiles/monaco-setup/monaco-setup.feature'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { CopyableFieldShared } from '@shared/ui/copyable-field/copyable-field'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { queryClient } from '@shared/api'
import classes from './SnippetsDrawer.module.css'
@ -48,9 +48,8 @@ import classes from './SnippetsDrawer.module.css'
export const SnippetsDrawerWidget = () => {
const { t, i18n } = useTranslation()
const { internalState } = useModalsStore(
(state) => state.modals[MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER]
)
const { isOpen } = useModalState(MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER)
const close = useModalClose(MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER)
const monacoRef = useRef<Monaco | null>(null)
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null)
@ -63,8 +62,6 @@ export const SnippetsDrawerWidget = () => {
const isMobile = useMediaQuery('(max-width: 1200px)')
const { close } = useModalsStore()
const [createModalOpened, { open: openCreateModal, close: closeCreateModal }] =
useDisclosure(false)
const [editModalOpened, { open: openEditModal, close: closeEditModal }] = useDisclosure(false)
@ -93,7 +90,7 @@ export const SnippetsDrawerWidget = () => {
const { data: snippets, isLoading } = useGetSnippets({
rQueryParams: {
enabled: !!internalState
enabled: !!isOpen
}
})
@ -771,12 +768,7 @@ export const SnippetsDrawerWidget = () => {
>
<TbRefresh size={18} />
</ActionIcon>
<ActionIcon
color="gray"
onClick={() => close(MODALS.CONFIG_PROFILE_SHOW_SNIPPETS_DRAWER)}
size="sm"
variant="subtle"
>
<ActionIcon color="gray" onClick={close} size="sm" variant="subtle">
<TbX size={18} />
</ActionIcon>
</Group>

View file

@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'
import { motion } from 'motion/react'
import clsx from 'clsx'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { formatInt } from '@shared/utils/misc'
import classes from './external-squad-card.module.css'
@ -45,28 +45,21 @@ export function ExternalSquadCardWidget(props: IProps) {
const { t } = useTranslation()
const [opened, handlers] = useDisclosure(false)
const { open, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const { membersCount } = externalSquad.info
const isActive = membersCount > 0
const handleOpenInbounds = () => {
setInternalData({
internalState: externalSquad,
modalKey: MODALS.EXTERNAL_SQUAD_DRAWER
})
open(MODALS.EXTERNAL_SQUAD_DRAWER)
openModalWithData(MODALS.EXTERNAL_SQUAD_DRAWER, externalSquad)
}
const handleRename = () => {
setInternalData({
internalState: {
name: externalSquad.name,
uuid: externalSquad.uuid
},
modalKey: MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL
openModalWithData(MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL, {
name: externalSquad.name,
uuid: externalSquad.uuid
})
open(MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL)
}
return (

View file

@ -18,7 +18,7 @@ import { PiCheck, PiCopy, PiUsers } from 'react-icons/pi'
import { useTranslation } from 'react-i18next'
import { useState } from 'react'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { useGetSubscriptionTemplates } from '@shared/api/hooks'
import { LoaderModalShared } from '@shared/ui/loader-modal'
import { formatInt } from '@shared/utils/misc'
@ -39,10 +39,8 @@ export const ExternalSquadsDrawer = () => {
const [activeTab, setActiveTab] = useState<TabType>('templates')
const { isOpen, internalState: externalSquad } = useModalsStore(
(state) => state.modals[MODALS.EXTERNAL_SQUAD_DRAWER]
)
const { close } = useModalsStore()
const { isOpen, internalState: externalSquad } = useModalState(MODALS.EXTERNAL_SQUAD_DRAWER)
const close = useModalClose(MODALS.EXTERNAL_SQUAD_DRAWER)
const { isLoading: isTemplatesLoading } = useGetSubscriptionTemplates()
@ -204,7 +202,7 @@ export const ExternalSquadsDrawer = () => {
return (
<Drawer
keepMounted={true}
onClose={() => close(MODALS.EXTERNAL_SQUAD_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="md"

View file

@ -7,15 +7,15 @@ import { useEffect, useState } from 'react'
import { useForm } from '@mantine/form'
import { QueryKeys, useCreateHost, useGetConfigProfiles, useGetNodes } from '@shared/api/hooks'
import { useHostsStoreActions, useHostsStoreCreateModalIsOpen } from '@entities/dashboard'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { BaseHostForm } from '@shared/ui/forms/hosts/base-host-form'
import { queryClient } from '@shared/api'
export const CreateHostModalWidget = () => {
const { t } = useTranslation()
const isModalOpen = useHostsStoreCreateModalIsOpen()
const actions = useHostsStoreActions()
const { isOpen } = useModalState(MODALS.CREATE_HOST_MODAL)
const close = useModalClose(MODALS.CREATE_HOST_MODAL)
const { data: configProfiles } = useGetConfigProfiles()
const { data: nodes } = useGetNodes()
@ -35,7 +35,7 @@ export const CreateHostModalWidget = () => {
})
const handleClose = () => {
actions.toggleCreateModal(false)
close()
setAdvancedOpened(false)
form.reset()
@ -104,7 +104,7 @@ export const CreateHostModalWidget = () => {
<Drawer
keepMounted={false}
onClose={handleClose}
opened={isModalOpen}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"
position="right"

View file

@ -15,22 +15,18 @@ import {
useGetNodes,
useUpdateHost
} from '@shared/api/hooks'
import {
useHostsStoreActions,
useHostsStoreEditModalHost,
useHostsStoreEditModalIsOpen
} from '@entities/dashboard'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { BaseHostForm } from '@shared/ui/forms/hosts/base-host-form'
import { queryClient } from '@shared/api'
import {} from '@entities/dashboard'
export const EditHostModalWidget = () => {
const { t } = useTranslation()
const [advancedOpened, setAdvancedOpened] = useState(false)
const { isOpen, internalState: host } = useModalState(MODALS.EDIT_HOST_MODAL)
const close = useModalClose(MODALS.EDIT_HOST_MODAL)
const isModalOpen = useHostsStoreEditModalIsOpen()
const actions = useHostsStoreActions()
const host = useHostsStoreEditModalHost()
const [advancedOpened, setAdvancedOpened] = useState(false)
const { data: configProfiles } = useGetConfigProfiles()
const { data: nodes } = useGetNodes()
@ -48,7 +44,7 @@ export const EditHostModalWidget = () => {
})
const handleClose = () => {
actions.toggleEditModal(false)
close()
setTimeout(() => {
form.reset()
@ -285,7 +281,7 @@ export const EditHostModalWidget = () => {
<Drawer
keepMounted={false}
onClose={handleClose}
opened={isModalOpen}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"
position="right"

View file

@ -10,9 +10,10 @@ import { CSS } from '@dnd-kit/utilities'
import ColorHash from 'color-hash'
import cx from 'clsx'
import { useHostsStoreActions, useHostsStoreFilters } from '@entities/dashboard'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { resolveCountryCode } from '@shared/utils/misc/resolve-country-code'
import { SEARCH_PARAMS } from '@shared/constants/search-params'
import { useHostsStoreFilters } from '@entities/dashboard'
import { XrayLogo } from '@shared/ui/logos'
import classes from './HostCard.module.css'
@ -32,7 +33,9 @@ export function HostCardWidget(props: IProps) {
const [searchParams, setSearchParams] = useSearchParams()
const filters = useHostsStoreFilters()
const actions = useHostsStoreActions()
const openModalWithData = useModalsStoreOpenWithData()
const [isHovered, setIsHovered] = useState(false)
const isMobile = useMediaQuery('(max-width: 48em)')
@ -59,8 +62,7 @@ export function HostCardWidget(props: IProps) {
}
const handleEdit = () => {
actions.setHost(item)
actions.toggleEditModal(true)
openModalWithData(MODALS.EDIT_HOST_MODAL, item)
}
useEffect(() => {

View file

@ -10,17 +10,15 @@ import dayjs from 'dayjs'
import { SelectInfraProviderShared } from '@shared/ui/infra-billing/select-infra-provider/select-infra-provider.shared'
import { SelectBillingNodeShared } from '@shared/ui/infra-billing/select-billing-node/select-billing-node.shared'
import { MODALS, useModalClose, useModalIsOpen } from '@entities/dashboard/modal-store'
import { QueryKeys, useCreateInfraBillingNode } from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { handleFormErrors } from '@shared/utils/misc'
import { queryClient } from '@shared/api'
export function CreateInfraBillingNodeModalWidget() {
const { isOpen } = useModalsStore(
(state) => state.modals[MODALS.CREATE_INFRA_BILLING_NODE_MODAL]
)
const isOpen = useModalIsOpen(MODALS.CREATE_INFRA_BILLING_NODE_MODAL)
const close = useModalClose(MODALS.CREATE_INFRA_BILLING_NODE_MODAL)
const { close } = useModalsStore()
const { t } = useTranslation()
const form = useForm<CreateInfraBillingNodeCommand.Request>({
@ -51,7 +49,7 @@ export function CreateInfraBillingNodeModalWidget() {
form.reset()
close(MODALS.CREATE_INFRA_BILLING_NODE_MODAL)
close()
},
onError: (error) => {
handleFormErrors(form, error)
@ -90,7 +88,7 @@ export function CreateInfraBillingNodeModalWidget() {
onClose={() => {
form.reset()
close(MODALS.CREATE_INFRA_BILLING_NODE_MODAL)
close()
}}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}

View file

@ -9,17 +9,15 @@ import { useForm } from '@mantine/form'
import dayjs from 'dayjs'
import { SelectInfraProviderShared } from '@shared/ui/infra-billing/select-infra-provider/select-infra-provider.shared'
import { MODALS, useModalClose, useModalIsOpen } from '@entities/dashboard/modal-store'
import { QueryKeys, useCreateInfraBillingHistoryRecord } from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { handleFormErrors } from '@shared/utils/misc'
import { queryClient } from '@shared/api'
export function CreateInfraBillingRecordDrawerWidget() {
const { isOpen } = useModalsStore(
(state) => state.modals[MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER]
)
const isOpen = useModalIsOpen(MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER)
const close = useModalClose(MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER)
const { close } = useModalsStore()
const { t } = useTranslation()
const form = useForm<CreateInfraBillingHistoryRecordCommand.Request>({
@ -60,7 +58,7 @@ export function CreateInfraBillingRecordDrawerWidget() {
form.reset()
close(MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER)
close()
},
onError: (error) => {
handleFormErrors(form, error)
@ -97,7 +95,7 @@ export function CreateInfraBillingRecordDrawerWidget() {
onClose={() => {
form.reset()
close(MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER)
close()
}}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}

View file

@ -4,18 +4,17 @@ import { zodResolver } from 'mantine-form-zod-resolver'
import { useTranslation } from 'react-i18next'
import { useForm } from '@mantine/form'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalIsOpen } from '@entities/dashboard/modal-store'
import { QueryKeys, useCreateInfraProvider } from '@shared/api/hooks'
import { handleFormErrors } from '@shared/utils/misc'
import { queryClient } from '@shared/api'
export function CreateInfraProviderDrawerWidget() {
const { isOpen } = useModalsStore((state) => state.modals[MODALS.CREATE_INFRA_PROVIDER_DRAWER])
const isOpen = useModalIsOpen(MODALS.CREATE_INFRA_PROVIDER_DRAWER)
const close = useModalClose(MODALS.CREATE_INFRA_PROVIDER_DRAWER)
const { t } = useTranslation()
const { close } = useModalsStore()
const form = useForm<CreateInfraProviderCommand.Request>({
name: 'create-infra-provider-form',
mode: 'uncontrolled',
@ -30,7 +29,7 @@ export function CreateInfraProviderDrawerWidget() {
queryKey: QueryKeys.infraBilling.getInfraProviders.queryKey
})
close(MODALS.CREATE_INFRA_PROVIDER_DRAWER)
close()
},
onError: (error) => {
handleFormErrors(form, error)
@ -51,7 +50,7 @@ export function CreateInfraProviderDrawerWidget() {
return (
<Drawer
keepMounted={false}
onClose={() => close(MODALS.CREATE_INFRA_PROVIDER_DRAWER)}
onClose={close}
onExitTransitionEnd={() => {
form.reset()
}}

View file

@ -24,7 +24,7 @@ import {
useGetInfraBillingNodes,
useUpdateInfraBillingNode
} from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { DataTableShared } from '@shared/ui/table'
import { queryClient } from '@shared/api'
@ -39,7 +39,8 @@ export function InfraBillingNodesTableWidget() {
refetch: refetchInfraBillingNodes
} = useGetInfraBillingNodes()
const { open: openModal, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const { t } = useTranslation()
const [multiSelectedRecords, setMultiSelectedRecords] = useState<
@ -114,14 +115,10 @@ export function InfraBillingNodesTableWidget() {
const handleClickBillingAt = (
node: GetInfraBillingNodesCommand.Response['response']['billingNodes'][number]
) => {
setInternalData({
internalState: {
uuids: [node.uuid],
nextBillingAt: node.nextBillingAt
},
modalKey: MODALS.UPDATE_BILLING_DATE_MODAL
openModalWithData(MODALS.UPDATE_BILLING_DATE_MODAL, {
uuids: [node.uuid],
nextBillingAt: node.nextBillingAt
})
openModal(MODALS.UPDATE_BILLING_DATE_MODAL)
}
const clearMultiSelectedRecords = () => {
@ -129,14 +126,10 @@ export function InfraBillingNodesTableWidget() {
}
const handleUpdateMultipleNodes = () => {
setInternalData({
internalState: {
uuids: multiSelectedRecords.map((node) => node.uuid),
callback: clearMultiSelectedRecords
},
modalKey: MODALS.UPDATE_BILLING_DATE_MODAL
openModalWithData(MODALS.UPDATE_BILLING_DATE_MODAL, {
uuids: multiSelectedRecords.map((node) => node.uuid),
callback: clearMultiSelectedRecords
})
openModal(MODALS.UPDATE_BILLING_DATE_MODAL)
}
return (
@ -203,7 +196,10 @@ export function InfraBillingNodesTableWidget() {
<ActionIcon
color="teal"
onClick={() => {
openModal(MODALS.CREATE_INFRA_BILLING_NODE_MODAL)
openModalWithData(
MODALS.CREATE_INFRA_BILLING_NODE_MODAL,
undefined
)
}}
size="lg"
variant="light"
@ -233,7 +229,9 @@ export function InfraBillingNodesTableWidget() {
{t('infra-billing-nodes.widget.no-nodes-found')}
</Text>
<Button
onClick={() => openModal(MODALS.CREATE_INFRA_BILLING_NODE_MODAL)}
onClick={() =>
openModalWithData(MODALS.CREATE_INFRA_BILLING_NODE_MODAL, undefined)
}
style={{ pointerEvents: 'all' }}
variant="light"
>

View file

@ -20,7 +20,7 @@ import {
useDeleteInfraBillingHistoryRecord,
useGetInfraBillingHistoryRecords
} from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { DataTableShared } from '@shared/ui/table'
import { getInfraBillingRecordsColumns } from './use-infra-billing-records-columns'
@ -40,10 +40,14 @@ export function InfraBillingRecordsTableWidget() {
size: PAGE_SIZE
}
})
const { open: openModal } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const { t } = useTranslation()
useHotkeys([['mod + K', () => openModal(MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER)]])
const handleOpenModal = () => {
openModalWithData(MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER, undefined)
}
useHotkeys([['mod + K', handleOpenModal]])
const memoizedInfraBillingRecords = useMemo(() => infraBillingRecords, [infraBillingRecords])
@ -99,9 +103,7 @@ export function InfraBillingRecordsTableWidget() {
<Tooltip label={t('common.create')} withArrow>
<ActionIcon
color="teal"
onClick={() => {
openModal(MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER)
}}
onClick={handleOpenModal}
size="lg"
variant="light"
>
@ -126,7 +128,7 @@ export function InfraBillingRecordsTableWidget() {
{t('infra-billing-records-table.widget.no-billing-records-found')}
</Text>
<Button
onClick={() => openModal(MODALS.CREATE_INFRA_BILLING_RECORD_DRAWER)}
onClick={handleOpenModal}
style={{ pointerEvents: 'all' }}
variant="light"
>

View file

@ -17,7 +17,7 @@ import { modals } from '@mantine/modals'
import { useMemo } from 'react'
import { QueryKeys, useDeleteInfraProvider, useGetInfraProviders } from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { DataTableShared } from '@shared/ui/table'
import { queryClient } from '@shared/api'
@ -31,7 +31,9 @@ export function InfraProvidersTableWidget() {
isFetching: infraProvidersLoading,
refetch: refetchInfraProviders
} = useGetInfraProviders()
const { open: openModal, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const { t } = useTranslation()
const memoizedInfraProviders = useMemo(() => infraProviders, [infraProviders])
@ -64,11 +66,7 @@ export function InfraProvidersTableWidget() {
const handleOpenModal = (
row: GetInfraProvidersCommand.Response['response']['providers'][number]
) => {
setInternalData({
internalState: row,
modalKey: MODALS.VIEW_INFRA_PROVIDER_DRAWER
})
openModal(MODALS.VIEW_INFRA_PROVIDER_DRAWER)
openModalWithData(MODALS.VIEW_INFRA_PROVIDER_DRAWER, row)
}
const handleDeleteInfraProvider = (uuid: string) =>
@ -122,7 +120,10 @@ export function InfraProvidersTableWidget() {
<ActionIcon
color="teal"
onClick={() => {
openModal(MODALS.CREATE_INFRA_PROVIDER_DRAWER)
openModalWithData(
MODALS.CREATE_INFRA_PROVIDER_DRAWER,
undefined
)
}}
size="lg"
variant="light"
@ -154,7 +155,9 @@ export function InfraProvidersTableWidget() {
{t('infra-providers-table.widget.no-providers-found')}
</Text>
<Button
onClick={() => openModal(MODALS.CREATE_INFRA_PROVIDER_DRAWER)}
onClick={() =>
openModalWithData(MODALS.CREATE_INFRA_PROVIDER_DRAWER, undefined)
}
style={{ pointerEvents: 'all' }}
variant="light"
>

View file

@ -4,17 +4,16 @@ import { DatePicker } from '@mantine/dates'
import { useEffect, useState } from 'react'
import dayjs from 'dayjs'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { QueryKeys, useUpdateInfraBillingNode } from '@shared/api/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { queryClient } from '@shared/api'
import styles from './UpdateModal.module.css'
export function UpdateBillingDateModalWidget() {
const { isOpen, internalState: billingNode } = useModalsStore(
(state) => state.modals[MODALS.UPDATE_BILLING_DATE_MODAL]
)
const { close } = useModalsStore()
const { isOpen, internalState: billingNode } = useModalState(MODALS.UPDATE_BILLING_DATE_MODAL)
const close = useModalClose(MODALS.UPDATE_BILLING_DATE_MODAL)
const [selectedDate, setSelectedDate] = useState<Date | null>(null)
const { t } = useTranslation()
@ -27,7 +26,7 @@ export function UpdateBillingDateModalWidget() {
billingNode.callback()
}
close(MODALS.UPDATE_BILLING_DATE_MODAL)
close()
setSelectedDate(null)
},
onError: () => {}
@ -57,7 +56,7 @@ export function UpdateBillingDateModalWidget() {
}
const handleClose = () => {
close(MODALS.UPDATE_BILLING_DATE_MODAL)
close()
setTimeout(() => {
setSelectedDate(null)

View file

@ -5,16 +5,17 @@ import { useTranslation } from 'react-i18next'
import { useForm } from '@mantine/form'
import { useEffect } from 'react'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { QueryKeys, useUpdateInfraProvider } from '@shared/api/hooks'
import { handleFormErrors } from '@shared/utils/misc'
import { queryClient } from '@shared/api'
export function ViewInfraProviderDrawerWidget() {
const { isOpen, internalState: infraProvider } = useModalsStore(
(state) => state.modals[MODALS.VIEW_INFRA_PROVIDER_DRAWER]
const { isOpen, internalState: infraProvider } = useModalState(
MODALS.VIEW_INFRA_PROVIDER_DRAWER
)
const { close } = useModalsStore()
const close = useModalClose(MODALS.VIEW_INFRA_PROVIDER_DRAWER)
const { t } = useTranslation()
const form = useForm<UpdateInfraProviderCommand.Request>({
@ -31,7 +32,7 @@ export function ViewInfraProviderDrawerWidget() {
queryKey: QueryKeys.infraBilling.getInfraProviders.queryKey
})
close(MODALS.VIEW_INFRA_PROVIDER_DRAWER)
close()
},
onError: (error) => {
handleFormErrors(form, error)
@ -64,7 +65,7 @@ export function ViewInfraProviderDrawerWidget() {
return (
<Drawer
keepMounted={false}
onClose={() => close(MODALS.VIEW_INFRA_PROVIDER_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"

View file

@ -21,7 +21,7 @@ import { TbServer } from 'react-icons/tb'
import { PiTag } from 'react-icons/pi'
import ColorHash from 'color-hash'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { useGetInternalSquadAccessibleNodes } from '@shared/api/hooks'
import { XrayLogo } from '@shared/ui/logos'
@ -77,10 +77,8 @@ const ch = new ColorHash({
export const InternalSquadAccessibleNodesModalWidget = () => {
const { t } = useTranslation()
const { isOpen, internalState } = useModalsStore(
(state) => state.modals[MODALS.INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER]
)
const { close } = useModalsStore()
const { isOpen, internalState } = useModalState(MODALS.INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER)
const close = useModalClose(MODALS.INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER)
const { data: internalSquadAccessibleNodes, isLoading } = useGetInternalSquadAccessibleNodes({
route: {
@ -300,7 +298,7 @@ export const InternalSquadAccessibleNodesModalWidget = () => {
return (
<Drawer
keepMounted={false}
onClose={() => close(MODALS.INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"

View file

@ -25,7 +25,7 @@ import { useTranslation } from 'react-i18next'
import { motion } from 'motion/react'
import clsx from 'clsx'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { formatInt } from '@shared/utils/misc'
import classes from './internal-squad-card.module.css'
@ -51,39 +51,14 @@ export function InternalSquadCardWidget(props: IProps) {
const { t } = useTranslation()
const [opened, handlers] = useDisclosure(false)
const { open, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const { membersCount } = internalSquad.info
const { inboundsCount } = internalSquad.info
const isActive = membersCount > 0
const handleOpenInbounds = () => {
setInternalData({
internalState: internalSquad,
modalKey: MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS
})
open(MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS)
}
const handleOpenAccessibleNodes = () => {
setInternalData({
internalState: {
squadUuid: internalSquad.uuid
},
modalKey: MODALS.INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER
})
open(MODALS.INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER)
}
const handleRename = () => {
setInternalData({
internalState: {
name: internalSquad.name,
uuid: internalSquad.uuid
},
modalKey: MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL
})
open(MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL)
openModalWithData(MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS, internalSquad)
}
return (
@ -230,7 +205,14 @@ export function InternalSquadCardWidget(props: IProps) {
<Menu.Item
leftSection={<TbServerCog size={18} />}
onClick={handleOpenAccessibleNodes}
onClick={() =>
openModalWithData(
MODALS.INTERNAL_SQUAD_ACCESSIBLE_NODES_DRAWER,
{
squadUuid: internalSquad.uuid
}
)
}
>
{t('view-user-modal.widget.view-accessible-nodes')}
</Menu.Item>
@ -255,7 +237,15 @@ export function InternalSquadCardWidget(props: IProps) {
<Menu.Item
leftSection={<PiPencil size={18} />}
onClick={handleRename}
onClick={() =>
openModalWithData(
MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL,
{
name: internalSquad.name,
uuid: internalSquad.uuid
}
)
}
>
{t('common.rename')}
</Menu.Item>

View file

@ -10,7 +10,6 @@ import {
useDeleteUsersFromInternalSquad,
useGetInternalSquads
} from '@shared/api/hooks'
import { InternalSquadsDrawerWithStore } from '@widgets/dashboard/users/internal-squads-drawer-with-store'
import { baseNotificationsMutations } from '@shared/ui/notifications/base-notification-mutations'
import { queryClient } from '@shared/api/query-client'
import { sToMs } from '@shared/utils/time-utils'
@ -192,8 +191,6 @@ export function InternalSquadsGridWidget(props: IProps) {
key={internalSquad.uuid}
/>
))}
<InternalSquadsDrawerWithStore />
</SimpleGrid>
)
}

View file

@ -14,8 +14,8 @@ import {
useGetPubKey,
useUpdateNode
} from '@shared/api/hooks'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { BaseNodeForm } from '@shared/ui/forms/nodes/base-node-form/base-node-form'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { bytesToGbUtil, gbToBytesUtil } from '@shared/utils/bytes'
import { LoaderModalShared } from '@shared/ui/loader-modal'
import { ModalFooter } from '@shared/ui/modal-footer'
@ -26,11 +26,8 @@ import { NodeDetailsCardWidget } from '../node-details-card/node-details-card.wi
export const EditNodeByUuidModalWidget = () => {
const { t } = useTranslation()
const { isOpen, internalState: nodeUuid } = useModalsStore(
(state) => state.modals[MODALS.EDIT_NODE_BY_UUID_MODAL]
)
const { close } = useModalsStore()
const { isOpen, internalState: nodeUuid } = useModalState(MODALS.EDIT_NODE_BY_UUID_MODAL)
const close = useModalClose(MODALS.EDIT_NODE_BY_UUID_MODAL)
const isMobile = useMediaQuery(`(max-width: ${em(768)})`)
@ -57,7 +54,7 @@ export const EditNodeByUuidModalWidget = () => {
const handleClose = (closeModal: boolean = false) => {
if (closeModal) {
close(MODALS.EDIT_NODE_BY_UUID_MODAL)
close()
}
queryClient.removeQueries({
@ -135,7 +132,7 @@ export const EditNodeByUuidModalWidget = () => {
centered
closeOnEscape={false}
fullScreen={isMobile}
onClose={() => close(MODALS.EDIT_NODE_BY_UUID_MODAL)}
onClose={close}
onExitTransitionEnd={() => handleClose()}
opened={isOpen}
size="900px"

View file

@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
import ColorHash from 'color-hash'
import { memo } from 'react'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { useGetConfigProfiles, useGetHosts } from '@shared/api/hooks'
import { SEARCH_PARAMS } from '@shared/constants/search-params'
import { XrayLogo } from '@shared/ui/logos'
@ -16,10 +16,8 @@ import { LoadingScreen } from '@shared/ui'
import styles from './LinkedHosts.module.css'
export const LinkedHostsDrawer = memo(() => {
const { isOpen, internalState: nodeUuid } = useModalsStore(
(state) => state.modals[MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER]
)
const { close } = useModalsStore()
const { isOpen, internalState: nodeUuid } = useModalState(MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER)
const close = useModalClose(MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER)
const { t } = useTranslation()
@ -32,7 +30,7 @@ export const LinkedHostsDrawer = memo(() => {
return (
<Drawer
keepMounted={false}
onClose={() => close(MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"
@ -50,7 +48,7 @@ export const LinkedHostsDrawer = memo(() => {
return (
<Drawer
keepMounted={false}
onClose={() => close(MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"
@ -78,7 +76,7 @@ export const LinkedHostsDrawer = memo(() => {
<Box
className={styles.item}
onClick={() => {
close(MODALS.SHOW_NODE_LINKED_HOSTS_DRAWER)
close()
navigate({
pathname: ROUTES.DASHBOARD.MANAGEMENT.HOSTS,

View file

@ -33,7 +33,7 @@ import * as Highcharts from 'highcharts'
import dayjs from 'dayjs'
import { useHighchartsDataProcessor } from '@shared/hooks/use-highcharts-data-processor'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { useGetNodeUsersUsageByRange } from '@shared/api/hooks'
import { prettyBytesToAnyUtil } from '@shared/utils/bytes'
@ -51,10 +51,8 @@ interface DayDataDetails {
}
export const NodeUsersUsageDrawer = memo(() => {
const { isOpen, internalState: nodeUuid } = useModalsStore(
(state) => state.modals[MODALS.SHOW_NODE_USERS_USAGE_DRAWER]
)
const { close } = useModalsStore()
const { isOpen, internalState: nodeUuid } = useModalState(MODALS.SHOW_NODE_USERS_USAGE_DRAWER)
const close = useModalClose(MODALS.SHOW_NODE_USERS_USAGE_DRAWER)
const { t } = useTranslation()
@ -390,12 +388,7 @@ export const NodeUsersUsageDrawer = memo(() => {
if (error) {
return (
<Drawer
onClose={() => close(MODALS.SHOW_NODE_USERS_USAGE_DRAWER)}
opened={isOpen}
size="400px"
title="Error"
>
<Drawer onClose={close} opened={isOpen} size="400px" title="Error">
<Text c="red">Error processing data: {error}</Text>
</Drawer>
)
@ -404,7 +397,7 @@ export const NodeUsersUsageDrawer = memo(() => {
return (
<Drawer
keepMounted={false}
onClose={() => close(MODALS.SHOW_NODE_USERS_USAGE_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"

View file

@ -28,7 +28,7 @@ import { TbServer, TbServer2 } from 'react-icons/tb'
import { memo, useCallback, useMemo } from 'react'
import { useMediaQuery } from '@mantine/hooks'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { MetricCard } from '@shared/ui/metrics/metric-card'
import { useGetNodesMetrics } from '@shared/api/hooks'
import { formatInt } from '@shared/utils/misc'
@ -70,18 +70,14 @@ const StatCard = memo(
export const NodeMetricsWidget = () => {
const { data: nodeMetrics, isLoading } = useGetNodesMetrics()
const { open, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const isMobile = useMediaQuery('(max-width: 768px)')
const handleNodeClick = useCallback(
(nodeUuid: string) => {
setInternalData({
internalState: { nodeUuid },
modalKey: MODALS.EDIT_NODE_BY_UUID_MODAL
})
open(MODALS.EDIT_NODE_BY_UUID_MODAL)
openModalWithData(MODALS.EDIT_NODE_BY_UUID_MODAL, { nodeUuid })
},
[open, setInternalData]
[openModalWithData]
)
const overallStats = useMemo(() => {

View file

@ -9,7 +9,7 @@ import { motion } from 'framer-motion'
import { ReactNode } from 'react'
import clsx from 'clsx'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { ROUTES } from '@shared/constants'
import classes from './templates-card.module.css'
@ -28,7 +28,8 @@ export function TemplatesCardWidget(props: IProps) {
const { t } = useTranslation()
const { open, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const [opened, handlers] = useDisclosure(false)
const navigate = useNavigate()
@ -167,14 +168,13 @@ export function TemplatesCardWidget(props: IProps) {
disabled={template.name === 'Default'}
leftSection={<PiPencil size={18} />}
onClick={() => {
setInternalData({
internalState: {
openModalWithData(
MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL,
{
name: template.name,
uuid: template.uuid
},
modalKey: MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL
})
open(MODALS.RENAME_SQUAD_OR_CONFIG_PROFILE_MODAL)
}
)
}}
>
{t('common.rename')}

View file

@ -1,5 +1,4 @@
/* eslint-disable @stylistic/indent */
import {
Accordion,
ActionIcon,
@ -31,7 +30,7 @@ import {
useUpdateInternalSquad
} from '@shared/api/hooks'
import { ConfigProfileCardShared } from '@shared/ui/config-profiles/config-profile-card/config-profile-card.shared'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { LoaderModalShared } from '@shared/ui/loader-modal'
import { queryClient } from '@shared/api/query-client'
import { formatInt } from '@shared/utils/misc'
@ -39,20 +38,12 @@ import { formatInt } from '@shared/utils/misc'
import classes from './internal-squads-with-store.module.css'
export const InternalSquadsDrawerWithStore = () => {
const { isOpen, internalState: internalSquad } = useModalsStore(
(state) => state.modals[MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS]
const { isOpen, internalState: internalSquad } = useModalState(
MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS
)
const { close } = useModalsStore()
const { t } = useTranslation()
const close = useModalClose(MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS)
// const { data: internalSquad, isLoading: isInternalSquadLoading } = useGetInternalSquad({
// route: {
// uuid: internalState?.internalSquadUuid ?? ''
// },
// rQueryParams: {
// enabled: !!internalState?.internalSquadUuid
// }
// })
const { t } = useTranslation()
const { data: configProfiles, isLoading: isConfigProfilesLoading } = useGetConfigProfiles()
@ -200,7 +191,7 @@ export const InternalSquadsDrawerWithStore = () => {
queryClient.refetchQueries({
queryKey: internalSquadsQueryKeys.getInternalSquads.queryKey
})
close(MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS)
close()
}
}
})
@ -483,7 +474,7 @@ export const InternalSquadsDrawerWithStore = () => {
return (
<Drawer
keepMounted={true}
onClose={() => close(MODALS.INTERNAL_SQUAD_SHOW_INBOUNDS)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="md"

View file

@ -20,7 +20,7 @@ import ReactCountryFlag from 'react-country-flag'
import { useTranslation } from 'react-i18next'
import { PiTag } from 'react-icons/pi'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { MODALS, useModalClose, useModalState } from '@entities/dashboard/modal-store'
import { useGetUserAccessibleNodes } from '@shared/api/hooks'
import { XrayLogo } from '@shared/ui/logos'
@ -48,10 +48,8 @@ interface RenderTreeNodeProps {
export const UserAccessibleNodesModalWidget = () => {
const { t } = useTranslation()
const { isOpen, internalState } = useModalsStore(
(state) => state.modals[MODALS.USER_ACCESSIBLE_NODES_DRAWER]
)
const { close } = useModalsStore()
const { isOpen, internalState } = useModalState(MODALS.USER_ACCESSIBLE_NODES_DRAWER)
const close = useModalClose(MODALS.USER_ACCESSIBLE_NODES_DRAWER)
const { data: userAccessibleNodes, isLoading } = useGetUserAccessibleNodes({
route: {
@ -345,7 +343,7 @@ export const UserAccessibleNodesModalWidget = () => {
return (
<Drawer
keepMounted={false}
onClose={() => close(MODALS.USER_ACCESSIBLE_NODES_DRAWER)}
onClose={close}
opened={isOpen}
overlayProps={{ backgroundOpacity: 0.6, blur: 0 }}
padding="lg"

View file

@ -39,9 +39,9 @@ import { RevokeSubscriptionUserFeature } from '@features/ui/dashboard/users/revo
import { useBulkUsersActionsStoreActions } from '@entities/dashboard/users/bulk-users-actions-store'
import { GetHwidUserDevicesFeature } from '@features/ui/dashboard/users/get-hwid-user-devices'
import { ResetUsageUserFeature } from '@features/ui/dashboard/users/reset-usage-user'
import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store'
import { GetUserUsageFeature } from '@features/ui/dashboard/users/get-user-usage'
import { DeleteUserFeature } from '@features/ui/dashboard/users/delete-user'
import { MODALS, useModalsStore } from '@entities/dashboard/modal-store'
import { bytesToGbUtil, gbToBytesUtil } from '@shared/utils/bytes'
import { LoaderModalShared } from '@shared/ui/loader-modal'
import { handleFormErrors } from '@shared/utils/misc'
@ -80,7 +80,7 @@ export const ViewUserModal = () => {
const isMobile = useMediaQuery(`(max-width: ${em(768)})`)
const { open: openModal, setInternalData } = useModalsStore()
const openModalWithData = useModalsStoreOpenWithData()
const { data: internalSquads } = useGetInternalSquads()
const { data: externalSquads } = useGetExternalSquads()
@ -400,13 +400,9 @@ export const ViewUserModal = () => {
<Menu.Item
leftSection={<TbServerCog size={14} />}
onClick={() => {
setInternalData({
internalState: {
userUuid: user.uuid
},
modalKey: MODALS.USER_ACCESSIBLE_NODES_DRAWER
openModalWithData(MODALS.USER_ACCESSIBLE_NODES_DRAWER, {
userUuid: user.uuid
})
openModal(MODALS.USER_ACCESSIBLE_NODES_DRAWER)
}}
>
{t('view-user-modal.widget.view-accessible-nodes')}