feat(frontend): update eslint rules
This commit is contained in:
parent
dd9ae0b7c0
commit
457f4bbed2
@ -1,26 +1,26 @@
|
|||||||
export const items1 = [
|
export const items1 = [
|
||||||
{
|
{
|
||||||
id: '1',
|
|
||||||
expenseListId: '1',
|
|
||||||
title: 'Счет за интернет',
|
|
||||||
description: 'Ежемесячная плата за интернет',
|
|
||||||
date: '2024-04-01',
|
|
||||||
amount: 500,
|
amount: 500,
|
||||||
currency: 'RUB',
|
|
||||||
count: 1,
|
count: 1,
|
||||||
createdAt: '2024-04-01T12:00:00Z',
|
createdAt: '2024-04-01T12:00:00Z',
|
||||||
|
currency: 'RUB',
|
||||||
|
date: '2024-04-01',
|
||||||
|
description: 'Ежемесячная плата за интернет',
|
||||||
|
expenseListId: '1',
|
||||||
|
id: '1',
|
||||||
|
title: 'Счет за интернет',
|
||||||
updatedAt: '2024-04-01T12:00:00Z',
|
updatedAt: '2024-04-01T12:00:00Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
|
||||||
expenseListId: '1',
|
|
||||||
title: 'Покупка продуктов',
|
|
||||||
description: 'Продукты на неделю',
|
|
||||||
date: '2024-04-02',
|
|
||||||
amount: 3000,
|
amount: 3000,
|
||||||
currency: 'RUB',
|
|
||||||
count: 1,
|
count: 1,
|
||||||
createdAt: '2024-04-02T12:00:00Z',
|
createdAt: '2024-04-02T12:00:00Z',
|
||||||
|
currency: 'RUB',
|
||||||
|
date: '2024-04-02',
|
||||||
|
description: 'Продукты на неделю',
|
||||||
|
expenseListId: '1',
|
||||||
|
id: '2',
|
||||||
|
title: 'Покупка продуктов',
|
||||||
updatedAt: '2024-04-02T12:00:00Z',
|
updatedAt: '2024-04-02T12:00:00Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -31,42 +31,42 @@ export const items3 = null;
|
|||||||
|
|
||||||
export const items4 = [
|
export const items4 = [
|
||||||
{
|
{
|
||||||
id: '3',
|
|
||||||
expenseListId: '4',
|
|
||||||
title: 'Бензин',
|
|
||||||
description: null,
|
|
||||||
date: '2024-04-03',
|
|
||||||
amount: 1500,
|
amount: 1500,
|
||||||
currency: 'RUB',
|
|
||||||
count: null,
|
count: null,
|
||||||
createdAt: '2024-04-03T12:00:00Z',
|
createdAt: '2024-04-03T12:00:00Z',
|
||||||
|
currency: 'RUB',
|
||||||
|
date: '2024-04-03',
|
||||||
|
description: null,
|
||||||
|
expenseListId: '4',
|
||||||
|
id: '3',
|
||||||
|
title: 'Бензин',
|
||||||
updatedAt: '2024-04-03T12:00:00Z',
|
updatedAt: '2024-04-03T12:00:00Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const items5 = [
|
export const items5 = [
|
||||||
{
|
{
|
||||||
id: '4',
|
|
||||||
expenseListId: '5',
|
|
||||||
title: 'Ноутбук',
|
|
||||||
description: 'Покупка нового ноутбука',
|
|
||||||
date: '2024-04-04',
|
|
||||||
amount: 50000,
|
amount: 50000,
|
||||||
currency: 'RUB',
|
|
||||||
count: 1,
|
count: 1,
|
||||||
createdAt: '2024-04-04T12:00:00Z',
|
createdAt: '2024-04-04T12:00:00Z',
|
||||||
|
currency: 'RUB',
|
||||||
|
date: '2024-04-04',
|
||||||
|
description: 'Покупка нового ноутбука',
|
||||||
|
expenseListId: '5',
|
||||||
|
id: '4',
|
||||||
|
title: 'Ноутбук',
|
||||||
updatedAt: '2024-04-04T12:00:00Z',
|
updatedAt: '2024-04-04T12:00:00Z',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
|
||||||
expenseListId: '5',
|
|
||||||
title: 'Мышка',
|
|
||||||
description: 'Механическая мышка',
|
|
||||||
date: '2024-04-05',
|
|
||||||
amount: 3000,
|
amount: 3000,
|
||||||
currency: 'RUB',
|
|
||||||
count: 1,
|
count: 1,
|
||||||
createdAt: '2024-04-05T12:00:00Z',
|
createdAt: '2024-04-05T12:00:00Z',
|
||||||
|
currency: 'RUB',
|
||||||
|
date: '2024-04-05',
|
||||||
|
description: 'Механическая мышка',
|
||||||
|
expenseListId: '5',
|
||||||
|
id: '5',
|
||||||
|
title: 'Мышка',
|
||||||
updatedAt: '2024-04-05T12:00:00Z',
|
updatedAt: '2024-04-05T12:00:00Z',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -7,41 +7,41 @@ import {
|
|||||||
} from './items';
|
} from './items';
|
||||||
|
|
||||||
export const list1 = {
|
export const list1 = {
|
||||||
id: '1',
|
|
||||||
title: 'Расходы на быт',
|
|
||||||
items: items1,
|
|
||||||
createdAt: '2024-03-28T10:00:00Z',
|
createdAt: '2024-03-28T10:00:00Z',
|
||||||
|
id: '1',
|
||||||
|
items: items1,
|
||||||
|
title: 'Расходы на быт',
|
||||||
updatedAt: '2024-04-02T12:00:00Z',
|
updatedAt: '2024-04-02T12:00:00Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const list2 = {
|
export const list2 = {
|
||||||
id: '2',
|
|
||||||
title: 'Дополнительные расходы',
|
|
||||||
items: items2,
|
|
||||||
createdAt: '2024-04-01T10:00:00Z',
|
createdAt: '2024-04-01T10:00:00Z',
|
||||||
|
id: '2',
|
||||||
|
items: items2,
|
||||||
|
title: 'Дополнительные расходы',
|
||||||
updatedAt: '2024-04-01T10:00:00Z',
|
updatedAt: '2024-04-01T10:00:00Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const list3 = {
|
export const list3 = {
|
||||||
id: '3',
|
|
||||||
title: 'Кредиты',
|
|
||||||
items: items3,
|
|
||||||
createdAt: '2024-04-01T11:00:00Z',
|
createdAt: '2024-04-01T11:00:00Z',
|
||||||
|
id: '3',
|
||||||
|
items: items3,
|
||||||
|
title: 'Кредиты',
|
||||||
updatedAt: '2024-04-01T11:00:00Z',
|
updatedAt: '2024-04-01T11:00:00Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const list4 = {
|
export const list4 = {
|
||||||
id: '4',
|
|
||||||
title: 'Автомобиль',
|
|
||||||
items: items4,
|
|
||||||
createdAt: '2024-04-03T10:00:00Z',
|
createdAt: '2024-04-03T10:00:00Z',
|
||||||
|
id: '4',
|
||||||
|
items: items4,
|
||||||
|
title: 'Автомобиль',
|
||||||
updatedAt: '2024-04-03T10:00:00Z',
|
updatedAt: '2024-04-03T10:00:00Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const list5 = {
|
export const list5 = {
|
||||||
id: '5',
|
|
||||||
title: 'Техника',
|
|
||||||
items: items5,
|
|
||||||
createdAt: '2024-04-04T10:00:00Z',
|
createdAt: '2024-04-04T10:00:00Z',
|
||||||
|
id: '5',
|
||||||
|
items: items5,
|
||||||
|
title: 'Техника',
|
||||||
updatedAt: '2024-04-05T10:00:00Z',
|
updatedAt: '2024-04-05T10:00:00Z',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import { http, HttpResponse } from 'msw';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
list1,
|
list1,
|
||||||
list2,
|
list2,
|
||||||
@ -7,6 +5,7 @@ import {
|
|||||||
list4,
|
list4,
|
||||||
list5,
|
list5,
|
||||||
} from '@mocks/browser/data/expenses/lists';
|
} from '@mocks/browser/data/expenses/lists';
|
||||||
|
import { http, HttpResponse } from 'msw';
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export { getExpenseList } from './expense/list/get';
|
|
||||||
export { getExpenseItemListById } from './expense/list/:id/get';
|
|
||||||
export { loginHandler } from './auth/login/post';
|
export { loginHandler } from './auth/login/post';
|
||||||
|
export { getExpenseItemListById } from './expense/list/:id/get';
|
||||||
|
export { getExpenseList } from './expense/list/get';
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { setupWorker } from 'msw/browser';
|
import { setupWorker } from 'msw/browser';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getExpenseList,
|
|
||||||
getExpenseItemListById,
|
getExpenseItemListById,
|
||||||
|
getExpenseList,
|
||||||
loginHandler,
|
loginHandler,
|
||||||
} from './handlers';
|
} from './handlers';
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
|
||||||
export const createReactQueryWrapper = () => {
|
export const createReactQueryWrapper = () => {
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import pluginQuery from '@tanstack/eslint-plugin-query';
|
import pluginQuery from '@tanstack/eslint-plugin-query';
|
||||||
// @ts-expect-error fix package
|
// @ts-expect-error fix package
|
||||||
import config from 'eslint-config-ksv741';
|
import config from 'eslint-config-ksv741';
|
||||||
|
import perfectionist from 'eslint-plugin-perfectionist';
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-anonymous-default-export
|
// eslint-disable-next-line import/no-anonymous-default-export
|
||||||
export default [
|
export default [
|
||||||
@ -9,6 +10,10 @@ export default [
|
|||||||
...config,
|
...config,
|
||||||
{
|
{
|
||||||
rules: {
|
rules: {
|
||||||
|
'@stylistic/jsx-sort-props': 'off',
|
||||||
|
'@typescript-eslint/no-floating-promises': 'off',
|
||||||
|
'@typescript-eslint/strict-boolean-expressions': 'off',
|
||||||
|
'func-style': 'off',
|
||||||
'import/no-extraneous-dependencies': ['error', {
|
'import/no-extraneous-dependencies': ['error', {
|
||||||
devDependencies: [
|
devDependencies: [
|
||||||
'eslint.config.ts',
|
'eslint.config.ts',
|
||||||
@ -23,71 +28,13 @@ export default [
|
|||||||
'__mocks__/**',
|
'__mocks__/**',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
'import/order': [
|
'import/order': 'off',
|
||||||
'error', {
|
|
||||||
pathGroups: [
|
|
||||||
{
|
|
||||||
pattern: '__mocks__/**',
|
|
||||||
group: 'builtin',
|
|
||||||
position: 'before',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'apps/**',
|
|
||||||
group: 'internal',
|
|
||||||
position: 'after',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'pages/**',
|
|
||||||
group: 'internal',
|
|
||||||
position: 'after',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'widgets/**',
|
|
||||||
group: 'internal',
|
|
||||||
position: 'after',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'features/**',
|
|
||||||
group: 'internal',
|
|
||||||
position: 'after',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'entities/**',
|
|
||||||
group: 'internal',
|
|
||||||
position: 'after',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'shared/**',
|
|
||||||
group: 'internal',
|
|
||||||
position: 'after',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
distinctGroup: true,
|
|
||||||
groups: [
|
|
||||||
'builtin',
|
|
||||||
'external',
|
|
||||||
'internal',
|
|
||||||
'parent',
|
|
||||||
'sibling',
|
|
||||||
'object',
|
|
||||||
'index',
|
|
||||||
'type',
|
|
||||||
],
|
|
||||||
'newlines-between': 'always',
|
|
||||||
alphabetize: {
|
|
||||||
order: 'asc',
|
|
||||||
caseInsensitive: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@typescript-eslint/strict-boolean-expressions': 'off',
|
|
||||||
'@typescript-eslint/no-floating-promises': 'off',
|
|
||||||
'func-style': 'off',
|
|
||||||
'jsx-a11y/click-events-have-key-events': 'off',
|
'jsx-a11y/click-events-have-key-events': 'off',
|
||||||
'jsx-a11y/no-static-element-interactions': 'off',
|
|
||||||
'jsx-a11y/no-noninteractive-element-interactions': 'off',
|
'jsx-a11y/no-noninteractive-element-interactions': 'off',
|
||||||
|
'jsx-a11y/no-static-element-interactions': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
perfectionist.configs['recommended-natural'],
|
||||||
{
|
{
|
||||||
ignores: ['./src/shared/api/schema.ts'],
|
ignores: ['./src/shared/api/schema.ts'],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,14 @@
|
|||||||
import type { Config } from 'jest';
|
import type { Config } from 'jest';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
moduleNameMapper: {
|
||||||
|
'@/(.*)': '<rootDir>/src/$1',
|
||||||
|
'@mocks/(.*)': '<rootDir>/__mocks__/$1',
|
||||||
|
'\\.(css|scss|sass|less)$': 'identity-obj-proxy',
|
||||||
|
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/jest/fileMock.js',
|
||||||
|
},
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/configs/jest/setupTests.ts'],
|
||||||
|
testEnvironment: '<rootDir>/configs/jest/jest-fixed-jsdom.ts',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.(t|j)sx?$': ['@swc/jest', {
|
'^.+\\.(t|j)sx?$': ['@swc/jest', {
|
||||||
jsc: {
|
jsc: {
|
||||||
@ -12,12 +20,4 @@ export default {
|
|||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
testEnvironment: '<rootDir>/configs/jest/jest-fixed-jsdom.ts',
|
|
||||||
setupFilesAfterEnv: ['<rootDir>/configs/jest/setupTests.ts'],
|
|
||||||
moduleNameMapper: {
|
|
||||||
'@/(.*)': '<rootDir>/src/$1',
|
|
||||||
'@mocks/(.*)': '<rootDir>/__mocks__/$1',
|
|
||||||
'\\.(css|scss|sass|less)$': 'identity-obj-proxy',
|
|
||||||
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/jest/fileMock.js',
|
|
||||||
},
|
|
||||||
} as Config;
|
} as Config;
|
||||||
|
|||||||
@ -4,6 +4,9 @@ import { pluginReact } from '@rsbuild/plugin-react';
|
|||||||
import { pluginTypeCheck } from '@rsbuild/plugin-type-check';
|
import { pluginTypeCheck } from '@rsbuild/plugin-type-check';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
html: {
|
||||||
|
title: 'Investments Control',
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
pluginReact(),
|
pluginReact(),
|
||||||
pluginEslint({
|
pluginEslint({
|
||||||
@ -15,17 +18,14 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
pluginTypeCheck(),
|
pluginTypeCheck(),
|
||||||
],
|
],
|
||||||
html: {
|
|
||||||
title: 'Investments Control',
|
|
||||||
},
|
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
[`${process.env.PUBLIC_API_BASE_URL}`]: {
|
[`${process.env.PUBLIC_API_BASE_URL}`]: {
|
||||||
target: process.env.BACKEND_BASE_URL,
|
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: {
|
pathRewrite: {
|
||||||
[`^${process.env.PUBLIC_API_BASE_URL}`]: '',
|
[`^${process.env.PUBLIC_API_BASE_URL}`]: '',
|
||||||
},
|
},
|
||||||
|
target: process.env.BACKEND_BASE_URL,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type LoginRequestBody = paths['/auth/login']['post']['requestBody']['content']['application/json'];
|
type LoginRequestBody = paths['/auth/login']['post']['requestBody']['content']['application/json'];
|
||||||
type LoginSuccessResponse = paths['/auth/login']['post']['responses']['200']['content']['application/json'];
|
type LoginSuccessResponse = paths['/auth/login']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type LogoutSuccessResponse = paths['/auth/logout']['post']['responses']['200']['content']['application/json'];
|
type LogoutSuccessResponse = paths['/auth/logout']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function logout() {
|
export async function logout() {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type RegisterRequestBody = paths['/auth/register']['post']['requestBody']['content']['application/json'];
|
type RegisterRequestBody = paths['/auth/register']['post']['requestBody']['content']['application/json'];
|
||||||
type RegisterSuccessResponse = paths['/auth/register']['post']['responses']['200']['content']['application/json'];
|
type RegisterSuccessResponse = paths['/auth/register']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -13,16 +13,16 @@ export const useLogin = () => {
|
|||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: login,
|
mutationFn: login,
|
||||||
onSuccess: async (data) => {
|
|
||||||
setAccessToken(data.accessToken);
|
|
||||||
localStorage.setItem('accessToken', data.accessToken);
|
|
||||||
await navigate(PAGES.Home);
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.log('Неуспешный логин', error);
|
console.log('Неуспешный логин', error);
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
},
|
},
|
||||||
|
onSuccess: async (data) => {
|
||||||
|
setAccessToken(data.accessToken);
|
||||||
|
localStorage.setItem('accessToken', data.accessToken);
|
||||||
|
await navigate(PAGES.Home);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export { useLogin } from './hooks/useLogin';
|
export { useLogin } from './hooks/useLogin';
|
||||||
export { useRegister } from './hooks/useRegister';
|
|
||||||
export { useLogout } from './hooks/useLogout';
|
export { useLogout } from './hooks/useLogout';
|
||||||
|
export { useRegister } from './hooks/useRegister';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type AddBankCardBody = paths['/bank-card']['post']['requestBody']['content']['application/json'];
|
export type AddBankCardBody = paths['/bank-card']['post']['requestBody']['content']['application/json'];
|
||||||
export type AddBankCardResponse = paths['/bank-card']['post']['responses']['200']['content']['application/json'];
|
export type AddBankCardResponse = paths['/bank-card']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type DeleteBankCardPath = paths['/bank-card/{id}']['delete']['parameters']['path'];
|
export type DeleteBankCardPath = paths['/bank-card/{id}']['delete']['parameters']['path'];
|
||||||
export type DeleteBankCardResponse = paths['/bank-card/{id}']['delete']['responses']['200']['content']['application/json'];
|
export type DeleteBankCardResponse = paths['/bank-card/{id}']['delete']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type EditBankCardPath = paths['/bank-card/{id}']['patch']['parameters']['path'];
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type EditBankCardBody = paths['/bank-card/{id}']['patch']['requestBody']['content']['application/json'];
|
export type EditBankCardBody = paths['/bank-card/{id}']['patch']['requestBody']['content']['application/json'];
|
||||||
|
export type EditBankCardPath = paths['/bank-card/{id}']['patch']['parameters']['path'];
|
||||||
export type EditBankCardResponse = paths['/bank-card/{id}']['patch']['responses']['200']['content']['application/json'];
|
export type EditBankCardResponse = paths['/bank-card/{id}']['patch']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function editBankCard({ path, data }: { path: EditBankCardPath; data: EditBankCardBody }) {
|
export async function editBankCard({ data, path }: { data: EditBankCardBody; path: EditBankCardPath }) {
|
||||||
const { data: result } = await api.patch<EditBankCardResponse>(`/bank-card/${path.id}`, data);
|
const { data: result } = await api.patch<EditBankCardResponse>(`/bank-card/${path.id}`, data);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type BankCardsResponse = paths['/bank-card']['get']['responses']['200']['content']['application/json'];
|
type BankCardsResponse = paths['/bank-card']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function getBankCards() {
|
export async function getBankCards() {
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
export { useBankCards } from './hooks/useBankCards';
|
|
||||||
export { useAddBankCard } from './hooks/useAddBankCard';
|
|
||||||
export { useDeleteBankCard } from './hooks/useDeleteBankCard';
|
|
||||||
export { useEditBankCard } from './hooks/useEditBankCard';
|
|
||||||
|
|
||||||
export { AddBankCardForm } from './ui/AddBankCardForm';
|
|
||||||
export { DeleteBankCardForm } from './ui/DeleteBankCardForm';
|
|
||||||
export { EditBankCardForm } from './ui/EditBankCardForm';
|
|
||||||
|
|
||||||
export type { AddBankCardBody, AddBankCardResponse } from './api/addBankCard';
|
export type { AddBankCardBody, AddBankCardResponse } from './api/addBankCard';
|
||||||
export type { DeleteBankCardPath, DeleteBankCardResponse } from './api/deleteBankCard';
|
export type { DeleteBankCardPath, DeleteBankCardResponse } from './api/deleteBankCard';
|
||||||
export type { EditBankCardBody, EditBankCardPath, EditBankCardResponse } from './api/editBankCard';
|
export type { EditBankCardBody, EditBankCardPath, EditBankCardResponse } from './api/editBankCard';
|
||||||
|
|
||||||
export type { AddBankCardFormValues } from './ui/AddBankCardForm';
|
export { useAddBankCard } from './hooks/useAddBankCard';
|
||||||
export type { EditBankCardFormValues } from './ui/EditBankCardForm';
|
export { useBankCards } from './hooks/useBankCards';
|
||||||
|
export { useDeleteBankCard } from './hooks/useDeleteBankCard';
|
||||||
|
export { useEditBankCard } from './hooks/useEditBankCard';
|
||||||
|
|
||||||
export type { BankCards, BankCard } from './types';
|
export type { BankCard, BankCards } from './types';
|
||||||
|
|
||||||
|
export type { AddBankCardFormValues } from './ui/AddBankCardForm';
|
||||||
|
export { AddBankCardForm } from './ui/AddBankCardForm';
|
||||||
|
|
||||||
|
export { DeleteBankCardForm } from './ui/DeleteBankCardForm';
|
||||||
|
|
||||||
|
export type { EditBankCardFormValues } from './ui/EditBankCardForm';
|
||||||
|
export { EditBankCardForm } from './ui/EditBankCardForm';
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type BankCards = paths['/bank-card']['get']['responses']['200']['content']['application/json'];
|
|
||||||
export type BankCard = paths['/bank-card/{id}']['get']['responses']['200']['content']['application/json'];
|
export type BankCard = paths['/bank-card/{id}']['get']['responses']['200']['content']['application/json'];
|
||||||
|
export type BankCards = paths['/bank-card']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import AddBankCardForm from './AddBankCardForm.ui';
|
|
||||||
|
|
||||||
import type { AddBankCardFormProps } from './AddBankCardForm.ui';
|
import type { AddBankCardFormProps } from './AddBankCardForm.ui';
|
||||||
|
|
||||||
|
import AddBankCardForm from './AddBankCardForm.ui';
|
||||||
|
|
||||||
const renderForm = (props?: Partial<AddBankCardFormProps>) => {
|
const renderForm = (props?: Partial<AddBankCardFormProps>) => {
|
||||||
const onSubmit = jest.fn();
|
const onSubmit = jest.fn();
|
||||||
|
|
||||||
@ -15,10 +15,10 @@ const renderForm = (props?: Partial<AddBankCardFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Добавить');
|
const submitButtonElem = screen.getByText('Добавить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nameInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
submitButtonElem,
|
nameInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,10 +44,10 @@ describe('Test AddBankCardForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
nameInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
submitButtonElem,
|
nameInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.type(nameInputElem, 'Тестовая карта');
|
await userEvent.type(nameInputElem, 'Тестовая карта');
|
||||||
@ -57,8 +57,8 @@ describe('Test AddBankCardForm', () => {
|
|||||||
|
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||||
expect(onSubmit).toHaveBeenCalledWith({
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
name: 'Тестовая карта',
|
|
||||||
balance: 500,
|
balance: 500,
|
||||||
|
name: 'Тестовая карта',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './AddBankCardForm.module.css';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -9,12 +5,9 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
export type AddBankCardFormValues = {
|
import { Input } from '@/shared/ui/Input';
|
||||||
name: string;
|
|
||||||
balance: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import classes from './AddBankCardForm.module.css';
|
||||||
|
|
||||||
export type AddBankCardFormProps = BaseFormProps & {
|
export type AddBankCardFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@ -22,11 +15,18 @@ export type AddBankCardFormProps = BaseFormProps & {
|
|||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AddBankCardFormValues = {
|
||||||
|
balance: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const AddBankCardForm: FC<AddBankCardFormProps> = (props) => {
|
const AddBankCardForm: FC<AddBankCardFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
ref,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
ref,
|
||||||
...formProps
|
...formProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -40,26 +40,26 @@ const AddBankCardForm: FC<AddBankCardFormProps> = (props) => {
|
|||||||
|
|
||||||
if (typeof name === 'string' && typeof balance === 'string') {
|
if (typeof name === 'string' && typeof balance === 'string') {
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
name,
|
|
||||||
balance: Number(balance),
|
balance: Number(balance),
|
||||||
|
name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...formProps}
|
{...formProps}
|
||||||
>
|
>
|
||||||
<h2>Добавить карту</h2>
|
<h2>Добавить карту</h2>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Название"
|
label="Название"
|
||||||
name="name"
|
name="name"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import DeleteBankCardForm from './DeleteBankCardForm.ui';
|
|
||||||
|
|
||||||
import type { DeleteBankCardFormProps } from './DeleteBankCardForm.ui';
|
import type { DeleteBankCardFormProps } from './DeleteBankCardForm.ui';
|
||||||
|
|
||||||
|
import DeleteBankCardForm from './DeleteBankCardForm.ui';
|
||||||
|
|
||||||
const deletingBankCard: DeleteBankCardFormProps['bankCard'] = {
|
const deletingBankCard: DeleteBankCardFormProps['bankCard'] = {
|
||||||
|
balance: 1000,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
||||||
name: 'Тестовая карта',
|
name: 'Тестовая карта',
|
||||||
balance: 1000,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<DeleteBankCardFormProps>) => {
|
const renderForm = (props?: Partial<DeleteBankCardFormProps>) => {
|
||||||
@ -28,8 +28,8 @@ const renderForm = (props?: Partial<DeleteBankCardFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Удалить');
|
const submitButtonElem = screen.getByText('Удалить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ describe('Test DeleteExpensesListForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.click(submitButtonElem);
|
await userEvent.click(submitButtonElem);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { BankCard } from '../../../types';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -6,7 +5,7 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import type { BankCard } from '../../../types';
|
||||||
|
|
||||||
export type DeleteBankCardFormProps = BaseFormProps & {
|
export type DeleteBankCardFormProps = BaseFormProps & {
|
||||||
bankCard: BankCard;
|
bankCard: BankCard;
|
||||||
@ -15,6 +14,8 @@ export type DeleteBankCardFormProps = BaseFormProps & {
|
|||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const DeleteBankCardForm: FC<DeleteBankCardFormProps> = (props) => {
|
const DeleteBankCardForm: FC<DeleteBankCardFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
bankCard,
|
bankCard,
|
||||||
@ -30,7 +31,7 @@ const DeleteBankCardForm: FC<DeleteBankCardFormProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form ref={ref} onSubmit={onFormSubmitHandler} {...restProps}>
|
<form onSubmit={onFormSubmitHandler} ref={ref} {...restProps}>
|
||||||
<h2>Удалить список расходов</h2>
|
<h2>Удалить список расходов</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import EditBankCardForm from './EditBankCardForm.ui';
|
|
||||||
|
|
||||||
import type { EditBankCardFormProps } from './EditBankCardForm.ui';
|
import type { EditBankCardFormProps } from './EditBankCardForm.ui';
|
||||||
|
|
||||||
|
import EditBankCardForm from './EditBankCardForm.ui';
|
||||||
|
|
||||||
const editingBankCard = {
|
const editingBankCard = {
|
||||||
|
balance: 1000,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
||||||
name: 'Тестовая карта',
|
name: 'Тестовая карта',
|
||||||
balance: 1000,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<EditBankCardFormProps>) => {
|
const renderForm = (props?: Partial<EditBankCardFormProps>) => {
|
||||||
@ -30,10 +30,10 @@ const renderForm = (props?: Partial<EditBankCardFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Сохранить');
|
const submitButtonElem = screen.getByText('Сохранить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,8 +47,8 @@ describe('Test EditBankCardForm', () => {
|
|||||||
|
|
||||||
test('should inputs have default value', () => {
|
test('should inputs have default value', () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
expect(titleInputElem).toHaveValue('Тестовая карта');
|
expect(titleInputElem).toHaveValue('Тестовая карта');
|
||||||
expect(balanceInputElem).toHaveValue(1000);
|
expect(balanceInputElem).toHaveValue(1000);
|
||||||
@ -68,10 +68,10 @@ describe('Test EditBankCardForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.clear(titleInputElem);
|
await userEvent.clear(titleInputElem);
|
||||||
@ -84,8 +84,8 @@ describe('Test EditBankCardForm', () => {
|
|||||||
|
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||||
expect(onSubmit).toHaveBeenCalledWith({
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
name: 'Новое название карты',
|
|
||||||
balance: 5000,
|
balance: 5000,
|
||||||
|
name: 'Новое название карты',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './EditBankCardForm.module.css';
|
|
||||||
|
|
||||||
import type { BankCard } from '@/entity/cards';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -10,12 +5,11 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import type { BankCard } from '@/entity/cards';
|
||||||
|
|
||||||
export type EditBankCardFormValues = {
|
import { Input } from '@/shared/ui/Input';
|
||||||
name: string;
|
|
||||||
balance: number;
|
import classes from './EditBankCardForm.module.css';
|
||||||
};
|
|
||||||
|
|
||||||
export type EditBankCardFormProps = BaseFormProps & {
|
export type EditBankCardFormProps = BaseFormProps & {
|
||||||
bankCard: BankCard;
|
bankCard: BankCard;
|
||||||
@ -24,6 +18,13 @@ export type EditBankCardFormProps = BaseFormProps & {
|
|||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EditBankCardFormValues = {
|
||||||
|
balance: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const EditBankCardForm: FC<EditBankCardFormProps> = (props) => {
|
const EditBankCardForm: FC<EditBankCardFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
bankCard,
|
bankCard,
|
||||||
@ -43,27 +44,27 @@ const EditBankCardForm: FC<EditBankCardFormProps> = (props) => {
|
|||||||
|
|
||||||
if (typeof name === 'string' && typeof balance === 'string') {
|
if (typeof name === 'string' && typeof balance === 'string') {
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
name,
|
|
||||||
balance: Number(balance),
|
balance: Number(balance),
|
||||||
|
name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<h2>Добавить карту</h2>
|
<h2>Добавить карту</h2>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
defaultValue={bankCard.name}
|
defaultValue={bankCard.name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Название"
|
label="Название"
|
||||||
name="name"
|
name="name"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type AddCashBody = paths['/cash']['post']['requestBody']['content']['application/json'];
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type AdCashResponse = paths['/cash']['post']['responses']['200']['content']['application/json'];
|
export type AdCashResponse = paths['/cash']['post']['responses']['200']['content']['application/json'];
|
||||||
|
export type AddCashBody = paths['/cash']['post']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
export async function addCash(data: AddCashBody) {
|
export async function addCash(data: AddCashBody) {
|
||||||
const { data: result } = await api.post<AdCashResponse>('/cash', data);
|
const { data: result } = await api.post<AdCashResponse>('/cash', data);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type DeleteCashPath = paths['/cash/{id}']['delete']['parameters']['path'];
|
export type DeleteCashPath = paths['/cash/{id}']['delete']['parameters']['path'];
|
||||||
export type DeleteCashResponse = paths['/cash/{id}']['delete']['responses']['200']['content']['application/json'];
|
export type DeleteCashResponse = paths['/cash/{id}']['delete']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type EditCashPath = paths['/cash/{id}']['patch']['parameters']['path'];
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type EditCashBody = paths['/cash/{id}']['patch']['requestBody']['content']['application/json'];
|
export type EditCashBody = paths['/cash/{id}']['patch']['requestBody']['content']['application/json'];
|
||||||
|
export type EditCashPath = paths['/cash/{id}']['patch']['parameters']['path'];
|
||||||
export type EditCashResponse = paths['/cash/{id}']['patch']['responses']['200']['content']['application/json'];
|
export type EditCashResponse = paths['/cash/{id}']['patch']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function editCash({ path, data }: { path: EditCashPath; data: EditCashBody }) {
|
export async function editCash({ data, path }: { data: EditCashBody; path: EditCashPath }) {
|
||||||
const { data: result } = await api.patch<EditCashResponse>(`/cash/${path.id}`, data);
|
const { data: result } = await api.patch<EditCashResponse>(`/cash/${path.id}`, data);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type CashResponse = paths['/cash']['get']['responses']['200']['content']['application/json'];
|
type CashResponse = paths['/cash']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function getCash() {
|
export async function getCash() {
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
|
export type { AdCashResponse, AddCashBody } from './api/addCash';
|
||||||
|
export type { DeleteCashPath, DeleteCashResponse } from './api/deleteCash';
|
||||||
|
export type { EditCashBody, EditCashPath, EditCashResponse } from './api/editCash';
|
||||||
|
|
||||||
export { useAddCash } from './hooks/useAddCash';
|
export { useAddCash } from './hooks/useAddCash';
|
||||||
export { useCash } from './hooks/useCash';
|
export { useCash } from './hooks/useCash';
|
||||||
export { useDeleteCash } from './hooks/useDeleteCash';
|
export { useDeleteCash } from './hooks/useDeleteCash';
|
||||||
export { useEditCash } from './hooks/useEditCash';
|
export { useEditCash } from './hooks/useEditCash';
|
||||||
|
|
||||||
|
export type { CashItem, CashList } from './types';
|
||||||
|
|
||||||
export { AddCashForm } from './ui/AddCashForm';
|
export { AddCashForm } from './ui/AddCashForm';
|
||||||
export { DeleteCashForm } from './ui/DeleteCashForm';
|
|
||||||
export { EditCashForm } from './ui/EditCashForm';
|
|
||||||
|
|
||||||
export type { CashList, CashItem } from './types';
|
|
||||||
|
|
||||||
export type { AddCashFormValues } from './ui/AddCashForm';
|
export type { AddCashFormValues } from './ui/AddCashForm';
|
||||||
|
|
||||||
|
export { DeleteCashForm } from './ui/DeleteCashForm';
|
||||||
|
|
||||||
|
export { EditCashForm } from './ui/EditCashForm';
|
||||||
export type { EditCashFormValues } from './ui/EditCashForm';
|
export type { EditCashFormValues } from './ui/EditCashForm';
|
||||||
export type { AddCashBody, AdCashResponse } from './api/addCash';
|
|
||||||
export type { DeleteCashPath, DeleteCashResponse } from './api/deleteCash';
|
|
||||||
export type { EditCashBody, EditCashPath, EditCashResponse } from './api/editCash';
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type CashList = paths['/cash']['get']['responses']['200']['content']['application/json'];
|
|
||||||
export type CashItem = paths['/cash/{id}']['get']['responses']['200']['content']['application/json'];
|
export type CashItem = paths['/cash/{id}']['get']['responses']['200']['content']['application/json'];
|
||||||
|
export type CashList = paths['/cash']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import AddCashForm from './AddCashForm.ui';
|
|
||||||
|
|
||||||
import type { AddCashFormProps } from './AddCashForm.ui';
|
import type { AddCashFormProps } from './AddCashForm.ui';
|
||||||
|
|
||||||
|
import AddCashForm from './AddCashForm.ui';
|
||||||
|
|
||||||
const renderForm = (props?: Partial<AddCashFormProps>) => {
|
const renderForm = (props?: Partial<AddCashFormProps>) => {
|
||||||
const onSubmit = jest.fn();
|
const onSubmit = jest.fn();
|
||||||
|
|
||||||
@ -15,10 +15,10 @@ const renderForm = (props?: Partial<AddCashFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Добавить');
|
const submitButtonElem = screen.getByText('Добавить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nameInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
submitButtonElem,
|
nameInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,10 +44,10 @@ describe('Test AddCashForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
nameInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
submitButtonElem,
|
nameInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.type(nameInputElem, 'Наличные');
|
await userEvent.type(nameInputElem, 'Наличные');
|
||||||
@ -57,8 +57,8 @@ describe('Test AddCashForm', () => {
|
|||||||
|
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||||
expect(onSubmit).toHaveBeenCalledWith({
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
name: 'Наличные',
|
|
||||||
balance: 500,
|
balance: 500,
|
||||||
|
name: 'Наличные',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './AddCashForm.module.css';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -9,12 +5,9 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
export type AddCashFormValues = {
|
import { Input } from '@/shared/ui/Input';
|
||||||
name: string;
|
|
||||||
balance: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import classes from './AddCashForm.module.css';
|
||||||
|
|
||||||
export type AddCashFormProps = BaseFormProps & {
|
export type AddCashFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@ -22,11 +15,18 @@ export type AddCashFormProps = BaseFormProps & {
|
|||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AddCashFormValues = {
|
||||||
|
balance: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const AddCashForm: FC<AddCashFormProps> = (props) => {
|
const AddCashForm: FC<AddCashFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
ref,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
ref,
|
||||||
...formProps
|
...formProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -40,26 +40,26 @@ const AddCashForm: FC<AddCashFormProps> = (props) => {
|
|||||||
|
|
||||||
if (typeof name === 'string' && typeof balance === 'string') {
|
if (typeof name === 'string' && typeof balance === 'string') {
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
name,
|
|
||||||
balance: Number(balance),
|
balance: Number(balance),
|
||||||
|
name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...formProps}
|
{...formProps}
|
||||||
>
|
>
|
||||||
<h2>Добавить наличные</h2>
|
<h2>Добавить наличные</h2>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Название"
|
label="Название"
|
||||||
name="name"
|
name="name"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import DeleteCashForm from './DeleteCashForm.ui';
|
|
||||||
|
|
||||||
import type { DeleteCashFormProps } from './DeleteCashForm.ui';
|
import type { DeleteCashFormProps } from './DeleteCashForm.ui';
|
||||||
|
|
||||||
|
import DeleteCashForm from './DeleteCashForm.ui';
|
||||||
|
|
||||||
const deletingCash: DeleteCashFormProps['cash'] = {
|
const deletingCash: DeleteCashFormProps['cash'] = {
|
||||||
|
balance: 1000,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
||||||
name: 'Наличные',
|
name: 'Наличные',
|
||||||
balance: 1000,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<DeleteCashFormProps>) => {
|
const renderForm = (props?: Partial<DeleteCashFormProps>) => {
|
||||||
@ -28,8 +28,8 @@ const renderForm = (props?: Partial<DeleteCashFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Удалить');
|
const submitButtonElem = screen.getByText('Удалить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ describe('Test DeleteCashForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.click(submitButtonElem);
|
await userEvent.click(submitButtonElem);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { CashItem } from '../../../types';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -6,7 +5,7 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import type { CashItem } from '../../../types';
|
||||||
|
|
||||||
export type DeleteCashFormProps = BaseFormProps & {
|
export type DeleteCashFormProps = BaseFormProps & {
|
||||||
cash: CashItem;
|
cash: CashItem;
|
||||||
@ -15,6 +14,8 @@ export type DeleteCashFormProps = BaseFormProps & {
|
|||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const DeleteCashForm: FC<DeleteCashFormProps> = (props) => {
|
const DeleteCashForm: FC<DeleteCashFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
cash,
|
cash,
|
||||||
@ -30,7 +31,7 @@ const DeleteCashForm: FC<DeleteCashFormProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form ref={ref} onSubmit={onFormSubmitHandler} {...restProps}>
|
<form onSubmit={onFormSubmitHandler} ref={ref} {...restProps}>
|
||||||
<h2>Удалить наличные</h2>
|
<h2>Удалить наличные</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import EditCashForm from './EditCashForm.ui';
|
|
||||||
|
|
||||||
import type { EditCashFormProps } from './EditCashForm.ui';
|
import type { EditCashFormProps } from './EditCashForm.ui';
|
||||||
|
|
||||||
|
import EditCashForm from './EditCashForm.ui';
|
||||||
|
|
||||||
const editingCash = {
|
const editingCash = {
|
||||||
|
balance: 1000,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
||||||
name: 'Наличные',
|
name: 'Наличные',
|
||||||
balance: 1000,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<EditCashFormProps>) => {
|
const renderForm = (props?: Partial<EditCashFormProps>) => {
|
||||||
@ -30,10 +30,10 @@ const renderForm = (props?: Partial<EditCashFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Сохранить');
|
const submitButtonElem = screen.getByText('Сохранить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,8 +47,8 @@ describe('Test EditCashForm', () => {
|
|||||||
|
|
||||||
test('should inputs have default value', () => {
|
test('should inputs have default value', () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
expect(titleInputElem).toHaveValue('Наличные');
|
expect(titleInputElem).toHaveValue('Наличные');
|
||||||
expect(balanceInputElem).toHaveValue(1000);
|
expect(balanceInputElem).toHaveValue(1000);
|
||||||
@ -68,10 +68,10 @@ describe('Test EditCashForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
balanceInputElem,
|
balanceInputElem,
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.clear(titleInputElem);
|
await userEvent.clear(titleInputElem);
|
||||||
@ -84,8 +84,8 @@ describe('Test EditCashForm', () => {
|
|||||||
|
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||||
expect(onSubmit).toHaveBeenCalledWith({
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
name: 'Новое название наличных',
|
|
||||||
balance: 5000,
|
balance: 5000,
|
||||||
|
name: 'Новое название наличных',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './EditCashForm.module.css';
|
|
||||||
|
|
||||||
import type { CashItem } from '@/entity/cash';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -10,12 +5,11 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import type { CashItem } from '@/entity/cash';
|
||||||
|
|
||||||
export type EditCashFormValues = {
|
import { Input } from '@/shared/ui/Input';
|
||||||
name: string;
|
|
||||||
balance: number;
|
import classes from './EditCashForm.module.css';
|
||||||
};
|
|
||||||
|
|
||||||
export type EditCashFormProps = BaseFormProps & {
|
export type EditCashFormProps = BaseFormProps & {
|
||||||
cash: CashItem;
|
cash: CashItem;
|
||||||
@ -24,6 +18,13 @@ export type EditCashFormProps = BaseFormProps & {
|
|||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EditCashFormValues = {
|
||||||
|
balance: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const EditCashForm: FC<EditCashFormProps> = (props) => {
|
const EditCashForm: FC<EditCashFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
cash,
|
cash,
|
||||||
@ -43,27 +44,27 @@ const EditCashForm: FC<EditCashFormProps> = (props) => {
|
|||||||
|
|
||||||
if (typeof name === 'string' && typeof balance === 'string') {
|
if (typeof name === 'string' && typeof balance === 'string') {
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
name,
|
|
||||||
balance: Number(balance),
|
balance: Number(balance),
|
||||||
|
name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<h2>Редактировать наличные</h2>
|
<h2>Редактировать наличные</h2>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
defaultValue={cash.name}
|
defaultValue={cash.name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Название"
|
label="Название"
|
||||||
name="name"
|
name="name"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type AddExpenseItemBody = paths['/expenses/list/{id}/items']['post']['requestBody']['content']['application/json'];
|
export type AddExpenseItemBody = paths['/expenses/list/{id}/items']['post']['requestBody']['content']['application/json'];
|
||||||
export type AddExpenseItemPath = paths['/expenses/list/{id}/items']['post']['parameters']['path'];
|
export type AddExpenseItemPath = paths['/expenses/list/{id}/items']['post']['parameters']['path'];
|
||||||
export type AddExpenseItemSuccessResponse = paths['/expenses/list/{id}/items']['post']['responses']['200']['content']['application/json'];
|
export type AddExpenseItemSuccessResponse = paths['/expenses/list/{id}/items']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type DeleteExpenseItemPath = paths['/expenses/list/{listId}/items/{id}']['delete']['parameters']['path'];
|
export type DeleteExpenseItemPath = paths['/expenses/list/{listId}/items/{id}']['delete']['parameters']['path'];
|
||||||
export type DeleteExpenseItemSuccessResponse = paths['/expenses/list/{id}/items']['post']['responses']['200']['content']['application/json'];
|
export type DeleteExpenseItemSuccessResponse = paths['/expenses/list/{id}/items']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type EditExpenseItemBody = paths['/expenses/list/{listId}/items/{id}']['patch']['requestBody']['content']['application/json'];
|
export type EditExpenseItemBody = paths['/expenses/list/{listId}/items/{id}']['patch']['requestBody']['content']['application/json'];
|
||||||
export type EditExpenseItemPath = paths['/expenses/list/{listId}/items/{id}']['patch']['parameters']['path'];
|
export type EditExpenseItemPath = paths['/expenses/list/{listId}/items/{id}']['patch']['parameters']['path'];
|
||||||
export type EditExpenseItemSuccessResponse = paths['/expenses/list/{listId}/items/{id}']['patch']['responses']['200']['content']['application/json'];
|
export type EditExpenseItemSuccessResponse = paths['/expenses/list/{listId}/items/{id}']['patch']['responses']['200']['content']['application/json'];
|
||||||
|
|||||||
@ -2,10 +2,10 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
|
|
||||||
import { QueryKeys } from '@/shared/api/queryKeys';
|
import { QueryKeys } from '@/shared/api/queryKeys';
|
||||||
|
|
||||||
import { deleteExpenseItem } from '../api/deleteExpenseItem';
|
|
||||||
|
|
||||||
import type { DeleteExpenseItemPath } from '../api/deleteExpenseItem';
|
import type { DeleteExpenseItemPath } from '../api/deleteExpenseItem';
|
||||||
|
|
||||||
|
import { deleteExpenseItem } from '../api/deleteExpenseItem';
|
||||||
|
|
||||||
export const useDeleteExpenseItem = (path: DeleteExpenseItemPath) => {
|
export const useDeleteExpenseItem = (path: DeleteExpenseItemPath) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
|
export type { AddExpenseItemBody, AddExpenseItemPath, AddExpenseItemSuccessResponse } from './api/addExpenseItem';
|
||||||
|
export type { DeleteExpenseItemPath, DeleteExpenseItemSuccessResponse } from './api/deleteExpenseItem';
|
||||||
|
export type { EditExpenseItemBody, EditExpenseItemPath, EditExpenseItemSuccessResponse } from './api/editExpenseItem';
|
||||||
|
|
||||||
export { useAddExpenseItem } from './hooks/useAddExpenseItem';
|
export { useAddExpenseItem } from './hooks/useAddExpenseItem';
|
||||||
export { useDeleteExpenseItem } from './hooks/useDeleteExpenseItem';
|
export { useDeleteExpenseItem } from './hooks/useDeleteExpenseItem';
|
||||||
export { useEditExpenseItem } from './hooks/useEditExpenseItem';
|
export { useEditExpenseItem } from './hooks/useEditExpenseItem';
|
||||||
|
|
||||||
export type { AddExpenseItemSuccessResponse, AddExpenseItemBody, AddExpenseItemPath } from './api/addExpenseItem';
|
export type { ExpenseListItem } from './types';
|
||||||
export type { DeleteExpenseItemPath, DeleteExpenseItemSuccessResponse } from './api/deleteExpenseItem';
|
|
||||||
export type { EditExpenseItemBody, EditExpenseItemPath, EditExpenseItemSuccessResponse } from './api/editExpenseItem';
|
|
||||||
|
|
||||||
export type { AddExpenseItemFormValues } from './ui/AddExpenseItemForm/AddExpenseItemForm.ui';
|
export type { AddExpenseItemFormValues } from './ui/AddExpenseItemForm/AddExpenseItemForm.ui';
|
||||||
|
|
||||||
export { default as AddExpenseItemForm } from './ui/AddExpenseItemForm/AddExpenseItemForm.ui';
|
export { default as AddExpenseItemForm } from './ui/AddExpenseItemForm/AddExpenseItemForm.ui';
|
||||||
|
|
||||||
export type { EditExpenseItemFormValues } from './ui/EditExpenseItemForm/EditExpenseItemForm.ui';
|
|
||||||
export { default as EditExpenseItemForm } from './ui/EditExpenseItemForm/EditExpenseItemForm.ui';
|
|
||||||
|
|
||||||
export { default as DeleteExpenseItemForm } from './ui/DeleteExpenseItemForm/DeleteExpenseItemForm.ui';
|
export { default as DeleteExpenseItemForm } from './ui/DeleteExpenseItemForm/DeleteExpenseItemForm.ui';
|
||||||
|
|
||||||
export type { ExpenseListItem } from './types';
|
export type { EditExpenseItemFormValues } from './ui/EditExpenseItemForm/EditExpenseItemForm.ui';
|
||||||
|
|
||||||
|
export { default as EditExpenseItemForm } from './ui/EditExpenseItemForm/EditExpenseItemForm.ui';
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import AddExpenseItemForm from './AddExpenseItemForm.ui';
|
|
||||||
|
|
||||||
import type { AddExpenseItemFormProps } from './AddExpenseItemForm.ui';
|
import type { AddExpenseItemFormProps } from './AddExpenseItemForm.ui';
|
||||||
|
|
||||||
|
import AddExpenseItemForm from './AddExpenseItemForm.ui';
|
||||||
|
|
||||||
const renderForm = (props?: Partial<AddExpenseItemFormProps>) => {
|
const renderForm = (props?: Partial<AddExpenseItemFormProps>) => {
|
||||||
const onSubmit = jest.fn();
|
const onSubmit = jest.fn();
|
||||||
|
|
||||||
@ -18,13 +18,13 @@ const renderForm = (props?: Partial<AddExpenseItemFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Добавить');
|
const submitButtonElem = screen.getByText('Добавить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
countInputElem,
|
countInputElem,
|
||||||
submitButtonElem,
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -50,13 +50,13 @@ describe('Test AddExpenseItemForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
countInputElem,
|
countInputElem,
|
||||||
submitButtonElem,
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.type(titleInputElem, 'Название элемента');
|
await userEvent.type(titleInputElem, 'Название элемента');
|
||||||
@ -69,12 +69,12 @@ describe('Test AddExpenseItemForm', () => {
|
|||||||
|
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||||
expect(onSubmit).toHaveBeenCalledWith({
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
title: 'Название элемента',
|
|
||||||
description: 'Описание элемента',
|
|
||||||
date: '2025-08-29T00:00:00.000Z',
|
|
||||||
amount: 12,
|
amount: 12,
|
||||||
count: 1,
|
count: 1,
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
|
date: '2025-08-29T00:00:00.000Z',
|
||||||
|
description: 'Описание элемента',
|
||||||
|
title: 'Название элемента',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './AddExpenseItemForm.module.css';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -9,16 +5,9 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
export type AddExpenseItemFormValues = {
|
import { Input } from '@/shared/ui/Input';
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
date: string;
|
|
||||||
amount: number;
|
|
||||||
count: number;
|
|
||||||
currency: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import classes from './AddExpenseItemForm.module.css';
|
||||||
|
|
||||||
export type AddExpenseItemFormProps = BaseFormProps & {
|
export type AddExpenseItemFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@ -26,6 +15,17 @@ export type AddExpenseItemFormProps = BaseFormProps & {
|
|||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AddExpenseItemFormValues = {
|
||||||
|
amount: number;
|
||||||
|
count: number;
|
||||||
|
currency: string;
|
||||||
|
date: string;
|
||||||
|
description: string;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const AddExpenseItemForm: FC<AddExpenseItemFormProps> = (props) => {
|
const AddExpenseItemForm: FC<AddExpenseItemFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
disabled,
|
disabled,
|
||||||
@ -47,30 +47,30 @@ const AddExpenseItemForm: FC<AddExpenseItemFormProps> = (props) => {
|
|||||||
|
|
||||||
if (typeof title === 'string' && typeof date === 'string' && typeof amount === 'string' && typeof description === 'string' && typeof count === 'string') {
|
if (typeof title === 'string' && typeof date === 'string' && typeof amount === 'string' && typeof description === 'string' && typeof count === 'string') {
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
title,
|
|
||||||
date: new Date(date).toISOString(),
|
|
||||||
amount: Number(amount),
|
amount: Number(amount),
|
||||||
description,
|
|
||||||
currency: 'RUB',
|
|
||||||
count: Number(count),
|
count: Number(count),
|
||||||
|
currency: 'RUB',
|
||||||
|
date: new Date(date).toISOString(),
|
||||||
|
description,
|
||||||
|
title,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...formProps}
|
{...formProps}
|
||||||
>
|
>
|
||||||
<h2>Добавить список расходов</h2>
|
<h2>Добавить список расходов</h2>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Название"
|
label="Название"
|
||||||
name="title"
|
name="title"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
@ -80,18 +80,18 @@ const AddExpenseItemForm: FC<AddExpenseItemFormProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Дата"
|
label="Дата"
|
||||||
name="date"
|
name="date"
|
||||||
|
required
|
||||||
type="date"
|
type="date"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Сумма"
|
label="Сумма"
|
||||||
name="amount"
|
name="amount"
|
||||||
|
required
|
||||||
step="0.01"
|
step="0.01"
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import DeleteExpenseItemForm from './DeleteExpenseItemForm.ui';
|
|
||||||
|
|
||||||
import type { DeleteExpenseItemFormProps } from './DeleteExpenseItemForm.ui';
|
import type { DeleteExpenseItemFormProps } from './DeleteExpenseItemForm.ui';
|
||||||
|
|
||||||
|
import DeleteExpenseItemForm from './DeleteExpenseItemForm.ui';
|
||||||
|
|
||||||
const deletingItem = {
|
const deletingItem = {
|
||||||
id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6',
|
|
||||||
title: 'Mock item',
|
|
||||||
amount: 12,
|
amount: 12,
|
||||||
count: 12,
|
count: 12,
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
expenseListId: '10cc093c-3875-4d33-a40a-df526266e262',
|
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
date: '2025-08-22T00:00:03.000Z',
|
date: '2025-08-22T00:00:03.000Z',
|
||||||
description: 'Mock description',
|
description: 'Mock description',
|
||||||
|
expenseListId: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
|
id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6',
|
||||||
|
title: 'Mock item',
|
||||||
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<DeleteExpenseItemFormProps>) => {
|
const renderForm = (props?: Partial<DeleteExpenseItemFormProps>) => {
|
||||||
@ -33,8 +33,8 @@ const renderForm = (props?: Partial<DeleteExpenseItemFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Удалить');
|
const submitButtonElem = screen.getByText('Удалить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ describe('Test DeleteExpenseItemForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.click(submitButtonElem);
|
await userEvent.click(submitButtonElem);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { ExpenseListItem } from '../../types';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -6,21 +5,23 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import type { ExpenseListItem } from '../../types';
|
||||||
|
|
||||||
export type DeleteExpenseItemFormProps = BaseFormProps & {
|
export type DeleteExpenseItemFormProps = BaseFormProps & {
|
||||||
|
disabled?: boolean;
|
||||||
item: ExpenseListItem;
|
item: ExpenseListItem;
|
||||||
onSubmit?: (item: ExpenseListItem) => Promise<void> | void;
|
onSubmit?: (item: ExpenseListItem) => Promise<void> | void;
|
||||||
disabled?: boolean;
|
|
||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const DeleteExpenseItemForm: FC<DeleteExpenseItemFormProps> = (props) => {
|
const DeleteExpenseItemForm: FC<DeleteExpenseItemFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
|
disabled,
|
||||||
item,
|
item,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
ref,
|
ref,
|
||||||
disabled,
|
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
const onFormSubmitHandler: FormEventHandler<HTMLFormElement> = (event) => {
|
const onFormSubmitHandler: FormEventHandler<HTMLFormElement> = (event) => {
|
||||||
@ -29,7 +30,7 @@ const DeleteExpenseItemForm: FC<DeleteExpenseItemFormProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form ref={ref} onSubmit={onFormSubmitHandler} {...restProps}>
|
<form onSubmit={onFormSubmitHandler} ref={ref} {...restProps}>
|
||||||
<h2>Удалить элемент ?</h2>
|
<h2>Удалить элемент ?</h2>
|
||||||
|
|
||||||
<button disabled={disabled} type="submit">Удалить</button>
|
<button disabled={disabled} type="submit">Удалить</button>
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import EditExpenseItemForm from './EditExpenseItemForm.ui';
|
|
||||||
|
|
||||||
import type { EditExpenseItemFormProps } from './EditExpenseItemForm.ui';
|
import type { EditExpenseItemFormProps } from './EditExpenseItemForm.ui';
|
||||||
|
|
||||||
|
import EditExpenseItemForm from './EditExpenseItemForm.ui';
|
||||||
|
|
||||||
const editingItem = {
|
const editingItem = {
|
||||||
id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6',
|
|
||||||
title: 'Mock item',
|
|
||||||
amount: 12,
|
amount: 12,
|
||||||
count: 12,
|
count: 12,
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
expenseListId: '10cc093c-3875-4d33-a40a-df526266e262',
|
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
date: '2025-08-22T00:00:03.000Z',
|
date: '2025-08-22T00:00:03.000Z',
|
||||||
description: 'Mock description',
|
description: 'Mock description',
|
||||||
|
expenseListId: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
|
id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6',
|
||||||
|
title: 'Mock item',
|
||||||
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<EditExpenseItemFormProps>) => {
|
const renderForm = (props?: Partial<EditExpenseItemFormProps>) => {
|
||||||
@ -38,13 +38,13 @@ const renderForm = (props?: Partial<EditExpenseItemFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Сохранить');
|
const submitButtonElem = screen.getByText('Сохранить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
countInputElem,
|
countInputElem,
|
||||||
submitButtonElem,
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,11 +58,11 @@ describe('Test EditExpenseItemForm', () => {
|
|||||||
|
|
||||||
test('should inputs have default value', () => {
|
test('should inputs have default value', () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
countInputElem,
|
countInputElem,
|
||||||
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
expect(titleInputElem).toHaveValue('Mock item');
|
expect(titleInputElem).toHaveValue('Mock item');
|
||||||
expect(descriptionInputElem).toHaveValue('Mock description');
|
expect(descriptionInputElem).toHaveValue('Mock description');
|
||||||
@ -85,13 +85,13 @@ describe('Test EditExpenseItemForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
countInputElem,
|
countInputElem,
|
||||||
submitButtonElem,
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.clear(titleInputElem);
|
await userEvent.clear(titleInputElem);
|
||||||
@ -113,12 +113,12 @@ describe('Test EditExpenseItemForm', () => {
|
|||||||
|
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||||
expect(onSubmit).toHaveBeenCalledWith({
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
title: 'Название элемента',
|
|
||||||
description: 'Описание элемента',
|
|
||||||
date: '2025-08-29T00:00:00.000Z',
|
|
||||||
amount: 12,
|
amount: 12,
|
||||||
count: 1,
|
count: 1,
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
|
date: '2025-08-29T00:00:00.000Z',
|
||||||
|
description: 'Описание элемента',
|
||||||
|
title: 'Название элемента',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './EditExpenseItemForm.module.css';
|
|
||||||
|
|
||||||
import type { ExpenseListItem } from '../../types';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -10,29 +5,35 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
|
import { Input } from '@/shared/ui/Input';
|
||||||
|
|
||||||
|
import type { ExpenseListItem } from '../../types';
|
||||||
|
|
||||||
|
import classes from './EditExpenseItemForm.module.css';
|
||||||
|
|
||||||
|
export type EditExpenseItemFormProps = BaseFormProps & {
|
||||||
|
disabled?: boolean;
|
||||||
|
item: ExpenseListItem;
|
||||||
|
onSubmit?: (data: EditExpenseItemFormValues) => Promise<void> | void;
|
||||||
|
ref?: RefObject<HTMLFormElement>;
|
||||||
|
};
|
||||||
|
|
||||||
export type EditExpenseItemFormValues = {
|
export type EditExpenseItemFormValues = {
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
date: string;
|
|
||||||
amount: number;
|
amount: number;
|
||||||
count: number;
|
count: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
|
date: string;
|
||||||
|
description: string;
|
||||||
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
export type EditExpenseItemFormProps = BaseFormProps & {
|
|
||||||
disabled?: boolean;
|
|
||||||
onSubmit?: (data: EditExpenseItemFormValues) => Promise<void> | void;
|
|
||||||
ref?: RefObject<HTMLFormElement>;
|
|
||||||
item: ExpenseListItem;
|
|
||||||
};
|
|
||||||
|
|
||||||
const EditExpenseItemForm: FC<EditExpenseItemFormProps> = (props) => {
|
const EditExpenseItemForm: FC<EditExpenseItemFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
disabled,
|
disabled,
|
||||||
|
item,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
ref,
|
ref,
|
||||||
item,
|
|
||||||
...formProps
|
...formProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -49,21 +50,21 @@ const EditExpenseItemForm: FC<EditExpenseItemFormProps> = (props) => {
|
|||||||
|
|
||||||
if (typeof title === 'string' && typeof date === 'string' && typeof amount === 'string' && typeof description === 'string' && typeof count === 'string') {
|
if (typeof title === 'string' && typeof date === 'string' && typeof amount === 'string' && typeof description === 'string' && typeof count === 'string') {
|
||||||
await onSubmit?.({
|
await onSubmit?.({
|
||||||
title,
|
|
||||||
date: new Date(date).toISOString(),
|
|
||||||
amount: Number(amount),
|
amount: Number(amount),
|
||||||
description,
|
|
||||||
currency: 'RUB',
|
|
||||||
count: Number(count),
|
count: Number(count),
|
||||||
|
currency: 'RUB',
|
||||||
|
date: new Date(date).toISOString(),
|
||||||
|
description,
|
||||||
|
title,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...formProps}
|
{...formProps}
|
||||||
>
|
>
|
||||||
<h2>Добавить список расходов</h2>
|
<h2>Добавить список расходов</h2>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type AddExpenseListPath = paths['/expenses/list']['post']['parameters']['path'];
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type AddExpenseListBody = paths['/expenses/list']['post']['requestBody']['content']['application/json'];
|
export type AddExpenseListBody = paths['/expenses/list']['post']['requestBody']['content']['application/json'];
|
||||||
|
export type AddExpenseListPath = paths['/expenses/list']['post']['parameters']['path'];
|
||||||
export type AddExpenseListSuccessResponse = paths['/expenses/list']['post']['responses']['200']['content']['application/json'];
|
export type AddExpenseListSuccessResponse = paths['/expenses/list']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function addExpensesList(data: AddExpenseListBody) {
|
export async function addExpensesList(data: AddExpenseListBody) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type DeleteExpenseListPath = paths['/expenses/list/{id}']['delete']['parameters']['path'];
|
export type DeleteExpenseListPath = paths['/expenses/list/{id}']['delete']['parameters']['path'];
|
||||||
export type DeleteExpenseSuccessResponse = paths['/expenses/list/{id}']['delete']['responses']['200']['content']['application/json'];
|
export type DeleteExpenseSuccessResponse = paths['/expenses/list/{id}']['delete']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type EditExpenseListPath = paths['/expenses/list/{id}']['patch']['parameters']['path'];
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type EditExpenseListBody = paths['/expenses/list/{id}']['patch']['requestBody']['content']['application/json'];
|
export type EditExpenseListBody = paths['/expenses/list/{id}']['patch']['requestBody']['content']['application/json'];
|
||||||
|
export type EditExpenseListPath = paths['/expenses/list/{id}']['patch']['parameters']['path'];
|
||||||
export type EditExpenseListSuccessResponse = paths['/expenses/list/{id}']['patch']['responses']['200']['content']['application/json'];
|
export type EditExpenseListSuccessResponse = paths['/expenses/list/{id}']['patch']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function editExpensesList({ data, path }: {
|
export async function editExpensesList({ data, path }: {
|
||||||
path: EditExpenseListPath;
|
|
||||||
data: EditExpenseListBody;
|
data: EditExpenseListBody;
|
||||||
|
path: EditExpenseListPath;
|
||||||
}) {
|
}) {
|
||||||
const { id } = path;
|
const { id } = path;
|
||||||
const { data: result } = await api.patch<EditExpenseListSuccessResponse>(`/expenses/list/${id}`, data);
|
const { data: result } = await api.patch<EditExpenseListSuccessResponse>(`/expenses/list/${id}`, data);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type ExpenseListPath = paths['/expenses/list/{id}']['get']['parameters']['path'];
|
type ExpenseListPath = paths['/expenses/list/{id}']['get']['parameters']['path'];
|
||||||
type ExpenseListResponse = paths['/expenses/list/{id}']['get']['responses']['200']['content']['application/json'];
|
type ExpenseListResponse = paths['/expenses/list/{id}']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type ExpensesListResponse = paths['/expenses/list']['get']['responses']['200']['content']['application/json'];
|
type ExpensesListResponse = paths['/expenses/list']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function getExpenseLists() {
|
export async function getExpenseLists() {
|
||||||
|
|||||||
@ -2,10 +2,10 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
|
|
||||||
import { QueryKeys } from '@/shared/api/queryKeys';
|
import { QueryKeys } from '@/shared/api/queryKeys';
|
||||||
|
|
||||||
import { getExpenseList } from '../api/getExpenseList';
|
|
||||||
|
|
||||||
import type { ExpenseList } from '../types';
|
import type { ExpenseList } from '../types';
|
||||||
|
|
||||||
|
import { getExpenseList } from '../api/getExpenseList';
|
||||||
|
|
||||||
export const useExpenseList = (id: ExpenseList['id']) => useQuery({
|
export const useExpenseList = (id: ExpenseList['id']) => useQuery({
|
||||||
queryFn: async () => getExpenseList({ id }),
|
queryFn: async () => getExpenseList({ id }),
|
||||||
queryKey: [QueryKeys.ExpenseList, id],
|
queryKey: [QueryKeys.ExpenseList, id],
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
export type { ExpenseList } from './types';
|
export type { AddExpenseListBody, AddExpenseListPath, AddExpenseListSuccessResponse } from './api/addExpensesList';
|
||||||
export { useDeleteExpensesList } from './hooks/useDeleteExpensesList';
|
export type { DeleteExpenseListPath, DeleteExpenseSuccessResponse } from './api/deleteExpensesList';
|
||||||
export { useAddExpensesList } from './hooks/useAddExpensesList';
|
export type { EditExpenseListBody, EditExpenseListPath, EditExpenseListSuccessResponse } from './api/editExpensesList';
|
||||||
export { useExpensesList } from './hooks/useExpensesList';
|
|
||||||
export { useEditExpensesList } from './hooks/useEditExpensesList';
|
|
||||||
|
|
||||||
|
export { useAddExpensesList } from './hooks/useAddExpensesList';
|
||||||
|
export { useDeleteExpensesList } from './hooks/useDeleteExpensesList';
|
||||||
|
export { useEditExpensesList } from './hooks/useEditExpensesList';
|
||||||
export { useExpenseList } from './hooks/useExpenseList';
|
export { useExpenseList } from './hooks/useExpenseList';
|
||||||
|
export { useExpensesList } from './hooks/useExpensesList';
|
||||||
|
|
||||||
|
export type { ExpenseList } from './types';
|
||||||
|
|
||||||
export type { AddExpenseListFormValues } from './ui/AddExpensesListForm/ui/AddExpensesListForm.ui';
|
export type { AddExpenseListFormValues } from './ui/AddExpensesListForm/ui/AddExpensesListForm.ui';
|
||||||
export { default as AddExpensesListForm } from './ui/AddExpensesListForm/ui/AddExpensesListForm.ui';
|
export { default as AddExpensesListForm } from './ui/AddExpensesListForm/ui/AddExpensesListForm.ui';
|
||||||
@ -13,7 +17,3 @@ export { default as DeleteExpensesListForm } from './ui/DeleteExpensesListForm/u
|
|||||||
|
|
||||||
export type { EditExpenseListFormValues } from './ui/EditExpensesListForm/ui/EditExpensesListForm.ui';
|
export type { EditExpenseListFormValues } from './ui/EditExpensesListForm/ui/EditExpensesListForm.ui';
|
||||||
export { default as EditExpensesListForm } from './ui/EditExpensesListForm/ui/EditExpensesListForm.ui';
|
export { default as EditExpensesListForm } from './ui/EditExpensesListForm/ui/EditExpensesListForm.ui';
|
||||||
|
|
||||||
export type { AddExpenseListPath, AddExpenseListBody, AddExpenseListSuccessResponse } from './api/addExpensesList';
|
|
||||||
export type { DeleteExpenseListPath, DeleteExpenseSuccessResponse } from './api/deleteExpensesList';
|
|
||||||
export type { EditExpenseListPath, EditExpenseListBody, EditExpenseListSuccessResponse } from './api/editExpensesList';
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import AddExpensesListForm from './AddExpensesListForm.ui';
|
|
||||||
|
|
||||||
import type { AddExpenseListFormProps } from './AddExpensesListForm.ui';
|
import type { AddExpenseListFormProps } from './AddExpensesListForm.ui';
|
||||||
|
|
||||||
|
import AddExpensesListForm from './AddExpensesListForm.ui';
|
||||||
|
|
||||||
const renderForm = (props?: Partial<AddExpenseListFormProps>) => {
|
const renderForm = (props?: Partial<AddExpenseListFormProps>) => {
|
||||||
const onSubmit = jest.fn();
|
const onSubmit = jest.fn();
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ const renderForm = (props?: Partial<AddExpenseListFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Добавить');
|
const submitButtonElem = screen.getByText('Добавить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,9 +42,9 @@ describe('Test AddExpensesListForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.type(titleInputElem, 'Название списка');
|
await userEvent.type(titleInputElem, 'Название списка');
|
||||||
|
|||||||
@ -1,31 +1,31 @@
|
|||||||
|
import type {
|
||||||
|
FC,
|
||||||
|
FormEventHandler,
|
||||||
|
FormHTMLAttributes,
|
||||||
|
RefObject,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Input } from '@/shared/ui/Input';
|
import { Input } from '@/shared/ui/Input';
|
||||||
|
|
||||||
import classes from './AddExpenseListForm.module.css';
|
import classes from './AddExpenseListForm.module.css';
|
||||||
|
|
||||||
import type {
|
|
||||||
FormHTMLAttributes,
|
|
||||||
RefObject,
|
|
||||||
FormEventHandler,
|
|
||||||
FC,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
export type AddExpenseListFormValues = {
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
|
||||||
|
|
||||||
export type AddExpenseListFormProps = BaseFormProps & {
|
export type AddExpenseListFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onSubmit: (data: AddExpenseListFormValues) => Promise<void> | void;
|
onSubmit: (data: AddExpenseListFormValues) => Promise<void> | void;
|
||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AddExpenseListFormValues = {
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const AddExpensesListForm: FC<AddExpenseListFormProps> = (props) => {
|
const AddExpensesListForm: FC<AddExpenseListFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
ref,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -45,9 +45,9 @@ const AddExpensesListForm: FC<AddExpenseListFormProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<h2>Добавить список расходов</h2>
|
<h2>Добавить список расходов</h2>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import DeleteExpensesListForm from './DeleteExpensesListForm.ui';
|
|
||||||
|
|
||||||
import type { DeleteExpenseListFormProps } from './DeleteExpensesListForm.ui';
|
import type { DeleteExpenseListFormProps } from './DeleteExpensesListForm.ui';
|
||||||
|
|
||||||
|
import DeleteExpensesListForm from './DeleteExpensesListForm.ui';
|
||||||
|
|
||||||
const deletingList = {
|
const deletingList = {
|
||||||
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
|
||||||
title: 'Mock list',
|
|
||||||
items: [],
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
|
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
|
items: [],
|
||||||
|
title: 'Mock list',
|
||||||
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<DeleteExpenseListFormProps>) => {
|
const renderForm = (props?: Partial<DeleteExpenseListFormProps>) => {
|
||||||
@ -28,8 +28,8 @@ const renderForm = (props?: Partial<DeleteExpenseListFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Удалить');
|
const submitButtonElem = screen.getByText('Удалить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ describe('Test DeleteExpensesListForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.click(submitButtonElem);
|
await userEvent.click(submitButtonElem);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { ExpenseList } from '@/entity/expenses/list';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -6,21 +5,23 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import type { ExpenseList } from '@/entity/expenses/list';
|
||||||
|
|
||||||
export type DeleteExpenseListFormProps = BaseFormProps & {
|
export type DeleteExpenseListFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
list: ExpenseList;
|
||||||
onSubmit?: (list: ExpenseList) => Promise<void> | void;
|
onSubmit?: (list: ExpenseList) => Promise<void> | void;
|
||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
list: ExpenseList;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const DeleteExpensesListForm: FC<DeleteExpenseListFormProps> = (props) => {
|
const DeleteExpensesListForm: FC<DeleteExpenseListFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
ref,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
|
||||||
list,
|
list,
|
||||||
|
onSubmit,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ const DeleteExpensesListForm: FC<DeleteExpenseListFormProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form ref={ref} onSubmit={onFormSubmitHandler} {...restProps}>
|
<form onSubmit={onFormSubmitHandler} ref={ref} {...restProps}>
|
||||||
<h2>Удалить список расходов</h2>
|
<h2>Удалить список расходов</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import EditExpensesListForm from './EditExpensesListForm.ui';
|
|
||||||
|
|
||||||
import type { EditExpensesListFormProps } from './EditExpensesListForm.ui';
|
import type { EditExpensesListFormProps } from './EditExpensesListForm.ui';
|
||||||
|
|
||||||
|
import EditExpensesListForm from './EditExpensesListForm.ui';
|
||||||
|
|
||||||
const editingList = {
|
const editingList = {
|
||||||
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
|
||||||
title: 'Mock list',
|
|
||||||
items: [],
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
|
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
|
items: [],
|
||||||
|
title: 'Mock list',
|
||||||
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<EditExpensesListFormProps>) => {
|
const renderForm = (props?: Partial<EditExpensesListFormProps>) => {
|
||||||
@ -29,9 +29,9 @@ const renderForm = (props?: Partial<EditExpensesListFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Сохранить');
|
const submitButtonElem = screen.getByText('Сохранить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,9 +64,9 @@ describe('Test EditExpensesListForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.clear(titleInputElem);
|
await userEvent.clear(titleInputElem);
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './EditExpenseListForm.module.css';
|
|
||||||
|
|
||||||
import type { ExpenseList } from '@/entity/expenses/list';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -10,25 +5,31 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
|
import type { ExpenseList } from '@/entity/expenses/list';
|
||||||
|
|
||||||
|
import { Input } from '@/shared/ui/Input';
|
||||||
|
|
||||||
|
import classes from './EditExpenseListForm.module.css';
|
||||||
|
|
||||||
export type EditExpenseListFormValues = {
|
export type EditExpenseListFormValues = {
|
||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
|
||||||
|
|
||||||
export type EditExpensesListFormProps = BaseFormProps & {
|
export type EditExpensesListFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
list: ExpenseList;
|
||||||
onSubmit: (data: EditExpenseListFormValues) => Promise<void> | void;
|
onSubmit: (data: EditExpenseListFormValues) => Promise<void> | void;
|
||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
list: ExpenseList;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const EditExpensesListForm: FC<EditExpensesListFormProps> = (props) => {
|
const EditExpensesListForm: FC<EditExpensesListFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
ref,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
|
||||||
list,
|
list,
|
||||||
|
onSubmit,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -46,9 +47,9 @@ const EditExpensesListForm: FC<EditExpensesListFormProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<h2>Редактировать список расходов</h2>
|
<h2>Редактировать список расходов</h2>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type AddIncomeItemBody = paths['/incomes/list/{id}/items']['post']['requestBody']['content']['application/json'];
|
export type AddIncomeItemBody = paths['/incomes/list/{id}/items']['post']['requestBody']['content']['application/json'];
|
||||||
export type AddIncomeItemPath = paths['/incomes/list/{id}/items']['post']['parameters']['path'];
|
export type AddIncomeItemPath = paths['/incomes/list/{id}/items']['post']['parameters']['path'];
|
||||||
export type AddIncomeItemSuccessResponse = paths['/incomes/list/{id}/items']['post']['responses']['200']['content']['application/json'];
|
export type AddIncomeItemSuccessResponse = paths['/incomes/list/{id}/items']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type DeleteIncomeItemPath = paths['/incomes/list/{listId}/items/{id}']['delete']['parameters']['path'];
|
export type DeleteIncomeItemPath = paths['/incomes/list/{listId}/items/{id}']['delete']['parameters']['path'];
|
||||||
export type DeleteIncomeItemSuccessResponse = paths['/incomes/list/{id}/items']['post']['responses']['200']['content']['application/json'];
|
export type DeleteIncomeItemSuccessResponse = paths['/incomes/list/{id}/items']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type EditIncomeItemBody = paths['/incomes/list/{listId}/items/{id}']['patch']['requestBody']['content']['application/json'];
|
export type EditIncomeItemBody = paths['/incomes/list/{listId}/items/{id}']['patch']['requestBody']['content']['application/json'];
|
||||||
export type EditIncomeItemPath = paths['/incomes/list/{listId}/items/{id}']['patch']['parameters']['path'];
|
export type EditIncomeItemPath = paths['/incomes/list/{listId}/items/{id}']['patch']['parameters']['path'];
|
||||||
export type EditIncomeItemSuccessResponse = paths['/incomes/list/{listId}/items/{id}']['patch']['responses']['200']['content']['application/json'];
|
export type EditIncomeItemSuccessResponse = paths['/incomes/list/{listId}/items/{id}']['patch']['responses']['200']['content']['application/json'];
|
||||||
|
|||||||
@ -2,10 +2,10 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
|
|
||||||
import { QueryKeys } from '@/shared/api/queryKeys';
|
import { QueryKeys } from '@/shared/api/queryKeys';
|
||||||
|
|
||||||
import { deleteIncomeItem } from '../api/deleteIncomeItem';
|
|
||||||
|
|
||||||
import type { DeleteIncomeItemPath } from '../api/deleteIncomeItem';
|
import type { DeleteIncomeItemPath } from '../api/deleteIncomeItem';
|
||||||
|
|
||||||
|
import { deleteIncomeItem } from '../api/deleteIncomeItem';
|
||||||
|
|
||||||
export const useDeleteIncomeItem = (path: DeleteIncomeItemPath) => {
|
export const useDeleteIncomeItem = (path: DeleteIncomeItemPath) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
|
export type { AddIncomeItemBody, AddIncomeItemPath, AddIncomeItemSuccessResponse } from './api/addIncomeItem';
|
||||||
|
export type { DeleteIncomeItemPath, DeleteIncomeItemSuccessResponse } from './api/deleteIncomeItem';
|
||||||
|
export type { EditIncomeItemBody, EditIncomeItemPath, EditIncomeItemSuccessResponse } from './api/editIncomeItem';
|
||||||
|
|
||||||
export { useAddIncomeItem } from './hooks/useAddIncomeItem';
|
export { useAddIncomeItem } from './hooks/useAddIncomeItem';
|
||||||
export { useDeleteIncomeItem } from './hooks/useDeleteIncomeItem';
|
export { useDeleteIncomeItem } from './hooks/useDeleteIncomeItem';
|
||||||
export { useEditIncomeItem } from './hooks/useEditIncomeItem';
|
export { useEditIncomeItem } from './hooks/useEditIncomeItem';
|
||||||
|
|
||||||
export type { AddIncomeItemPath, AddIncomeItemBody, AddIncomeItemSuccessResponse } from './api/addIncomeItem';
|
export type { IncomeListItem } from './types';
|
||||||
export type { DeleteIncomeItemPath, DeleteIncomeItemSuccessResponse } from './api/deleteIncomeItem';
|
|
||||||
export type { EditIncomeItemPath, EditIncomeItemBody, EditIncomeItemSuccessResponse } from './api/editIncomeItem';
|
|
||||||
|
|
||||||
export type { AddIncomeItemFormValues } from './ui/AddIncomeItemForm/AddIncomeItemForm.ui';
|
export type { AddIncomeItemFormValues } from './ui/AddIncomeItemForm/AddIncomeItemForm.ui';
|
||||||
|
|
||||||
export { default as AddIncomeItemForm } from './ui/AddIncomeItemForm/AddIncomeItemForm.ui';
|
export { default as AddIncomeItemForm } from './ui/AddIncomeItemForm/AddIncomeItemForm.ui';
|
||||||
|
|
||||||
export type { EditIncomeItemFormValues } from './ui/EditIncomeItemForm/EditIncomeItemForm.ui';
|
|
||||||
export { default as EditIncomeItemForm } from './ui/EditIncomeItemForm/EditIncomeItemForm.ui';
|
|
||||||
|
|
||||||
export { default as DeleteIncomeItemForm } from './ui/DeleteIncomeItemForm/DeleteIncomeItemForm.ui';
|
export { default as DeleteIncomeItemForm } from './ui/DeleteIncomeItemForm/DeleteIncomeItemForm.ui';
|
||||||
|
|
||||||
export type { IncomeListItem } from './types';
|
export type { EditIncomeItemFormValues } from './ui/EditIncomeItemForm/EditIncomeItemForm.ui';
|
||||||
|
|
||||||
|
export { default as EditIncomeItemForm } from './ui/EditIncomeItemForm/EditIncomeItemForm.ui';
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import AddIncomeItemForm from './AddIncomeItemForm.ui';
|
|
||||||
|
|
||||||
import type { AddIncomeItemFormProps } from './AddIncomeItemForm.ui';
|
import type { AddIncomeItemFormProps } from './AddIncomeItemForm.ui';
|
||||||
|
|
||||||
|
import AddIncomeItemForm from './AddIncomeItemForm.ui';
|
||||||
|
|
||||||
const renderForm = (props?: Partial<AddIncomeItemFormProps>) => {
|
const renderForm = (props?: Partial<AddIncomeItemFormProps>) => {
|
||||||
const onSubmit = jest.fn();
|
const onSubmit = jest.fn();
|
||||||
|
|
||||||
@ -17,12 +17,12 @@ const renderForm = (props?: Partial<AddIncomeItemFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Добавить');
|
const submitButtonElem = screen.getByText('Добавить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
submitButtonElem,
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,12 +48,12 @@ describe('Test AddIncomeItemForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
submitButtonElem,
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.type(titleInputElem, 'Название элемента');
|
await userEvent.type(titleInputElem, 'Название элемента');
|
||||||
@ -65,11 +65,11 @@ describe('Test AddIncomeItemForm', () => {
|
|||||||
|
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||||
expect(onSubmit).toHaveBeenCalledWith({
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
title: 'Название элемента',
|
|
||||||
description: 'Описание элемента',
|
|
||||||
date: '2025-08-29T00:00:00.000Z',
|
|
||||||
amount: 12,
|
amount: 12,
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
|
date: '2025-08-29T00:00:00.000Z',
|
||||||
|
description: 'Описание элемента',
|
||||||
|
title: 'Название элемента',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './AddIncomeItemForm.module.css';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -9,15 +5,9 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
export type AddIncomeItemFormValues = {
|
import { Input } from '@/shared/ui/Input';
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
date: string;
|
|
||||||
amount: number;
|
|
||||||
currency: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import classes from './AddIncomeItemForm.module.css';
|
||||||
|
|
||||||
export type AddIncomeItemFormProps = BaseFormProps & {
|
export type AddIncomeItemFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@ -25,6 +15,16 @@ export type AddIncomeItemFormProps = BaseFormProps & {
|
|||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AddIncomeItemFormValues = {
|
||||||
|
amount: number;
|
||||||
|
currency: string;
|
||||||
|
date: string;
|
||||||
|
description: string;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const AddIncomeItemForm: FC<AddIncomeItemFormProps> = (props) => {
|
const AddIncomeItemForm: FC<AddIncomeItemFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
disabled,
|
disabled,
|
||||||
@ -45,29 +45,29 @@ const AddIncomeItemForm: FC<AddIncomeItemFormProps> = (props) => {
|
|||||||
|
|
||||||
if (typeof title === 'string' && typeof date === 'string' && typeof amount === 'string' && typeof description === 'string') {
|
if (typeof title === 'string' && typeof date === 'string' && typeof amount === 'string' && typeof description === 'string') {
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
title,
|
|
||||||
date: new Date(date).toISOString(),
|
|
||||||
amount: Number(amount),
|
amount: Number(amount),
|
||||||
description,
|
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
|
date: new Date(date).toISOString(),
|
||||||
|
description,
|
||||||
|
title,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...formProps}
|
{...formProps}
|
||||||
>
|
>
|
||||||
<h2>Добавить список доходов</h2>
|
<h2>Добавить список доходов</h2>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Название"
|
label="Название"
|
||||||
name="title"
|
name="title"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
@ -77,18 +77,18 @@ const AddIncomeItemForm: FC<AddIncomeItemFormProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Дата"
|
label="Дата"
|
||||||
name="date"
|
name="date"
|
||||||
|
required
|
||||||
type="date"
|
type="date"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
required
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
label="Сумма"
|
label="Сумма"
|
||||||
name="amount"
|
name="amount"
|
||||||
|
required
|
||||||
step="0.01"
|
step="0.01"
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import DeleteIncomeItemForm from './DeleteIncomeItemForm.ui';
|
|
||||||
|
|
||||||
import type { DeleteIncomeItemFormProps } from './DeleteIncomeItemForm.ui';
|
import type { DeleteIncomeItemFormProps } from './DeleteIncomeItemForm.ui';
|
||||||
|
|
||||||
|
import DeleteIncomeItemForm from './DeleteIncomeItemForm.ui';
|
||||||
|
|
||||||
const deletingItem = {
|
const deletingItem = {
|
||||||
id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6',
|
|
||||||
title: 'Mock item',
|
|
||||||
amount: 12,
|
amount: 12,
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
incomeListId: '10cc093c-3875-4d33-a40a-df526266e262',
|
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
date: '2025-08-22T00:00:03.000Z',
|
date: '2025-08-22T00:00:03.000Z',
|
||||||
description: 'Mock description',
|
description: 'Mock description',
|
||||||
|
id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6',
|
||||||
|
incomeListId: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
|
title: 'Mock item',
|
||||||
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<DeleteIncomeItemFormProps>) => {
|
const renderForm = (props?: Partial<DeleteIncomeItemFormProps>) => {
|
||||||
@ -32,8 +32,8 @@ const renderForm = (props?: Partial<DeleteIncomeItemFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Удалить');
|
const submitButtonElem = screen.getByText('Удалить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,8 +47,8 @@ describe('Test DeleteIncomeItemForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.click(submitButtonElem);
|
await userEvent.click(submitButtonElem);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { IncomeListItem } from '../../types';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -6,21 +5,23 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import type { IncomeListItem } from '../../types';
|
||||||
|
|
||||||
export type DeleteIncomeItemFormProps = BaseFormProps & {
|
export type DeleteIncomeItemFormProps = BaseFormProps & {
|
||||||
|
disabled?: boolean;
|
||||||
item: IncomeListItem;
|
item: IncomeListItem;
|
||||||
onSubmit?: (item: IncomeListItem) => Promise<void> | void;
|
onSubmit?: (item: IncomeListItem) => Promise<void> | void;
|
||||||
disabled?: boolean;
|
|
||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const DeleteIncomeItemForm: FC<DeleteIncomeItemFormProps> = (props) => {
|
const DeleteIncomeItemForm: FC<DeleteIncomeItemFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
|
disabled,
|
||||||
item,
|
item,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
ref,
|
ref,
|
||||||
disabled,
|
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
const onFormSubmitHandler: FormEventHandler<HTMLFormElement> = (event) => {
|
const onFormSubmitHandler: FormEventHandler<HTMLFormElement> = (event) => {
|
||||||
@ -29,7 +30,7 @@ const DeleteIncomeItemForm: FC<DeleteIncomeItemFormProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form ref={ref} onSubmit={onFormSubmitHandler} {...restProps}>
|
<form onSubmit={onFormSubmitHandler} ref={ref} {...restProps}>
|
||||||
<h2>Удалить элемент ?</h2>
|
<h2>Удалить элемент ?</h2>
|
||||||
|
|
||||||
<button disabled={disabled} type="submit">Удалить</button>
|
<button disabled={disabled} type="submit">Удалить</button>
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import EditIncomeItemForm from './EditIncomeItemForm.ui';
|
|
||||||
|
|
||||||
import type { EditIncomeItemFormProps } from './EditIncomeItemForm.ui';
|
import type { EditIncomeItemFormProps } from './EditIncomeItemForm.ui';
|
||||||
|
|
||||||
|
import EditIncomeItemForm from './EditIncomeItemForm.ui';
|
||||||
|
|
||||||
const editingItem = {
|
const editingItem = {
|
||||||
id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6',
|
|
||||||
title: 'Mock item',
|
|
||||||
amount: 12,
|
amount: 12,
|
||||||
count: 12,
|
count: 12,
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
incomeListId: '10cc093c-3875-4d33-a40a-df526266e262',
|
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
date: '2025-08-22T00:00:03.000Z',
|
date: '2025-08-22T00:00:03.000Z',
|
||||||
description: 'Mock description',
|
description: 'Mock description',
|
||||||
|
id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6',
|
||||||
|
incomeListId: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
|
title: 'Mock item',
|
||||||
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<EditIncomeItemFormProps>) => {
|
const renderForm = (props?: Partial<EditIncomeItemFormProps>) => {
|
||||||
@ -37,12 +37,12 @@ const renderForm = (props?: Partial<EditIncomeItemFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Сохранить');
|
const submitButtonElem = screen.getByText('Сохранить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
submitButtonElem,
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,10 +56,10 @@ describe('Test EditIncomeItemForm', () => {
|
|||||||
|
|
||||||
test('should inputs have default value', () => {
|
test('should inputs have default value', () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
expect(titleInputElem).toHaveValue('Mock item');
|
expect(titleInputElem).toHaveValue('Mock item');
|
||||||
expect(descriptionInputElem).toHaveValue('Mock description');
|
expect(descriptionInputElem).toHaveValue('Mock description');
|
||||||
@ -81,12 +81,12 @@ describe('Test EditIncomeItemForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
descriptionInputElem,
|
|
||||||
dateInputElem,
|
|
||||||
amountInputElem,
|
amountInputElem,
|
||||||
submitButtonElem,
|
dateInputElem,
|
||||||
|
descriptionInputElem,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.clear(titleInputElem);
|
await userEvent.clear(titleInputElem);
|
||||||
@ -105,11 +105,11 @@ describe('Test EditIncomeItemForm', () => {
|
|||||||
|
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||||
expect(onSubmit).toHaveBeenCalledWith({
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
title: 'Название элемента',
|
|
||||||
description: 'Описание элемента',
|
|
||||||
date: '2025-08-29T00:00:00.000Z',
|
|
||||||
amount: 12,
|
amount: 12,
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
|
date: '2025-08-29T00:00:00.000Z',
|
||||||
|
description: 'Описание элемента',
|
||||||
|
title: 'Название элемента',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './EditIncomeItemForm.module.css';
|
|
||||||
|
|
||||||
import type { IncomeListItem } from '../../types';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -10,28 +5,34 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
export type EditIncomeItemFormValues = {
|
import { Input } from '@/shared/ui/Input';
|
||||||
title: string;
|
|
||||||
description: string;
|
import type { IncomeListItem } from '../../types';
|
||||||
date: string;
|
|
||||||
amount: number;
|
import classes from './EditIncomeItemForm.module.css';
|
||||||
currency: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
|
||||||
export type EditIncomeItemFormProps = BaseFormProps & {
|
export type EditIncomeItemFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
item: IncomeListItem;
|
||||||
onSubmit?: (data: EditIncomeItemFormValues) => Promise<void> | void;
|
onSubmit?: (data: EditIncomeItemFormValues) => Promise<void> | void;
|
||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
item: IncomeListItem;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EditIncomeItemFormValues = {
|
||||||
|
amount: number;
|
||||||
|
currency: string;
|
||||||
|
date: string;
|
||||||
|
description: string;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const EditIncomeItemForm: FC<EditIncomeItemFormProps> = (props) => {
|
const EditIncomeItemForm: FC<EditIncomeItemFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
disabled,
|
disabled,
|
||||||
|
item,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
ref,
|
ref,
|
||||||
item,
|
|
||||||
...formProps
|
...formProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -47,20 +48,20 @@ const EditIncomeItemForm: FC<EditIncomeItemFormProps> = (props) => {
|
|||||||
|
|
||||||
if (typeof title === 'string' && typeof date === 'string' && typeof amount === 'string' && typeof description === 'string') {
|
if (typeof title === 'string' && typeof date === 'string' && typeof amount === 'string' && typeof description === 'string') {
|
||||||
await onSubmit?.({
|
await onSubmit?.({
|
||||||
title,
|
|
||||||
date: new Date(date).toISOString(),
|
|
||||||
amount: Number(amount),
|
amount: Number(amount),
|
||||||
description,
|
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
|
date: new Date(date).toISOString(),
|
||||||
|
description,
|
||||||
|
title,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...formProps}
|
{...formProps}
|
||||||
>
|
>
|
||||||
<h2>Изменить доход</h2>
|
<h2>Изменить доход</h2>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type AddIncomeListPath = paths['/incomes/list']['post']['parameters']['path'];
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type AddIncomeListBody = paths['/incomes/list']['post']['requestBody']['content']['application/json'];
|
export type AddIncomeListBody = paths['/incomes/list']['post']['requestBody']['content']['application/json'];
|
||||||
|
export type AddIncomeListPath = paths['/incomes/list']['post']['parameters']['path'];
|
||||||
export type AddIncomeListSuccessResponse = paths['/incomes/list']['post']['responses']['200']['content']['application/json'];
|
export type AddIncomeListSuccessResponse = paths['/incomes/list']['post']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function addIncomeList(data: AddIncomeListBody) {
|
export async function addIncomeList(data: AddIncomeListBody) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type DeleteIncomeListPath = paths['/incomes/list/{id}']['delete']['parameters']['path'];
|
export type DeleteIncomeListPath = paths['/incomes/list/{id}']['delete']['parameters']['path'];
|
||||||
export type DeleteIncomeListSuccessResponse = paths['/incomes/list/{id}']['delete']['responses']['200']['content']['application/json'];
|
export type DeleteIncomeListSuccessResponse = paths['/incomes/list/{id}']['delete']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
export type EditIncomeListPath = paths['/incomes/list/{id}']['patch']['parameters']['path'];
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
export type EditIncomeListBody = paths['/incomes/list/{id}']['patch']['requestBody']['content']['application/json'];
|
export type EditIncomeListBody = paths['/incomes/list/{id}']['patch']['requestBody']['content']['application/json'];
|
||||||
|
export type EditIncomeListPath = paths['/incomes/list/{id}']['patch']['parameters']['path'];
|
||||||
export type EditIncomeListSuccessResponse = paths['/incomes/list/{id}']['patch']['responses']['200']['content']['application/json'];
|
export type EditIncomeListSuccessResponse = paths['/incomes/list/{id}']['patch']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function editIncomeList({ data, path }: {
|
export async function editIncomeList({ data, path }: {
|
||||||
path: EditIncomeListPath;
|
|
||||||
data: EditIncomeListBody;
|
data: EditIncomeListBody;
|
||||||
|
path: EditIncomeListPath;
|
||||||
}) {
|
}) {
|
||||||
const { id } = path;
|
const { id } = path;
|
||||||
const { data: result } = await api.patch<EditIncomeListSuccessResponse>(`/incomes/list/${id}`, data);
|
const { data: result } = await api.patch<EditIncomeListSuccessResponse>(`/incomes/list/${id}`, data);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type IncomeListPath = paths['/incomes/list/{id}']['get']['parameters']['path'];
|
type IncomeListPath = paths['/incomes/list/{id}']['get']['parameters']['path'];
|
||||||
type IncomeListResponse = paths['/incomes/list/{id}']['get']['responses']['200']['content']['application/json'];
|
type IncomeListResponse = paths['/incomes/list/{id}']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { api } from '@/shared/api/client';
|
|
||||||
|
|
||||||
import type { paths } from '@/shared/api/schema';
|
import type { paths } from '@/shared/api/schema';
|
||||||
|
|
||||||
|
import { api } from '@/shared/api/client';
|
||||||
|
|
||||||
type IncomesListResponse = paths['/incomes/list']['get']['responses']['200']['content']['application/json'];
|
type IncomesListResponse = paths['/incomes/list']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
export async function getIncomeLists() {
|
export async function getIncomeLists() {
|
||||||
|
|||||||
@ -2,10 +2,10 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
|
|
||||||
import { QueryKeys } from '@/shared/api/queryKeys';
|
import { QueryKeys } from '@/shared/api/queryKeys';
|
||||||
|
|
||||||
import { getIncomeList } from '../api/getIncomeList';
|
|
||||||
|
|
||||||
import type { IncomeList } from '../types';
|
import type { IncomeList } from '../types';
|
||||||
|
|
||||||
|
import { getIncomeList } from '../api/getIncomeList';
|
||||||
|
|
||||||
export const useIncomeList = (id: IncomeList['id']) => useQuery({
|
export const useIncomeList = (id: IncomeList['id']) => useQuery({
|
||||||
queryFn: async () => getIncomeList({ id }),
|
queryFn: async () => getIncomeList({ id }),
|
||||||
queryKey: [QueryKeys.IncomesList, id],
|
queryKey: [QueryKeys.IncomesList, id],
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
export type { IncomeList } from './types';
|
export type { AddIncomeListBody, AddIncomeListPath, AddIncomeListSuccessResponse } from './api/addIncomeList';
|
||||||
export { useDeleteIncomesList } from './hooks/useDeleteIncomesList';
|
export type { DeleteIncomeListPath, DeleteIncomeListSuccessResponse } from './api/deleteIncomeList';
|
||||||
export { useAddIncomesList } from './hooks/useAddIncomesList';
|
export type { EditIncomeListBody, EditIncomeListPath, EditIncomeListSuccessResponse } from './api/editIncomeList';
|
||||||
export { useIncomesList } from './hooks/useIncomesList';
|
|
||||||
export { useEditIncomesList } from './hooks/useEditIncomesList';
|
|
||||||
|
|
||||||
|
export { useAddIncomesList } from './hooks/useAddIncomesList';
|
||||||
|
export { useDeleteIncomesList } from './hooks/useDeleteIncomesList';
|
||||||
|
export { useEditIncomesList } from './hooks/useEditIncomesList';
|
||||||
export { useIncomeList } from './hooks/useIncomeList';
|
export { useIncomeList } from './hooks/useIncomeList';
|
||||||
|
export { useIncomesList } from './hooks/useIncomesList';
|
||||||
|
|
||||||
|
export type { IncomeList } from './types';
|
||||||
|
|
||||||
export type { AddIncomeListFormValues } from './ui/AddIncomeListForm/ui/AddIncomeListForm.ui';
|
export type { AddIncomeListFormValues } from './ui/AddIncomeListForm/ui/AddIncomeListForm.ui';
|
||||||
export { default as AddIncomeListForm } from './ui/AddIncomeListForm/ui/AddIncomeListForm.ui';
|
export { default as AddIncomeListForm } from './ui/AddIncomeListForm/ui/AddIncomeListForm.ui';
|
||||||
@ -13,7 +17,3 @@ export { default as DeleteIncomeListForm } from './ui/DeleteIncomeListForm/ui/De
|
|||||||
|
|
||||||
export type { EditIncomeListFormValues } from './ui/EditIncomeListForm/ui/EditIncomeListForm.ui';
|
export type { EditIncomeListFormValues } from './ui/EditIncomeListForm/ui/EditIncomeListForm.ui';
|
||||||
export { default as EditIncomeListForm } from './ui/EditIncomeListForm/ui/EditIncomeListForm.ui';
|
export { default as EditIncomeListForm } from './ui/EditIncomeListForm/ui/EditIncomeListForm.ui';
|
||||||
|
|
||||||
export type { AddIncomeListBody, AddIncomeListPath, AddIncomeListSuccessResponse } from './api/addIncomeList';
|
|
||||||
export type { DeleteIncomeListPath, DeleteIncomeListSuccessResponse } from './api/deleteIncomeList';
|
|
||||||
export type { EditIncomeListBody, EditIncomeListPath, EditIncomeListSuccessResponse } from './api/editIncomeList';
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import AddIncomeListForm from './AddIncomeListForm.ui';
|
|
||||||
|
|
||||||
import type { AddIncomeListFormProps } from './AddIncomeListForm.ui';
|
import type { AddIncomeListFormProps } from './AddIncomeListForm.ui';
|
||||||
|
|
||||||
|
import AddIncomeListForm from './AddIncomeListForm.ui';
|
||||||
|
|
||||||
const renderForm = (props?: Partial<AddIncomeListFormProps>) => {
|
const renderForm = (props?: Partial<AddIncomeListFormProps>) => {
|
||||||
const onSubmit = jest.fn();
|
const onSubmit = jest.fn();
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ const renderForm = (props?: Partial<AddIncomeListFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Добавить');
|
const submitButtonElem = screen.getByText('Добавить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,9 +42,9 @@ describe('Test AddIncomeListForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.type(titleInputElem, 'Название списка');
|
await userEvent.type(titleInputElem, 'Название списка');
|
||||||
|
|||||||
@ -1,31 +1,31 @@
|
|||||||
|
import type {
|
||||||
|
FC,
|
||||||
|
FormEventHandler,
|
||||||
|
FormHTMLAttributes,
|
||||||
|
RefObject,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { Input } from '@/shared/ui/Input';
|
import { Input } from '@/shared/ui/Input';
|
||||||
|
|
||||||
import classes from './AddIncomeListForm.module.css';
|
import classes from './AddIncomeListForm.module.css';
|
||||||
|
|
||||||
import type {
|
|
||||||
FormHTMLAttributes,
|
|
||||||
RefObject,
|
|
||||||
FormEventHandler,
|
|
||||||
FC,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
export type AddIncomeListFormValues = {
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
|
||||||
|
|
||||||
export type AddIncomeListFormProps = BaseFormProps & {
|
export type AddIncomeListFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onSubmit: (data: AddIncomeListFormValues) => Promise<void> | void;
|
onSubmit: (data: AddIncomeListFormValues) => Promise<void> | void;
|
||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AddIncomeListFormValues = {
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const AddIncomeListForm: FC<AddIncomeListFormProps> = (props) => {
|
const AddIncomeListForm: FC<AddIncomeListFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
ref,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -45,9 +45,9 @@ const AddIncomeListForm: FC<AddIncomeListFormProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<h2>Добавить список доходов</h2>
|
<h2>Добавить список доходов</h2>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import DeleteIncomeListForm from './DeleteIncomeListForm.ui';
|
|
||||||
|
|
||||||
import type { DeleteIncomeListFormProps } from './DeleteIncomeListForm.ui';
|
import type { DeleteIncomeListFormProps } from './DeleteIncomeListForm.ui';
|
||||||
|
|
||||||
|
import DeleteIncomeListForm from './DeleteIncomeListForm.ui';
|
||||||
|
|
||||||
const deletingList = {
|
const deletingList = {
|
||||||
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
|
||||||
title: 'Mock list',
|
|
||||||
items: [],
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
|
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
|
items: [],
|
||||||
|
title: 'Mock list',
|
||||||
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<DeleteIncomeListFormProps>) => {
|
const renderForm = (props?: Partial<DeleteIncomeListFormProps>) => {
|
||||||
@ -28,8 +28,8 @@ const renderForm = (props?: Partial<DeleteIncomeListFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Удалить');
|
const submitButtonElem = screen.getByText('Удалить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ describe('Test DeleteIncomeListForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.click(submitButtonElem);
|
await userEvent.click(submitButtonElem);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { IncomeList } from '../../../types';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -6,21 +5,23 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
import type { IncomeList } from '../../../types';
|
||||||
|
|
||||||
export type DeleteIncomeListFormProps = BaseFormProps & {
|
export type DeleteIncomeListFormProps = BaseFormProps & {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
list: IncomeList;
|
||||||
onSubmit?: (list: IncomeList) => Promise<void> | void;
|
onSubmit?: (list: IncomeList) => Promise<void> | void;
|
||||||
ref?: RefObject<HTMLFormElement>;
|
ref?: RefObject<HTMLFormElement>;
|
||||||
list: IncomeList;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
const DeleteIncomeListForm: FC<DeleteIncomeListFormProps> = (props) => {
|
const DeleteIncomeListForm: FC<DeleteIncomeListFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
ref,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
|
||||||
list,
|
list,
|
||||||
|
onSubmit,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ const DeleteIncomeListForm: FC<DeleteIncomeListFormProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form ref={ref} onSubmit={onFormSubmitHandler} {...restProps}>
|
<form onSubmit={onFormSubmitHandler} ref={ref} {...restProps}>
|
||||||
<h2>Удалить список доходов</h2>
|
<h2>Удалить список доходов</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
import EditIncomeListForm from './EditIncomeListForm.ui';
|
|
||||||
|
|
||||||
import type { EditExpensesListFormProps } from './EditIncomeListForm.ui';
|
import type { EditExpensesListFormProps } from './EditIncomeListForm.ui';
|
||||||
|
|
||||||
|
import EditIncomeListForm from './EditIncomeListForm.ui';
|
||||||
|
|
||||||
const editingList = {
|
const editingList = {
|
||||||
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
|
||||||
title: 'Mock list',
|
|
||||||
items: [],
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
|
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
|
items: [],
|
||||||
|
title: 'Mock list',
|
||||||
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderForm = (props?: Partial<EditExpensesListFormProps>) => {
|
const renderForm = (props?: Partial<EditExpensesListFormProps>) => {
|
||||||
@ -29,9 +29,9 @@ const renderForm = (props?: Partial<EditExpensesListFormProps>) => {
|
|||||||
const submitButtonElem = screen.getByText('Сохранить');
|
const submitButtonElem = screen.getByText('Сохранить');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
titleInputElem,
|
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,9 +64,9 @@ describe('Test EditIncomeListForm', () => {
|
|||||||
|
|
||||||
test('should call onSubmit with values', async () => {
|
test('should call onSubmit with values', async () => {
|
||||||
const {
|
const {
|
||||||
titleInputElem,
|
|
||||||
submitButtonElem,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
submitButtonElem,
|
||||||
|
titleInputElem,
|
||||||
} = renderForm();
|
} = renderForm();
|
||||||
|
|
||||||
await userEvent.clear(titleInputElem);
|
await userEvent.clear(titleInputElem);
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import { Input } from '@/shared/ui/Input';
|
|
||||||
|
|
||||||
import classes from './EditIncomeListForm.module.css';
|
|
||||||
|
|
||||||
import type { IncomeList } from '../../../types';
|
|
||||||
import type {
|
import type {
|
||||||
FC,
|
FC,
|
||||||
FormEventHandler,
|
FormEventHandler,
|
||||||
@ -10,25 +5,31 @@ import type {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
|
import { Input } from '@/shared/ui/Input';
|
||||||
|
|
||||||
|
import type { IncomeList } from '../../../types';
|
||||||
|
|
||||||
|
import classes from './EditIncomeListForm.module.css';
|
||||||
|
|
||||||
|
export type EditExpensesListFormProps = BaseFormProps & {
|
||||||
|
disabled?: boolean;
|
||||||
|
list: IncomeList;
|
||||||
|
onSubmit: (data: EditIncomeListFormValues) => Promise<void> | void;
|
||||||
|
ref?: RefObject<HTMLFormElement>;
|
||||||
|
};
|
||||||
|
|
||||||
export type EditIncomeListFormValues = {
|
export type EditIncomeListFormValues = {
|
||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
type BaseFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, 'onSubmit'>;
|
||||||
|
|
||||||
export type EditExpensesListFormProps = BaseFormProps & {
|
|
||||||
disabled?: boolean;
|
|
||||||
onSubmit: (data: EditIncomeListFormValues) => Promise<void> | void;
|
|
||||||
ref?: RefObject<HTMLFormElement>;
|
|
||||||
list: IncomeList;
|
|
||||||
};
|
|
||||||
|
|
||||||
const EditExpensesListForm: FC<EditExpensesListFormProps> = (props) => {
|
const EditExpensesListForm: FC<EditExpensesListFormProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
ref,
|
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
|
||||||
list,
|
list,
|
||||||
|
onSubmit,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -46,9 +47,9 @@ const EditExpensesListForm: FC<EditExpensesListFormProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={ref}
|
|
||||||
className={classes.form}
|
className={classes.form}
|
||||||
onSubmit={onFormSubmitHandler}
|
onSubmit={onFormSubmitHandler}
|
||||||
|
ref={ref}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<h2>Редактировать список доходов</h2>
|
<h2>Редактировать список доходов</h2>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useLogout } from '@/entity/account';
|
import { useLogout } from '@/entity/account';
|
||||||
|
|
||||||
const LogoutButton = () => {
|
const LogoutButton = () => {
|
||||||
const { mutate: logout, isPending } = useLogout();
|
const { isPending, mutate: logout } = useLogout();
|
||||||
|
|
||||||
const onClickHandler = () => {
|
const onClickHandler = () => {
|
||||||
logout();
|
logout();
|
||||||
@ -10,8 +10,8 @@ const LogoutButton = () => {
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
type="button"
|
|
||||||
onClick={onClickHandler}
|
onClick={onClickHandler}
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { type MouseEventHandler, useState } from 'react';
|
import { type MouseEventHandler, useState } from 'react';
|
||||||
|
|
||||||
import { useAddBankCard } from '@/entity/cards';
|
|
||||||
|
|
||||||
import type { AddBankCardFormValues } from '@/entity/cards';
|
import type { AddBankCardFormValues } from '@/entity/cards';
|
||||||
|
|
||||||
|
import { useAddBankCard } from '@/entity/cards';
|
||||||
|
|
||||||
type UseAddBankCardButtonArgs = {
|
type UseAddBankCardButtonArgs = {
|
||||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||||
};
|
};
|
||||||
@ -12,7 +12,7 @@ export const useAddBankCardButton = (args: UseAddBankCardButtonArgs) => {
|
|||||||
const { onClick } = args;
|
const { onClick } = args;
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const { mutateAsync: addBankCardMutation, isPending } = useAddBankCard();
|
const { isPending, mutateAsync: addBankCardMutation } = useAddBankCard();
|
||||||
|
|
||||||
const onCloseModalHandler = () => {
|
const onCloseModalHandler = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@ -31,8 +31,8 @@ export const useAddBankCardButton = (args: UseAddBankCardButtonArgs) => {
|
|||||||
return {
|
return {
|
||||||
isOpen,
|
isOpen,
|
||||||
isPending,
|
isPending,
|
||||||
onCloseModalHandler,
|
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
|
onCloseModalHandler,
|
||||||
onFormSubmitHandler,
|
onFormSubmitHandler,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
|
import { server } from '@mocks/jest/server';
|
||||||
|
import { createReactQueryWrapper } from '@mocks/jest/wrappers';
|
||||||
import { render, screen, within } from '@testing-library/react';
|
import { render, screen, within } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { http, HttpResponse } from 'msw';
|
import { http, HttpResponse } from 'msw';
|
||||||
|
|
||||||
import { server } from '@mocks/jest/server';
|
import type { AddBankCardBody, AddBankCardResponse } from '@/entity/cards';
|
||||||
import { createReactQueryWrapper } from '@mocks/jest/wrappers';
|
|
||||||
|
|
||||||
import AddBankCardButton from './AddBankCardButton.ui';
|
import AddBankCardButton from './AddBankCardButton.ui';
|
||||||
|
|
||||||
import type { AddBankCardBody, AddBankCardResponse } from '@/entity/cards';
|
|
||||||
|
|
||||||
const renderButton = () => {
|
const renderButton = () => {
|
||||||
render(
|
render(
|
||||||
<AddBankCardButton data-testid="test-button" />,
|
<AddBankCardButton data-testid="test-button" />,
|
||||||
@ -44,11 +43,11 @@ const submitHandler = (requestSpy: jest.Mock) => http.post<never, AddBankCardBod
|
|||||||
requestSpy(body);
|
requestSpy(body);
|
||||||
|
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
|
balance: 999,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
name: 'Название карты',
|
name: 'Название карты',
|
||||||
balance: 999,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,37 +1,37 @@
|
|||||||
|
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
||||||
|
|
||||||
import { AddBankCardForm } from '@/entity/cards';
|
import { AddBankCardForm } from '@/entity/cards';
|
||||||
import { Modal } from '@/shared/ui/Modal';
|
import { Modal } from '@/shared/ui/Modal';
|
||||||
|
|
||||||
import { useAddBankCardButton } from '../hooks/useAddBankCardButton';
|
import { useAddBankCardButton } from '../hooks/useAddBankCardButton';
|
||||||
|
|
||||||
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
|
||||||
|
|
||||||
type AddBankCardButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
type AddBankCardButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||||
ref?: Ref<HTMLButtonElement>;
|
ref?: Ref<HTMLButtonElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddBankCardButton: FC<AddBankCardButtonProps> = (props) => {
|
const AddBankCardButton: FC<AddBankCardButtonProps> = (props) => {
|
||||||
const { ref, onClick, ...restProps } = props;
|
const { onClick, ref, ...restProps } = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen,
|
isOpen,
|
||||||
isPending,
|
isPending,
|
||||||
onCloseModalHandler,
|
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
|
onCloseModalHandler,
|
||||||
onFormSubmitHandler,
|
onFormSubmitHandler,
|
||||||
} = useAddBankCardButton({ onClick });
|
} = useAddBankCardButton({ onClick });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
onClick={onClickHandler}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClickHandler}
|
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
Добавить
|
Добавить
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Modal open={isOpen} onClose={onCloseModalHandler}>
|
<Modal onClose={onCloseModalHandler} open={isOpen}>
|
||||||
<AddBankCardForm disabled={isPending} onSubmit={onFormSubmitHandler} />
|
<AddBankCardForm disabled={isPending} onSubmit={onFormSubmitHandler} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useDeleteBankCard } from '@/entity/cards';
|
|
||||||
|
|
||||||
import type { BankCard } from '@/entity/cards';
|
import type { BankCard } from '@/entity/cards';
|
||||||
|
|
||||||
|
import { useDeleteBankCard } from '@/entity/cards';
|
||||||
|
|
||||||
type UseDeleteBankCardButtonArgs = {
|
type UseDeleteBankCardButtonArgs = {
|
||||||
id: BankCard['id'];
|
id: BankCard['id'];
|
||||||
};
|
};
|
||||||
@ -12,7 +12,7 @@ export const useDeleteBankCardButton = (args: UseDeleteBankCardButtonArgs) => {
|
|||||||
const { id } = args;
|
const { id } = args;
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const { mutateAsync: deleteBankCardAsync, isPending } = useDeleteBankCard();
|
const { isPending, mutateAsync: deleteBankCardAsync } = useDeleteBankCard();
|
||||||
|
|
||||||
const onClickHandler = () => {
|
const onClickHandler = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
|
|||||||
@ -1,21 +1,20 @@
|
|||||||
|
import { server } from '@mocks/jest/server';
|
||||||
|
import { createReactQueryWrapper } from '@mocks/jest/wrappers';
|
||||||
import { render, screen, within } from '@testing-library/react';
|
import { render, screen, within } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { http, HttpResponse } from 'msw';
|
import { http, HttpResponse } from 'msw';
|
||||||
|
|
||||||
import { server } from '@mocks/jest/server';
|
|
||||||
import { createReactQueryWrapper } from '@mocks/jest/wrappers';
|
|
||||||
|
|
||||||
import DeleteBankCardButton from './DeleteBankCardButton.ui';
|
|
||||||
|
|
||||||
import type { BankCard } from '@/entity/cards';
|
import type { BankCard } from '@/entity/cards';
|
||||||
import type { DeleteExpenseListPath } from '@/entity/expenses/list';
|
import type { DeleteExpenseListPath } from '@/entity/expenses/list';
|
||||||
|
|
||||||
|
import DeleteBankCardButton from './DeleteBankCardButton.ui';
|
||||||
|
|
||||||
const deletingBankCard: BankCard = {
|
const deletingBankCard: BankCard = {
|
||||||
|
balance: 1000,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
||||||
name: 'Тестовая карта',
|
name: 'Тестовая карта',
|
||||||
balance: 1000,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderButton = () => {
|
const renderButton = () => {
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
|
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
||||||
|
|
||||||
|
import type { BankCard } from '@/entity/cards';
|
||||||
|
|
||||||
import { DeleteBankCardForm } from '@/entity/cards';
|
import { DeleteBankCardForm } from '@/entity/cards';
|
||||||
import { Modal } from '@/shared/ui/Modal';
|
import { Modal } from '@/shared/ui/Modal';
|
||||||
|
|
||||||
import { useDeleteBankCardButton } from '../hooks/useDeleteBankCardButton';
|
import { useDeleteBankCardButton } from '../hooks/useDeleteBankCardButton';
|
||||||
|
|
||||||
import type { BankCard } from '@/entity/cards';
|
|
||||||
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
|
||||||
|
|
||||||
export type DeleteBankCardButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
export type DeleteBankCardButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||||
bankCard: BankCard;
|
bankCard: BankCard;
|
||||||
ref?: Ref<HTMLButtonElement>;
|
ref?: Ref<HTMLButtonElement>;
|
||||||
@ -14,26 +15,26 @@ export type DeleteBankCardButtonProps = ButtonHTMLAttributes<HTMLButtonElement>
|
|||||||
const DeleteBankCardButton: FC<DeleteBankCardButtonProps> = (props) => {
|
const DeleteBankCardButton: FC<DeleteBankCardButtonProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
bankCard,
|
bankCard,
|
||||||
ref,
|
|
||||||
onClick,
|
onClick,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen,
|
isOpen,
|
||||||
isPending,
|
isPending,
|
||||||
onCloseModalHandler,
|
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
|
onCloseModalHandler,
|
||||||
onFormSubmitHandler,
|
onFormSubmitHandler,
|
||||||
} = useDeleteBankCardButton({ id: bankCard.id });
|
} = useDeleteBankCardButton({ id: bankCard.id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button type="button" onClick={onClickHandler} {...restProps}>
|
<button onClick={onClickHandler} type="button" {...restProps}>
|
||||||
Удалить
|
Удалить
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Modal open={isOpen} onClose={onCloseModalHandler}>
|
<Modal onClose={onCloseModalHandler} open={isOpen}>
|
||||||
<DeleteBankCardForm
|
<DeleteBankCardForm
|
||||||
bankCard={bankCard}
|
bankCard={bankCard}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useEditBankCard } from '@/entity/cards';
|
|
||||||
|
|
||||||
import type { BankCard, EditBankCardFormValues } from '@/entity/cards';
|
import type { BankCard, EditBankCardFormValues } from '@/entity/cards';
|
||||||
|
|
||||||
|
import { useEditBankCard } from '@/entity/cards';
|
||||||
|
|
||||||
type UseDeleteBankCardButtonArgs = {
|
type UseDeleteBankCardButtonArgs = {
|
||||||
bankCard: BankCard;
|
bankCard: BankCard;
|
||||||
};
|
};
|
||||||
@ -12,7 +12,7 @@ export const useEditBankCardButton = (args: UseDeleteBankCardButtonArgs) => {
|
|||||||
const { bankCard } = args;
|
const { bankCard } = args;
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const { mutateAsync: editBankCardAsync, isPending } = useEditBankCard();
|
const { isPending, mutateAsync: editBankCardAsync } = useEditBankCard();
|
||||||
|
|
||||||
const onClickHandler = () => {
|
const onClickHandler = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
|
import { server } from '@mocks/jest/server';
|
||||||
|
import { createReactQueryWrapper } from '@mocks/jest/wrappers';
|
||||||
import { render, screen, within } from '@testing-library/react';
|
import { render, screen, within } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { http, HttpResponse } from 'msw';
|
import { http, HttpResponse } from 'msw';
|
||||||
|
|
||||||
import { server } from '@mocks/jest/server';
|
|
||||||
import { createReactQueryWrapper } from '@mocks/jest/wrappers';
|
|
||||||
|
|
||||||
import EditBankCardButton from './EditBankCardButton.ui';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
BankCard,
|
BankCard,
|
||||||
EditBankCardBody,
|
EditBankCardBody,
|
||||||
@ -14,12 +11,14 @@ import type {
|
|||||||
EditBankCardResponse,
|
EditBankCardResponse,
|
||||||
} from '@/entity/cards';
|
} from '@/entity/cards';
|
||||||
|
|
||||||
|
import EditBankCardButton from './EditBankCardButton.ui';
|
||||||
|
|
||||||
const editingBankCard: BankCard = {
|
const editingBankCard: BankCard = {
|
||||||
|
balance: 1000,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
||||||
name: 'Тестовая карта',
|
name: 'Тестовая карта',
|
||||||
balance: 1000,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fillForm = async (data: EditBankCardBody) => {
|
const fillForm = async (data: EditBankCardBody) => {
|
||||||
@ -64,11 +63,11 @@ const submitHandler = (requestSpy: jest.Mock) => http.patch<EditBankCardPath, Ed
|
|||||||
requestSpy(body);
|
requestSpy(body);
|
||||||
|
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
|
balance: 1000,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
id: 'af6da4e1-f24f-4b43-8556-fa885626897d',
|
||||||
name: 'Тестовая карта',
|
name: 'Тестовая карта',
|
||||||
balance: 1000,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
|
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
||||||
|
|
||||||
|
import type { BankCard } from '@/entity/cards';
|
||||||
|
|
||||||
import { EditBankCardForm } from '@/entity/cards';
|
import { EditBankCardForm } from '@/entity/cards';
|
||||||
import { Modal } from '@/shared/ui/Modal';
|
import { Modal } from '@/shared/ui/Modal';
|
||||||
|
|
||||||
import { useEditBankCardButton } from '../hooks/useEditBankCardButton';
|
import { useEditBankCardButton } from '../hooks/useEditBankCardButton';
|
||||||
|
|
||||||
import type { BankCard } from '@/entity/cards';
|
|
||||||
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
|
||||||
|
|
||||||
export type EditBankCardButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
export type EditBankCardButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||||
bankCard: BankCard;
|
bankCard: BankCard;
|
||||||
ref?: Ref<HTMLButtonElement>;
|
ref?: Ref<HTMLButtonElement>;
|
||||||
@ -13,26 +14,26 @@ export type EditBankCardButtonProps = ButtonHTMLAttributes<HTMLButtonElement> &
|
|||||||
const EditBankCardButton: FC<EditBankCardButtonProps> = (props) => {
|
const EditBankCardButton: FC<EditBankCardButtonProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
bankCard,
|
bankCard,
|
||||||
ref,
|
|
||||||
onClick,
|
onClick,
|
||||||
|
ref,
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen,
|
isOpen,
|
||||||
isPending,
|
isPending,
|
||||||
onCloseModalHandler,
|
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
|
onCloseModalHandler,
|
||||||
onFormSubmitHandler,
|
onFormSubmitHandler,
|
||||||
} = useEditBankCardButton({ bankCard });
|
} = useEditBankCardButton({ bankCard });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button type="button" onClick={onClickHandler} {...restProps}>
|
<button onClick={onClickHandler} type="button" {...restProps}>
|
||||||
Редактировать
|
Редактировать
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Modal open={isOpen} onClose={onCloseModalHandler}>
|
<Modal onClose={onCloseModalHandler} open={isOpen}>
|
||||||
<EditBankCardForm
|
<EditBankCardForm
|
||||||
bankCard={bankCard}
|
bankCard={bankCard}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { type MouseEventHandler, useState } from 'react';
|
import { type MouseEventHandler, useState } from 'react';
|
||||||
|
|
||||||
import { useAddCash } from '@/entity/cash';
|
|
||||||
|
|
||||||
import type { AddCashFormValues } from '@/entity/cash';
|
import type { AddCashFormValues } from '@/entity/cash';
|
||||||
|
|
||||||
|
import { useAddCash } from '@/entity/cash';
|
||||||
|
|
||||||
type UseAddCashButtonArgs = {
|
type UseAddCashButtonArgs = {
|
||||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||||
};
|
};
|
||||||
@ -12,7 +12,7 @@ export const useAddCashButton = (args: UseAddCashButtonArgs) => {
|
|||||||
const { onClick } = args;
|
const { onClick } = args;
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const { mutateAsync: addCashMutation, isPending } = useAddCash();
|
const { isPending, mutateAsync: addCashMutation } = useAddCash();
|
||||||
|
|
||||||
const onCloseModalHandler = () => {
|
const onCloseModalHandler = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@ -31,8 +31,8 @@ export const useAddCashButton = (args: UseAddCashButtonArgs) => {
|
|||||||
return {
|
return {
|
||||||
isOpen,
|
isOpen,
|
||||||
isPending,
|
isPending,
|
||||||
onCloseModalHandler,
|
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
|
onCloseModalHandler,
|
||||||
onFormSubmitHandler,
|
onFormSubmitHandler,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
|
import { server } from '@mocks/jest/server';
|
||||||
|
import { createReactQueryWrapper } from '@mocks/jest/wrappers';
|
||||||
import { render, screen, within } from '@testing-library/react';
|
import { render, screen, within } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { http, HttpResponse } from 'msw';
|
import { http, HttpResponse } from 'msw';
|
||||||
|
|
||||||
import { server } from '@mocks/jest/server';
|
import type { AdCashResponse, AddCashBody } from '@/entity/cash';
|
||||||
import { createReactQueryWrapper } from '@mocks/jest/wrappers';
|
|
||||||
|
|
||||||
import AddCashButton from './AddCashButton.ui';
|
import AddCashButton from './AddCashButton.ui';
|
||||||
|
|
||||||
import type { AddCashBody, AdCashResponse } from '@/entity/cash';
|
|
||||||
|
|
||||||
const renderButton = () => {
|
const renderButton = () => {
|
||||||
render(
|
render(
|
||||||
<AddCashButton data-testid="test-button" />,
|
<AddCashButton data-testid="test-button" />,
|
||||||
@ -44,11 +43,11 @@ const submitHandler = (requestSpy: jest.Mock) => http.post<never, AddCashBody, A
|
|||||||
requestSpy(body);
|
requestSpy(body);
|
||||||
|
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
|
balance: 999,
|
||||||
|
createdAt: '2025-08-29T07:57:40.503Z',
|
||||||
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
id: '10cc093c-3875-4d33-a40a-df526266e262',
|
||||||
name: 'Наличные',
|
name: 'Наличные',
|
||||||
balance: 999,
|
|
||||||
updatedAt: '2025-08-29T07:57:40.503Z',
|
updatedAt: '2025-08-29T07:57:40.503Z',
|
||||||
createdAt: '2025-08-29T07:57:40.503Z',
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,37 +1,37 @@
|
|||||||
|
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
||||||
|
|
||||||
import { AddCashForm } from '@/entity/cash';
|
import { AddCashForm } from '@/entity/cash';
|
||||||
import { Modal } from '@/shared/ui/Modal';
|
import { Modal } from '@/shared/ui/Modal';
|
||||||
|
|
||||||
import { useAddCashButton } from '../hooks/useAddCashButton';
|
import { useAddCashButton } from '../hooks/useAddCashButton';
|
||||||
|
|
||||||
import type { ButtonHTMLAttributes, FC, Ref } from 'react';
|
|
||||||
|
|
||||||
type AddCashButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
type AddCashButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||||
ref?: Ref<HTMLButtonElement>;
|
ref?: Ref<HTMLButtonElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddCashButton: FC<AddCashButtonProps> = (props) => {
|
const AddCashButton: FC<AddCashButtonProps> = (props) => {
|
||||||
const { ref, onClick, ...restProps } = props;
|
const { onClick, ref, ...restProps } = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen,
|
isOpen,
|
||||||
isPending,
|
isPending,
|
||||||
onCloseModalHandler,
|
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
|
onCloseModalHandler,
|
||||||
onFormSubmitHandler,
|
onFormSubmitHandler,
|
||||||
} = useAddCashButton({ onClick });
|
} = useAddCashButton({ onClick });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
onClick={onClickHandler}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClickHandler}
|
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
Добавить
|
Добавить
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Modal open={isOpen} onClose={onCloseModalHandler}>
|
<Modal onClose={onCloseModalHandler} open={isOpen}>
|
||||||
<AddCashForm disabled={isPending} onSubmit={onFormSubmitHandler} />
|
<AddCashForm disabled={isPending} onSubmit={onFormSubmitHandler} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useDeleteCash } from '@/entity/cash';
|
|
||||||
|
|
||||||
import type { CashItem } from '@/entity/cash';
|
import type { CashItem } from '@/entity/cash';
|
||||||
|
|
||||||
|
import { useDeleteCash } from '@/entity/cash';
|
||||||
|
|
||||||
type UseDeleteCashButtonArgs = {
|
type UseDeleteCashButtonArgs = {
|
||||||
id: CashItem['id'];
|
id: CashItem['id'];
|
||||||
};
|
};
|
||||||
@ -12,7 +12,7 @@ export const useDeleteCashButton = (args: UseDeleteCashButtonArgs) => {
|
|||||||
const { id } = args;
|
const { id } = args;
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const { mutateAsync: deleteCashAsync, isPending } = useDeleteCash();
|
const { isPending, mutateAsync: deleteCashAsync } = useDeleteCash();
|
||||||
|
|
||||||
const onClickHandler = () => {
|
const onClickHandler = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user