From c4786b0b4ee511e466a62a875aba3e238016365d Mon Sep 17 00:00:00 2001 From: ludacirs Date: Tue, 12 Sep 2023 15:32:15 +0900 Subject: [PATCH 01/18] =?UTF-8?q?fix:=20codegen=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=EC=9D=84=20=EB=B0=94=EA=BE=B8=EA=B3=A0=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/realworld/orval.config.js | 20 +- .../article-like-button.tsx | 2 +- .../src/shared/api/realworld/apis.ts | 1419 ----------------- .../api/realworld/axios/axiosInstance.ts | 9 + .../endpoints/articles/articles.msw.ts | 142 ++ .../realworld/endpoints/articles/articles.ts | 314 ++++ .../endpoints/comments/comments.msw.ts | 51 + .../realworld/endpoints/comments/comments.ts | 189 +++ .../endpoints/default/default.msw.ts | 21 + .../realworld/endpoints/default/default.ts | 54 + .../endpoints/favorites/favorites.msw.ts | 62 + .../endpoints/favorites/favorites.ts | 90 ++ .../endpoints/profile/profile.msw.ts | 48 + .../realworld/endpoints/profile/profile.ts | 172 ++ .../user-and-authentication.msw.ts | 64 + .../user-and-authentication.ts | 212 +++ .../shared/api/realworld/models/article.ts | 2 +- .../shared/api/realworld/models/comment.ts | 2 +- .../api/realworld/models/genericErrorModel.ts | 2 +- .../src/shared/api/realworld/models/index.ts | 54 +- .../api/realworld/models/loginUserRequest.ts | 2 +- .../models/multipleArticlesResponse.ts | 2 +- .../models/multipleCommentsResponse.ts | 2 +- .../api/realworld/models/newArticleRequest.ts | 2 +- .../api/realworld/models/newUserRequest.ts | 2 +- .../api/realworld/models/profileResponse.ts | 2 +- .../realworld/models/singleArticleResponse.ts | 2 +- .../realworld/models/singleCommentResponse.ts | 2 +- .../realworld/models/updateArticleRequest.ts | 2 +- .../api/realworld/models/updateUserRequest.ts | 2 +- .../api/realworld/models/userResponse.ts | 2 +- .../src/widgets/GNB/ui/GNB/gnb.test.tsx | 39 + apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx | 35 + .../article/article-list/article-list.tsx | 2 +- 34 files changed, 1558 insertions(+), 1469 deletions(-) delete mode 100644 apps/realworld/src/shared/api/realworld/apis.ts create mode 100644 apps/realworld/src/shared/api/realworld/axios/axiosInstance.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/articles/articles.msw.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/articles/articles.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/comments/comments.msw.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/comments/comments.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/default/default.msw.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/default/default.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/favorites/favorites.msw.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/favorites/favorites.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/profile/profile.msw.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/profile/profile.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/user-and-authentication/user-and-authentication.msw.ts create mode 100644 apps/realworld/src/shared/api/realworld/endpoints/user-and-authentication/user-and-authentication.ts create mode 100644 apps/realworld/src/widgets/GNB/ui/GNB/gnb.test.tsx create mode 100644 apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx diff --git a/apps/realworld/orval.config.js b/apps/realworld/orval.config.js index 9a6010f4..13c0021f 100644 --- a/apps/realworld/orval.config.js +++ b/apps/realworld/orval.config.js @@ -1,18 +1,24 @@ -import { defineConfig } from "orval"; +import { defineConfig } from 'orval'; export default defineConfig({ - myproject: { + realworld: { output: { - mode: "single", - target: "src/shared/api/realworld/apis.ts", - schemas: "src/shared/api/realworld/models", - client: "react-query", + mode: 'tags-split', + target: 'src/shared/api/realworld/endpoints', + schemas: 'src/shared/api/realworld/models', + client: 'react-query', prettier: true, clean: true, mock: true, + override: { + mutator: { + path: 'src/shared/api/realworld/axios/axiosInstance.ts', + name: 'axiosInstance', + }, + }, }, input: { - target: "./realworld.json", + target: './realworld.json', }, }, }); diff --git a/apps/realworld/src/features/article/ui/article-like-button/article-like-button.tsx b/apps/realworld/src/features/article/ui/article-like-button/article-like-button.tsx index 1e5371a0..d887f3b0 100644 --- a/apps/realworld/src/features/article/ui/article-like-button/article-like-button.tsx +++ b/apps/realworld/src/features/article/ui/article-like-button/article-like-button.tsx @@ -1,5 +1,5 @@ 'use client'; -import { useCreateArticleFavorite } from '@/shared/api/realworld/apis'; +import { useCreateArticleFavorite } from '@/shared/api/realworld/endpoints/favorites/favorites'; import { Button } from '@packages/ui'; import React from 'react'; import { IoIosHeart } from 'react-icons/io'; diff --git a/apps/realworld/src/shared/api/realworld/apis.ts b/apps/realworld/src/shared/api/realworld/apis.ts deleted file mode 100644 index f4b00eb4..00000000 --- a/apps/realworld/src/shared/api/realworld/apis.ts +++ /dev/null @@ -1,1419 +0,0 @@ -/** - * Generated by orval v6.17.0 🍺 - * Do not edit manually. - * Conduit API - * Conduit API - * OpenAPI spec version: 1.0.0 - */ -import { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'; -import defaultAxios from 'axios'; -import { useQuery, useMutation } from '@tanstack/react-query'; -import type { - UseQueryOptions, - UseMutationOptions, - QueryFunction, - MutationFunction, - UseQueryResult, - QueryKey, -} from '@tanstack/react-query'; -import type { - UserResponse, - GenericErrorModel, - LoginUserRequest, - NewUserRequest, - UpdateUserRequest, - ProfileResponse, - MultipleArticlesResponse, - GetArticlesFeedParams, - GetArticlesParams, - SingleArticleResponse, - NewArticleRequest, - UpdateArticleRequest, - MultipleCommentsResponse, - SingleCommentResponse, - NewCommentRequest, - TagsResponse, -} from './models'; -import { rest } from 'msw'; -import { faker } from '@faker-js/faker'; - -const axios = defaultAxios.create({ - baseURL: 'https://api.realworld.io/api/', - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - accept: 'application/json', - }, -}); -/** - * Login for existing user - * @summary Existing user login - */ -export const login = ( - loginUserRequest: LoginUserRequest, - options?: AxiosRequestConfig, -): Promise> => { - return axios.post(`/users/login`, loginUserRequest, options); -}; - -export const getLoginMutationOptions = , TContext = unknown>(options?: { - mutation?: UseMutationOptions>, TError, { data: LoginUserRequest }, TContext>; - axios?: AxiosRequestConfig; -}): UseMutationOptions>, TError, { data: LoginUserRequest }, TContext> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction>, { data: LoginUserRequest }> = props => { - const { data } = props ?? {}; - - return login(data, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type LoginMutationResult = NonNullable>>; -export type LoginMutationBody = LoginUserRequest; -export type LoginMutationError = AxiosError; - -/** - * @summary Existing user login - */ -export const useLogin = , TContext = unknown>(options?: { - mutation?: UseMutationOptions>, TError, { data: LoginUserRequest }, TContext>; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getLoginMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Register a new user - * @summary Register a new user - */ -export const createUser = ( - newUserRequest: NewUserRequest, - options?: AxiosRequestConfig, -): Promise> => { - return axios.post(`/users`, newUserRequest, options); -}; - -export const getCreateUserMutationOptions = , TContext = unknown>(options?: { - mutation?: UseMutationOptions>, TError, { data: NewUserRequest }, TContext>; - axios?: AxiosRequestConfig; -}): UseMutationOptions>, TError, { data: NewUserRequest }, TContext> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction>, { data: NewUserRequest }> = props => { - const { data } = props ?? {}; - - return createUser(data, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type CreateUserMutationResult = NonNullable>>; -export type CreateUserMutationBody = NewUserRequest; -export type CreateUserMutationError = AxiosError; - -/** - * @summary Register a new user - */ -export const useCreateUser = , TContext = unknown>(options?: { - mutation?: UseMutationOptions>, TError, { data: NewUserRequest }, TContext>; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getCreateUserMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Gets the currently logged-in user - * @summary Get current user - */ -export const getCurrentUser = (options?: AxiosRequestConfig): Promise> => { - return axios.get(`/user`, options); -}; - -export const getGetCurrentUserQueryKey = () => [`/user`] as const; - -export const getGetCurrentUserQueryOptions = < - TData = Awaited>, - TError = AxiosError, ->(options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; -}): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { - const { query: queryOptions, axios: axiosOptions } = options ?? {}; - - const queryKey = queryOptions?.queryKey ?? getGetCurrentUserQueryKey(); - - const queryFn: QueryFunction>> = ({ signal }) => - getCurrentUser({ signal, ...axiosOptions }); - - return { queryKey, queryFn, ...queryOptions }; -}; - -export type GetCurrentUserQueryResult = NonNullable>>; -export type GetCurrentUserQueryError = AxiosError; - -/** - * @summary Get current user - */ -export const useGetCurrentUser = < - TData = Awaited>, - TError = AxiosError, ->(options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; -}): UseQueryResult & { queryKey: QueryKey } => { - const queryOptions = getGetCurrentUserQueryOptions(options); - - const query = useQuery(queryOptions) as UseQueryResult & { - queryKey: QueryKey; - }; - - query.queryKey = queryOptions.queryKey; - - return query; -}; - -/** - * Updated user information for current user - * @summary Update current user - */ -export const updateCurrentUser = ( - updateUserRequest: UpdateUserRequest, - options?: AxiosRequestConfig, -): Promise> => { - return axios.put(`/user`, updateUserRequest, options); -}; - -export const getUpdateCurrentUserMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { data: UpdateUserRequest }, - TContext - >; - axios?: AxiosRequestConfig; -}): UseMutationOptions< - Awaited>, - TError, - { data: UpdateUserRequest }, - TContext -> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction< - Awaited>, - { data: UpdateUserRequest } - > = props => { - const { data } = props ?? {}; - - return updateCurrentUser(data, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type UpdateCurrentUserMutationResult = NonNullable>>; -export type UpdateCurrentUserMutationBody = UpdateUserRequest; -export type UpdateCurrentUserMutationError = AxiosError; - -/** - * @summary Update current user - */ -export const useUpdateCurrentUser = , TContext = unknown>(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { data: UpdateUserRequest }, - TContext - >; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getUpdateCurrentUserMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Get a profile of a user of the system. Auth is optional - * @summary Get a profile - */ -export const getProfileByUsername = ( - username: string, - options?: AxiosRequestConfig, -): Promise> => { - return axios.get(`/profiles/${username}`, options); -}; - -export const getGetProfileByUsernameQueryKey = (username: string) => [`/profiles/${username}`] as const; - -export const getGetProfileByUsernameQueryOptions = < - TData = Awaited>, - TError = AxiosError, ->( - username: string, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { - const { query: queryOptions, axios: axiosOptions } = options ?? {}; - - const queryKey = queryOptions?.queryKey ?? getGetProfileByUsernameQueryKey(username); - - const queryFn: QueryFunction>> = ({ signal }) => - getProfileByUsername(username, { signal, ...axiosOptions }); - - return { queryKey, queryFn, enabled: !!username, ...queryOptions }; -}; - -export type GetProfileByUsernameQueryResult = NonNullable>>; -export type GetProfileByUsernameQueryError = AxiosError; - -/** - * @summary Get a profile - */ -export const useGetProfileByUsername = < - TData = Awaited>, - TError = AxiosError, ->( - username: string, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryResult & { queryKey: QueryKey } => { - const queryOptions = getGetProfileByUsernameQueryOptions(username, options); - - const query = useQuery(queryOptions) as UseQueryResult & { - queryKey: QueryKey; - }; - - query.queryKey = queryOptions.queryKey; - - return query; -}; - -/** - * Follow a user by username - * @summary Follow a user - */ -export const followUserByUsername = ( - username: string, - options?: AxiosRequestConfig, -): Promise> => { - return axios.post(`/profiles/${username}/follow`, undefined, options); -}; - -export const getFollowUserByUsernameMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { username: string }, - TContext - >; - axios?: AxiosRequestConfig; -}): UseMutationOptions>, TError, { username: string }, TContext> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction< - Awaited>, - { username: string } - > = props => { - const { username } = props ?? {}; - - return followUserByUsername(username, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type FollowUserByUsernameMutationResult = NonNullable>>; - -export type FollowUserByUsernameMutationError = AxiosError; - -/** - * @summary Follow a user - */ -export const useFollowUserByUsername = , TContext = unknown>(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { username: string }, - TContext - >; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getFollowUserByUsernameMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Unfollow a user by username - * @summary Unfollow a user - */ -export const unfollowUserByUsername = ( - username: string, - options?: AxiosRequestConfig, -): Promise> => { - return axios.delete(`/profiles/${username}/follow`, options); -}; - -export const getUnfollowUserByUsernameMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { username: string }, - TContext - >; - axios?: AxiosRequestConfig; -}): UseMutationOptions>, TError, { username: string }, TContext> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction< - Awaited>, - { username: string } - > = props => { - const { username } = props ?? {}; - - return unfollowUserByUsername(username, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type UnfollowUserByUsernameMutationResult = NonNullable>>; - -export type UnfollowUserByUsernameMutationError = AxiosError; - -/** - * @summary Unfollow a user - */ -export const useUnfollowUserByUsername = , TContext = unknown>(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { username: string }, - TContext - >; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getUnfollowUserByUsernameMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Get most recent articles from users you follow. Use query parameters to limit. Auth is required - * @summary Get recent articles from users you follow - */ -export const getArticlesFeed = ( - params?: GetArticlesFeedParams, - options?: AxiosRequestConfig, -): Promise> => { - return axios.get(`/articles/feed`, { - ...options, - params: { ...params, ...options?.params }, - }); -}; - -export const getGetArticlesFeedQueryKey = (params?: GetArticlesFeedParams) => - [`/articles/feed`, ...(params ? [params] : [])] as const; - -export const getGetArticlesFeedQueryOptions = < - TData = Awaited>, - TError = AxiosError, ->( - params?: GetArticlesFeedParams, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { - const { query: queryOptions, axios: axiosOptions } = options ?? {}; - - const queryKey = queryOptions?.queryKey ?? getGetArticlesFeedQueryKey(params); - - const queryFn: QueryFunction>> = ({ signal }) => - getArticlesFeed(params, { signal, ...axiosOptions }); - - return { queryKey, queryFn, ...queryOptions }; -}; - -export type GetArticlesFeedQueryResult = NonNullable>>; -export type GetArticlesFeedQueryError = AxiosError; - -/** - * @summary Get recent articles from users you follow - */ -export const useGetArticlesFeed = < - TData = Awaited>, - TError = AxiosError, ->( - params?: GetArticlesFeedParams, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryResult & { queryKey: QueryKey } => { - const queryOptions = getGetArticlesFeedQueryOptions(params, options); - - const query = useQuery(queryOptions) as UseQueryResult & { - queryKey: QueryKey; - }; - - query.queryKey = queryOptions.queryKey; - - return query; -}; - -/** - * Get most recent articles globally. Use query parameters to filter results. Auth is optional - * @summary Get recent articles globally - */ -export const getArticles = ( - params?: GetArticlesParams, - options?: AxiosRequestConfig, -): Promise> => { - return axios.get(`/articles`, { - ...options, - params: { ...params, ...options?.params }, - }); -}; - -export const getGetArticlesQueryKey = (params?: GetArticlesParams) => - [`/articles`, ...(params ? [params] : [])] as const; - -export const getGetArticlesQueryOptions = < - TData = Awaited>, - TError = AxiosError, ->( - params?: GetArticlesParams, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryOptions>, TError, TData> & { - queryKey: QueryKey; -} => { - const { query: queryOptions, axios: axiosOptions } = options ?? {}; - - const queryKey = queryOptions?.queryKey ?? getGetArticlesQueryKey(params); - - const queryFn: QueryFunction>> = ({ signal }) => - getArticles(params, { signal, ...axiosOptions }); - - return { queryKey, queryFn, ...queryOptions }; -}; - -export type GetArticlesQueryResult = NonNullable>>; -export type GetArticlesQueryError = AxiosError; - -/** - * @summary Get recent articles globally - */ -export const useGetArticles = < - TData = Awaited>, - TError = AxiosError, ->( - params?: GetArticlesParams, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryResult & { queryKey: QueryKey } => { - const queryOptions = getGetArticlesQueryOptions(params, options); - - const query = useQuery(queryOptions) as UseQueryResult & { - queryKey: QueryKey; - }; - - query.queryKey = queryOptions.queryKey; - - return query; -}; - -/** - * Create an article. Auth is required - * @summary Create an article - */ -export const createArticle = ( - newArticleRequest: NewArticleRequest, - options?: AxiosRequestConfig, -): Promise> => { - return axios.post(`/articles`, newArticleRequest, options); -}; - -export const getCreateArticleMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { data: NewArticleRequest }, - TContext - >; - axios?: AxiosRequestConfig; -}): UseMutationOptions>, TError, { data: NewArticleRequest }, TContext> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction< - Awaited>, - { data: NewArticleRequest } - > = props => { - const { data } = props ?? {}; - - return createArticle(data, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type CreateArticleMutationResult = NonNullable>>; -export type CreateArticleMutationBody = NewArticleRequest; -export type CreateArticleMutationError = AxiosError; - -/** - * @summary Create an article - */ -export const useCreateArticle = , TContext = unknown>(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { data: NewArticleRequest }, - TContext - >; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getCreateArticleMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Get an article. Auth not required - * @summary Get an article - */ -export const getArticle = ( - slug: string, - options?: AxiosRequestConfig, -): Promise> => { - return axios.get(`/articles/${slug}`, options); -}; - -export const getGetArticleQueryKey = (slug: string) => [`/articles/${slug}`] as const; - -export const getGetArticleQueryOptions = < - TData = Awaited>, - TError = AxiosError, ->( - slug: string, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryOptions>, TError, TData> & { - queryKey: QueryKey; -} => { - const { query: queryOptions, axios: axiosOptions } = options ?? {}; - - const queryKey = queryOptions?.queryKey ?? getGetArticleQueryKey(slug); - - const queryFn: QueryFunction>> = ({ signal }) => - getArticle(slug, { signal, ...axiosOptions }); - - return { queryKey, queryFn, enabled: !!slug, ...queryOptions }; -}; - -export type GetArticleQueryResult = NonNullable>>; -export type GetArticleQueryError = AxiosError; - -/** - * @summary Get an article - */ -export const useGetArticle = >, TError = AxiosError>( - slug: string, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryResult & { queryKey: QueryKey } => { - const queryOptions = getGetArticleQueryOptions(slug, options); - - const query = useQuery(queryOptions) as UseQueryResult & { - queryKey: QueryKey; - }; - - query.queryKey = queryOptions.queryKey; - - return query; -}; - -/** - * Update an article. Auth is required - * @summary Update an article - */ -export const updateArticle = ( - slug: string, - updateArticleRequest: UpdateArticleRequest, - options?: AxiosRequestConfig, -): Promise> => { - return axios.put(`/articles/${slug}`, updateArticleRequest, options); -}; - -export const getUpdateArticleMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { slug: string; data: UpdateArticleRequest }, - TContext - >; - axios?: AxiosRequestConfig; -}): UseMutationOptions< - Awaited>, - TError, - { slug: string; data: UpdateArticleRequest }, - TContext -> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction< - Awaited>, - { slug: string; data: UpdateArticleRequest } - > = props => { - const { slug, data } = props ?? {}; - - return updateArticle(slug, data, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type UpdateArticleMutationResult = NonNullable>>; -export type UpdateArticleMutationBody = UpdateArticleRequest; -export type UpdateArticleMutationError = AxiosError; - -/** - * @summary Update an article - */ -export const useUpdateArticle = , TContext = unknown>(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { slug: string; data: UpdateArticleRequest }, - TContext - >; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getUpdateArticleMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Delete an article. Auth is required - * @summary Delete an article - */ -export const deleteArticle = (slug: string, options?: AxiosRequestConfig): Promise> => { - return axios.delete(`/articles/${slug}`, options); -}; - -export const getDeleteArticleMutationOptions = , TContext = unknown>(options?: { - mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; - axios?: AxiosRequestConfig; -}): UseMutationOptions>, TError, { slug: string }, TContext> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction>, { slug: string }> = props => { - const { slug } = props ?? {}; - - return deleteArticle(slug, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type DeleteArticleMutationResult = NonNullable>>; - -export type DeleteArticleMutationError = AxiosError; - -/** - * @summary Delete an article - */ -export const useDeleteArticle = , TContext = unknown>(options?: { - mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getDeleteArticleMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Get the comments for an article. Auth is optional - * @summary Get comments for an article - */ -export const getArticleComments = ( - slug: string, - options?: AxiosRequestConfig, -): Promise> => { - return axios.get(`/articles/${slug}/comments`, options); -}; - -export const getGetArticleCommentsQueryKey = (slug: string) => [`/articles/${slug}/comments`] as const; - -export const getGetArticleCommentsQueryOptions = < - TData = Awaited>, - TError = AxiosError, ->( - slug: string, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { - const { query: queryOptions, axios: axiosOptions } = options ?? {}; - - const queryKey = queryOptions?.queryKey ?? getGetArticleCommentsQueryKey(slug); - - const queryFn: QueryFunction>> = ({ signal }) => - getArticleComments(slug, { signal, ...axiosOptions }); - - return { queryKey, queryFn, enabled: !!slug, ...queryOptions }; -}; - -export type GetArticleCommentsQueryResult = NonNullable>>; -export type GetArticleCommentsQueryError = AxiosError; - -/** - * @summary Get comments for an article - */ -export const useGetArticleComments = < - TData = Awaited>, - TError = AxiosError, ->( - slug: string, - options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; - }, -): UseQueryResult & { queryKey: QueryKey } => { - const queryOptions = getGetArticleCommentsQueryOptions(slug, options); - - const query = useQuery(queryOptions) as UseQueryResult & { - queryKey: QueryKey; - }; - - query.queryKey = queryOptions.queryKey; - - return query; -}; - -/** - * Create a comment for an article. Auth is required - * @summary Create a comment for an article - */ -export const createArticleComment = ( - slug: string, - newCommentRequest: NewCommentRequest, - options?: AxiosRequestConfig, -): Promise> => { - return axios.post(`/articles/${slug}/comments`, newCommentRequest, options); -}; - -export const getCreateArticleCommentMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { slug: string; data: NewCommentRequest }, - TContext - >; - axios?: AxiosRequestConfig; -}): UseMutationOptions< - Awaited>, - TError, - { slug: string; data: NewCommentRequest }, - TContext -> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction< - Awaited>, - { slug: string; data: NewCommentRequest } - > = props => { - const { slug, data } = props ?? {}; - - return createArticleComment(slug, data, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type CreateArticleCommentMutationResult = NonNullable>>; -export type CreateArticleCommentMutationBody = NewCommentRequest; -export type CreateArticleCommentMutationError = AxiosError; - -/** - * @summary Create a comment for an article - */ -export const useCreateArticleComment = , TContext = unknown>(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { slug: string; data: NewCommentRequest }, - TContext - >; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getCreateArticleCommentMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Delete a comment for an article. Auth is required - * @summary Delete a comment for an article - */ -export const deleteArticleComment = ( - slug: string, - id: number, - options?: AxiosRequestConfig, -): Promise> => { - return axios.delete(`/articles/${slug}/comments/${id}`, options); -}; - -export const getDeleteArticleCommentMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { slug: string; id: number }, - TContext - >; - axios?: AxiosRequestConfig; -}): UseMutationOptions< - Awaited>, - TError, - { slug: string; id: number }, - TContext -> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction< - Awaited>, - { slug: string; id: number } - > = props => { - const { slug, id } = props ?? {}; - - return deleteArticleComment(slug, id, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type DeleteArticleCommentMutationResult = NonNullable>>; - -export type DeleteArticleCommentMutationError = AxiosError; - -/** - * @summary Delete a comment for an article - */ -export const useDeleteArticleComment = , TContext = unknown>(options?: { - mutation?: UseMutationOptions< - Awaited>, - TError, - { slug: string; id: number }, - TContext - >; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getDeleteArticleCommentMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Favorite an article. Auth is required - * @summary Favorite an article - */ -export const createArticleFavorite = ( - slug: string, - options?: AxiosRequestConfig, -): Promise> => { - return axios.post(`/articles/${slug}/favorite`, undefined, options); -}; - -export const getCreateArticleFavoriteMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; - axios?: AxiosRequestConfig; -}): UseMutationOptions>, TError, { slug: string }, TContext> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction>, { slug: string }> = props => { - const { slug } = props ?? {}; - - return createArticleFavorite(slug, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type CreateArticleFavoriteMutationResult = NonNullable>>; - -export type CreateArticleFavoriteMutationError = AxiosError; - -/** - * @summary Favorite an article - */ -export const useCreateArticleFavorite = , TContext = unknown>(options?: { - mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getCreateArticleFavoriteMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Unfavorite an article. Auth is required - * @summary Unfavorite an article - */ -export const deleteArticleFavorite = ( - slug: string, - options?: AxiosRequestConfig, -): Promise> => { - return axios.delete(`/articles/${slug}/favorite`, options); -}; - -export const getDeleteArticleFavoriteMutationOptions = < - TError = AxiosError, - TContext = unknown, ->(options?: { - mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; - axios?: AxiosRequestConfig; -}): UseMutationOptions>, TError, { slug: string }, TContext> => { - const { mutation: mutationOptions, axios: axiosOptions } = options ?? {}; - - const mutationFn: MutationFunction>, { slug: string }> = props => { - const { slug } = props ?? {}; - - return deleteArticleFavorite(slug, axiosOptions); - }; - - return { mutationFn, ...mutationOptions }; -}; - -export type DeleteArticleFavoriteMutationResult = NonNullable>>; - -export type DeleteArticleFavoriteMutationError = AxiosError; - -/** - * @summary Unfavorite an article - */ -export const useDeleteArticleFavorite = , TContext = unknown>(options?: { - mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; - axios?: AxiosRequestConfig; -}) => { - const mutationOptions = getDeleteArticleFavoriteMutationOptions(options); - - return useMutation(mutationOptions); -}; - -/** - * Get tags. Auth not required - * @summary Get tags - */ -export const getTags = (options?: AxiosRequestConfig): Promise> => { - return axios.get(`/tags`, options); -}; - -export const getGetTagsQueryKey = () => [`/tags`] as const; - -export const getGetTagsQueryOptions = < - TData = Awaited>, - TError = AxiosError, ->(options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; -}): UseQueryOptions>, TError, TData> & { - queryKey: QueryKey; -} => { - const { query: queryOptions, axios: axiosOptions } = options ?? {}; - - const queryKey = queryOptions?.queryKey ?? getGetTagsQueryKey(); - - const queryFn: QueryFunction>> = ({ signal }) => - getTags({ signal, ...axiosOptions }); - - return { queryKey, queryFn, ...queryOptions }; -}; - -export type GetTagsQueryResult = NonNullable>>; -export type GetTagsQueryError = AxiosError; - -/** - * @summary Get tags - */ -export const useGetTags = < - TData = Awaited>, - TError = AxiosError, ->(options?: { - query?: UseQueryOptions>, TError, TData>; - axios?: AxiosRequestConfig; -}): UseQueryResult & { queryKey: QueryKey } => { - const queryOptions = getGetTagsQueryOptions(options); - - const query = useQuery(queryOptions) as UseQueryResult & { - queryKey: QueryKey; - }; - - query.queryKey = queryOptions.queryKey; - - return query; -}; - -export const getLoginMock = () => ({ - user: { - email: faker.random.word(), - token: faker.random.word(), - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - }, -}); - -export const getCreateUserMock = () => ({ - user: { - email: faker.random.word(), - token: faker.random.word(), - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - }, -}); - -export const getGetCurrentUserMock = () => ({ - user: { - email: faker.random.word(), - token: faker.random.word(), - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - }, -}); - -export const getUpdateCurrentUserMock = () => ({ - user: { - email: faker.random.word(), - token: faker.random.word(), - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - }, -}); - -export const getGetProfileByUsernameMock = () => ({ - profile: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, -}); - -export const getFollowUserByUsernameMock = () => ({ - profile: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, -}); - -export const getUnfollowUserByUsernameMock = () => ({ - profile: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, -}); - -export const getGetArticlesFeedMock = () => ({ - articles: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ - slug: faker.random.word(), - title: faker.random.word(), - description: faker.random.word(), - body: faker.random.word(), - tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => - faker.random.word(), - ), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - favorited: faker.datatype.boolean(), - favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - })), - articlesCount: faker.datatype.number({ min: undefined, max: undefined }), -}); - -export const getGetArticlesMock = () => ({ - articles: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ - slug: faker.random.word(), - title: faker.random.word(), - description: faker.random.word(), - body: faker.random.word(), - tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => - faker.random.word(), - ), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - favorited: faker.datatype.boolean(), - favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - })), - articlesCount: faker.datatype.number({ min: undefined, max: undefined }), -}); - -export const getCreateArticleMock = () => ({ - article: { - slug: faker.random.word(), - title: faker.random.word(), - description: faker.random.word(), - body: faker.random.word(), - tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => - faker.random.word(), - ), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - favorited: faker.datatype.boolean(), - favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - }, -}); - -export const getGetArticleMock = () => ({ - article: { - slug: faker.random.word(), - title: faker.random.word(), - description: faker.random.word(), - body: faker.random.word(), - tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => - faker.random.word(), - ), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - favorited: faker.datatype.boolean(), - favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - }, -}); - -export const getUpdateArticleMock = () => ({ - article: { - slug: faker.random.word(), - title: faker.random.word(), - description: faker.random.word(), - body: faker.random.word(), - tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => - faker.random.word(), - ), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - favorited: faker.datatype.boolean(), - favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - }, -}); - -export const getGetArticleCommentsMock = () => ({ - comments: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ - id: faker.datatype.number({ min: undefined, max: undefined }), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - body: faker.random.word(), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - })), -}); - -export const getCreateArticleCommentMock = () => ({ - comment: { - id: faker.datatype.number({ min: undefined, max: undefined }), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - body: faker.random.word(), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - }, -}); - -export const getCreateArticleFavoriteMock = () => ({ - article: { - slug: faker.random.word(), - title: faker.random.word(), - description: faker.random.word(), - body: faker.random.word(), - tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => - faker.random.word(), - ), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - favorited: faker.datatype.boolean(), - favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - }, -}); - -export const getDeleteArticleFavoriteMock = () => ({ - article: { - slug: faker.random.word(), - title: faker.random.word(), - description: faker.random.word(), - body: faker.random.word(), - tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => - faker.random.word(), - ), - createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, - favorited: faker.datatype.boolean(), - favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), - author: { - username: faker.random.word(), - bio: faker.random.word(), - image: faker.random.word(), - following: faker.datatype.boolean(), - }, - }, -}); - -export const getGetTagsMock = () => ({ - tags: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => - faker.random.word(), - ), -}); - -export const getConduitAPIMSW = () => [ - rest.post('*/users/login', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getLoginMock())); - }), - rest.post('*/users', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getCreateUserMock())); - }), - rest.get('*/user', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetCurrentUserMock())); - }), - rest.put('*/user', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getUpdateCurrentUserMock())); - }), - rest.get('*/profiles/:username', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetProfileByUsernameMock())); - }), - rest.post('*/profiles/:username/follow', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getFollowUserByUsernameMock())); - }), - rest.delete('*/profiles/:username/follow', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getUnfollowUserByUsernameMock())); - }), - rest.get('*/articles/feed', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetArticlesFeedMock())); - }), - rest.get('*/articles', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetArticlesMock())); - }), - rest.post('*/articles', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getCreateArticleMock())); - }), - rest.get('*/articles/:slug', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetArticleMock())); - }), - rest.put('*/articles/:slug', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getUpdateArticleMock())); - }), - rest.delete('*/articles/:slug', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status')); - }), - rest.get('*/articles/:slug/comments', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetArticleCommentsMock())); - }), - rest.post('*/articles/:slug/comments', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getCreateArticleCommentMock())); - }), - rest.delete('*/articles/:slug/comments/:id', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status')); - }), - rest.post('*/articles/:slug/favorite', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getCreateArticleFavoriteMock())); - }), - rest.delete('*/articles/:slug/favorite', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getDeleteArticleFavoriteMock())); - }), - rest.get('*/tags', (_req, res, ctx) => { - return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetTagsMock())); - }), -]; diff --git a/apps/realworld/src/shared/api/realworld/axios/axiosInstance.ts b/apps/realworld/src/shared/api/realworld/axios/axiosInstance.ts new file mode 100644 index 00000000..2a18a79e --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/axios/axiosInstance.ts @@ -0,0 +1,9 @@ +import defaultAxios from 'axios'; + +export const axiosInstance = defaultAxios.create({ + baseURL: 'https://api.realworld.io/api/', + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + accept: 'application/json', + }, +}); diff --git a/apps/realworld/src/shared/api/realworld/endpoints/articles/articles.msw.ts b/apps/realworld/src/shared/api/realworld/endpoints/articles/articles.msw.ts new file mode 100644 index 00000000..6a5c2309 --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/articles/articles.msw.ts @@ -0,0 +1,142 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { rest } from 'msw'; +import { faker } from '@faker-js/faker'; + +export const getGetArticlesFeedMock = () => ({ + articles: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ + slug: faker.random.word(), + title: faker.random.word(), + description: faker.random.word(), + body: faker.random.word(), + tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.random.word(), + ), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + favorited: faker.datatype.boolean(), + favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + })), + articlesCount: faker.datatype.number({ min: undefined, max: undefined }), +}); + +export const getGetArticlesMock = () => ({ + articles: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ + slug: faker.random.word(), + title: faker.random.word(), + description: faker.random.word(), + body: faker.random.word(), + tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.random.word(), + ), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + favorited: faker.datatype.boolean(), + favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + })), + articlesCount: faker.datatype.number({ min: undefined, max: undefined }), +}); + +export const getCreateArticleMock = () => ({ + article: { + slug: faker.random.word(), + title: faker.random.word(), + description: faker.random.word(), + body: faker.random.word(), + tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.random.word(), + ), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + favorited: faker.datatype.boolean(), + favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + }, +}); + +export const getGetArticleMock = () => ({ + article: { + slug: faker.random.word(), + title: faker.random.word(), + description: faker.random.word(), + body: faker.random.word(), + tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.random.word(), + ), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + favorited: faker.datatype.boolean(), + favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + }, +}); + +export const getUpdateArticleMock = () => ({ + article: { + slug: faker.random.word(), + title: faker.random.word(), + description: faker.random.word(), + body: faker.random.word(), + tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.random.word(), + ), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + favorited: faker.datatype.boolean(), + favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + }, +}); + +export const getArticlesMSW = () => [ + rest.get('*/articles/feed', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetArticlesFeedMock())); + }), + rest.get('*/articles', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetArticlesMock())); + }), + rest.post('*/articles', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getCreateArticleMock())); + }), + rest.get('*/articles/:slug', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetArticleMock())); + }), + rest.put('*/articles/:slug', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getUpdateArticleMock())); + }), + rest.delete('*/articles/:slug', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status')); + }), +]; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/articles/articles.ts b/apps/realworld/src/shared/api/realworld/endpoints/articles/articles.ts new file mode 100644 index 00000000..93aaac35 --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/articles/articles.ts @@ -0,0 +1,314 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { useQuery, useMutation } from '@tanstack/react-query'; +import type { + UseQueryOptions, + UseMutationOptions, + QueryFunction, + MutationFunction, + UseQueryResult, + QueryKey, +} from '@tanstack/react-query'; +import type { + MultipleArticlesResponse, + GenericErrorModel, + GetArticlesFeedParams, + GetArticlesParams, + SingleArticleResponse, + NewArticleRequest, + UpdateArticleRequest, +} from '../../models'; +import { axiosInstance } from '../../axios/axiosInstance'; + +/** + * Get most recent articles from users you follow. Use query parameters to limit. Auth is required + * @summary Get recent articles from users you follow + */ +export const getArticlesFeed = (params?: GetArticlesFeedParams, signal?: AbortSignal) => { + return axiosInstance({ url: `/articles/feed`, method: 'get', params, signal }); +}; + +export const getGetArticlesFeedQueryKey = (params?: GetArticlesFeedParams) => + [`/articles/feed`, ...(params ? [params] : [])] as const; + +export const getGetArticlesFeedQueryOptions = < + TData = Awaited>, + TError = void | GenericErrorModel, +>( + params?: GetArticlesFeedParams, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetArticlesFeedQueryKey(params); + + const queryFn: QueryFunction>> = ({ signal }) => + getArticlesFeed(params, signal); + + return { queryKey, queryFn, ...queryOptions }; +}; + +export type GetArticlesFeedQueryResult = NonNullable>>; +export type GetArticlesFeedQueryError = void | GenericErrorModel; + +/** + * @summary Get recent articles from users you follow + */ +export const useGetArticlesFeed = < + TData = Awaited>, + TError = void | GenericErrorModel, +>( + params?: GetArticlesFeedParams, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetArticlesFeedQueryOptions(params, options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; + +/** + * Get most recent articles globally. Use query parameters to filter results. Auth is optional + * @summary Get recent articles globally + */ +export const getArticles = (params?: GetArticlesParams, signal?: AbortSignal) => { + return axiosInstance({ url: `/articles`, method: 'get', params, signal }); +}; + +export const getGetArticlesQueryKey = (params?: GetArticlesParams) => + [`/articles`, ...(params ? [params] : [])] as const; + +export const getGetArticlesQueryOptions = < + TData = Awaited>, + TError = void | GenericErrorModel, +>( + params?: GetArticlesParams, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetArticlesQueryKey(params); + + const queryFn: QueryFunction>> = ({ signal }) => getArticles(params, signal); + + return { queryKey, queryFn, ...queryOptions }; +}; + +export type GetArticlesQueryResult = NonNullable>>; +export type GetArticlesQueryError = void | GenericErrorModel; + +/** + * @summary Get recent articles globally + */ +export const useGetArticles = >, TError = void | GenericErrorModel>( + params?: GetArticlesParams, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetArticlesQueryOptions(params, options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; + +/** + * Create an article. Auth is required + * @summary Create an article + */ +export const createArticle = (newArticleRequest: NewArticleRequest) => { + return axiosInstance({ + url: `/articles`, + method: 'post', + headers: { 'Content-Type': 'application/json' }, + data: newArticleRequest, + }); +}; + +export const getCreateArticleMutationOptions = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: NewArticleRequest }, + TContext + >; +}): UseMutationOptions>, TError, { data: NewArticleRequest }, TContext> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction< + Awaited>, + { data: NewArticleRequest } + > = props => { + const { data } = props ?? {}; + + return createArticle(data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type CreateArticleMutationResult = NonNullable>>; +export type CreateArticleMutationBody = NewArticleRequest; +export type CreateArticleMutationError = void | GenericErrorModel; + +/** + * @summary Create an article + */ +export const useCreateArticle = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: NewArticleRequest }, + TContext + >; +}) => { + const mutationOptions = getCreateArticleMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * Get an article. Auth not required + * @summary Get an article + */ +export const getArticle = (slug: string, signal?: AbortSignal) => { + return axiosInstance({ url: `/articles/${slug}`, method: 'get', signal }); +}; + +export const getGetArticleQueryKey = (slug: string) => [`/articles/${slug}`] as const; + +export const getGetArticleQueryOptions = >, TError = GenericErrorModel>( + slug: string, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetArticleQueryKey(slug); + + const queryFn: QueryFunction>> = ({ signal }) => getArticle(slug, signal); + + return { queryKey, queryFn, enabled: !!slug, ...queryOptions }; +}; + +export type GetArticleQueryResult = NonNullable>>; +export type GetArticleQueryError = GenericErrorModel; + +/** + * @summary Get an article + */ +export const useGetArticle = >, TError = GenericErrorModel>( + slug: string, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetArticleQueryOptions(slug, options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; + +/** + * Update an article. Auth is required + * @summary Update an article + */ +export const updateArticle = (slug: string, updateArticleRequest: UpdateArticleRequest) => { + return axiosInstance({ + url: `/articles/${slug}`, + method: 'put', + headers: { 'Content-Type': 'application/json' }, + data: updateArticleRequest, + }); +}; + +export const getUpdateArticleMutationOptions = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { slug: string; data: UpdateArticleRequest }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { slug: string; data: UpdateArticleRequest }, + TContext +> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction< + Awaited>, + { slug: string; data: UpdateArticleRequest } + > = props => { + const { slug, data } = props ?? {}; + + return updateArticle(slug, data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type UpdateArticleMutationResult = NonNullable>>; +export type UpdateArticleMutationBody = UpdateArticleRequest; +export type UpdateArticleMutationError = void | GenericErrorModel; + +/** + * @summary Update an article + */ +export const useUpdateArticle = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { slug: string; data: UpdateArticleRequest }, + TContext + >; +}) => { + const mutationOptions = getUpdateArticleMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * Delete an article. Auth is required + * @summary Delete an article + */ +export const deleteArticle = (slug: string) => { + return axiosInstance({ url: `/articles/${slug}`, method: 'delete' }); +}; + +export const getDeleteArticleMutationOptions = (options?: { + mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; +}): UseMutationOptions>, TError, { slug: string }, TContext> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction>, { slug: string }> = props => { + const { slug } = props ?? {}; + + return deleteArticle(slug); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteArticleMutationResult = NonNullable>>; + +export type DeleteArticleMutationError = GenericErrorModel; + +/** + * @summary Delete an article + */ +export const useDeleteArticle = (options?: { + mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; +}) => { + const mutationOptions = getDeleteArticleMutationOptions(options); + + return useMutation(mutationOptions); +}; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/comments/comments.msw.ts b/apps/realworld/src/shared/api/realworld/endpoints/comments/comments.msw.ts new file mode 100644 index 00000000..34869175 --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/comments/comments.msw.ts @@ -0,0 +1,51 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { rest } from 'msw'; +import { faker } from '@faker-js/faker'; + +export const getGetArticleCommentsMock = () => ({ + comments: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ + id: faker.datatype.number({ min: undefined, max: undefined }), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + body: faker.random.word(), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + })), +}); + +export const getCreateArticleCommentMock = () => ({ + comment: { + id: faker.datatype.number({ min: undefined, max: undefined }), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + body: faker.random.word(), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + }, +}); + +export const getCommentsMSW = () => [ + rest.get('*/articles/:slug/comments', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetArticleCommentsMock())); + }), + rest.post('*/articles/:slug/comments', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getCreateArticleCommentMock())); + }), + rest.delete('*/articles/:slug/comments/:id', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status')); + }), +]; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/comments/comments.ts b/apps/realworld/src/shared/api/realworld/endpoints/comments/comments.ts new file mode 100644 index 00000000..8d288912 --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/comments/comments.ts @@ -0,0 +1,189 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { useQuery, useMutation } from '@tanstack/react-query'; +import type { + UseQueryOptions, + UseMutationOptions, + QueryFunction, + MutationFunction, + UseQueryResult, + QueryKey, +} from '@tanstack/react-query'; +import type { + MultipleCommentsResponse, + GenericErrorModel, + SingleCommentResponse, + NewCommentRequest, +} from '../../models'; +import { axiosInstance } from '../../axios/axiosInstance'; + +/** + * Get the comments for an article. Auth is optional + * @summary Get comments for an article + */ +export const getArticleComments = (slug: string, signal?: AbortSignal) => { + return axiosInstance({ url: `/articles/${slug}/comments`, method: 'get', signal }); +}; + +export const getGetArticleCommentsQueryKey = (slug: string) => [`/articles/${slug}/comments`] as const; + +export const getGetArticleCommentsQueryOptions = < + TData = Awaited>, + TError = void | GenericErrorModel, +>( + slug: string, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetArticleCommentsQueryKey(slug); + + const queryFn: QueryFunction>> = ({ signal }) => + getArticleComments(slug, signal); + + return { queryKey, queryFn, enabled: !!slug, ...queryOptions }; +}; + +export type GetArticleCommentsQueryResult = NonNullable>>; +export type GetArticleCommentsQueryError = void | GenericErrorModel; + +/** + * @summary Get comments for an article + */ +export const useGetArticleComments = < + TData = Awaited>, + TError = void | GenericErrorModel, +>( + slug: string, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetArticleCommentsQueryOptions(slug, options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; + +/** + * Create a comment for an article. Auth is required + * @summary Create a comment for an article + */ +export const createArticleComment = (slug: string, newCommentRequest: NewCommentRequest) => { + return axiosInstance({ + url: `/articles/${slug}/comments`, + method: 'post', + headers: { 'Content-Type': 'application/json' }, + data: newCommentRequest, + }); +}; + +export const getCreateArticleCommentMutationOptions = < + TError = void | GenericErrorModel, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { slug: string; data: NewCommentRequest }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { slug: string; data: NewCommentRequest }, + TContext +> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction< + Awaited>, + { slug: string; data: NewCommentRequest } + > = props => { + const { slug, data } = props ?? {}; + + return createArticleComment(slug, data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type CreateArticleCommentMutationResult = NonNullable>>; +export type CreateArticleCommentMutationBody = NewCommentRequest; +export type CreateArticleCommentMutationError = void | GenericErrorModel; + +/** + * @summary Create a comment for an article + */ +export const useCreateArticleComment = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { slug: string; data: NewCommentRequest }, + TContext + >; +}) => { + const mutationOptions = getCreateArticleCommentMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * Delete a comment for an article. Auth is required + * @summary Delete a comment for an article + */ +export const deleteArticleComment = (slug: string, id: number) => { + return axiosInstance({ url: `/articles/${slug}/comments/${id}`, method: 'delete' }); +}; + +export const getDeleteArticleCommentMutationOptions = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { slug: string; id: number }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { slug: string; id: number }, + TContext +> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction< + Awaited>, + { slug: string; id: number } + > = props => { + const { slug, id } = props ?? {}; + + return deleteArticleComment(slug, id); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteArticleCommentMutationResult = NonNullable>>; + +export type DeleteArticleCommentMutationError = GenericErrorModel; + +/** + * @summary Delete a comment for an article + */ +export const useDeleteArticleComment = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { slug: string; id: number }, + TContext + >; +}) => { + const mutationOptions = getDeleteArticleCommentMutationOptions(options); + + return useMutation(mutationOptions); +}; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/default/default.msw.ts b/apps/realworld/src/shared/api/realworld/endpoints/default/default.msw.ts new file mode 100644 index 00000000..42026d33 --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/default/default.msw.ts @@ -0,0 +1,21 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { rest } from 'msw'; +import { faker } from '@faker-js/faker'; + +export const getGetTagsMock = () => ({ + tags: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.random.word(), + ), +}); + +export const getDefaultMSW = () => [ + rest.get('*/tags', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetTagsMock())); + }), +]; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/default/default.ts b/apps/realworld/src/shared/api/realworld/endpoints/default/default.ts new file mode 100644 index 00000000..3e31e53d --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/default/default.ts @@ -0,0 +1,54 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryFunction, UseQueryResult, QueryKey } from '@tanstack/react-query'; +import type { TagsResponse, GenericErrorModel } from '../../models'; +import { axiosInstance } from '../../axios/axiosInstance'; + +/** + * Get tags. Auth not required + * @summary Get tags + */ +export const getTags = (signal?: AbortSignal) => { + return axiosInstance({ url: `/tags`, method: 'get', signal }); +}; + +export const getGetTagsQueryKey = () => [`/tags`] as const; + +export const getGetTagsQueryOptions = < + TData = Awaited>, + TError = GenericErrorModel, +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetTagsQueryKey(); + + const queryFn: QueryFunction>> = ({ signal }) => getTags(signal); + + return { queryKey, queryFn, ...queryOptions }; +}; + +export type GetTagsQueryResult = NonNullable>>; +export type GetTagsQueryError = GenericErrorModel; + +/** + * @summary Get tags + */ +export const useGetTags = >, TError = GenericErrorModel>(options?: { + query?: UseQueryOptions>, TError, TData>; +}): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetTagsQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/favorites/favorites.msw.ts b/apps/realworld/src/shared/api/realworld/endpoints/favorites/favorites.msw.ts new file mode 100644 index 00000000..ddf00e2c --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/favorites/favorites.msw.ts @@ -0,0 +1,62 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { rest } from 'msw'; +import { faker } from '@faker-js/faker'; + +export const getCreateArticleFavoriteMock = () => ({ + article: { + slug: faker.random.word(), + title: faker.random.word(), + description: faker.random.word(), + body: faker.random.word(), + tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.random.word(), + ), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + favorited: faker.datatype.boolean(), + favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + }, +}); + +export const getDeleteArticleFavoriteMock = () => ({ + article: { + slug: faker.random.word(), + title: faker.random.word(), + description: faker.random.word(), + body: faker.random.word(), + tagList: Array.from({ length: faker.datatype.number({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.random.word(), + ), + createdAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + updatedAt: `${faker.date.past().toISOString().split('.')[0]}Z`, + favorited: faker.datatype.boolean(), + favoritesCount: faker.datatype.number({ min: undefined, max: undefined }), + author: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, + }, +}); + +export const getFavoritesMSW = () => [ + rest.post('*/articles/:slug/favorite', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getCreateArticleFavoriteMock())); + }), + rest.delete('*/articles/:slug/favorite', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getDeleteArticleFavoriteMock())); + }), +]; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/favorites/favorites.ts b/apps/realworld/src/shared/api/realworld/endpoints/favorites/favorites.ts new file mode 100644 index 00000000..6984d002 --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/favorites/favorites.ts @@ -0,0 +1,90 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { useMutation } from '@tanstack/react-query'; +import type { UseMutationOptions, MutationFunction } from '@tanstack/react-query'; +import type { SingleArticleResponse, GenericErrorModel } from '../../models'; +import { axiosInstance } from '../../axios/axiosInstance'; + +/** + * Favorite an article. Auth is required + * @summary Favorite an article + */ +export const createArticleFavorite = (slug: string) => { + return axiosInstance({ url: `/articles/${slug}/favorite`, method: 'post' }); +}; + +export const getCreateArticleFavoriteMutationOptions = < + TError = void | GenericErrorModel, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; +}): UseMutationOptions>, TError, { slug: string }, TContext> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction>, { slug: string }> = props => { + const { slug } = props ?? {}; + + return createArticleFavorite(slug); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type CreateArticleFavoriteMutationResult = NonNullable>>; + +export type CreateArticleFavoriteMutationError = void | GenericErrorModel; + +/** + * @summary Favorite an article + */ +export const useCreateArticleFavorite = (options?: { + mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; +}) => { + const mutationOptions = getCreateArticleFavoriteMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * Unfavorite an article. Auth is required + * @summary Unfavorite an article + */ +export const deleteArticleFavorite = (slug: string) => { + return axiosInstance({ url: `/articles/${slug}/favorite`, method: 'delete' }); +}; + +export const getDeleteArticleFavoriteMutationOptions = < + TError = void | GenericErrorModel, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; +}): UseMutationOptions>, TError, { slug: string }, TContext> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction>, { slug: string }> = props => { + const { slug } = props ?? {}; + + return deleteArticleFavorite(slug); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteArticleFavoriteMutationResult = NonNullable>>; + +export type DeleteArticleFavoriteMutationError = void | GenericErrorModel; + +/** + * @summary Unfavorite an article + */ +export const useDeleteArticleFavorite = (options?: { + mutation?: UseMutationOptions>, TError, { slug: string }, TContext>; +}) => { + const mutationOptions = getDeleteArticleFavoriteMutationOptions(options); + + return useMutation(mutationOptions); +}; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/profile/profile.msw.ts b/apps/realworld/src/shared/api/realworld/endpoints/profile/profile.msw.ts new file mode 100644 index 00000000..8e21bccb --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/profile/profile.msw.ts @@ -0,0 +1,48 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { rest } from 'msw'; +import { faker } from '@faker-js/faker'; + +export const getGetProfileByUsernameMock = () => ({ + profile: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, +}); + +export const getFollowUserByUsernameMock = () => ({ + profile: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, +}); + +export const getUnfollowUserByUsernameMock = () => ({ + profile: { + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + following: faker.datatype.boolean(), + }, +}); + +export const getProfileMSW = () => [ + rest.get('*/profiles/:username', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetProfileByUsernameMock())); + }), + rest.post('*/profiles/:username/follow', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getFollowUserByUsernameMock())); + }), + rest.delete('*/profiles/:username/follow', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getUnfollowUserByUsernameMock())); + }), +]; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/profile/profile.ts b/apps/realworld/src/shared/api/realworld/endpoints/profile/profile.ts new file mode 100644 index 00000000..697e3358 --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/profile/profile.ts @@ -0,0 +1,172 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { useQuery, useMutation } from '@tanstack/react-query'; +import type { + UseQueryOptions, + UseMutationOptions, + QueryFunction, + MutationFunction, + UseQueryResult, + QueryKey, +} from '@tanstack/react-query'; +import type { ProfileResponse, GenericErrorModel } from '../../models'; +import { axiosInstance } from '../../axios/axiosInstance'; + +/** + * Get a profile of a user of the system. Auth is optional + * @summary Get a profile + */ +export const getProfileByUsername = (username: string, signal?: AbortSignal) => { + return axiosInstance({ url: `/profiles/${username}`, method: 'get', signal }); +}; + +export const getGetProfileByUsernameQueryKey = (username: string) => [`/profiles/${username}`] as const; + +export const getGetProfileByUsernameQueryOptions = < + TData = Awaited>, + TError = void | GenericErrorModel, +>( + username: string, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetProfileByUsernameQueryKey(username); + + const queryFn: QueryFunction>> = ({ signal }) => + getProfileByUsername(username, signal); + + return { queryKey, queryFn, enabled: !!username, ...queryOptions }; +}; + +export type GetProfileByUsernameQueryResult = NonNullable>>; +export type GetProfileByUsernameQueryError = void | GenericErrorModel; + +/** + * @summary Get a profile + */ +export const useGetProfileByUsername = < + TData = Awaited>, + TError = void | GenericErrorModel, +>( + username: string, + options?: { query?: UseQueryOptions>, TError, TData> }, +): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetProfileByUsernameQueryOptions(username, options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; + +/** + * Follow a user by username + * @summary Follow a user + */ +export const followUserByUsername = (username: string) => { + return axiosInstance({ url: `/profiles/${username}/follow`, method: 'post' }); +}; + +export const getFollowUserByUsernameMutationOptions = < + TError = void | GenericErrorModel, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { username: string }, + TContext + >; +}): UseMutationOptions>, TError, { username: string }, TContext> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction< + Awaited>, + { username: string } + > = props => { + const { username } = props ?? {}; + + return followUserByUsername(username); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type FollowUserByUsernameMutationResult = NonNullable>>; + +export type FollowUserByUsernameMutationError = void | GenericErrorModel; + +/** + * @summary Follow a user + */ +export const useFollowUserByUsername = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { username: string }, + TContext + >; +}) => { + const mutationOptions = getFollowUserByUsernameMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * Unfollow a user by username + * @summary Unfollow a user + */ +export const unfollowUserByUsername = (username: string) => { + return axiosInstance({ url: `/profiles/${username}/follow`, method: 'delete' }); +}; + +export const getUnfollowUserByUsernameMutationOptions = < + TError = void | GenericErrorModel, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { username: string }, + TContext + >; +}): UseMutationOptions>, TError, { username: string }, TContext> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction< + Awaited>, + { username: string } + > = props => { + const { username } = props ?? {}; + + return unfollowUserByUsername(username); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type UnfollowUserByUsernameMutationResult = NonNullable>>; + +export type UnfollowUserByUsernameMutationError = void | GenericErrorModel; + +/** + * @summary Unfollow a user + */ +export const useUnfollowUserByUsername = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { username: string }, + TContext + >; +}) => { + const mutationOptions = getUnfollowUserByUsernameMutationOptions(options); + + return useMutation(mutationOptions); +}; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/user-and-authentication/user-and-authentication.msw.ts b/apps/realworld/src/shared/api/realworld/endpoints/user-and-authentication/user-and-authentication.msw.ts new file mode 100644 index 00000000..a0a962af --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/user-and-authentication/user-and-authentication.msw.ts @@ -0,0 +1,64 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { rest } from 'msw'; +import { faker } from '@faker-js/faker'; + +export const getLoginMock = () => ({ + user: { + email: faker.random.word(), + token: faker.random.word(), + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + }, +}); + +export const getCreateUserMock = () => ({ + user: { + email: faker.random.word(), + token: faker.random.word(), + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + }, +}); + +export const getGetCurrentUserMock = () => ({ + user: { + email: faker.random.word(), + token: faker.random.word(), + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + }, +}); + +export const getUpdateCurrentUserMock = () => ({ + user: { + email: faker.random.word(), + token: faker.random.word(), + username: faker.random.word(), + bio: faker.random.word(), + image: faker.random.word(), + }, +}); + +export const getUserAndAuthenticationMSW = () => [ + rest.post('*/users/login', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getLoginMock())); + }), + rest.post('*/users', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getCreateUserMock())); + }), + rest.get('*/user', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getGetCurrentUserMock())); + }), + rest.put('*/user', (_req, res, ctx) => { + return res(ctx.delay(1000), ctx.status(200, 'Mocked status'), ctx.json(getUpdateCurrentUserMock())); + }), +]; diff --git a/apps/realworld/src/shared/api/realworld/endpoints/user-and-authentication/user-and-authentication.ts b/apps/realworld/src/shared/api/realworld/endpoints/user-and-authentication/user-and-authentication.ts new file mode 100644 index 00000000..c3b73b73 --- /dev/null +++ b/apps/realworld/src/shared/api/realworld/endpoints/user-and-authentication/user-and-authentication.ts @@ -0,0 +1,212 @@ +/** + * Generated by orval v6.17.0 🍺 + * Do not edit manually. + * Conduit API + * Conduit API + * OpenAPI spec version: 1.0.0 + */ +import { useQuery, useMutation } from '@tanstack/react-query'; +import type { + UseQueryOptions, + UseMutationOptions, + QueryFunction, + MutationFunction, + UseQueryResult, + QueryKey, +} from '@tanstack/react-query'; +import type { + UserResponse, + GenericErrorModel, + LoginUserRequest, + NewUserRequest, + UpdateUserRequest, +} from '../../models'; +import { axiosInstance } from '../../axios/axiosInstance'; + +/** + * Login for existing user + * @summary Existing user login + */ +export const login = (loginUserRequest: LoginUserRequest) => { + return axiosInstance({ + url: `/users/login`, + method: 'post', + headers: { 'Content-Type': 'application/json' }, + data: loginUserRequest, + }); +}; + +export const getLoginMutationOptions = (options?: { + mutation?: UseMutationOptions>, TError, { data: LoginUserRequest }, TContext>; +}): UseMutationOptions>, TError, { data: LoginUserRequest }, TContext> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction>, { data: LoginUserRequest }> = props => { + const { data } = props ?? {}; + + return login(data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type LoginMutationResult = NonNullable>>; +export type LoginMutationBody = LoginUserRequest; +export type LoginMutationError = void | GenericErrorModel; + +/** + * @summary Existing user login + */ +export const useLogin = (options?: { + mutation?: UseMutationOptions>, TError, { data: LoginUserRequest }, TContext>; +}) => { + const mutationOptions = getLoginMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * Register a new user + * @summary Register a new user + */ +export const createUser = (newUserRequest: NewUserRequest) => { + return axiosInstance({ + url: `/users`, + method: 'post', + headers: { 'Content-Type': 'application/json' }, + data: newUserRequest, + }); +}; + +export const getCreateUserMutationOptions = (options?: { + mutation?: UseMutationOptions>, TError, { data: NewUserRequest }, TContext>; +}): UseMutationOptions>, TError, { data: NewUserRequest }, TContext> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction>, { data: NewUserRequest }> = props => { + const { data } = props ?? {}; + + return createUser(data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type CreateUserMutationResult = NonNullable>>; +export type CreateUserMutationBody = NewUserRequest; +export type CreateUserMutationError = GenericErrorModel; + +/** + * @summary Register a new user + */ +export const useCreateUser = (options?: { + mutation?: UseMutationOptions>, TError, { data: NewUserRequest }, TContext>; +}) => { + const mutationOptions = getCreateUserMutationOptions(options); + + return useMutation(mutationOptions); +}; +/** + * Gets the currently logged-in user + * @summary Get current user + */ +export const getCurrentUser = (signal?: AbortSignal) => { + return axiosInstance({ url: `/user`, method: 'get', signal }); +}; + +export const getGetCurrentUserQueryKey = () => [`/user`] as const; + +export const getGetCurrentUserQueryOptions = < + TData = Awaited>, + TError = void | GenericErrorModel, +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}): UseQueryOptions>, TError, TData> & { queryKey: QueryKey } => { + const { query: queryOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetCurrentUserQueryKey(); + + const queryFn: QueryFunction>> = ({ signal }) => getCurrentUser(signal); + + return { queryKey, queryFn, ...queryOptions }; +}; + +export type GetCurrentUserQueryResult = NonNullable>>; +export type GetCurrentUserQueryError = void | GenericErrorModel; + +/** + * @summary Get current user + */ +export const useGetCurrentUser = < + TData = Awaited>, + TError = void | GenericErrorModel, +>(options?: { + query?: UseQueryOptions>, TError, TData>; +}): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetCurrentUserQueryOptions(options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; + +/** + * Updated user information for current user + * @summary Update current user + */ +export const updateCurrentUser = (updateUserRequest: UpdateUserRequest) => { + return axiosInstance({ + url: `/user`, + method: 'put', + headers: { 'Content-Type': 'application/json' }, + data: updateUserRequest, + }); +}; + +export const getUpdateCurrentUserMutationOptions = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: UpdateUserRequest }, + TContext + >; +}): UseMutationOptions< + Awaited>, + TError, + { data: UpdateUserRequest }, + TContext +> => { + const { mutation: mutationOptions } = options ?? {}; + + const mutationFn: MutationFunction< + Awaited>, + { data: UpdateUserRequest } + > = props => { + const { data } = props ?? {}; + + return updateCurrentUser(data); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type UpdateCurrentUserMutationResult = NonNullable>>; +export type UpdateCurrentUserMutationBody = UpdateUserRequest; +export type UpdateCurrentUserMutationError = void | GenericErrorModel; + +/** + * @summary Update current user + */ +export const useUpdateCurrentUser = (options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: UpdateUserRequest }, + TContext + >; +}) => { + const mutationOptions = getUpdateCurrentUserMutationOptions(options); + + return useMutation(mutationOptions); +}; diff --git a/apps/realworld/src/shared/api/realworld/models/article.ts b/apps/realworld/src/shared/api/realworld/models/article.ts index 013e9f93..bbfaa08e 100644 --- a/apps/realworld/src/shared/api/realworld/models/article.ts +++ b/apps/realworld/src/shared/api/realworld/models/article.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { Profile } from "./profile"; +import type { Profile } from './profile'; export interface Article { slug: string; diff --git a/apps/realworld/src/shared/api/realworld/models/comment.ts b/apps/realworld/src/shared/api/realworld/models/comment.ts index f172180e..e1ae6a9f 100644 --- a/apps/realworld/src/shared/api/realworld/models/comment.ts +++ b/apps/realworld/src/shared/api/realworld/models/comment.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { Profile } from "./profile"; +import type { Profile } from './profile'; export interface Comment { id: number; diff --git a/apps/realworld/src/shared/api/realworld/models/genericErrorModel.ts b/apps/realworld/src/shared/api/realworld/models/genericErrorModel.ts index 9c7d0441..726c942c 100644 --- a/apps/realworld/src/shared/api/realworld/models/genericErrorModel.ts +++ b/apps/realworld/src/shared/api/realworld/models/genericErrorModel.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { GenericErrorModelErrors } from "./genericErrorModelErrors"; +import type { GenericErrorModelErrors } from './genericErrorModelErrors'; export interface GenericErrorModel { errors: GenericErrorModelErrors; diff --git a/apps/realworld/src/shared/api/realworld/models/index.ts b/apps/realworld/src/shared/api/realworld/models/index.ts index 085599ab..138c69ee 100644 --- a/apps/realworld/src/shared/api/realworld/models/index.ts +++ b/apps/realworld/src/shared/api/realworld/models/index.ts @@ -6,30 +6,30 @@ * OpenAPI spec version: 1.0.0 */ -export * from "./article"; -export * from "./comment"; -export * from "./genericErrorModel"; -export * from "./genericErrorModelErrors"; -export * from "./getArticlesFeedParams"; -export * from "./getArticlesParams"; -export * from "./loginUser"; -export * from "./loginUserRequest"; -export * from "./multipleArticlesResponse"; -export * from "./multipleCommentsResponse"; -export * from "./newArticle"; -export * from "./newArticleRequest"; -export * from "./newComment"; -export * from "./newCommentRequest"; -export * from "./newUser"; -export * from "./newUserRequest"; -export * from "./profile"; -export * from "./profileResponse"; -export * from "./singleArticleResponse"; -export * from "./singleCommentResponse"; -export * from "./tagsResponse"; -export * from "./updateArticle"; -export * from "./updateArticleRequest"; -export * from "./updateUser"; -export * from "./updateUserRequest"; -export * from "./user"; -export * from "./userResponse"; +export * from './article'; +export * from './comment'; +export * from './genericErrorModel'; +export * from './genericErrorModelErrors'; +export * from './getArticlesFeedParams'; +export * from './getArticlesParams'; +export * from './loginUser'; +export * from './loginUserRequest'; +export * from './multipleArticlesResponse'; +export * from './multipleCommentsResponse'; +export * from './newArticle'; +export * from './newArticleRequest'; +export * from './newComment'; +export * from './newCommentRequest'; +export * from './newUser'; +export * from './newUserRequest'; +export * from './profile'; +export * from './profileResponse'; +export * from './singleArticleResponse'; +export * from './singleCommentResponse'; +export * from './tagsResponse'; +export * from './updateArticle'; +export * from './updateArticleRequest'; +export * from './updateUser'; +export * from './updateUserRequest'; +export * from './user'; +export * from './userResponse'; diff --git a/apps/realworld/src/shared/api/realworld/models/loginUserRequest.ts b/apps/realworld/src/shared/api/realworld/models/loginUserRequest.ts index 0257b7e8..ad3e9169 100644 --- a/apps/realworld/src/shared/api/realworld/models/loginUserRequest.ts +++ b/apps/realworld/src/shared/api/realworld/models/loginUserRequest.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { LoginUser } from "./loginUser"; +import type { LoginUser } from './loginUser'; export interface LoginUserRequest { user: LoginUser; diff --git a/apps/realworld/src/shared/api/realworld/models/multipleArticlesResponse.ts b/apps/realworld/src/shared/api/realworld/models/multipleArticlesResponse.ts index 79ec8f45..14acf302 100644 --- a/apps/realworld/src/shared/api/realworld/models/multipleArticlesResponse.ts +++ b/apps/realworld/src/shared/api/realworld/models/multipleArticlesResponse.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { Article } from "./article"; +import type { Article } from './article'; export interface MultipleArticlesResponse { articles: Article[]; diff --git a/apps/realworld/src/shared/api/realworld/models/multipleCommentsResponse.ts b/apps/realworld/src/shared/api/realworld/models/multipleCommentsResponse.ts index 5ace7040..aab7a1e5 100644 --- a/apps/realworld/src/shared/api/realworld/models/multipleCommentsResponse.ts +++ b/apps/realworld/src/shared/api/realworld/models/multipleCommentsResponse.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { Comment } from "./comment"; +import type { Comment } from './comment'; export interface MultipleCommentsResponse { comments: Comment[]; diff --git a/apps/realworld/src/shared/api/realworld/models/newArticleRequest.ts b/apps/realworld/src/shared/api/realworld/models/newArticleRequest.ts index 37c51693..ee4dbfc1 100644 --- a/apps/realworld/src/shared/api/realworld/models/newArticleRequest.ts +++ b/apps/realworld/src/shared/api/realworld/models/newArticleRequest.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { NewArticle } from "./newArticle"; +import type { NewArticle } from './newArticle'; export interface NewArticleRequest { article: NewArticle; diff --git a/apps/realworld/src/shared/api/realworld/models/newUserRequest.ts b/apps/realworld/src/shared/api/realworld/models/newUserRequest.ts index 86229c37..e1ae178d 100644 --- a/apps/realworld/src/shared/api/realworld/models/newUserRequest.ts +++ b/apps/realworld/src/shared/api/realworld/models/newUserRequest.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { NewUser } from "./newUser"; +import type { NewUser } from './newUser'; export interface NewUserRequest { user: NewUser; diff --git a/apps/realworld/src/shared/api/realworld/models/profileResponse.ts b/apps/realworld/src/shared/api/realworld/models/profileResponse.ts index bf7509f3..a2b95afa 100644 --- a/apps/realworld/src/shared/api/realworld/models/profileResponse.ts +++ b/apps/realworld/src/shared/api/realworld/models/profileResponse.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { Profile } from "./profile"; +import type { Profile } from './profile'; export interface ProfileResponse { profile: Profile; diff --git a/apps/realworld/src/shared/api/realworld/models/singleArticleResponse.ts b/apps/realworld/src/shared/api/realworld/models/singleArticleResponse.ts index 944f7b3b..4d4305e1 100644 --- a/apps/realworld/src/shared/api/realworld/models/singleArticleResponse.ts +++ b/apps/realworld/src/shared/api/realworld/models/singleArticleResponse.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { Article } from "./article"; +import type { Article } from './article'; export interface SingleArticleResponse { article: Article; diff --git a/apps/realworld/src/shared/api/realworld/models/singleCommentResponse.ts b/apps/realworld/src/shared/api/realworld/models/singleCommentResponse.ts index b0b6116d..a32e1bf5 100644 --- a/apps/realworld/src/shared/api/realworld/models/singleCommentResponse.ts +++ b/apps/realworld/src/shared/api/realworld/models/singleCommentResponse.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { Comment } from "./comment"; +import type { Comment } from './comment'; export interface SingleCommentResponse { comment: Comment; diff --git a/apps/realworld/src/shared/api/realworld/models/updateArticleRequest.ts b/apps/realworld/src/shared/api/realworld/models/updateArticleRequest.ts index bdfaae08..0eb8174b 100644 --- a/apps/realworld/src/shared/api/realworld/models/updateArticleRequest.ts +++ b/apps/realworld/src/shared/api/realworld/models/updateArticleRequest.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { UpdateArticle } from "./updateArticle"; +import type { UpdateArticle } from './updateArticle'; export interface UpdateArticleRequest { article: UpdateArticle; diff --git a/apps/realworld/src/shared/api/realworld/models/updateUserRequest.ts b/apps/realworld/src/shared/api/realworld/models/updateUserRequest.ts index bdce6df1..1d75e925 100644 --- a/apps/realworld/src/shared/api/realworld/models/updateUserRequest.ts +++ b/apps/realworld/src/shared/api/realworld/models/updateUserRequest.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { UpdateUser } from "./updateUser"; +import type { UpdateUser } from './updateUser'; export interface UpdateUserRequest { user: UpdateUser; diff --git a/apps/realworld/src/shared/api/realworld/models/userResponse.ts b/apps/realworld/src/shared/api/realworld/models/userResponse.ts index 29da65a0..83e80833 100644 --- a/apps/realworld/src/shared/api/realworld/models/userResponse.ts +++ b/apps/realworld/src/shared/api/realworld/models/userResponse.ts @@ -5,7 +5,7 @@ * Conduit API * OpenAPI spec version: 1.0.0 */ -import type { User } from "./user"; +import type { User } from './user'; export interface UserResponse { user: User; diff --git a/apps/realworld/src/widgets/GNB/ui/GNB/gnb.test.tsx b/apps/realworld/src/widgets/GNB/ui/GNB/gnb.test.tsx new file mode 100644 index 00000000..14e9dc57 --- /dev/null +++ b/apps/realworld/src/widgets/GNB/ui/GNB/gnb.test.tsx @@ -0,0 +1,39 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import GNB from './gnb'; + +describe('GNB', () => { + it('GNB에 conduit, Home, Sign in, Sign up 버튼이 표시된다.', () => { + render(); + + const logo = screen.getByTestId('gnbLogo'); + const home = screen.getByTestId('gnbHome'); + const signIn = screen.getByTestId('gnbSignIn'); + const signUp = screen.getByTestId('gnbSignUp'); + + expect(logo).toBeInTheDocument(); + expect(home).toBeInTheDocument(); + expect(signIn).toBeInTheDocument(); + expect(signUp).toBeInTheDocument(); + }); + + // 페이지 이동을 어떻게 테스트할까? + // - 세부 구현사항을 테스트하지마라 + // - 유저 행동과 최대한 유사한 테스트코드를 작성하라 + // + // 1. url로 테스트 <-- 페이지 이동은 성공했지만 유저가 해당 페이지에서 정상적인 컨텐츠를 볼 수 없을 수 있다. (유저는 url을 확인하지 않는다. <- 유저행동과 거리가 멂) + // 2. 해당 페이지에 컨텐츠를 테스트 <-- 해당 페이지에 컨텐츠가 바뀔 확률이 있다. (이는 유저는 행동과 가깝다. 그럼 GNB와 하위 컴포넌트를 같이 불러와야하는데? 그럼 그게 GNB테스트는 아니지 않을까? 앱 전체 테스트인것같은데..) + + // it('conduit 버튼을 누르면 루트페이지(/)로 이동한다.', () => { + // render(); + // }); + // it('Home 버튼을 누르면 루트페이지(/)로 이동한다.', () => { + // render(); + // }); + // it('Sign in 버튼을 누르면 로그인 페이지로 이동한다.', () => { + // render(); + // }); + // it('Sign up 버튼을 누르면 로그인 페이지로 이동한다.', () => { + // render(); + // }); +}); diff --git a/apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx b/apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx new file mode 100644 index 00000000..9fa3d4ae --- /dev/null +++ b/apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx @@ -0,0 +1,35 @@ +'use client'; + +import Link from 'next/link'; +import React from 'react'; + +const GNB = () => { + return ( + + ); +}; + +export default GNB; diff --git a/apps/realworld/src/widgets/article/article-list/article-list.tsx b/apps/realworld/src/widgets/article/article-list/article-list.tsx index b178142f..b7eeadd4 100644 --- a/apps/realworld/src/widgets/article/article-list/article-list.tsx +++ b/apps/realworld/src/widgets/article/article-list/article-list.tsx @@ -1,10 +1,10 @@ 'use client'; -import { useGetArticles } from '@/shared/api/realworld/apis'; import React from 'react'; import { ArticleListItem } from '..'; import { ArticleListPagination } from '@/features/article'; import { getItemIndex } from '@/shared/utils/array'; +import { useGetArticles } from '@/shared/api/realworld/endpoints/articles/articles'; interface ArticleListProps {} From a0019e2f398cd70ff2940f0cae360df7701ccec9 Mon Sep 17 00:00:00 2001 From: ludacirs Date: Wed, 13 Sep 2023 09:51:12 +0900 Subject: [PATCH 02/18] =?UTF-8?q?chore:=20=EB=8C=80/=EC=86=8C=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=20=EC=9D=B4=EC=8A=88=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/realworld/src/widgets/GNB/index.ts | 3 -- .../src/widgets/GNB/ui/GNB/GNB.test.tsx | 39 ------------------- apps/realworld/src/widgets/GNB/ui/GNB/GNB.tsx | 35 ----------------- .../src/widgets/GNB/ui/GNB/gnb.test.tsx | 39 ------------------- apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx | 35 ----------------- 5 files changed, 151 deletions(-) delete mode 100644 apps/realworld/src/widgets/GNB/index.ts delete mode 100644 apps/realworld/src/widgets/GNB/ui/GNB/GNB.test.tsx delete mode 100644 apps/realworld/src/widgets/GNB/ui/GNB/GNB.tsx delete mode 100644 apps/realworld/src/widgets/GNB/ui/GNB/gnb.test.tsx delete mode 100644 apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx diff --git a/apps/realworld/src/widgets/GNB/index.ts b/apps/realworld/src/widgets/GNB/index.ts deleted file mode 100644 index 1c9f3a8b..00000000 --- a/apps/realworld/src/widgets/GNB/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import GNB from './ui/gnb/gnb'; - -export { GNB }; diff --git a/apps/realworld/src/widgets/GNB/ui/GNB/GNB.test.tsx b/apps/realworld/src/widgets/GNB/ui/GNB/GNB.test.tsx deleted file mode 100644 index 14e9dc57..00000000 --- a/apps/realworld/src/widgets/GNB/ui/GNB/GNB.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import GNB from './gnb'; - -describe('GNB', () => { - it('GNB에 conduit, Home, Sign in, Sign up 버튼이 표시된다.', () => { - render(); - - const logo = screen.getByTestId('gnbLogo'); - const home = screen.getByTestId('gnbHome'); - const signIn = screen.getByTestId('gnbSignIn'); - const signUp = screen.getByTestId('gnbSignUp'); - - expect(logo).toBeInTheDocument(); - expect(home).toBeInTheDocument(); - expect(signIn).toBeInTheDocument(); - expect(signUp).toBeInTheDocument(); - }); - - // 페이지 이동을 어떻게 테스트할까? - // - 세부 구현사항을 테스트하지마라 - // - 유저 행동과 최대한 유사한 테스트코드를 작성하라 - // - // 1. url로 테스트 <-- 페이지 이동은 성공했지만 유저가 해당 페이지에서 정상적인 컨텐츠를 볼 수 없을 수 있다. (유저는 url을 확인하지 않는다. <- 유저행동과 거리가 멂) - // 2. 해당 페이지에 컨텐츠를 테스트 <-- 해당 페이지에 컨텐츠가 바뀔 확률이 있다. (이는 유저는 행동과 가깝다. 그럼 GNB와 하위 컴포넌트를 같이 불러와야하는데? 그럼 그게 GNB테스트는 아니지 않을까? 앱 전체 테스트인것같은데..) - - // it('conduit 버튼을 누르면 루트페이지(/)로 이동한다.', () => { - // render(); - // }); - // it('Home 버튼을 누르면 루트페이지(/)로 이동한다.', () => { - // render(); - // }); - // it('Sign in 버튼을 누르면 로그인 페이지로 이동한다.', () => { - // render(); - // }); - // it('Sign up 버튼을 누르면 로그인 페이지로 이동한다.', () => { - // render(); - // }); -}); diff --git a/apps/realworld/src/widgets/GNB/ui/GNB/GNB.tsx b/apps/realworld/src/widgets/GNB/ui/GNB/GNB.tsx deleted file mode 100644 index 9fa3d4ae..00000000 --- a/apps/realworld/src/widgets/GNB/ui/GNB/GNB.tsx +++ /dev/null @@ -1,35 +0,0 @@ -'use client'; - -import Link from 'next/link'; -import React from 'react'; - -const GNB = () => { - return ( - - ); -}; - -export default GNB; diff --git a/apps/realworld/src/widgets/GNB/ui/GNB/gnb.test.tsx b/apps/realworld/src/widgets/GNB/ui/GNB/gnb.test.tsx deleted file mode 100644 index 14e9dc57..00000000 --- a/apps/realworld/src/widgets/GNB/ui/GNB/gnb.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import GNB from './gnb'; - -describe('GNB', () => { - it('GNB에 conduit, Home, Sign in, Sign up 버튼이 표시된다.', () => { - render(); - - const logo = screen.getByTestId('gnbLogo'); - const home = screen.getByTestId('gnbHome'); - const signIn = screen.getByTestId('gnbSignIn'); - const signUp = screen.getByTestId('gnbSignUp'); - - expect(logo).toBeInTheDocument(); - expect(home).toBeInTheDocument(); - expect(signIn).toBeInTheDocument(); - expect(signUp).toBeInTheDocument(); - }); - - // 페이지 이동을 어떻게 테스트할까? - // - 세부 구현사항을 테스트하지마라 - // - 유저 행동과 최대한 유사한 테스트코드를 작성하라 - // - // 1. url로 테스트 <-- 페이지 이동은 성공했지만 유저가 해당 페이지에서 정상적인 컨텐츠를 볼 수 없을 수 있다. (유저는 url을 확인하지 않는다. <- 유저행동과 거리가 멂) - // 2. 해당 페이지에 컨텐츠를 테스트 <-- 해당 페이지에 컨텐츠가 바뀔 확률이 있다. (이는 유저는 행동과 가깝다. 그럼 GNB와 하위 컴포넌트를 같이 불러와야하는데? 그럼 그게 GNB테스트는 아니지 않을까? 앱 전체 테스트인것같은데..) - - // it('conduit 버튼을 누르면 루트페이지(/)로 이동한다.', () => { - // render(); - // }); - // it('Home 버튼을 누르면 루트페이지(/)로 이동한다.', () => { - // render(); - // }); - // it('Sign in 버튼을 누르면 로그인 페이지로 이동한다.', () => { - // render(); - // }); - // it('Sign up 버튼을 누르면 로그인 페이지로 이동한다.', () => { - // render(); - // }); -}); diff --git a/apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx b/apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx deleted file mode 100644 index 9fa3d4ae..00000000 --- a/apps/realworld/src/widgets/GNB/ui/GNB/gnb.tsx +++ /dev/null @@ -1,35 +0,0 @@ -'use client'; - -import Link from 'next/link'; -import React from 'react'; - -const GNB = () => { - return ( - - ); -}; - -export default GNB; From 8c577948f582142f236013b37152dff91aa7f47e Mon Sep 17 00:00:00 2001 From: ludacirs Date: Wed, 13 Sep 2023 17:33:30 +0900 Subject: [PATCH 03/18] =?UTF-8?q?chore:=20dayjs=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/realworld/package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/apps/realworld/package.json b/apps/realworld/package.json index bc4b7e8a..f2a714f1 100644 --- a/apps/realworld/package.json +++ b/apps/realworld/package.json @@ -20,6 +20,7 @@ "@types/react-dom": "18.2.7", "autoprefixer": "^10.4.15", "axios": "^1.5.0", + "dayjs": "^1.11.9", "eslint": "8.48.0", "eslint-config-next": "13.4.19", "next": "13.4.19", diff --git a/yarn.lock b/yarn.lock index 75a11823..4f07d1af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2822,6 +2822,11 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" +dayjs@^1.11.9: + version "1.11.9" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" + integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" From 22e5c641d05348fc876654b784efb120fe38426d Mon Sep 17 00:00:00 2001 From: ludacirs Date: Wed, 13 Sep 2023 17:34:07 +0900 Subject: [PATCH 04/18] =?UTF-8?q?ui=20fix:=20`Avatar`=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ui/src/components/Avatar/Avatar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/Avatar/Avatar.tsx b/packages/ui/src/components/Avatar/Avatar.tsx index ac7c70e6..962b39a9 100644 --- a/packages/ui/src/components/Avatar/Avatar.tsx +++ b/packages/ui/src/components/Avatar/Avatar.tsx @@ -12,7 +12,7 @@ const Avatar = forwardRef( ) => { return ( {src} Date: Wed, 13 Sep 2023 17:34:45 +0900 Subject: [PATCH 05/18] =?UTF-8?q?[tailwind-config]=20feat:=20`text-`=20?= =?UTF-8?q?=EC=A0=91=EB=91=90=EC=96=B4=20=EC=8A=A4=ED=83=80=EC=9D=BC=20bui?= =?UTF-8?q?ld=EC=8B=9C=20=ED=8F=AC=ED=95=A8=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/tailwindcss-config/base.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/tailwindcss-config/base.config.js b/packages/tailwindcss-config/base.config.js index a10c58a3..9d18333d 100644 --- a/packages/tailwindcss-config/base.config.js +++ b/packages/tailwindcss-config/base.config.js @@ -15,6 +15,9 @@ module.exports = { `src/**/*.{js,ts,jsx,tsx}`, "../../packages/ui/src/**/*.{js,ts,jsx,tsx}", ], + safelist: { + pattern: /text-/, + }, theme: { extend: { colors, From 9befe000582a690e594140fc7d8de1516dca86c7 Mon Sep 17 00:00:00 2001 From: ludacirs Date: Wed, 13 Sep 2023 17:35:41 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat:=20`article-tag-list`=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=20=EA=B0=80=EB=8A=A5/=EB=B6=88=EA=B0=80=EB=8A=A5=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tag/ui/article-tag-list/article-tag-list.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/realworld/src/entities/tag/ui/article-tag-list/article-tag-list.tsx b/apps/realworld/src/entities/tag/ui/article-tag-list/article-tag-list.tsx index 2fb92153..f452e457 100644 --- a/apps/realworld/src/entities/tag/ui/article-tag-list/article-tag-list.tsx +++ b/apps/realworld/src/entities/tag/ui/article-tag-list/article-tag-list.tsx @@ -4,15 +4,24 @@ import React from 'react'; interface ArticleTagListProps { tagList: string[]; - slug: string; + slug?: string; } const ArticleTagList = ({ tagList, slug }: ArticleTagListProps) => { + if (slug) { + return ( + + {tagList.map(label => ( + + ))} + + ); + } return ( - +
{tagList.map(label => ( ))} - +
); }; From ca6ff11158811e4346ac033198eda6a5edb7d3c6 Mon Sep 17 00:00:00 2001 From: ludacirs Date: Wed, 13 Sep 2023 22:56:23 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20un/follow=20?= =?UTF-8?q?=EB=B0=8F=20=ED=86=A0=EA=B8=80=20=EB=B2=84=ED=8A=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/realworld/src/features/article/index.ts | 5 +-- .../article-favorite-button.tsx} | 22 +++++------- .../article-unfavorite-button.tsx | 34 +++++++++++++++++++ .../src/features/article/ui/index.ts | 5 +++ apps/realworld/src/features/user/index.ts | 1 + apps/realworld/src/features/user/ui/index.ts | 4 +++ .../user-follow-button/user-follow-button.tsx | 30 ++++++++++++++++ .../user-unfollow-button.tsx | 30 ++++++++++++++++ .../article-favorite-toggle-button.tsx | 18 ++++++++++ apps/realworld/src/widgets/user/index.ts | 1 + apps/realworld/src/widgets/user/ui/index.ts | 3 ++ .../user-follow-toggle-button.tsx | 13 +++++++ 12 files changed, 148 insertions(+), 18 deletions(-) rename apps/realworld/src/features/article/ui/{article-like-button/article-like-button.tsx => article-favorite-button/article-favorite-button.tsx} (54%) create mode 100644 apps/realworld/src/features/article/ui/article-unfavorite-button/article-unfavorite-button.tsx create mode 100644 apps/realworld/src/features/article/ui/index.ts create mode 100644 apps/realworld/src/features/user/ui/index.ts create mode 100644 apps/realworld/src/features/user/ui/user-follow-button/user-follow-button.tsx create mode 100644 apps/realworld/src/features/user/ui/user-unfollow-button/user-unfollow-button.tsx create mode 100644 apps/realworld/src/widgets/article/article-favorite-toggle-button/article-favorite-toggle-button.tsx create mode 100644 apps/realworld/src/widgets/user/index.ts create mode 100644 apps/realworld/src/widgets/user/ui/index.ts create mode 100644 apps/realworld/src/widgets/user/ui/user-follow-toggle-button/user-follow-toggle-button.tsx diff --git a/apps/realworld/src/features/article/index.ts b/apps/realworld/src/features/article/index.ts index 9a66181e..5ecdd1f3 100644 --- a/apps/realworld/src/features/article/index.ts +++ b/apps/realworld/src/features/article/index.ts @@ -1,4 +1 @@ -import ArticleLikeButton from './ui/article-like-button/article-like-button'; -import ArticleListPagination from './ui/article-list-pagination/article-list-pagination'; - -export { ArticleLikeButton, ArticleListPagination }; +export * from './ui'; diff --git a/apps/realworld/src/features/article/ui/article-like-button/article-like-button.tsx b/apps/realworld/src/features/article/ui/article-favorite-button/article-favorite-button.tsx similarity index 54% rename from apps/realworld/src/features/article/ui/article-like-button/article-like-button.tsx rename to apps/realworld/src/features/article/ui/article-favorite-button/article-favorite-button.tsx index d887f3b0..71a1327a 100644 --- a/apps/realworld/src/features/article/ui/article-like-button/article-like-button.tsx +++ b/apps/realworld/src/features/article/ui/article-favorite-button/article-favorite-button.tsx @@ -4,38 +4,32 @@ import { Button } from '@packages/ui'; import React from 'react'; import { IoIosHeart } from 'react-icons/io'; -interface ArticleLikeButtonProps { - label: number; - favorited: boolean; +interface ArticleFavoriteButtonProps { slug: string; + favoritesCount: number; } -const ArticleLikeButton = ({ label, favorited, slug }: ArticleLikeButtonProps) => { - const variant = getButtonVariant(favorited); +const ArticleFavoriteButton = ({ slug, favoritesCount }: ArticleFavoriteButtonProps) => { const { mutateAsync: createArticleFavorite } = useCreateArticleFavorite(); - const handleLikeButtonClick = () => { + const handleFavoriteButtonClick = () => { createArticleFavorite({ slug }); // TODO: validate queries }; return ( ); }; -export default ArticleLikeButton; - -const getButtonVariant = (favorited: boolean) => { - return favorited ? 'fill' : 'outlined'; -}; +export default ArticleFavoriteButton; diff --git a/apps/realworld/src/features/article/ui/article-unfavorite-button/article-unfavorite-button.tsx b/apps/realworld/src/features/article/ui/article-unfavorite-button/article-unfavorite-button.tsx new file mode 100644 index 00000000..1c5a38d1 --- /dev/null +++ b/apps/realworld/src/features/article/ui/article-unfavorite-button/article-unfavorite-button.tsx @@ -0,0 +1,34 @@ +'use client'; +import { useDeleteArticleFavorite } from '@/shared/api/realworld/endpoints/favorites/favorites'; +import { Button } from '@packages/ui'; +import React from 'react'; +import { IoIosHeart } from 'react-icons/io'; + +interface ArticleUnfavoriteButtonProps { + slug: string; + favoritesCount: number; +} + +const ArticleUnfavoriteButton = ({ slug, favoritesCount }: ArticleUnfavoriteButtonProps) => { + const { mutateAsync: deleteArticleFavorite } = useDeleteArticleFavorite(); + + const handleUnfavoriteButtonClick = () => { + deleteArticleFavorite({ slug }); + // TODO: validate queries + }; + return ( + + ); +}; + +export default ArticleUnfavoriteButton; diff --git a/apps/realworld/src/features/article/ui/index.ts b/apps/realworld/src/features/article/ui/index.ts new file mode 100644 index 00000000..1b6d3770 --- /dev/null +++ b/apps/realworld/src/features/article/ui/index.ts @@ -0,0 +1,5 @@ +import ArticleFavoriteButton from './article-favorite-button/article-favorite-button'; +import ArticleListPagination from './article-list-pagination/article-list-pagination'; +import ArticleUnfavoriteButton from './article-unfavorite-button/article-unfavorite-button'; + +export { ArticleFavoriteButton, ArticleListPagination, ArticleUnfavoriteButton }; diff --git a/apps/realworld/src/features/user/index.ts b/apps/realworld/src/features/user/index.ts index e69de29b..5ecdd1f3 100644 --- a/apps/realworld/src/features/user/index.ts +++ b/apps/realworld/src/features/user/index.ts @@ -0,0 +1 @@ +export * from './ui'; diff --git a/apps/realworld/src/features/user/ui/index.ts b/apps/realworld/src/features/user/ui/index.ts new file mode 100644 index 00000000..e31378ab --- /dev/null +++ b/apps/realworld/src/features/user/ui/index.ts @@ -0,0 +1,4 @@ +import UserFollowButton from './user-follow-button/user-follow-button'; +import UserUnfollowButton from './user-unfollow-button/user-unfollow-button'; + +export { UserFollowButton, UserUnfollowButton }; diff --git a/apps/realworld/src/features/user/ui/user-follow-button/user-follow-button.tsx b/apps/realworld/src/features/user/ui/user-follow-button/user-follow-button.tsx new file mode 100644 index 00000000..6df127a3 --- /dev/null +++ b/apps/realworld/src/features/user/ui/user-follow-button/user-follow-button.tsx @@ -0,0 +1,30 @@ +import { useFollowUserByUsername } from '@/shared/api/realworld/endpoints/profile/profile'; +import { Button } from '@packages/ui'; +import React from 'react'; +import { IoIosAdd } from 'react-icons/io'; + +interface UserFollowButtonProps { + username: string; +} + +const UserFollowButton = ({ username }: UserFollowButtonProps) => { + const { mutateAsync: followUserByUsername } = useFollowUserByUsername(); + const handleFollowUser = () => { + followUserByUsername({ username }); + }; + return ( + + ); +}; + +export default UserFollowButton; diff --git a/apps/realworld/src/features/user/ui/user-unfollow-button/user-unfollow-button.tsx b/apps/realworld/src/features/user/ui/user-unfollow-button/user-unfollow-button.tsx new file mode 100644 index 00000000..f817a972 --- /dev/null +++ b/apps/realworld/src/features/user/ui/user-unfollow-button/user-unfollow-button.tsx @@ -0,0 +1,30 @@ +import { useFollowUserByUsername, useUnfollowUserByUsername } from '@/shared/api/realworld/endpoints/profile/profile'; +import { Button } from '@packages/ui'; +import React from 'react'; +import { IoIosAdd } from 'react-icons/io'; + +interface UserUnfollowButtonProps { + username: string; +} + +const UserUnfollowButton = ({ username }: UserUnfollowButtonProps) => { + const { mutateAsync: followUserByUsername } = useUnfollowUserByUsername(); + const handleFollowUser = () => { + followUserByUsername({ username }); + }; + return ( + + ); +}; + +export default UserUnfollowButton; diff --git a/apps/realworld/src/widgets/article/article-favorite-toggle-button/article-favorite-toggle-button.tsx b/apps/realworld/src/widgets/article/article-favorite-toggle-button/article-favorite-toggle-button.tsx new file mode 100644 index 00000000..2b825ef2 --- /dev/null +++ b/apps/realworld/src/widgets/article/article-favorite-toggle-button/article-favorite-toggle-button.tsx @@ -0,0 +1,18 @@ +import { ArticleFavoriteButton, ArticleUnfavoriteButton } from '@/features/article'; +import React from 'react'; + +interface ArticleFavoriteToggleButtonProps { + favorited: boolean; + slug: string; + favoritesCount: number; +} + +const ArticleFavoriteToggleButton = ({ favorited, favoritesCount, slug }: ArticleFavoriteToggleButtonProps) => { + return favorited ? ( + + ) : ( + + ); +}; + +export default ArticleFavoriteToggleButton; diff --git a/apps/realworld/src/widgets/user/index.ts b/apps/realworld/src/widgets/user/index.ts new file mode 100644 index 00000000..5ecdd1f3 --- /dev/null +++ b/apps/realworld/src/widgets/user/index.ts @@ -0,0 +1 @@ +export * from './ui'; diff --git a/apps/realworld/src/widgets/user/ui/index.ts b/apps/realworld/src/widgets/user/ui/index.ts new file mode 100644 index 00000000..b8837557 --- /dev/null +++ b/apps/realworld/src/widgets/user/ui/index.ts @@ -0,0 +1,3 @@ +import UserFollowToggleButton from './user-follow-toggle-button/user-follow-toggle-button'; + +export { UserFollowToggleButton }; diff --git a/apps/realworld/src/widgets/user/ui/user-follow-toggle-button/user-follow-toggle-button.tsx b/apps/realworld/src/widgets/user/ui/user-follow-toggle-button/user-follow-toggle-button.tsx new file mode 100644 index 00000000..d9969cee --- /dev/null +++ b/apps/realworld/src/widgets/user/ui/user-follow-toggle-button/user-follow-toggle-button.tsx @@ -0,0 +1,13 @@ +import { UserFollowButton, UserUnfollowButton } from '@/features/user'; +import React from 'react'; + +interface UserFollowToggleButtonProps { + following: boolean; + username: string; +} + +const UserFollowToggleButton = ({ following, username }: UserFollowToggleButtonProps) => { + return following ? : ; +}; + +export default UserFollowToggleButton; From 0726f50e7adc5b5b5a6bb8243414cc24e78541c7 Mon Sep 17 00:00:00 2001 From: ludacirs Date: Wed, 13 Sep 2023 22:58:19 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20axios=20instance=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20return=20=EA=B0=92=20=EC=B6=94=EC=83=81?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/shared/api/realworld/axios/axiosInstance.ts | 11 +++++++++-- .../src/widgets/article/article-list/article-list.tsx | 11 +---------- .../src/widgets/tags/ui/popular-tags/popular-tags.tsx | 4 ++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/realworld/src/shared/api/realworld/axios/axiosInstance.ts b/apps/realworld/src/shared/api/realworld/axios/axiosInstance.ts index 2a18a79e..aaea5a58 100644 --- a/apps/realworld/src/shared/api/realworld/axios/axiosInstance.ts +++ b/apps/realworld/src/shared/api/realworld/axios/axiosInstance.ts @@ -1,9 +1,16 @@ -import defaultAxios from 'axios'; +import defaultAxios, { AxiosRequestConfig } from 'axios'; -export const axiosInstance = defaultAxios.create({ +const axios = defaultAxios.create({ baseURL: 'https://api.realworld.io/api/', headers: { 'Content-Type': 'application/json; charset=UTF-8', accept: 'application/json', }, }); + +export const axiosInstance = async (config: AxiosRequestConfig): Promise => { + config.data; + const data = await axios(config); + + return data.data; +}; diff --git a/apps/realworld/src/widgets/article/article-list/article-list.tsx b/apps/realworld/src/widgets/article/article-list/article-list.tsx index b7eeadd4..54c289cd 100644 --- a/apps/realworld/src/widgets/article/article-list/article-list.tsx +++ b/apps/realworld/src/widgets/article/article-list/article-list.tsx @@ -11,16 +11,7 @@ interface ArticleListProps {} const LIMIT = 10; const ArticleList = ({}: ArticleListProps) => { - const { data: articlesResponse } = useGetArticles( - {}, - { - query: { - select: data => { - return data.data; - }, - }, - }, - ); + const { data: articlesResponse } = useGetArticles(); const { articles, articlesCount } = articlesResponse ?? { articles: [], articlesCount: 0, diff --git a/apps/realworld/src/widgets/tags/ui/popular-tags/popular-tags.tsx b/apps/realworld/src/widgets/tags/ui/popular-tags/popular-tags.tsx index 9a46fdce..07e457d4 100644 --- a/apps/realworld/src/widgets/tags/ui/popular-tags/popular-tags.tsx +++ b/apps/realworld/src/widgets/tags/ui/popular-tags/popular-tags.tsx @@ -1,14 +1,14 @@ 'use client'; -import { useGetTags } from '@/shared/api/realworld/apis'; import { ClickToMoveTag } from '@/entities/tag'; +import { useGetTags } from '@/shared/api/realworld/endpoints/default/default'; import React from 'react'; const PopularTags = () => { const { data: tags } = useGetTags({ query: { select: data => { - return data.data.tags; + return data.tags; }, }, }); From 869bc3d56583098a59c675b46808d2db547c1f42 Mon Sep 17 00:00:00 2001 From: ludacirs Date: Wed, 13 Sep 2023 22:59:27 +0900 Subject: [PATCH 09/18] =?UTF-8?q?fix:=20`ReactQueryStreamedHydration`=20wr?= =?UTF-8?q?apper=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/realworld/src/providers/Providers.tsx | 7 +------ .../realworld/src/shared/utils/reactQueryClient.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 apps/realworld/src/shared/utils/reactQueryClient.ts diff --git a/apps/realworld/src/providers/Providers.tsx b/apps/realworld/src/providers/Providers.tsx index 23b0bb1c..2b034e61 100644 --- a/apps/realworld/src/providers/Providers.tsx +++ b/apps/realworld/src/providers/Providers.tsx @@ -1,7 +1,6 @@ 'use client'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental'; import React, { ReactNode, useState } from 'react'; interface ProviderProps { @@ -19,11 +18,7 @@ const Provider = ({ children }: ProviderProps) => { }, }), ); - return ( - - {children} - - ); + return {children}; }; export default Provider; diff --git a/apps/realworld/src/shared/utils/reactQueryClient.ts b/apps/realworld/src/shared/utils/reactQueryClient.ts new file mode 100644 index 00000000..3c1a7d21 --- /dev/null +++ b/apps/realworld/src/shared/utils/reactQueryClient.ts @@ -0,0 +1,14 @@ +import { QueryClient } from '@tanstack/react-query'; +import { cache } from 'react'; + +const getQueryClient = cache( + () => + new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: process.env.NODE_ENV === 'production', + }, + }, + }), +); +export default getQueryClient; From 3303973f51719930fa8e617f117caf53f037d5a3 Mon Sep 17 00:00:00 2001 From: ludacirs Date: Wed, 13 Sep 2023 23:00:07 +0900 Subject: [PATCH 10/18] =?UTF-8?q?refactor:=20=EC=9D=BC=EA=B4=80=EB=90=9C?= =?UTF-8?q?=20=EB=B0=98=EC=9D=91=ED=98=95=20className=20=EB=B6=84=EB=A6=AC?= =?UTF-8?q?=20=EB=B0=8F=20=EC=9E=AC=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/realworld/src/app/page.tsx | 3 +- apps/realworld/src/app/profile/page.tsx | 37 ++++++------------- .../src/shared/css/responsive-width.ts | 2 + apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx | 3 +- 4 files changed, 18 insertions(+), 27 deletions(-) create mode 100644 apps/realworld/src/shared/css/responsive-width.ts diff --git a/apps/realworld/src/app/page.tsx b/apps/realworld/src/app/page.tsx index 6d54ab41..25b48b19 100644 --- a/apps/realworld/src/app/page.tsx +++ b/apps/realworld/src/app/page.tsx @@ -1,6 +1,7 @@ import { PopularTags } from '@/widgets/tags'; import { FeedToggle } from '@/widgets/tab'; import { ArticleList } from '@/widgets/article'; +import { responsiveWidth } from '@/shared/css/responsive-width'; export default function Home() { return ( @@ -12,7 +13,7 @@ export default function Home() { -
+
diff --git a/apps/realworld/src/app/profile/page.tsx b/apps/realworld/src/app/profile/page.tsx index abe184bf..3ffc654d 100644 --- a/apps/realworld/src/app/profile/page.tsx +++ b/apps/realworld/src/app/profile/page.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React from 'react'; const Profile = () => { return ( @@ -8,14 +8,11 @@ const Profile = () => {
- +

Eric Simons

- Cofounder @GoThinkster, lived in Aol's HQ for a few - months, kinda looks like Peeta from the Hunger Games + Cofounder @GoThinkster, lived in Aol's HQ for a few months, kinda looks like Peeta from the + Hunger Games

- +

How to build webapps that scale

This is the description for the post.

Read more...
    -
  • - realworld -
  • -
  • - implementations -
  • +
  • realworld
  • +
  • implementations
@@ -97,10 +87,7 @@ const Profile = () => {
-

- The song you won&aops;t ever stop singing. No matter how - hard you try. -

+

The song you won&aops;t ever stop singing. No matter how hard you try.

This is the description for the post.

Read more...
    @@ -112,12 +99,12 @@ const Profile = () => {
    • - + 1
    • - + 2
    • diff --git a/apps/realworld/src/shared/css/responsive-width.ts b/apps/realworld/src/shared/css/responsive-width.ts new file mode 100644 index 00000000..abb8d7cb --- /dev/null +++ b/apps/realworld/src/shared/css/responsive-width.ts @@ -0,0 +1,2 @@ +export const responsiveWidth = + 'flex justify-between w-full max-w-1140 max-desktop:max-w-940 max-tablet:max-w-720 max-mobile-l:max-w-576'; diff --git a/apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx b/apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx index 9fa3d4ae..e3a76dcd 100644 --- a/apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx +++ b/apps/realworld/src/widgets/gnb/ui/gnb/gnb.tsx @@ -1,12 +1,13 @@ 'use client'; +import { responsiveWidth } from '@/shared/css/responsive-width'; import Link from 'next/link'; import React from 'react'; const GNB = () => { return (