Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IFMD-54] feat(users): apply vertical slice architecture to users #24

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/[locale]/users-provider/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import UsersProviderPage from "@/src/ui/features/users/views/users-page/users-provider-page";
import UsersProviderPage from "@/src/users/presentation/pages/users-with-provider/users-with-provider";

export default UsersProviderPage;
2 changes: 1 addition & 1 deletion app/[locale]/users/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import UsersPage from "@/src/ui/features/users/views/users-page/users-page";
import UsersPage from "@/src/users/presentation/pages/users/users";

export default UsersPage;
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ import { useBreakpointsMatch } from "@front_web_mrmilu/hooks";

interface Props {
title: string;
subtitle: string;
subtitle?: string;
}

export default function PostsPageTitle({ title, subtitle }: Props) {
export default function PageTitle({ title, subtitle }: Props) {
const { mdAndUp } = useBreakpointsMatch();
return mdAndUp ? (
<div>
<h2>{title}</h2>
<br />
<h4>{subtitle}</h4>
{subtitle && (
<>
<br />
<h4>{subtitle}</h4>
</>
)}
</div>
) : (
<></>
Expand Down
4 changes: 2 additions & 2 deletions src/posts/presentation/pages/posts/posts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { TYPES } from "@/src/core/app/ioc/__generated__/types";
import type { Post } from "@/src/posts/domain/models/post";
import { notFound } from "next/navigation";
import { getTranslations } from "next-intl/server";
import PostsPageTitle from "@/src/posts/presentation/components/posts-page-title";
import PageTitle from "@/src/common/presentation/components/page-title";

export default async function PostsPage() {
let posts: Array<Post> = [];
Expand All @@ -21,7 +21,7 @@ export default async function PostsPage() {

return (
<div className={css.wrapper}>
<PostsPageTitle title={t("title")} subtitle={t("subtitle")} />
<PageTitle title={t("title")} subtitle={t("subtitle")} />
{posts.map((post, idx) => (
<SimpleCard key={`${post.id}_${idx}`} title={post.title} subtitle={post.body} />
))}
Expand Down
1 change: 1 addition & 0 deletions src/ui/@types/next-intl.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
interface Messages {
home: typeof import("../i18n/messages/en/home.json");
posts: typeof import("../i18n/messages/en/posts.json");
users: typeof import("../i18n/messages/en/users.json");
}
declare interface IntlMessages extends Messages {}
2 changes: 1 addition & 1 deletion src/ui/components/base-layout/base-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const BaseLayout = ({ children }: PropsWithChildren) => {
<nav className={css.nav}>
<ul className={css.ul}>
<ListElement href="/" label="home" />
<ListElement href="/users_provider" label="users (with zustand provider)" />
<ListElement href="/users-provider" label="users (with zustand provider)" />
<ListElement href="/users" label="users" />
<ListElement href="/create-post" label="create post" />
<ListElement href="/posts" label="list post" />
Expand Down
10 changes: 5 additions & 5 deletions src/ui/features/misc/containers/login-button/login-button.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
"use client";
import { Button } from "@/src/ui/components/button/button";
import { useUserProvider } from "@/src/ui/providers/user.provider";
import { useUserStore } from "@/src/users/application/stores/user-store";
import React, { useEffect } from "react";
import { LoggingModal } from "@/src/ui/components/logging-modal/logging-modal";
import { CookieUtils } from "@front_web_mrmilu/utils";
import { useUiProvider } from "@/src/ui/providers/ui.provider";
import { useSearchParams } from "next/navigation";

export default function LoginButton() {
const setLogged = useUserProvider((state) => state.setLogged);
const login = useUserProvider((state) => state.login);
const logout = useUserProvider((state) => state.logout);
const isUserLogged = useUserProvider((state) => state.logged);
const setLogged = useUserStore((state) => state.setLogged);
const login = useUserStore((state) => state.login);
const logout = useUserStore((state) => state.logout);
const isUserLogged = useUserStore((state) => state.logged);
const showModal = useUiProvider((state) => state.showModal);
const params = useSearchParams();
const protectedRouteAccessAttempt = params?.get("protectedRouteAccessAttempt");
Expand Down
6 changes: 0 additions & 6 deletions src/ui/features/users/state/users-list.provider.ts

This file was deleted.

This file was deleted.

19 changes: 0 additions & 19 deletions src/ui/features/users/views/users-page/users-page.css.ts

This file was deleted.

22 changes: 0 additions & 22 deletions src/ui/features/users/views/users-page/users-page.tsx

This file was deleted.

23 changes: 0 additions & 23 deletions src/ui/features/users/views/users-page/users-provider-page.tsx

This file was deleted.

17 changes: 0 additions & 17 deletions src/ui/features/users/views/users-page/users-provider-wrapper.tsx

This file was deleted.

3 changes: 2 additions & 1 deletion src/ui/i18n/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export default getRequestConfig(async ({ locale }) => {
return {
messages: {
home: (await import(`./messages/${locale}/home.json`)).default,
posts: (await import(`./messages/${locale}/posts.json`)).default
posts: (await import(`./messages/${locale}/posts.json`)).default,
users: (await import(`./messages/${locale}/users.json`)).default
}
};
});
4 changes: 4 additions & 0 deletions src/ui/i18n/messages/en/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Users SSR Page",
"withProviderTitle": "Users with provider SSR page"
}
4 changes: 4 additions & 0 deletions src/ui/i18n/messages/es/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Página de Usuarios SSR",
"withProviderTitle": "Página de Usuarios con provider SSR"
}
23 changes: 23 additions & 0 deletions src/users/application/providers/user-list-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";
import type { PropsWithChildren } from "react";
import { User } from "@/src/users/domain/models/user";
import type { ConstructorType } from "@/src/common/interfaces/constructor-type";

import { createProvider } from "@/src/common/utils/zustand";
import type { UsersListStateViewModel } from "@/src/users/presentation/view-models/users-list-state";

export const useUsersListProvider = createProvider<UsersListStateViewModel>(() => () => ({
users: []
}));

interface Props {
users: Array<Record<string, unknown>>;
}

export default function UsersProvider({ users, children }: PropsWithChildren<Props>) {
return (
<useUsersListProvider.State initialState={{ users: users.map((u) => new User(u as ConstructorType<User>)) }}>
{children}
</useUsersListProvider.State>
);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { UserStateViewModel } from "@/src/ui/view-models/user-state";
import type { UserStateViewModel } from "@/src/users/presentation/view-models/user-state";
import { CookieUtils } from "@front_web_mrmilu/utils";
import { createStore, useStore } from "zustand";
import { immer } from "zustand/middleware/immer";

export const userProvider = createStore<UserStateViewModel>()(
export const userStore = createStore<UserStateViewModel>()(
immer((set, get) => ({
logged: false,
setLogged(logged: boolean) {
Expand All @@ -23,8 +23,8 @@ export const userProvider = createStore<UserStateViewModel>()(
}))
);

export function useUserProvider(): UserStateViewModel;
export function useUserProvider<T>(selector: (state: UserStateViewModel) => T, equals?: (a: T, b: T) => boolean): T;
export function useUserProvider(selector?: any, equals?: any) {
return useStore(userProvider, selector, equals);
export function useUserStore(): UserStateViewModel;
export function useUserStore<T>(selector: (state: UserStateViewModel) => T, equals?: (a: T, b: T) => boolean): T;
export function useUserStore(selector?: any, equals?: any) {
return useStore(userStore, selector, equals);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Expose } from "class-transformer";
import { User } from "@/src/core/users/domain/models/user";
import { User } from "@/src/users/domain/models/user";
import type { DataModel } from "@/src/common/interfaces/data-model";

export class UserDataModel implements DataModel<User> {
export class UserDTO implements DataModel<User> {
@Expose()
id!: string;
@Expose()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { TYPES } from "@/src/core/app/ioc/__generated__/types";
import { inject, injectable } from "inversify";
import type { User } from "@/src/core/users/domain/models/user";
import type { User } from "@/src/users/domain/models/user";
import { plainToClass } from "class-transformer";
import type { IocProvider } from "@/src/core/app/ioc/interfaces";
import { UserDataModel } from "@/src/core/users/data/models/user-data-model";
import type { IUsersRepository } from "@/src/core/users/domain/interfaces/users-repository";
import { UserDTO } from "@/src/users/data/dtos/user";
import type { IUsersRepository } from "@/src/users/domain/interfaces/users-repository";
import type { RestService } from "@/src/core/app/data/services/rest-service";

@injectable()
Expand All @@ -16,7 +16,7 @@ export class UsersRepository implements IUsersRepository {
const response = await restService.get<Array<Record<string, unknown>>>("/users");
return (
response.map((user) => {
const dataModel = plainToClass(UserDataModel, user, { excludeExtraneousValues: true });
const dataModel = plainToClass(UserDTO, user, { excludeExtraneousValues: true });
return dataModel.toDomain();
}) ?? []
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { User } from "@/src/core/users/domain/models/user";
import type { User } from "@/src/users/domain/models/user";

export interface IUsersRepository {
users(): Promise<Array<User>>;
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { inject, injectable } from "inversify";
import type { User } from "@/src/core/users/domain/models/user";
import type { User } from "@/src/users/domain/models/user";
import type { IocProvider } from "@/src/core/app/ioc/interfaces";
import { TYPES } from "@/src/core/app/ioc/__generated__/types";
import type { IUsersRepository } from "@/src/core/users/domain/interfaces/users-repository";
import type { IUsersRepository } from "@/src/users/domain/interfaces/users-repository";

@injectable()
export class GetUsersUseCase {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { forwardRef } from "react";
import type { User } from "@/src/core/users/domain/models/user";
import type { User } from "@/src/users/domain/models/user";
import { ModalContent } from "@/src/ui/components/modal/modal";
import css from "./user-modal.css";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";
import { SimpleCard } from "@/src/ui/components/simple-card/simple-card";
import { useUiProvider } from "@/src/ui/providers/ui.provider";
import { User } from "@/src/core/users/domain/models/user";
import { UserModal } from "@/src/ui/features/users/components/user-modal/user-modal";
import { User } from "@/src/users/domain/models/user";
import { UserModal } from "@/src/users/presentation/components/user-modal/user-modal";
import { useMemo } from "react";
import type { ConstructorType } from "@/src/common/interfaces/constructor-type";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";
import { useUiProvider } from "@/src/ui/providers/ui.provider";
import { useUsersListProvider } from "@/src/ui/features/users/state/users-list.provider";
import type { User } from "@/src/core/users/domain/models/user";
import { UserModal } from "@/src/ui/features/users/components/user-modal/user-modal";
import { useUsersListProvider } from "@/src/users/application/providers/user-list-provider";
import type { User } from "@/src/users/domain/models/user";
import { UserModal } from "@/src/users/presentation/components/user-modal/user-modal";
import { SimpleCard } from "@/src/ui/components/simple-card/simple-card";

export default function UsersList() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import css from "@/src/common/presentation/styles/wrapper.css";
import { locator } from "@/src/core/app/ioc/__generated__";
import type { IocProvider } from "@/src/core/app/ioc/interfaces";
import type { GetUsersUseCase } from "@/src/users/domain/use-cases/get-users-use-case";
import { TYPES } from "@/src/core/app/ioc/__generated__/types";
import UsersList from "@/src/users/presentation/containers/users-list";
import { instanceToPlain } from "class-transformer";
import type { User } from "@/src/users/domain/models/user";
import UsersListProvider from "@/src/users/application/providers/user-list-provider";
import PageTitle from "@/src/common/presentation/components/page-title";
import { getTranslations } from "next-intl/server";
import { notFound } from "next/navigation";

export default async function UsersProviderPage() {
let users: Array<User> = [];
let t: Awaited<ReturnType<typeof getTranslations<"users">>>;

try {
const getUser = await locator.get<IocProvider<GetUsersUseCase>>(TYPES.GetUsersUseCase)();
[users, t] = await Promise.all([getUser.execute(), getTranslations("users")]);
} catch (e) {
notFound();
}

return (
<UsersListProvider users={instanceToPlain<User>(users)}>
<div className={css.wrapper}>
<PageTitle title={t("withProviderTitle")} />

<UsersList />
</div>
</UsersListProvider>
);
}
Loading