Skip to content

Commit 909871d

Browse files
committed
refactor: split components
1 parent 7c323d9 commit 909871d

26 files changed

+2968
-2454
lines changed

.dependency-cruiser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ module.exports = {
7575
/* Which modules not to follow further when encountered */
7676
doNotFollow: {
7777
/* path: an array of regular expressions in strings to match against */
78-
path: ['node_modules']
78+
path: ['node_modules', 'spec']
7979
},
8080

8181
/* Which modules to exclude */

app/_components/CreateCharacterForm.tsx renamed to app/_features/CreateCharacter/CreateCharacterForm.tsx

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,33 @@
11
'use client';
22

3-
import React, {FormEvent, useState} from 'react';
4-
import {useCreateCharacterController} from "@/app/_hooks/useCreateCharacterController";
3+
import React from 'react';
54
import {Button, Flex, Text} from '@radix-ui/themes';
5+
import {useCreateCharacterForm} from "@/app/_features/CreateCharacter/useCreateCharacterForm";
6+
import {CreateCharacterFormViewModel} from "@/src/client/presentation/viewModels/CreateCharacterFormViewModel";
67

7-
const CreateCharacterForm: React.FC = () => {
8-
const {createCharacter} = useCreateCharacterController();
8+
interface CreateCharacterFormProps {
9+
viewModel: CreateCharacterFormViewModel;
10+
}
911

10-
const [name, setName] = useState('');
11-
const [species, setSpecies] = useState('');
12-
const [homeworld, setHomeworld] = useState('');
13-
const [isLoading, setIsLoading] = useState(false);
14-
15-
const handleSubmit = async (e: FormEvent) => {
16-
setIsLoading(true);
17-
e.preventDefault();
18-
createCharacter({name, species, homeworld});
19-
setName('');
20-
setSpecies('');
21-
setHomeworld('');
22-
setIsLoading(false);
23-
};
12+
const CreateCharacterForm: React.FC<CreateCharacterFormProps> = ({viewModel}: CreateCharacterFormProps) => {
13+
const {
14+
name,
15+
species,
16+
homeworld,
17+
isLoading,
18+
setName,
19+
setSpecies,
20+
setHomeworld,
21+
handleSubmit
22+
} = useCreateCharacterForm(viewModel);
2423

2524
return (
2625
<form onSubmit={handleSubmit}>
2726
<Flex direction="row" gap="3">
2827
<input
2928
className="Input"
3029
id="name"
31-
placeholder="Enter character name"
30+
placeholder={viewModel.name.placeholder}
3231
value={name}
3332
onChange={(e) => setName(e.target.value)}
3433
required
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import CreateCharacterForm from "@/app/_features/CreateCharacter/CreateCharacterForm";
2+
import {CreateCharacterFormViewModel} from "@/src/client/presentation/viewModels/CreateCharacterFormViewModel";
3+
4+
const CreateCharacterFormContainer = () => {
5+
const viewModel: CreateCharacterFormViewModel = {
6+
name: {
7+
initialValue: '',
8+
placeholder: 'Enter character name'
9+
},
10+
species: {
11+
initialValue: '',
12+
placeholder: 'Enter character species'
13+
},
14+
homeworld: {
15+
initialValue: '',
16+
placeholder: 'Enter character homeworld'
17+
},
18+
button: {
19+
text: '+ Add character'
20+
},
21+
isLoading: {
22+
initialValue: false
23+
}
24+
};
25+
26+
return (
27+
<CreateCharacterForm viewModel={viewModel}/>
28+
);
29+
};
30+
31+
export default CreateCharacterFormContainer;

app/_hooks/useCreateCharacterController.tsx renamed to app/_features/CreateCharacter/useCreateCharacterForm.tsx

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,15 @@
1-
import {useMutation, useQueryClient} from "@tanstack/react-query";
1+
import {FormEvent, useState} from "react";
2+
import {CreateCharacterFormViewModel} from "@/src/client/presentation/viewModels/CreateCharacterFormViewModel";
23
import {useDependency} from "@/app/_hooks/useDependency";
4+
import {useMutation, useQueryClient} from "@tanstack/react-query";
35
import {CharacterViewModel} from "@/src/client/presentation/viewModels/CharacterViewModel";
6+
import {CharacterToCreateDTO} from "@/src/client/application/ports/ICharacterRepository";
47

5-
interface CharacterToCreateDTO {
6-
name: string;
7-
species: string;
8-
homeworld: string;
9-
}
10-
11-
interface IUseCreateCharacterController {
12-
createCharacter: (characterToCreate: CharacterToCreateDTO) => void;
13-
}
14-
15-
export const useCreateCharacterController = (): IUseCreateCharacterController => {
8+
export const useCreateCharacterForm = (viewModel: CreateCharacterFormViewModel) => {
169
const controller = useDependency('CREATE_CHARACTER_CONTROLLER');
1710
const queryClient = useQueryClient();
1811

19-
const mutationResult = useMutation({
12+
const {mutate: createCharacter} = useMutation({
2013
mutationFn: (characterToCreate: CharacterToCreateDTO) => controller.createCharacter(characterToCreate),
2114
onMutate: async (characterToCreate) => {
2215
const previousCharacters = queryClient.getQueryData<{ characters: CharacterViewModel[] }>(['characters']);
@@ -48,7 +41,29 @@ export const useCreateCharacterController = (): IUseCreateCharacterController =>
4841
},
4942
});
5043

44+
const [name, setName] = useState(viewModel.name.initialValue);
45+
const [species, setSpecies] = useState(viewModel.species.initialValue);
46+
const [homeworld, setHomeworld] = useState(viewModel.homeworld.initialValue);
47+
const [isLoading, setIsLoading] = useState(viewModel.isLoading.initialValue);
48+
49+
const handleSubmit = async (e: FormEvent) => {
50+
setIsLoading(true);
51+
e.preventDefault();
52+
createCharacter({name, species, homeworld});
53+
setName('');
54+
setSpecies('');
55+
setHomeworld('');
56+
setIsLoading(false);
57+
};
58+
5159
return {
52-
createCharacter: mutationResult.mutate,
60+
name,
61+
species,
62+
homeworld,
63+
isLoading,
64+
setName,
65+
setSpecies,
66+
setHomeworld,
67+
handleSubmit
5368
};
54-
};
69+
}

app/_components/CharactersList.spec.tsx renamed to app/_features/GetCharacters/CharactersList.spec.tsx

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import {render} from "@testing-library/react";
2-
import CharactersList from "@/app/_components/CharactersList";
2+
import CharactersList from "@/app/_features/GetCharacters/CharactersList";
33
import {DependencyProvider} from "@/app/_providers/DependencyProvider";
44
import {ReactQueryClientProvider} from "@/app/_providers/ReactQueryClientProvider";
5-
import {GetCharactersViewModel} from "@/src/client/presentation/viewModels/GetCharactersViewModel";
65
import {Container, createContainer} from "@evyweb/ioctopus";
76
import {GetCharactersUseCase} from "@/src/client/application/usecases/GetCharacters/GetCharactersUseCase";
87
import {GetCharactersController} from "@/src/client/presentation/controllers/GetCharacters/GetCharactersController";
98
import {DI_SYMBOLS} from "@/src/client/DependencyInjection";
109
import {InMemoryCharacterRepository} from "@/src/client/specs/utils/fakes/InMemoryCharacterRepository";
10+
import {CharactersListViewModel} from "@/src/client/presentation/viewModels/CharactersListViewModel";
1111

1212
describe('CharactersList', () => {
1313
let container: Container;
@@ -22,31 +22,35 @@ describe('CharactersList', () => {
2222
describe('When the characters list is displayed', () => {
2323
it('should have all initially given characters', async () => {
2424
// Arrange
25-
const initialCharacters = {
26-
characters: [
27-
{
28-
id: '1',
29-
name: 'Luke Skywalker',
30-
description: 'Human from Tatooine',
31-
loadedFrom: 'Luke Test data',
32-
},
33-
{
34-
id: '2',
35-
name: 'Leia Organa',
36-
description: 'Human from Alderaan',
37-
loadedFrom: 'Leia Test data',
38-
},
39-
{
40-
id: '3',
41-
name: 'Han Solo',
42-
description: 'Human from Corellia',
43-
loadedFrom: 'Han Test data',
44-
},
45-
]
25+
const viewModel: CharactersListViewModel = {
26+
loadingMessage: 'Loading characters...',
27+
errorMessage: 'An error occurred while loading characters',
28+
initialData: {
29+
characters: [
30+
{
31+
id: '1',
32+
name: 'Luke Skywalker',
33+
description: 'Human from Tatooine',
34+
loadedFrom: 'Luke Test data',
35+
},
36+
{
37+
id: '2',
38+
name: 'Leia Organa',
39+
description: 'Human from Alderaan',
40+
loadedFrom: 'Leia Test data',
41+
},
42+
{
43+
id: '3',
44+
name: 'Han Solo',
45+
description: 'Human from Corellia',
46+
loadedFrom: 'Han Test data',
47+
},
48+
]
49+
}
4650
};
4751

4852
// Act
49-
const {findByText} = renderCharacterList(initialCharacters, container);
53+
const {findByText} = renderCharacterList(viewModel, container);
5054

5155
// Assert
5256
expect(await findByText('Luke Skywalker')).toBeInTheDocument();
@@ -63,11 +67,11 @@ describe('CharactersList', () => {
6367
});
6468
});
6569

66-
function renderCharacterList(initialCharacters: GetCharactersViewModel, container: Container) {
70+
function renderCharacterList(viewModel: CharactersListViewModel, container: Container) {
6771
return render(
6872
<DependencyProvider customContainer={container}>
6973
<ReactQueryClientProvider>
70-
<CharactersList initialData={initialCharacters}/>
74+
<CharactersList viewModel={viewModel}/>
7175
</ReactQueryClientProvider>
7276
</DependencyProvider>
7377
);

app/_components/CharactersList.tsx renamed to app/_features/GetCharacters/CharactersList.tsx

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,42 @@
11
'use client';
22

3-
import {useAutoAnimate} from '@formkit/auto-animate/react';
4-
import {useGetCharactersController} from "@/app/_hooks/useGetCharactersController";
5-
import {GetCharactersViewModel} from "@/src/client/presentation/viewModels/GetCharactersViewModel";
63
import {Avatar, Box, Card, Flex, Text} from '@radix-ui/themes';
74
import {PersonIcon} from '@radix-ui/react-icons';
5+
import {useCharactersList} from "@/app/_features/GetCharacters/useCharactersList";
6+
import {CharactersListViewModel} from "@/src/client/presentation/viewModels/CharactersListViewModel";
7+
import {CharacterViewModel} from "@/src/client/presentation/viewModels/CharacterViewModel";
88

99
interface CharactersListProps {
10-
initialData: GetCharactersViewModel
10+
viewModel: CharactersListViewModel
1111
}
1212

13-
const CharactersList = ({initialData}: CharactersListProps) => {
14-
const {data, isLoading, isError} = useGetCharactersController(initialData);
15-
const [animationParent] = useAutoAnimate();
13+
const CharactersList = ({viewModel}: CharactersListProps) => {
14+
const {isLoading, isError, characters, animationParent} = useCharactersList(viewModel);
1615

17-
if (isLoading) return <Text size="3">Loading...</Text>;
18-
if (isError) return <Text size="3" color="red">Error fetching characters</Text>;
16+
if (isLoading) return <Text size="3">{viewModel.loadingMessage}</Text>;
17+
if (isError) return <Text size="3" color="red">{viewModel.errorMessage}</Text>;
1918

2019
return (
2120
<Flex direction="column" gap="2" ref={animationParent}>
22-
{data?.characters.map((character: {
23-
id: string;
24-
name: string;
25-
description: string;
26-
loadedFrom: string;
27-
}) => (
21+
{characters.map((character: CharacterViewModel) => (
2822
<Card
2923
key={character.id}
3024
size="1">
3125
<Flex gap="3" align="center">
3226
<Avatar
3327
size="3"
3428
fallback={<PersonIcon/>}
35-
style={{
36-
backgroundColor: 'var(--color-background-translucent)',
37-
}}
3829
/>
3930
<Box width="100%">
4031
<Flex align="center" justify="between">
41-
<div>
32+
<Box>
4233
<Text as="div" size="3" weight="bold" color="iris">
4334
{character.name}
4435
</Text>
4536
<Text as="div" size="2" color="gray">
4637
{character.description}
4738
</Text>
48-
</div>
39+
</Box>
4940
<Text as="div" size="2" color="plum">
5041
{character.loadedFrom}
5142
</Text>

app/_components/CharactersListContainer.tsx renamed to app/_features/GetCharacters/CharactersListContainer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import CharactersList from "@/app/_components/CharactersList";
1+
import CharactersList from "@/app/_features/GetCharacters/CharactersList";
22
import {inject} from "@/src/server/DependencyInjection";
33

44
const CharactersListContainer = async () => {
55
const controller = inject('LOAD_CHARACTERS_CONTROLLER');
66
const viewModel = await controller.loadCharacters();
77

88
return (
9-
<CharactersList initialData={viewModel}/>
9+
<CharactersList viewModel={viewModel}/>
1010
);
1111
};
1212

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {useAutoAnimate} from "@formkit/auto-animate/react";
2+
import {CharactersListViewModel} from "@/src/client/presentation/viewModels/CharactersListViewModel";
3+
import {useDependency} from "@/app/_hooks/useDependency";
4+
import {useQuery} from "@tanstack/react-query";
5+
6+
export const useCharactersList = (viewModel: CharactersListViewModel) => {
7+
const controller = useDependency('GET_CHARACTERS_CONTROLLER');
8+
9+
const {isLoading, isError, data} = useQuery({
10+
queryKey: ['characters'],
11+
queryFn: () => controller.getCharacters(),
12+
initialData: viewModel.initialData
13+
});
14+
15+
const [animationParent] = useAutoAnimate();
16+
17+
return {
18+
characters: data?.characters || [],
19+
isLoading,
20+
isError,
21+
animationParent
22+
};
23+
}

app/_hooks/useGetCharactersController.tsx

Lines changed: 0 additions & 25 deletions
This file was deleted.

app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {DependencyProvider} from "@/app/_providers/DependencyProvider";
66
import {ReactNode} from "react";
77
import {ReactQueryClientProvider} from "@/app/_providers/ReactQueryClientProvider";
88
import {Container, Theme} from "@radix-ui/themes";
9-
import Background from "@/app/_components/Background";
9+
import Background from "@/app/_components/Background/Background";
1010

1111
const geistSans = localFont({
1212
src: "./fonts/GeistVF.woff",

0 commit comments

Comments
 (0)