diff --git a/frontend/src/pages/articles/components/empty-articles-placeholder/styles.module.scss b/frontend/src/pages/articles/components/empty-articles-placeholder/styles.module.scss index eac659e5..c9e41c56 100644 --- a/frontend/src/pages/articles/components/empty-articles-placeholder/styles.module.scss +++ b/frontend/src/pages/articles/components/empty-articles-placeholder/styles.module.scss @@ -7,6 +7,7 @@ gap: 20px; height: 100%; padding: 20px; + text-align: center; } .emptyArticlesIcon { diff --git a/frontend/src/pages/profile/components/user-activity/styles.module.scss b/frontend/src/pages/profile/components/user-activity/styles.module.scss index f6a8a309..45e711ec 100644 --- a/frontend/src/pages/profile/components/user-activity/styles.module.scss +++ b/frontend/src/pages/profile/components/user-activity/styles.module.scss @@ -140,10 +140,6 @@ } @media only screen and (max-width: breakpoints.$large) { - .wrapper { - align-items: flex-start; - } - .title { display: none; } diff --git a/frontend/src/pages/profile/components/user-activity/user-activity.tsx b/frontend/src/pages/profile/components/user-activity/user-activity.tsx index 70584b38..80641a9b 100644 --- a/frontend/src/pages/profile/components/user-activity/user-activity.tsx +++ b/frontend/src/pages/profile/components/user-activity/user-activity.tsx @@ -1,9 +1,11 @@ -import { DateFormat } from '~/libs/enums/enums.js'; +import { Loader } from '~/libs/components/components.js'; +import { DataStatus, DateFormat } from '~/libs/enums/enums.js'; import { getFormattedDate, getValidClassNames, makePluralOrSingular, } from '~/libs/helpers/helpers.js'; +import { useAppSelector } from '~/libs/hooks/hooks.js'; import { type UserActivityResponseDto } from '~/packages/users/users.js'; import { @@ -18,6 +20,8 @@ type Properties = { }; const UserActivity: React.FC = ({ userActivities, className }) => { + const { userActivitiesDataStatus } = useAppSelector(({ users }) => users); + const uniqueMonths: string[] = getUniqueMonths(userActivities); const activityStatistic = userActivities.map((activity, index) => { @@ -46,60 +50,60 @@ const UserActivity: React.FC = ({ userActivities, className }) => { ); }); + const isLoading = userActivitiesDataStatus === DataStatus.PENDING; + return ( - <> - {Boolean(userActivities.length) && ( -
-

Your writing activity

-
-
-
- Mon - Wed - Fri - Sun -
-
-
- {uniqueMonths.map((month) => ( - {month} - ))} -
-
{activityStatistic}
-
+
+

Your writing activity

+
+ +
+
+ Mon + Wed + Fri + Sun
-
- Less - - - - - More +
+
+ {uniqueMonths.map((month) => ( + {month} + ))} +
+
{activityStatistic}
-
-
- )} - +
+
+ Less + + + + + More +
+ +
+
); }; diff --git a/frontend/src/pages/profile/components/user-latest-articles/styles.module.scss b/frontend/src/pages/profile/components/user-latest-articles/styles.module.scss index e3de864d..d84fba5f 100644 --- a/frontend/src/pages/profile/components/user-latest-articles/styles.module.scss +++ b/frontend/src/pages/profile/components/user-latest-articles/styles.module.scss @@ -9,6 +9,7 @@ .wrapper { display: flex; flex-direction: column; + justify-content: center; height: 100%; } diff --git a/frontend/src/pages/profile/components/user-latest-articles/user-latest-articles.tsx b/frontend/src/pages/profile/components/user-latest-articles/user-latest-articles.tsx index 12dc6e75..92561e4c 100644 --- a/frontend/src/pages/profile/components/user-latest-articles/user-latest-articles.tsx +++ b/frontend/src/pages/profile/components/user-latest-articles/user-latest-articles.tsx @@ -1,5 +1,5 @@ -import { Link } from '~/libs/components/components.js'; -import { AppRoute } from '~/libs/enums/enums.js'; +import { Link, Loader } from '~/libs/components/components.js'; +import { AppRoute, DataStatus } from '~/libs/enums/enums.js'; import { getValidClassNames } from '~/libs/helpers/helpers.js'; import { useAppDispatch, @@ -19,9 +19,12 @@ type Properties = { const UserLatestArticles: React.FC = ({ className }) => { const dispatch = useAppDispatch(); - const { articles } = useAppSelector(({ articles }) => ({ - articles: articles.articles, - })); + const { articles, fetchArticlesDataStatus } = useAppSelector( + ({ articles }) => ({ + articles: articles.articles, + fetchArticlesDataStatus: articles.fetchArticlesDataStatus, + }), + ); const hasArticles = Boolean(articles.length); @@ -37,31 +40,36 @@ const UserLatestArticles: React.FC = ({ className }) => { }; }, [dispatch]); + const isLoading = fetchArticlesDataStatus === DataStatus.PENDING; + return (
-

- {hasArticles ? 'Your latest articles' : 'My articles'} -

+

Your latest articles

- {hasArticles ? ( - <> -
    - {articles.map((article) => ( -
  • - -
  • - ))} -
- - Show all - - - ) : ( - - )} + + {hasArticles ? ( + <> +
    + {articles.map((article) => ( +
  • + +
  • + ))} +
+ + Show all + + + ) : ( + + )} +
); diff --git a/frontend/src/pages/profile/profile-page.tsx b/frontend/src/pages/profile/profile-page.tsx index 5a8e06c8..76c9e564 100644 --- a/frontend/src/pages/profile/profile-page.tsx +++ b/frontend/src/pages/profile/profile-page.tsx @@ -20,7 +20,7 @@ import styles from './styles.module.scss'; const ProfilePage: React.FC = () => { const dispatch = useAppDispatch(); - const { user, userActivities, articles } = useAppSelector( + const { user, userActivities } = useAppSelector( ({ auth, users, articles }) => ({ user: auth.user as UserAuthResponseDto, userActivities: users.userActivities, @@ -28,8 +28,6 @@ const ProfilePage: React.FC = () => { }), ); - const hasArticles = Boolean(articles.length); - useEffect(() => { void dispatch(usersActions.getUserActivity()); }, [dispatch]); @@ -65,9 +63,14 @@ const ProfilePage: React.FC = () => { - + diff --git a/frontend/src/pages/profile/styles.module.scss b/frontend/src/pages/profile/styles.module.scss index 83d53c55..5a0a3853 100644 --- a/frontend/src/pages/profile/styles.module.scss +++ b/frontend/src/pages/profile/styles.module.scss @@ -41,16 +41,22 @@ } .achievements { - min-height: 244px; + min-height: 245px; } .activity { + align-items: normal; + min-height: 255px; padding: 10px 30px 10px 10px; } .stats { min-height: 436px; } + + .latestArticles { + min-height: 414px; + } } @media only screen and (max-width: breakpoints.$medium) { diff --git a/frontend/src/slices/users/users.slice.ts b/frontend/src/slices/users/users.slice.ts index fa1f9637..9d339052 100644 --- a/frontend/src/slices/users/users.slice.ts +++ b/frontend/src/slices/users/users.slice.ts @@ -23,6 +23,7 @@ type State = { authors: UserDetailsAuthorResponseDto[]; userArticlesGenresStats: UserArticlesGenreStatsItem[]; userArticlesGenresStatsStatus: ValueOf; + userActivitiesDataStatus: ValueOf; }; const initialState: State = { @@ -32,6 +33,7 @@ const initialState: State = { userArticlesGenresStats: [], dataStatus: DataStatus.IDLE, userArticlesGenresStatsStatus: DataStatus.IDLE, + userActivitiesDataStatus: DataStatus.IDLE, }; const { reducer, actions, name } = createSlice({ @@ -45,6 +47,7 @@ const { reducer, actions, name } = createSlice({ }); builder.addCase(getUserActivity.fulfilled, (state, action) => { state.userActivities = action.payload; + state.userActivitiesDataStatus = DataStatus.FULFILLED; }); builder.addCase(getAllAuthors.fulfilled, (state, action) => { state.dataStatus = DataStatus.FULFILLED; @@ -61,21 +64,24 @@ const { reducer, actions, name } = createSlice({ builder.addCase(getUserArticlesGenresStats.pending, (state) => { state.userArticlesGenresStatsStatus = DataStatus.PENDING; }); + builder.addCase(getUserActivity.pending, (state) => { + state.userActivitiesDataStatus = DataStatus.PENDING; + }); builder.addCase(getUserArticlesGenresStats.rejected, (state) => { state.userArticlesGenresStatsStatus = DataStatus.REJECTED; }); + builder.addCase(getUserActivity.rejected, (state) => { + state.userActivitiesDataStatus = DataStatus.REJECTED; + }); + builder.addCase(loadAll.rejected, (state) => { + state.dataStatus = DataStatus.REJECTED; + }); builder.addMatcher( - isAnyOf(loadAll.pending, getUserActivity.pending, getAllAuthors.pending), + isAnyOf(loadAll.pending, getAllAuthors.pending), (state) => { state.dataStatus = DataStatus.PENDING; }, ); - builder.addMatcher( - isAnyOf(loadAll.rejected, getUserActivity.rejected), - (state) => { - state.dataStatus = DataStatus.REJECTED; - }, - ); }, });