Skip to content

Commit

Permalink
Merge pull request #14 from MarkMelior/feature/VEES-34-loading-error-…
Browse files Browse the repository at this point in the history
…state

VEES-34 | Обработать все состояния отсутствия и загрузки данных
  • Loading branch information
MarkMelior authored Jan 18, 2025
2 parents 15eed12 + 95d009b commit 4c861af
Show file tree
Hide file tree
Showing 22 changed files with 442 additions and 200 deletions.
69 changes: 11 additions & 58 deletions src/app/(with-navbar)/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,80 +3,29 @@
import { useEffect } from 'react';

import { Card } from '@/widgets/Card';
import { Statistics } from '@/widgets/Statistics';

import { ArrowLeftIcon, FilterOutlineIcon, StopwatchOutlineIcon } from '@/shared/icons';
import { getDateDifference } from '@/shared/lib/date';
import { Badge, Button, Flex, Layout, List, Separator } from '@/shared/ui';
import { InfoBlock } from '@/shared/ui/client';
import { ArrowLeftIcon, FilterOutlineIcon } from '@/shared/icons';
import { Badge, Button, Flex, Layout, Separator } from '@/shared/ui';

import { FiltersModal } from '@/features/FiltersModal';
import { SortModal } from '@/features/SortModal';

import { useStats } from '@/entities/stats';
import { useVeesActive, useVeesList } from '@/entities/vees';
import { useVeesList } from '@/entities/vees';

import styles from '../page.module.scss';

export const ClientRender = () => {
const { dataVeesList, loadVeesList } = useVeesList();
const { dataStats, loadStats } = useStats();
const { dataVeesActive } = useVeesActive();
const { dataVeesList, errorVeesList, loadingVeesList, loadVeesList } = useVeesList();

useEffect(() => {
loadVeesList();
}, []);

useEffect(() => {
loadStats();
}, [dataVeesActive]);

if (!dataStats || !dataVeesList) {
return;
}

const {
approachCount,
betterCount,
dateFirstVees,
veesCount,
worseCount,
} = dataStats;

// Логика расчета стажа
const dateFirst = new Date(dateFirstVees);
const nowDate = new Date();

const { days, months, years } = getDateDifference(dateFirst, nowDate);

const experience = years
? `${years} г.` : months
? `${months} мес.` : `${days} д.`;
// * --- * //

return (
<>
<Layout>
<InfoBlock maxHeight={126} textButton="Показать всю статистику">
<List.Item
icon={<StopwatchOutlineIcon />}
title="Вы тренировались"
value="148 часов 56 минут"
/>
<List.Horizontal
items={[
{ title: 'Тренировок', value: veesCount },
{ title: 'Подходов', value: approachCount },
{ title: 'Стаж', value: experience },
]}
/>
<List.Horizontal
items={[
{ title: 'Прогресс в упражнениях', value: betterCount },
{ title: 'Регресс в упражнениях', value: worseCount },
]}
showDivider={false}
/>
</InfoBlock>
<Statistics />
<Separator size={18} />
<Flex gap={10} id="home-filters-buttons">
<FiltersModal>
Expand Down Expand Up @@ -111,7 +60,11 @@ export const ClientRender = () => {
</Layout>
<Separator size={32} />
<Layout padding={false}>
<Card.ExercisesList items={dataVeesList} />
<Card.ExercisesList
error={errorVeesList}
items={dataVeesList}
loading={loadingVeesList}
/>
</Layout>
</>
);
Expand Down
19 changes: 18 additions & 1 deletion src/app/(with-navbar)/settings/ui/SettingsContent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import localforage from 'localforage';
import { useTheme } from 'next-themes';

import { NOT_IMPLEMENTED } from '@/shared/constants';
Expand All @@ -9,6 +10,7 @@ import {
FolderOutlineIcon,
ImportOutlineIcon,
LangIcon,
ResetIcon,
ThemeIcon,
VeesIcon,
} from '@/shared/icons';
Expand Down Expand Up @@ -108,9 +110,24 @@ export const SettingsContent = () => {
duration: 5,
});
}}
showDivider={false}
title="Загрузить mock-данные"
/>
<Section.Item
color="#FA4838"
description="Все ваши данные будут удалены"
icon={<ResetIcon height={24} width={24} />}
onClick={() => {
localforage.clear();
openMessage({
content: 'Все ваши данные удалены!',
description: 'Перезапустите приложение',
duration: 5,
type: 'error',
});
}}
showDivider={false}
title="Очистить все данные"
/>
</Section>
<Button
className={styles.logout}
Expand Down
32 changes: 29 additions & 3 deletions src/app/vees/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,42 @@ import { Card } from '@/widgets/Card';

import { PaperOutlineIcon, StopwatchOutlineIcon } from '@/shared/icons';
import { getDateInfo, getDateRangeDuration } from '@/shared/lib/date';
import { Chip, Layout, List, Separator } from '@/shared/ui';
import { Alert, Button, Chip, Empty, Layout, List, Separator, Skeleton } from '@/shared/ui';
import { InfoBlock } from '@/shared/ui/client';

import { useVeesActive } from '@/entities/vees';

export const ClientRender = () => {
const { dataVeesActive } = useVeesActive();
const { dataVeesActive, errorVeesActive, loadingVeesActive } = useVeesActive();

if (loadingVeesActive) {
return (
<Layout>
<Skeleton height={216} />
<Separator size={32} />
<Skeleton.List />
</Layout>
);
}

if (errorVeesActive) {
return (
<Alert
closable={false}
content="Ошибка загрузки активной тренировки!"
description={errorVeesActive.message}
type="error"
/>
);
}

if (!dataVeesActive) {
return;
return (
<Empty
description={<Button>Начать</Button>}
title="Начните тренировку!"
/>
);
}

const { duration, exercises, exerciseTemplate, number } = dataVeesActive;
Expand Down
2 changes: 1 addition & 1 deletion src/entities/exercise-groups/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ export const useExerciseGroups = create<IExerciseGroupsStore>((set) => ({
set({ loadingExerciseGroup: false });
}
},
loadingExerciseGroup: false,
loadingExerciseGroup: true,
}));
2 changes: 1 addition & 1 deletion src/entities/stats/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface IStatsStore {
export const useStats = create<IStatsStore>((set) => ({
dataStats: null,
errorStats: null,
loadingStats: false,
loadingStats: true,
loadStats: async () => {
set({ errorStats: null, loadingStats: true });

Expand Down
2 changes: 1 addition & 1 deletion src/entities/vees/store/use-vees-active.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const useVeesActive = create<IVeesActiveStore>((set, get) => ({
},
dataVeesActive: null,
errorVeesActive: null,
loadingVeesActive: false,
loadingVeesActive: true,
loadVeesActive: async () => {
set({ errorVeesActive: null, loadingVeesActive: true });

Expand Down
2 changes: 1 addition & 1 deletion src/entities/vees/store/use-vees-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface IVeesListStore {
export const useVeesList = create<IVeesListStore>((set) => ({
dataVeesList: null,
errorVeesList: null,
loadingVeesList: false,
loadingVeesList: true,
loadVeesList: async () => {
set({ errorVeesList: null, loadingVeesList: true });

Expand Down
9 changes: 0 additions & 9 deletions src/features/AddExerciseModal/ui/AddExerciseModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useState } from 'react';

import { AddOutlineIcon, SettingsOutlineIcon } from '@/shared/icons';
import { Background, Button, Chip, Empty, Flex, Input, ModalBase, Text } from '@/shared/ui';
import { Image } from '@/shared/ui/client';

import type { IExerciseGroupsResponse } from '@/entities/exercise-groups';

Expand Down Expand Up @@ -105,14 +104,6 @@ export const AddExerciseModal = ({ items }: IAddExerciseModal) => {
) : (
<Empty
description="Добавьте первое упражнение!"
icon={(
<Image
alt="empty"
height={512}
src="/images/weight.png"
width={512}
/>
)}
title="Здесь пока нет упражнений..."
/>
)}
Expand Down
71 changes: 71 additions & 0 deletions src/shared/lib/_deprecated/use-melior-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useCallback, useEffect, useState } from 'react';

import { handleError } from '../handle-error';

interface IUseMeliorQuery<T> {
method: () => Promise<T>
showError?: boolean
}

interface IUseMeliorQueryResult<T> {
data: T | null
error: string | null
isError: boolean
isFetching: boolean
isLoading: boolean
isSuccess: boolean
isUninitialized: boolean
refetch: () => void
}

export const useMeliorQuery = <T>({
method,
showError = true,
}: IUseMeliorQuery<T>): IUseMeliorQueryResult<T> => {
const [data, setData] = useState<T | null>(null);
const [isFetching, setIsFetching] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [isUninitialized, setIsUninitialized] = useState(true);
const [isSuccess, setIsSuccess] = useState(false);
const [isError, setIsError] = useState(false);

const refetch = useCallback(async () => {
try {
setIsFetching(true);
setIsUninitialized(false);
setIsLoading(true);
setIsSuccess(false);
setIsError(false);

const response = await method();

setData(response);
setIsSuccess(true);
setIsLoading(false);
} catch (error) {
const { message } = handleError({ error, isMessage: showError });

setError(message);
setIsError(true);
setIsLoading(false);
} finally {
setIsFetching(false);
}
}, [method]);

useEffect(() => {
refetch();
}, [refetch]);

return {
data,
error,
isError,
isFetching,
isLoading,
isSuccess,
isUninitialized,
refetch,
};
};
65 changes: 1 addition & 64 deletions src/shared/lib/open-message/openMessage.module.scss
Original file line number Diff line number Diff line change
@@ -1,74 +1,11 @@
.wrapper {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
margin-bottom: 8px;
padding: 12px 16px;

.message {
animation: fade-in-from-top 0.3s ease-out forwards;
backdrop-filter: blur(theme('backdropBlur.xl'));

[data-message="closed"] & {
animation: fade-out-from-top 0.3s ease-in forwards;
}
}

.icon {
display: flex;
justify-content: center;
align-items: center;
min-width: 36px;
max-width: 36px;
min-height: 36px;
max-height: 36px;

border-radius: 50%;
}

.close {
margin-left: auto;
padding: 12px !important;
}



@mixin variant($color) {
color: theme('colors.#{$color}.400');
background-color: theme('colors.#{$color}.800 / 35%');
border-color: theme('colors.#{$color}.600 / 75%');

.icon {
background: theme('colors.#{$color}.600 / 20%');
border: 1px solid theme('colors.#{$color}.600');
box-shadow: inset 0 0 3px 0 theme('colors.#{$color}.400 / 50%');
}

.close:hover {
background: theme('colors.#{$color}.600 / 20%');
}
}

.success {
@include variant('green');
}

.warning {
@include variant('yellow');
}

.info {
@include variant('primary');
}

.error {
@include variant('red');
}

.base {
@include variant('base');
}

.shake {
animation: shake 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
Expand Down
Loading

0 comments on commit 4c861af

Please sign in to comment.