@pplancq/react-template 2.8.16 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/_README.md +57 -52
  3. package/mise.toml +2 -2
  4. package/package.json +28 -27
  5. package/scripts/removeDemo.cjs +105 -66
  6. package/src/README.md +81 -0
  7. package/src/app/App.tsx +18 -0
  8. package/src/{bootstrap.tsx → app/AppHTMLElement.tsx} +2 -2
  9. package/src/app/README.md +69 -0
  10. package/src/{__tests__/bootstraps.test.tsx → app/__tests__/AppHTMLElement.test.tsx} +5 -5
  11. package/src/app/config/serviceContainer.ts +6 -0
  12. package/src/app/providers/Providers/Providers.tsx +18 -0
  13. package/src/{providers → app/providers}/QueryClientProvider/QueryClientProvider.tsx +3 -3
  14. package/src/app/providers/ServiceProvider/ServiceContext.ts +8 -0
  15. package/src/app/providers/ServiceProvider/ServiceProvider.tsx +14 -0
  16. package/src/app/routing/Router.tsx +14 -0
  17. package/src/app/routing/appRoutes.ts +3 -0
  18. package/src/app/routing/routes.tsx +9 -0
  19. package/src/demo/README.md +148 -0
  20. package/src/demo/application/dtos/ArchitecturePrinciple.ts +5 -0
  21. package/src/{types/profileTypes.ts → demo/application/dtos/ProfileFormValues.ts} +1 -1
  22. package/src/demo/application/use-cases/GetArchitecturePrinciplesUseCase.ts +11 -0
  23. package/src/demo/application/use-cases/GetArchitecturePrinciplesUseCaseInterface.ts +5 -0
  24. package/src/demo/application/use-cases/GetDemoUsersUseCase.ts +11 -0
  25. package/src/demo/application/use-cases/GetDemoUsersUseCaseInterface.ts +5 -0
  26. package/src/demo/domain/entities/DemoUser.ts +7 -0
  27. package/src/demo/domain/repositories/ArchitecturePrincipleRepositoryInterface.d.ts +5 -0
  28. package/src/demo/domain/repositories/DemoUserRepositoryInterface.d.ts +5 -0
  29. package/src/demo/infrastructure/repositories/HttpDemoUserRepository.ts +29 -0
  30. package/src/demo/infrastructure/repositories/StaticArchitecturePrincipleRepository.ts +33 -0
  31. package/src/demo/serviceDemo.ts +39 -0
  32. package/src/demo/serviceIdentifiers.ts +6 -0
  33. package/src/demo/ui/components/DemoFooter/DemoFooter.tsx +17 -0
  34. package/src/{components/Footer → demo/ui/components/DemoFooter}/__tests__/Footer.test.tsx +2 -2
  35. package/src/{components/Footer → demo/ui/components/DemoFooter}/__tests__/__snapshots__/Footer.test.tsx.snap +3 -3
  36. package/src/demo/ui/components/DemoFooter/index.ts +1 -0
  37. package/src/demo/ui/components/DemoHeader/DemoHeader.tsx +25 -0
  38. package/src/{components/Header → demo/ui/components/DemoHeader}/__tests__/Header.test.tsx +2 -2
  39. package/src/demo/ui/components/DemoHeader/__tests__/__snapshots__/Header.test.tsx.snap +64 -0
  40. package/src/demo/ui/components/DemoHeader/index.ts +1 -0
  41. package/src/{ui/molecules/Navigation/Navigation.tsx → demo/ui/components/DemoNavigation/DemoNavigation.tsx} +4 -4
  42. package/src/demo/ui/components/DemoNavigation/index.ts +1 -0
  43. package/src/{components → demo/ui/components}/TextInput/TextInput.tsx +3 -3
  44. package/src/{forms → demo/ui/forms}/ProfileForm/ProfileForm.tsx +7 -7
  45. package/src/demo/ui/hooks/useDemoUsersQuery/useDemoUsersQuery.ts +13 -0
  46. package/src/demo/ui/layouts/DemoLayout/DemoLayout.tsx +12 -0
  47. package/src/demo/ui/layouts/MainTemplate/MainTemplate.module.css +21 -0
  48. package/src/{ui/templates → demo/ui/layouts}/MainTemplate/MainTemplate.tsx +7 -7
  49. package/src/demo/ui/pages/DemoHome/DemoHome.tsx +117 -0
  50. package/src/demo/ui/pages/DependencyInjectionDemo/DependencyInjectionDemo.tsx +47 -0
  51. package/src/{pages/Demo → demo/ui/pages}/ReactHookFormDemo/ReactHookFormDemo.tsx +7 -7
  52. package/src/demo/ui/pages/ReactQueryDemo/ReactQueryDemo.module.scss +38 -0
  53. package/src/{pages/Demo → demo/ui/pages}/ReactQueryDemo/ReactQueryDemo.tsx +5 -4
  54. package/src/demo/ui/routing/demoAppRoutes.ts +6 -0
  55. package/src/demo/ui/routing/demoRoutes.tsx +31 -0
  56. package/src/main.ts +2 -2
  57. package/src/shared/README.md +53 -0
  58. package/src/shared/application/README.md +49 -0
  59. package/src/shared/domain/README.md +52 -0
  60. package/src/shared/infrastructure/README.md +51 -0
  61. package/src/{api → shared/infrastructure/http}/fetchApi.ts +1 -1
  62. package/src/shared/ui/README.md +50 -0
  63. package/src/shared/ui/hooks/useService/useService.ts +13 -0
  64. package/tsconfig.json +3 -1
  65. package/vitest.config.mts +5 -1
  66. package/src/api/README.md +0 -65
  67. package/src/api/demoApi.ts +0 -10
  68. package/src/assets/README.md +0 -54
  69. package/src/components/App/App.tsx +0 -18
  70. package/src/components/App/index.ts +0 -1
  71. package/src/components/Footer/Footer.tsx +0 -16
  72. package/src/components/Footer/index.ts +0 -1
  73. package/src/components/Header/Header.tsx +0 -17
  74. package/src/components/Header/__tests__/__snapshots__/Header.test.tsx.snap +0 -37
  75. package/src/components/Header/index.ts +0 -1
  76. package/src/components/README.md +0 -33
  77. package/src/config/README.md +0 -63
  78. package/src/config/index.ts +0 -1
  79. package/src/forms/README.md +0 -142
  80. package/src/hooks/README.md +0 -83
  81. package/src/hooks/api/queryKey.ts +0 -1
  82. package/src/hooks/api/useDemoApi.ts +0 -16
  83. package/src/pages/Demo/Demo.tsx +0 -236
  84. package/src/pages/Demo/ReactHookFormDemo/index.ts +0 -1
  85. package/src/pages/Demo/ReactHookFormDemo/routes.tsx +0 -7
  86. package/src/pages/Demo/ReactQueryDemo/index.ts +0 -1
  87. package/src/pages/Demo/ReactQueryDemo/routes.tsx +0 -7
  88. package/src/pages/Demo/__tests__/Demo.test.tsx +0 -15
  89. package/src/pages/Demo/__tests__/__snapshots__/Demo.test.tsx.snap +0 -557
  90. package/src/pages/Demo/index.ts +0 -1
  91. package/src/pages/Demo/routes.tsx +0 -8
  92. package/src/pages/Layout/Layout.tsx +0 -12
  93. package/src/pages/Layout/index.ts +0 -1
  94. package/src/pages/README.md +0 -91
  95. package/src/pages/UnexpectedError/UnexpectedError.module.css +0 -8
  96. package/src/pages/UnexpectedError/UnexpectedError.tsx +0 -22
  97. package/src/pages/UnexpectedError/index.ts +0 -1
  98. package/src/providers/QueryClientProvider/index.ts +0 -1
  99. package/src/providers/README.md +0 -112
  100. package/src/providers/index.ts +0 -1
  101. package/src/providers/withProviders/index.ts +0 -1
  102. package/src/providers/withProviders/withProviders.tsx +0 -18
  103. package/src/routing/README.md +0 -76
  104. package/src/routing/appRoutes.ts +0 -5
  105. package/src/routing/routerFactory.ts +0 -12
  106. package/src/routing/routes.tsx +0 -13
  107. package/src/types/README.md +0 -62
  108. package/src/types/demoApi.ts +0 -25
  109. package/src/ui/README.md +0 -144
  110. package/src/ui/molecules/Navigation/index.ts +0 -1
  111. package/src/ui/organisms/Footer/Footer.module.css +0 -10
  112. package/src/ui/organisms/Footer/Footer.tsx +0 -10
  113. package/src/ui/organisms/Footer/index.ts +0 -1
  114. package/src/ui/organisms/Header/Header.module.css +0 -17
  115. package/src/ui/organisms/Header/Header.tsx +0 -16
  116. package/src/ui/organisms/Header/index.ts +0 -1
  117. package/src/ui/organisms/MainContent/MainContent.module.css +0 -3
  118. package/src/ui/organisms/MainContent/MainContent.tsx +0 -6
  119. package/src/ui/organisms/MainContent/index.ts +0 -1
  120. package/src/ui/templates/MainTemplate/MainTemplate.module.css +0 -5
  121. package/src/utils/README.md +0 -70
  122. /package/src/{assets → app/assets}/css/index.ts +0 -0
  123. /package/src/{assets → app/assets}/css/mainBody.css +0 -0
  124. /package/src/{assets → app/assets}/css/reset.css +0 -0
  125. /package/src/{config → app/config}/queryClientConfig.ts +0 -0
  126. /package/src/{assets → demo/ui/assets}/images/logo.png +0 -0
  127. /package/src/{assets → demo/ui/assets}/images/templatePlugins.png +0 -0
  128. /package/src/{components/Footer/Footer.module.css → demo/ui/components/DemoFooter/DemoFooter.module.css} +0 -0
  129. /package/src/{components/Header/Header.module.css → demo/ui/components/DemoHeader/DemoHeader.module.css} +0 -0
  130. /package/src/{ui/molecules/Navigation/Navigation.module.css → demo/ui/components/DemoNavigation/DemoNavigation.module.css} +0 -0
  131. /package/src/{ui/atoms → demo/ui/components}/Input/Input.module.scss +0 -0
  132. /package/src/{ui/atoms → demo/ui/components}/Input/Input.tsx +0 -0
  133. /package/src/{ui/atoms → demo/ui/components}/Input/index.ts +0 -0
  134. /package/src/{ui/atoms → demo/ui/components}/Logo/Logo.module.css +0 -0
  135. /package/src/{ui/atoms → demo/ui/components}/Logo/Logo.tsx +0 -0
  136. /package/src/{ui/atoms → demo/ui/components}/Logo/__tests__/Logo.test.tsx +0 -0
  137. /package/src/{ui/atoms → demo/ui/components}/Logo/index.ts +0 -0
  138. /package/src/{ui/atoms → demo/ui/components}/NavLink/NavLink.tsx +0 -0
  139. /package/src/{ui/atoms → demo/ui/components}/NavLink/index.ts +0 -0
  140. /package/src/{components → demo/ui/components}/TextInput/index.ts +0 -0
  141. /package/src/{forms → demo/ui/forms}/ProfileForm/ProfileForm.module.scss +0 -0
  142. /package/src/{forms → demo/ui/forms}/ProfileForm/index.ts +0 -0
  143. /package/src/{ui/templates → demo/ui/layouts}/MainTemplate/index.ts +0 -0
  144. /package/src/{pages/Demo/Demo.module.scss → demo/ui/pages/DemoHome/DemoHome.module.scss} +0 -0
  145. /package/src/{pages/Demo/ReactQueryDemo/ReactQueryDemo.module.scss → demo/ui/pages/DependencyInjectionDemo/DependencyInjectionDemo.module.scss} +0 -0
  146. /package/src/{pages/Demo/ReactHookFormDemo/ReactHookFormDemo.module.css → demo/ui/pages/ReactHookFormDemo/ReactHookFormDemo.module.scss} +0 -0
  147. /package/src/{api → shared/infrastructure/http}/constant.ts +0 -0
  148. /package/src/{types/api.ts → shared/infrastructure/http/fetchApi.type.d.ts} +0 -0
@@ -0,0 +1,64 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Header Component > should render the Header component correctly 1`] = `
4
+ <div>
5
+ <div
6
+ class="container"
7
+ >
8
+ <a
9
+ data-discover="true"
10
+ href="/"
11
+ >
12
+ <img
13
+ alt="logo"
14
+ class="logoStyle"
15
+ src="/src/demo/ui/assets/images/logo.png"
16
+ style="width: 50px;"
17
+ />
18
+ </a>
19
+ <nav>
20
+ <ul
21
+ class="navList"
22
+ >
23
+ <li>
24
+ <a
25
+ aria-current="page"
26
+ class="a active"
27
+ data-discover="true"
28
+ href="/"
29
+ >
30
+ Overview
31
+ </a>
32
+ </li>
33
+ <li>
34
+ <a
35
+ class="a"
36
+ data-discover="true"
37
+ href="/react-query"
38
+ >
39
+ React Query
40
+ </a>
41
+ </li>
42
+ <li>
43
+ <a
44
+ class="a"
45
+ data-discover="true"
46
+ href="/react-hook-form"
47
+ >
48
+ React Hook Form
49
+ </a>
50
+ </li>
51
+ <li>
52
+ <a
53
+ class="a"
54
+ data-discover="true"
55
+ href="/dependency-injection"
56
+ >
57
+ Dependency Injection
58
+ </a>
59
+ </li>
60
+ </ul>
61
+ </nav>
62
+ </div>
63
+ </div>
64
+ `;
@@ -0,0 +1 @@
1
+ export { DemoHeader } from './DemoHeader';
@@ -1,12 +1,12 @@
1
- import { NavLink } from '@Front/ui/atoms/NavLink';
1
+ import { NavLink } from '@Demo/ui/components/NavLink/NavLink';
2
2
  import { useId } from 'react';
3
- import classes from './Navigation.module.css';
3
+ import classes from './DemoNavigation.module.css';
4
4
 
5
- type NavigationProps = {
5
+ type DemoNavigationProps = {
6
6
  links: { label: string; url: string }[];
7
7
  };
8
8
 
9
- export const Navigation = ({ links }: NavigationProps) => {
9
+ export const DemoNavigation = ({ links }: DemoNavigationProps) => {
10
10
  const id = useId();
11
11
 
12
12
  return (
@@ -0,0 +1 @@
1
+ export { DemoNavigation } from './DemoNavigation';
@@ -1,6 +1,6 @@
1
- import { Input } from '@Front/ui/atoms/Input';
2
- import { InputHTMLAttributes } from 'react';
3
- import { FieldError, useFormContext } from 'react-hook-form';
1
+ import { Input } from '@Demo/ui/components/Input/Input';
2
+ import type { InputHTMLAttributes } from 'react';
3
+ import { type FieldError, useFormContext } from 'react-hook-form';
4
4
 
5
5
  interface TextInputProps extends InputHTMLAttributes<HTMLInputElement> {
6
6
  name: string;
@@ -1,12 +1,12 @@
1
- import { TextInput } from '@Front/components/TextInput';
2
- import { ProfileType } from '@Front/types/profileTypes';
1
+ import type { ProfileFormValues } from '@Demo/application/dtos/ProfileFormValues';
2
+ import { TextInput } from '@Demo/ui/components/TextInput/TextInput';
3
3
  import { yupResolver } from '@hookform/resolvers/yup';
4
- import { FormProvider, SubmitHandler, useForm } from 'react-hook-form';
4
+ import { FormProvider, type SubmitHandler, useForm } from 'react-hook-form';
5
5
  import * as yup from 'yup';
6
6
  import classes from './ProfileForm.module.scss';
7
7
 
8
8
  type ProfileFormProps = {
9
- onSubmit: SubmitHandler<ProfileType>;
9
+ onSubmit: SubmitHandler<ProfileFormValues>;
10
10
  resetForm: () => void;
11
11
  };
12
12
 
@@ -17,15 +17,15 @@ const schema = yup.object().shape({
17
17
  });
18
18
 
19
19
  export const ProfileForm = ({ onSubmit, resetForm }: ProfileFormProps) => {
20
- const methods = useForm<ProfileType>({
20
+ const methods = useForm<ProfileFormValues>({
21
21
  resolver: yupResolver(schema),
22
22
  });
23
23
 
24
24
  return (
25
25
  <FormProvider {...methods}>
26
26
  <form onSubmit={methods.handleSubmit(onSubmit)}>
27
- <p>The form is being submited {methods.formState.submitCount} times</p>
28
- <TextInput label="E-mail" type="email" name="mail" placeholder="Exemple@gmail.com" />
27
+ <p>The form has been submitted {methods.formState.submitCount} times.</p>
28
+ <TextInput label="E-mail" type="email" name="mail" placeholder="example@gmail.com" />
29
29
 
30
30
  <TextInput label="First Name" name="firstName" placeholder="First Name" />
31
31
 
@@ -0,0 +1,13 @@
1
+ import type { GetDemoUsersUseCaseInterface } from '@Demo/application/use-cases/GetDemoUsersUseCaseInterface';
2
+ import { DEMO_SERVICES } from '@Demo/serviceIdentifiers';
3
+ import { useService } from '@Shared/ui/hooks/useService/useService';
4
+ import { useQuery } from '@tanstack/react-query';
5
+
6
+ export const useDemoUsersQuery = () => {
7
+ const getDemoUsersUseCase = useService<GetDemoUsersUseCaseInterface>(DEMO_SERVICES.GetDemoUsersUseCase);
8
+
9
+ return useQuery({
10
+ queryKey: ['demo-users'],
11
+ queryFn: () => getDemoUsersUseCase.execute(),
12
+ });
13
+ };
@@ -0,0 +1,12 @@
1
+ import { DemoFooter } from '@Demo/ui/components/DemoFooter/DemoFooter';
2
+ import { DemoHeader } from '@Demo/ui/components/DemoHeader/DemoHeader';
3
+ import { MainTemplate } from '@Demo/ui/layouts/MainTemplate/MainTemplate';
4
+ import { Outlet } from 'react-router';
5
+
6
+ export const DemoLayout = () => {
7
+ return (
8
+ <MainTemplate headerProps={{ leftComponents: <DemoHeader /> }} footerProps={{ footerContent: <DemoFooter /> }}>
9
+ <Outlet />
10
+ </MainTemplate>
11
+ );
12
+ };
@@ -0,0 +1,21 @@
1
+ .mainTemplateStyle {
2
+ display: flex;
3
+ min-height: 100%;
4
+ flex-direction: column;
5
+ }
6
+
7
+ .header {
8
+ display: flex;
9
+ padding: 16px;
10
+ align-items: center;
11
+ justify-content: space-between;
12
+ gap: 16px;
13
+ }
14
+
15
+ .main {
16
+ flex: 1;
17
+ }
18
+
19
+ .footer {
20
+ padding: 16px;
21
+ }
@@ -1,7 +1,4 @@
1
- import { Footer } from '@Front/ui/organisms/Footer';
2
- import { Header } from '@Front/ui/organisms/Header';
3
- import { MainContent } from '@Front/ui/organisms/MainContent';
4
- import { PropsWithChildren, ReactNode } from 'react';
1
+ import type { PropsWithChildren, ReactNode } from 'react';
5
2
 
6
3
  import classes from './MainTemplate.module.css';
7
4
 
@@ -18,9 +15,12 @@ type MainTemplateProps = {
18
15
  export const MainTemplate = ({ headerProps, footerProps, children }: PropsWithChildren<MainTemplateProps>) => {
19
16
  return (
20
17
  <div className={classes.mainTemplateStyle}>
21
- <Header {...headerProps} />
22
- <MainContent>{children}</MainContent>
23
- <Footer {...footerProps} />
18
+ <header className={classes.header}>
19
+ {headerProps.leftComponents}
20
+ {headerProps.rightComponents}
21
+ </header>
22
+ <main className={classes.main}>{children}</main>
23
+ <footer className={classes.footer}>{footerProps.footerContent}</footer>
24
24
  </div>
25
25
  );
26
26
  };
@@ -0,0 +1,117 @@
1
+ import image from '@Demo/ui/assets/images/templatePlugins.png';
2
+ import { demoAppRoutes } from '@Demo/ui/routing/demoAppRoutes';
3
+ import { NavLink } from '@Demo/ui/components/NavLink/NavLink';
4
+ import classes from './DemoHome.module.scss';
5
+
6
+ export const DemoHome = () => {
7
+ return (
8
+ <div className={classes.container}>
9
+ <div className={classes.bigTitleStyle}>
10
+ Welcome to Your React Starter Kit <span>Clean Architecture</span>
11
+ </div>
12
+
13
+ <div className={classes.foo}>Vertical slices, shared kernel, and explicit composition.</div>
14
+
15
+ <div className={classes.paragraphContainer}>
16
+ <h1 className={classes.titleStyle}>Overview</h1>
17
+ <div className={classes.infoSection}>
18
+ <span className={classes.centredText}>
19
+ <p className={classes.paragraphStyle}>
20
+ This template now starts from a <span className={classes.highlight}>Clean Architecture</span> inspired
21
+ structure with a dedicated <span className={classes.boldText}>app</span> bootstrap layer, a{' '}
22
+ <span className={classes.boldText}>shared</span> common layer, and a{' '}
23
+ <span className={classes.boldText}>demo</span> bounded context.
24
+ </p>
25
+ <p className={classes.paragraphStyle}>
26
+ The goal is to help you start from explicit architecture choices while still keeping a runnable example
27
+ project that demonstrates routing, form handling, data fetching, and dependency injection.
28
+ </p>
29
+ </span>
30
+ </div>
31
+ </div>
32
+ <img src={image} alt="Template architecture overview" className={classes.imagePlace} />
33
+
34
+ <div className={classes.paragraphContainer}>
35
+ <h1 className={classes.titleStyle}>Folder Structure</h1>
36
+ <div className={classes.infoSection}>
37
+ <div className={classes.tableWrapper}>
38
+ <table>
39
+ <thead>
40
+ <tr>
41
+ <th>Folder</th>
42
+ <th>Description</th>
43
+ </tr>
44
+ </thead>
45
+ <tbody>
46
+ <tr>
47
+ <td>
48
+ <span className={classes.boldText}>app</span>
49
+ </td>
50
+ <td>Application bootstrap, routing, providers, config, and global assets.</td>
51
+ </tr>
52
+ <tr>
53
+ <td>
54
+ <span className={classes.boldText}>shared</span>
55
+ </td>
56
+ <td>Reusable common code with no demo-specific responsibility.</td>
57
+ </tr>
58
+ <tr>
59
+ <td>
60
+ <span className={classes.boldText}>demo</span>
61
+ </td>
62
+ <td>Example bounded context split into ui, application, domain, and infrastructure.</td>
63
+ </tr>
64
+ </tbody>
65
+ </table>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ <div className={classes.paragraphContainer}>
70
+ <h1 className={classes.titleStyle}>Pedagogical Demos</h1>
71
+ <div className={classes.infoSection}>
72
+ <p className={classes.paragraphStyle}>
73
+ Each page below demonstrates a different aspect of the template. The React Query example resolves its use
74
+ case through Inversify, the React Hook Form example stays focused on UI composition, and the dependency
75
+ injection example makes the layer boundaries explicit.
76
+ </p>
77
+ </div>
78
+
79
+ <div className={classes.infoSection}>
80
+ <p className={classes.paragraphStyle}>
81
+ <span className={classes.boldText}>React Query demo: </span>
82
+ fetches remote users through a repository and a use case.
83
+ </p>
84
+ <div className={classes.demoStyle}>
85
+ <NavLink to={demoAppRoutes.reactQuery} className={classes.a}>
86
+ Run demo
87
+ </NavLink>
88
+ </div>
89
+ </div>
90
+
91
+ <div className={classes.infoSection}>
92
+ <p className={classes.paragraphStyle}>
93
+ <span className={classes.boldText}>React Hook Form demo: </span>
94
+ shows a form-first UI example inside the new bounded context.
95
+ </p>
96
+ <div className={classes.demoStyle}>
97
+ <NavLink to={demoAppRoutes.reactHookForm} className={classes.a}>
98
+ Run demo
99
+ </NavLink>
100
+ </div>
101
+ </div>
102
+
103
+ <div className={classes.infoSection}>
104
+ <p className={classes.paragraphStyle}>
105
+ <span className={classes.boldText}>Dependency injection demo: </span>
106
+ explains how app, shared, and demo collaborate through explicit abstractions.
107
+ </p>
108
+ <div className={classes.demoStyle}>
109
+ <NavLink to={demoAppRoutes.dependencyInjection} className={classes.a}>
110
+ Run demo
111
+ </NavLink>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ );
117
+ };
@@ -0,0 +1,47 @@
1
+ import type { GetArchitecturePrinciplesUseCaseInterface } from '@Demo/application/use-cases/GetArchitecturePrinciplesUseCaseInterface';
2
+ import { DEMO_SERVICES } from '@Demo/serviceIdentifiers';
3
+ import { useService } from '@Shared/ui/hooks/useService/useService';
4
+ import classes from './DependencyInjectionDemo.module.scss';
5
+
6
+ export const DependencyInjectionDemo = () => {
7
+ const getArchitecturePrinciplesUseCase = useService<GetArchitecturePrinciplesUseCaseInterface>(
8
+ DEMO_SERVICES.GetArchitecturePrinciplesUseCase,
9
+ );
10
+ const architecturePrinciples = getArchitecturePrinciplesUseCase.execute();
11
+
12
+ return (
13
+ <div className={classes.container}>
14
+ <h1 className={classes.title}>Dependency Injection</h1>
15
+ <hr className={classes.lineStyle} color="#00008f" />
16
+
17
+ <div className={classes.fieldsetStyle}>
18
+ <p className={classes.paragraph}>
19
+ This page is driven by a use case resolved from the Inversify container. The page knows the use case contract,
20
+ but it does not instantiate infrastructure classes directly.
21
+ </p>
22
+
23
+ <h1 className={classes.dataTitle}>Clean Architecture Principles</h1>
24
+ <div className={classes.tableWrapper}>
25
+ <table>
26
+ <thead>
27
+ <tr>
28
+ <th>Layer</th>
29
+ <th>Principle</th>
30
+ <th>Description</th>
31
+ </tr>
32
+ </thead>
33
+ <tbody>
34
+ {architecturePrinciples.map(principle => (
35
+ <tr key={`${principle.layer}-${principle.title}`}>
36
+ <td>{principle.layer}</td>
37
+ <td>{principle.title}</td>
38
+ <td>{principle.description}</td>
39
+ </tr>
40
+ ))}
41
+ </tbody>
42
+ </table>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ );
47
+ };
@@ -1,13 +1,13 @@
1
- import { ProfileForm } from '@Front/forms/ProfileForm';
2
- import { ProfileType } from '@Front/types/profileTypes';
1
+ import type { ProfileFormValues } from '@Demo/application/dtos/ProfileFormValues';
2
+ import { ProfileForm } from '@Demo/ui/forms/ProfileForm/ProfileForm';
3
3
  import { useState } from 'react';
4
4
 
5
- import classes from './ReactHookFormDemo.module.css';
5
+ import classes from './ReactHookFormDemo.module.scss';
6
6
 
7
7
  export const ReactHookFormDemo = () => {
8
- const [profileData, setProfileData] = useState<ProfileType>();
8
+ const [profileData, setProfileData] = useState<ProfileFormValues>();
9
9
 
10
- const onSubmit = (data: ProfileType) => {
10
+ const onSubmit = (data: ProfileFormValues) => {
11
11
  setProfileData({ ...data });
12
12
  };
13
13
 
@@ -29,13 +29,13 @@ export const ReactHookFormDemo = () => {
29
29
  </p>
30
30
  <div className={classes.profileSectionStyle}>
31
31
  <fieldset>
32
- <legend>Formulaire</legend>
32
+ <legend>Form</legend>
33
33
  <ProfileForm onSubmit={onSubmit} resetForm={resetForm} />
34
34
  </fieldset>
35
35
 
36
36
  {profileData ? (
37
37
  <fieldset>
38
- <legend>Résultat</legend>
38
+ <legend>Result</legend>
39
39
  <p>E-mail: {profileData.mail} </p>
40
40
  <p>First-name: {profileData.firstName} </p>
41
41
  <p>Last-name: {profileData.lastName} </p>
@@ -0,0 +1,38 @@
1
+ .container {
2
+ padding: 32px;
3
+ }
4
+
5
+ .fieldsetStyle {
6
+ margin-top: 24px;
7
+ padding: 16px;
8
+ border: 1px solid black;
9
+ }
10
+
11
+ .title {
12
+ padding-top: 16px;
13
+ font-size: 20px;
14
+ letter-spacing: 4px;
15
+ text-align: center;
16
+ color: #00008f;
17
+ }
18
+
19
+ .tableWrapper {
20
+ overflow: auto;
21
+ }
22
+
23
+ .dataTitle {
24
+ padding-top: 16px;
25
+ padding-bottom: 16px;
26
+ font-size: 20px;
27
+ letter-spacing: 4px;
28
+ text-align: center;
29
+ color: #00008f;
30
+ }
31
+
32
+ .lineStyle {
33
+ width: 20%;
34
+ }
35
+
36
+ .paragraph {
37
+ padding-top: 16px;
38
+ }
@@ -1,8 +1,8 @@
1
- import { useDemoApi } from '@Front/hooks/api/useDemoApi';
1
+ import { useDemoUsersQuery } from '@Demo/ui/hooks/useDemoUsersQuery/useDemoUsersQuery';
2
2
  import classes from './ReactQueryDemo.module.scss';
3
3
 
4
4
  export const ReactQueryDemo = () => {
5
- const { result, isLoading, isError } = useDemoApi();
5
+ const { data = [], isLoading, isError } = useDemoUsersQuery();
6
6
 
7
7
  if (isError) {
8
8
  return <div>Error fetching data</div>;
@@ -16,7 +16,8 @@ export const ReactQueryDemo = () => {
16
16
  <div className={classes.fieldsetStyle}>
17
17
  <p className={classes.paragraph}>
18
18
  React Query is a popular library for data management in React applications. It simplifies data query
19
- management, caching, data invalidation, and loading state handling.
19
+ management, caching, data invalidation, and loading state handling. In this template the query calls a use
20
+ case resolved through Inversify, which keeps the UI layer independent from the infrastructure adapter.
20
21
  </p>
21
22
  {isLoading ? (
22
23
  <h1 className={classes.dataTitle}>Loading...</h1>
@@ -35,7 +36,7 @@ export const ReactQueryDemo = () => {
35
36
  </tr>
36
37
  </thead>
37
38
  <tbody>
38
- {result?.map(eachResult => (
39
+ {data.map(eachResult => (
39
40
  <tr key={eachResult.id}>
40
41
  <td>{eachResult.id}</td>
41
42
  <td>{eachResult.name}</td>
@@ -0,0 +1,6 @@
1
+ export const demoAppRoutes = Object.freeze({
2
+ home: '/',
3
+ reactQuery: '/react-query',
4
+ reactHookForm: '/react-hook-form',
5
+ dependencyInjection: '/dependency-injection',
6
+ });
@@ -0,0 +1,31 @@
1
+ import { demoAppRoutes } from '@Demo/ui/routing/demoAppRoutes';
2
+ import { DemoLayout } from '@Demo/ui/layouts/DemoLayout/DemoLayout';
3
+ import { DemoHome } from '@Demo/ui/pages/DemoHome/DemoHome';
4
+ import { DependencyInjectionDemo } from '@Demo/ui/pages/DependencyInjectionDemo/DependencyInjectionDemo';
5
+ import { ReactHookFormDemo } from '@Demo/ui/pages/ReactHookFormDemo/ReactHookFormDemo';
6
+ import { ReactQueryDemo } from '@Demo/ui/pages/ReactQueryDemo/ReactQueryDemo';
7
+ import type { RouteObject } from 'react-router';
8
+
9
+ const toRelativePath = (path: string) => path.replace(/^\//, '');
10
+
11
+ export const demoRoutes: RouteObject = {
12
+ element: <DemoLayout />,
13
+ children: [
14
+ {
15
+ index: true,
16
+ element: <DemoHome />,
17
+ },
18
+ {
19
+ path: toRelativePath(demoAppRoutes.reactQuery),
20
+ element: <ReactQueryDemo />,
21
+ },
22
+ {
23
+ path: toRelativePath(demoAppRoutes.reactHookForm),
24
+ element: <ReactHookFormDemo />,
25
+ },
26
+ {
27
+ path: toRelativePath(demoAppRoutes.dependencyInjection),
28
+ element: <DependencyInjectionDemo />,
29
+ },
30
+ ],
31
+ };
package/src/main.ts CHANGED
@@ -1,4 +1,4 @@
1
- import AppReact from './bootstrap';
1
+ import AppHTMLElement from '@App/AppHTMLElement';
2
2
 
3
3
  if (import.meta.env.FRONT_MOCK_ENABLE === 'true') {
4
4
  const { worker } = await import('@Mocks/browser');
@@ -7,4 +7,4 @@ if (import.meta.env.FRONT_MOCK_ENABLE === 'true') {
7
7
  });
8
8
  }
9
9
 
10
- customElements.define('app-react', AppReact);
10
+ customElements.define('app-react', AppHTMLElement);
@@ -0,0 +1,53 @@
1
+ # `shared`
2
+
3
+ The `shared` folder is the **Shared Kernel** of the application — a DDD term for a small, deliberately
4
+ stable set of code that multiple bounded contexts genuinely need.
5
+
6
+ Path alias: `@Shared/*`
7
+
8
+ ## What the Shared Kernel is (and is not)
9
+
10
+ In Domain-Driven Design, a **Shared Kernel** is a portion of the domain model that two or more bounded contexts
11
+ explicitly agree to share. It is held to a higher standard than context-specific code:
12
+
13
+ - **Small by intention.** Every addition to `shared` is available to every context. A mistake here propagates
14
+ everywhere. Err on the side of keeping code inside its bounded context.
15
+ - **Stable by discipline.** Changing `shared` can affect all contexts simultaneously. Any modification should
16
+ be consciously reviewed.
17
+ - **Agnostic by design.** `shared` must contain no feature logic specific to a bounded context. If a file
18
+ references a demo concept, it belongs in `demo`, not here.
19
+ - **Direction is one-way.** Bounded contexts may import from `shared`. `shared` must never import from a
20
+ bounded context (`@Demo/*`, etc.).
21
+
22
+ ## What belongs here
23
+
24
+ - Cross-context contracts and types
25
+ - Generic infrastructure helpers (e.g., HTTP client wrappers)
26
+ - Common application-level abstractions
27
+ - Shared UI utilities that are not tied to any specific context experience
28
+
29
+ ## What should not go here
30
+
31
+ - Demo pages, forms, layouts, or visual components
32
+ - Feature-specific services that belong inside a bounded context
33
+ - Convenience helpers without clear ownership or a concrete need to share them
34
+
35
+ ## Folder map
36
+
37
+ | Sub-folder | Role |
38
+ | ----------------------------------------------- | ----------------------------------------------------------------- |
39
+ | [`domain/`](./domain/README.md) | Shared pure contracts, types, and abstractions. Zero dependencies |
40
+ | [`application/`](./application/README.md) | Shared helpers for orchestration — must not depend on infra or UI |
41
+ | [`infrastructure/`](./infrastructure/README.md) | Shared technical adapters — may depend on external libraries |
42
+ | [`ui/`](./ui/README.md) | Shared React hooks and components — must not depend on infra |
43
+
44
+ ## Before you add to `shared`
45
+
46
+ Ask yourself:
47
+
48
+ 1. Does more than one bounded context genuinely need this?
49
+ 2. Is it stable enough to be shared without frequent modification?
50
+ 3. Does it belong to one of the four Clean Architecture layers?
51
+
52
+ If the answer to any of these is "not sure", keep it in its bounded context first and promote it to `shared`
53
+ only when the need is proven.
@@ -0,0 +1,49 @@
1
+ # `shared/application`
2
+
3
+ The `shared/application` folder contains the **application layer** of the shared kernel — code that
4
+ orchestrates the domain and defines the contracts that infrastructure must fulfill, without doing either
5
+ of those jobs itself.
6
+
7
+ ## What the application layer represents
8
+
9
+ In Clean Architecture, the **application layer** sits between the domain and the outside world. Its job is to:
10
+
11
+ 1. **Orchestrate use cases.** A use case is a single, named, testable business operation (e.g., "Get the list
12
+ of demo users"). It calls domain objects and repository interfaces; it does not contain business rules
13
+ itself — it delegates those to the domain.
14
+ 2. **Define infrastructure contracts.** Use cases depend on repository interfaces that are often declared in
15
+ the domain, but the application layer adds its own interface contracts for services (e.g., caching helpers,
16
+ event buses).
17
+ 3. **Carry data across layer boundaries via DTOs.** A **Data Transfer Object (DTO)** is a plain, typed
18
+ structure that carries data from one layer to another without exposing internal domain objects.
19
+
20
+ ## Dependency rules
21
+
22
+ ```
23
+ shared/application
24
+ ✅ can import from: shared/domain
25
+ ❌ must NOT import from: shared/infrastructure
26
+ ❌ must NOT import from: shared/ui
27
+ ❌ must NOT import from: any bounded context
28
+ ```
29
+
30
+ If application code needs something from infrastructure, it should define an interface that infrastructure
31
+ implements (Dependency Inversion).
32
+
33
+ ## What you can put here
34
+
35
+ - Shared use-case helper base classes or abstract factories
36
+ - Cross-context DTO types that carry data across layer boundaries
37
+ - Common abstractions used by multiple application layers (e.g., a `Result<T, E>` type)
38
+
39
+ ## What you should not put here
40
+
41
+ - Feature-specific use cases (those belong in their bounded context's `application` folder)
42
+ - React components, hooks, or JSX
43
+ - Infrastructure implementations
44
+ - Domain entities or business rules
45
+
46
+ ## Rule of thumb
47
+
48
+ If the code describes _what the application does_ (not _how it stores data_ or _how it looks_) and at least
49
+ two bounded contexts could plausibly depend on it, this folder is the right place.