delete files

This commit is contained in:
juhanikat
2026-08-06 15:10:50 +03:00
parent 6db37be7e9
commit 9ba304798e
4 changed files with 0 additions and 423 deletions
-40
View File
@@ -1,40 +0,0 @@
import { useMemo } from 'react'
import { skipToken } from '@reduxjs/toolkit/query'
import { projectToFormValues } from '@/api/projectsApi'
import type { ProjectFormValues } from '@/components/Project/ProjectForm'
import { useGetProjectDetailsQuery } from '@/redux/projectsSlice'
import type { ProjectDetailsType } from '@/shared/types'
import { useUsersApi } from './useUsersApi'
type UseProjectResult = {
project: ProjectDetailsType | undefined
initialValues: ProjectFormValues | null
users: ReturnType<typeof useUsersApi>['users']
isLoading: boolean
isError: boolean
refetch: () => Promise<unknown>
}
export const useProject = (id: string | number | null): UseProjectResult => {
const projectId = id ? String(id) : null
const projectQuery = useGetProjectDetailsQuery(projectId ?? skipToken)
const usersQuery = useUsersApi()
const initialValues = useMemo(() => {
if (!projectQuery.data) return null
if (!usersQuery.users.length) return null
return projectToFormValues(projectQuery.data, usersQuery.users)
}, [projectQuery.data, usersQuery.users])
return {
project: projectQuery.data,
initialValues,
users: usersQuery.users,
isLoading: projectQuery.isLoading || usersQuery.isLoading,
isError: projectQuery.isError || usersQuery.isError,
refetch: projectQuery.refetch,
}
}
export default useProject
-8
View File
@@ -1,8 +0,0 @@
export type { CreateProjectPayload, UpdateProjectPayload } from './projectReducer'
export {
useGetAllProjectsQuery,
useGetProjectDetailsQuery,
useCreateProjectMutation,
useUpdateProjectMutation,
useDeleteProjectMutation,
} from './projectReducer'
@@ -1,206 +0,0 @@
import React from 'react'
import { beforeEach, describe, expect, it, jest } from '@jest/globals'
import '@testing-library/jest-dom'
import { Link, MemoryRouter, Route, RouterProvider, Routes, createMemoryRouter, useLocation } from 'react-router-dom'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Provider } from 'react-redux'
import { Role } from '@/shared/types'
import { ProjectEditPage } from '@/pages/projects/ProjectEditPage'
import type { ProjectFormValues } from '@/components/Project/ProjectForm'
import { store } from '@/redux/store'
const mockUseUser = jest.fn()
const mockUseProject = jest.fn()
const mockUseUpdateProjectMutation = jest.fn()
const mockNotify = jest.fn()
const mockNavigate = jest.fn()
type UpdateProjectTrigger = (payload: ProjectFormValues) => {
unwrap: () => Promise<ProjectFormValues & { pid: number }>
}
let updateProjectMock: jest.MockedFunction<UpdateProjectTrigger>
jest.mock('@/hooks/user', () => ({
useUser: () => mockUseUser(),
}))
jest.mock('@/hooks/useProject', () => ({
useProject: (arg?: unknown) => mockUseProject(arg),
}))
jest.mock('@/redux/projectReducer', () => ({
useUpdateProjectMutation: () => mockUseUpdateProjectMutation(),
}))
jest.mock('@/hooks/notification', () => ({
useNotify: () => ({ notify: mockNotify }),
}))
jest.mock('react-router-dom', () => {
const actualRouterDom = jest.requireActual<typeof import('react-router-dom')>('react-router-dom')
return {
...actualRouterDom,
useNavigate: () => mockNavigate,
}
})
type User = { userId: number; label: string; initials: string }
const userFactory = (overrides: Partial<User> = {}): User => ({
userId: 1,
label: 'Doe, Jane',
initials: 'JD',
...overrides,
})
const defaultInitialValues: ProjectFormValues = {
projectCode: 'PRJ-100',
projectName: 'Existing Project',
coordinatorUserId: 1,
projectStatus: 'current',
recordStatus: true,
memberUserIds: [2],
}
const renderWithRouter = () => {
const Wrapper = () => {
const location = useLocation()
return (
<React.Fragment>
<div data-testid="location">{location.pathname}</div>
<ProjectEditPage />
</React.Fragment>
)
}
render(
<Provider store={store}>
<MemoryRouter initialEntries={['/project/7/edit']}>
<Routes>
<Route path="/project/:id/edit" element={<Wrapper />} />
</Routes>
</MemoryRouter>
</Provider>
)
}
const renderWithDataRouter = () => {
const router = createMemoryRouter(
[
{
path: '/project/:id/edit',
element: (
<React.Fragment>
<ProjectEditPage />
<Link to="/other">Go elsewhere</Link>
</React.Fragment>
),
},
{ path: '/other', element: <div>Other page</div> },
],
{ initialEntries: ['/project/7/edit'] }
)
render(
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
)
return router
}
describe('ProjectEditPage', () => {
beforeEach(() => {
mockUseUser.mockReturnValue({ token: 'token-123', role: Role.Admin })
mockUseProject.mockReturnValue({
project: { pid: 7, proj_name: 'Existing Project' },
initialValues: defaultInitialValues,
users: [userFactory(), userFactory({ userId: 2, label: 'Smith, Alex', initials: 'AS' })],
isLoading: false,
isError: false,
refetch: jest.fn(),
})
const updateProjectImpl: UpdateProjectTrigger = payload => ({
unwrap: () => Promise.resolve({ pid: 7, ...payload }),
})
updateProjectMock = jest.fn(updateProjectImpl) as jest.MockedFunction<UpdateProjectTrigger>
mockUseUpdateProjectMutation.mockReturnValue([
updateProjectMock as unknown as UpdateProjectTrigger,
{ isLoading: false },
])
mockNotify.mockReset()
mockNavigate.mockReset()
})
it('submits form data and navigates to the project detail page', async () => {
const user = userEvent.setup()
renderWithRouter()
await user.clear(screen.getByLabelText('Project Name'))
await user.type(screen.getByLabelText('Project Name'), 'Updated Project')
const form = document.querySelector('form')
expect(form).toBeTruthy()
act(() => {
fireEvent.submit(form as HTMLFormElement)
})
await waitFor(() => {
expect(updateProjectMock).toHaveBeenCalledWith(
expect.objectContaining({
projectCode: 'PRJ-100',
projectName: 'Updated Project',
coordinatorUserId: 1,
projectStatus: 'current',
recordStatus: true,
memberUserIds: [2],
})
)
})
await waitFor(() => {
expect(mockNavigate).toHaveBeenCalledWith('/project/7')
})
})
it('shows a prompt when attempting to leave a dirty form and stays on cancel', async () => {
const user = userEvent.setup()
const router = renderWithDataRouter()
await user.type(screen.getByLabelText('Project Name'), ' Updated')
await user.click(screen.getByRole('link', { name: /go elsewhere/i }))
await screen.findByRole('dialog')
await user.click(screen.getByRole('button', { name: /stay on page/i }))
expect(router.state.location.pathname).toBe('/project/7/edit')
})
it('allows navigation when the user confirms leaving a dirty form', async () => {
const user = userEvent.setup()
const router = renderWithDataRouter()
await user.type(screen.getByLabelText('Project Code'), ' UPDATED')
await user.click(screen.getByRole('link', { name: /go elsewhere/i }))
await user.click(await screen.findByRole('button', { name: /leave page/i }))
await waitFor(() => {
expect(router.state.location.pathname).toBe('/other')
})
})
it('navigates without prompting when the form is clean', async () => {
const user = userEvent.setup()
const router = renderWithDataRouter()
await user.click(screen.getByRole('link', { name: /go elsewhere/i }))
await waitFor(() => {
expect(router.state.location.pathname).toBe('/other')
})
})
})
@@ -1,169 +0,0 @@
import { beforeEach, describe, expect, it, jest } from '@jest/globals'
import { renderHook } from '@testing-library/react'
import { useProject } from '@/hooks/useProject'
import { useUsersApi } from '@/hooks/useUsersApi'
import { useGetProjectDetailsQuery } from '@/redux/projectsSlice'
import { mapProjectEditDataToUpdatePayload } from '@/api/projectsApi'
import type { EditDataType, ProjectDetailsType } from '@/shared/types'
jest.mock('@/redux/projectsSlice')
jest.mock('@/hooks/useUsersApi')
const mockUseGetProjectDetailsQuery = useGetProjectDetailsQuery as jest.MockedFunction<typeof useGetProjectDetailsQuery>
const mockUseUsersApi = useUsersApi as jest.MockedFunction<typeof useUsersApi>
describe('useProject', () => {
const baseProject = {
pid: 42,
proj_code: 'PRJ-42',
proj_name: 'Demo Project',
proj_status: 'current',
proj_records: true,
contact: 'JD',
now_proj_people: [
{ pid: 42, initials: 'JD' },
{ pid: 42, initials: 'AS' },
],
} as unknown as ProjectDetailsType
beforeEach(() => {
mockUseGetProjectDetailsQuery.mockReset()
mockUseUsersApi.mockReset()
})
it('prefills form values from project and user data', () => {
mockUseGetProjectDetailsQuery.mockReturnValue({
data: baseProject,
isLoading: false,
isError: false,
refetch: jest.fn(() => Promise.resolve(undefined)),
} as unknown as ReturnType<typeof useGetProjectDetailsQuery>)
mockUseUsersApi.mockReturnValue({
users: [
{ userId: 1, label: 'Doe, Jane', initials: 'JD' },
{ userId: 2, label: 'Smith, Alex', initials: 'AS' },
],
isLoading: false,
isError: false,
} as unknown as ReturnType<typeof useUsersApi>)
const { result } = renderHook(() => useProject(baseProject.pid))
expect(result.current.initialValues).toEqual({
projectCode: 'PRJ-42',
projectName: 'Demo Project',
coordinatorUserId: 1,
projectStatus: 'current',
recordStatus: true,
memberUserIds: [2],
})
expect(result.current.isLoading).toBe(false)
expect(result.current.isError).toBe(false)
})
it('surfaces loading and error states from either query', () => {
mockUseGetProjectDetailsQuery.mockReturnValue({
data: undefined,
isLoading: true,
isError: false,
refetch: jest.fn(() => Promise.resolve(undefined)),
} as unknown as ReturnType<typeof useGetProjectDetailsQuery>)
mockUseUsersApi.mockReturnValue({
users: [],
isLoading: false,
isError: true,
} as unknown as ReturnType<typeof useUsersApi>)
const { result } = renderHook(() => useProject(baseProject.pid))
expect(result.current.isLoading).toBe(true)
expect(result.current.isError).toBe(true)
expect(result.current.initialValues).toBeNull()
})
it('maps edit data to update payload while dropping removed members and keeping coordinator if selected', () => {
const editData = {
...baseProject,
proj_records: 'false',
now_proj_people: [
{ pid: 42, initials: 'JD', com_people: { user: { user_id: 1 } } },
{ pid: 42, initials: 'AS', com_people: { user: { user_id: 2 } }, rowState: 'removed' },
{ pid: 42, initials: 'MS', com_people: { user: { user_id: 3 } }, rowState: 'new' },
],
} as unknown as EditDataType<ProjectDetailsType>
const payload = mapProjectEditDataToUpdatePayload(editData, [
{ userId: 1, label: 'Doe, Jane', initials: 'JD' },
{ userId: 3, label: 'Smith, Morgan', initials: 'MS' },
])
expect(payload).toEqual({
pid: 42,
projectCode: 'PRJ-42',
projectName: 'Demo Project',
coordinatorUserId: 1,
projectStatus: 'current',
recordStatus: false,
memberUserIds: [1, 3],
})
})
it('maps boolean-like record status strings correctly', () => {
const editData = {
...baseProject,
proj_records: 'true',
} as unknown as EditDataType<ProjectDetailsType>
const payload = mapProjectEditDataToUpdatePayload(editData, [
{ userId: 1, label: 'Doe, Jane', initials: 'JD' },
{ userId: 2, label: 'Smith, Alex', initials: 'AS' },
])
expect(payload?.recordStatus).toBe(true)
})
it('sends an empty member list when all members are removed', () => {
const editData = {
...baseProject,
now_proj_people: [],
} as unknown as EditDataType<ProjectDetailsType>
const payload = mapProjectEditDataToUpdatePayload(editData, [{ userId: 1, label: 'Doe, Jane', initials: 'JD' }])
expect(payload).toEqual({
pid: 42,
projectCode: 'PRJ-42',
projectName: 'Demo Project',
coordinatorUserId: 1,
projectStatus: 'current',
recordStatus: true,
memberUserIds: [],
})
})
it('returns null when coordinator cannot be matched to a user', () => {
const payload = mapProjectEditDataToUpdatePayload(baseProject as EditDataType<ProjectDetailsType>, [
{ userId: 99, label: 'Other, Person', initials: 'XX' },
])
expect(payload).toBeNull()
})
it('keeps a single newly added member when building the payload', () => {
const editData = {
...baseProject,
now_proj_people: [
{ pid: 42, initials: 'JD', com_people: { user: { user_id: 1 } } },
{ pid: 42, initials: 'AS', com_people: { user: { user_id: 2 } }, rowState: 'new' },
],
} as unknown as EditDataType<ProjectDetailsType>
const payload = mapProjectEditDataToUpdatePayload(editData, [
{ userId: 1, label: 'Doe, Jane', initials: 'JD' },
{ userId: 2, label: 'Smith, Alex', initials: 'AS' },
])
expect(payload?.memberUserIds).toEqual([1, 2])
})
})