Skip to content

Commit

Permalink
Fix date bugs and update docs (#126)
Browse files Browse the repository at this point in the history
* Fixed history sort ordering

* Apply date selection only on day click

* Add RTK queries unsubscribe in loaders

* Removed page mentions from docs

* Use specific ubuntu version in GHA
  • Loading branch information
pkirilin authored Oct 14, 2024
1 parent 9e4f2ea commit 0c4a6ff
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 56 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ env:

jobs:
backend:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
defaults:
run:
working-directory: 'src/backend'
Expand All @@ -29,7 +29,7 @@ jobs:
- name: Run component tests
run: dotnet test tests/FoodDiary.ComponentTests --no-restore --verbosity normal
frontend:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
defaults:
run:
working-directory: 'src/frontend'
Expand All @@ -51,7 +51,7 @@ jobs:
needs:
- backend
- frontend
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
timeout-minutes: 10
defaults:
run:
Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:
if: always()
run: docker compose down
run-migrations:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: e2e-tests
if: ${{ github.ref == 'refs/heads/main' }}
steps:
Expand All @@ -96,7 +96,7 @@ jobs:
run: dotnet run --configuration Release --project src/backend/src/FoodDiary.Migrator/FoodDiary.Migrator.csproj "${{ secrets.Migrator_DatabaseConnectionString }}"

build-and-push-image:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: run-migrations
if: ${{ github.ref == 'refs/heads/main' }}
steps:
Expand All @@ -115,7 +115,7 @@ jobs:
docker push cr.yandex/$CR_REGISTRY/$CR_REPOSITORY:$IMAGE_TAG
deploy:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: build-and-push-image
if: ${{ github.ref == 'refs/heads/main' }}
steps:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/deploy-azure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:

jobs:
run-migrations:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Setup .NET
Expand All @@ -16,7 +16,7 @@ jobs:
run: dotnet run --configuration Release --project src/backend/src/FoodDiary.Migrator/FoodDiary.Migrator.csproj "${{ secrets.Migrator_DatabaseConnectionString }}"

push-image:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: run-migrations
steps:
- uses: actions/checkout@v4
Expand All @@ -38,7 +38,7 @@ jobs:
file: ./Dockerfile

deploy-yandex-cloud:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: push-image
steps:
- name: Checkout to the branch
Expand All @@ -57,7 +57,7 @@ jobs:
imageToDeploy: index.docker.io/${{ secrets.AzureAppService_ContainerUsername_6ff515138c474569b14fb663d02b6c5d }}/food-diary:${{ github.sha }}

deploy-container-apps:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: push-image
steps:
- name: Checkout to the branch
Expand All @@ -76,7 +76,7 @@ jobs:
imageToDeploy: index.docker.io/${{ secrets.AzureAppService_ContainerUsername_6ff515138c474569b14fb663d02b6c5d }}/food-diary:${{ github.sha }}

deploy-app-service:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: push-image
environment:
name: 'production'
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ env:

jobs:
run-migrations:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Setup .NET
Expand All @@ -21,7 +21,7 @@ jobs:
run: dotnet run --configuration Release --project src/backend/src/FoodDiary.Migrator/FoodDiary.Migrator.csproj "${{ secrets.Migrator_DatabaseConnectionString }}"

build-and-push-image:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: run-migrations
steps:
- name: Checkout to the branch
Expand All @@ -39,7 +39,7 @@ jobs:
docker push cr.yandex/$CR_REGISTRY/$CR_REPOSITORY:$IMAGE_TAG
deploy:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: build-and-push-image
steps:
- name: Deploy Serverless Container
Expand Down
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@

## Main idea and goal

The diary consists of **pages**. Each page is associated with some date and contains **notes** grouped by meal types (_breakfast, lunch etc._). Note contains information about **product** and its quantity. Products are grouped by **categories**. Each product has _name_ and _calories cost_ per 100 g of product's quantity recorded. Using this information the application is capable of calculating calories count of:
The diary is organized by dates. Each date contains **notes** that are grouped by meal types (*breakfast*, *lunch*, etc.). A note includes information about a **product** and its quantity. Products are categorized into **categories**. Each product has a name and a calorie cost per 100 g of the product's quantity recorded. Using this information, the application can calculate the calorie count of a single note or a group of notes (for a specific meal type or date).

- single note
- notes group (e.g. for specific meal type)
- the entire page.

This information can be extremely useful for people who want to keep track of energy value of meals they eat every day.
This information can be extremely useful for people who want to keep track of the energy value of the meals they consume daily.

## Quick start (docker-compose)

Expand Down Expand Up @@ -48,7 +44,7 @@ This information can be extremely useful for people who want to keep track of en
-d postgres:15.1-alpine
```

_Optional_: PgAdmin can be started like this:
*Optional*: PgAdmin can be started like this:

```shell
docker run -p 5050:80 --name pgadmin -e "PGADMIN_DEFAULT_EMAIL=name@example.com" -e "PGADMIN_DEFAULT_PASSWORD=postgres" -d dpage/pgadmin4
Expand All @@ -71,7 +67,7 @@ This information can be extremely useful for people who want to keep track of en
dotnet user-secrets --project src/backend/src/FoodDiary.API set "Integrations:OpenAI:ApiKey" "<your_OpenAI_api_key>"
```

_Allowed email should be compatible with Google Identity Provider_
*Allowed email should be compatible with Google Identity Provider*

1. Run migrations:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public async Task<IReadOnlyCollection<Note>> FindByDateRange(
.AsNoTracking()
.Where(n => n.Date >= from && n.Date <= to)
.Include(n => n.Product)
.OrderByDescending(n => n.Date)
.ToListAsync(cancellationToken);
}

Expand Down
20 changes: 13 additions & 7 deletions src/frontend/src/app/routing/AuthenticatedLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,23 @@ import { ErrorPage } from './ErrorPage';
import { useNavigationProgress } from './useNavigationProgress';

export const loader: LoaderFunction = async ({ request }) => {
const authStatusQuery = await store.dispatch(
const authStatusQueryPromise = store.dispatch(
authApi.endpoints.getStatus.initiate({}, { forceRefetch: true }),
);

if (!authStatusQuery.data?.isAuthenticated) {
const returnUrl = new URL(request.url).searchParams.get('returnUrl') ?? '/';
const loginUrl = createUrl('/login', { returnUrl });
return redirect(loginUrl);
}
try {
const authStatusQuery = await authStatusQueryPromise;

if (!authStatusQuery.data?.isAuthenticated) {
const returnUrl = new URL(request.url).searchParams.get('returnUrl') ?? '/';
const loginUrl = createUrl('/login', { returnUrl });
return redirect(loginUrl);
}

return ok();
return ok();
} finally {
authStatusQueryPromise.unsubscribe();
}
};

export const shouldRevalidate: ShouldRevalidateFunction = () => true;
Expand Down
3 changes: 2 additions & 1 deletion src/frontend/src/features/note/selectDate/ui/SelectDate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export const SelectDate: FC<Props> = ({ currentDate }) => {
<StaticDatePicker
displayStaticWrapperAs="desktop"
value={currentDate}
onChange={newDate => {
views={['year', 'month', 'day']}
onAccept={newDate => {
if (newDate) {
submit(new URLSearchParams({ date: dateLib.formatToISOStringWithoutTime(newDate) }), {
method: 'GET',
Expand Down
10 changes: 8 additions & 2 deletions src/frontend/src/pages/ui/CategoriesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import { Categories } from '@/features/categories';
import { ok } from '../lib';

export const loader: LoaderFunction = async () => {
await store.dispatch(categoryApi.endpoints.getCategories.initiate({}));
return ok();
const categoriesQueryPromise = store.dispatch(categoryApi.endpoints.getCategories.initiate({}));

try {
await categoriesQueryPromise;
return ok();
} finally {
categoriesQueryPromise.unsubscribe();
}
};

export const Component: FC = () => <Categories />;
22 changes: 14 additions & 8 deletions src/frontend/src/pages/ui/HistoryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,26 @@ export const loader: LoaderFunction = async ({ request }) => {
const year = url.searchParams.get('year') ?? getFallbackYear();
const date = new Date(+year, +month - 1);

const notesHistoryQuery = await store.dispatch(
const notesHistoryQueryPromise = store.dispatch(
noteApi.endpoints.notesHistory.initiate({
from: dateLib.formatToISOStringWithoutTime(date),
to: getEndOfMonth(date),
}),
);

return {
notes: notesHistoryQuery.data?.notesHistory ?? [],
navigation: {
title: 'History',
action: <FilterNotesHistory date={date} />,
},
} satisfies LoaderData;
try {
const notesHistoryQuery = await notesHistoryQueryPromise;

return {
notes: notesHistoryQuery.data?.notesHistory ?? [],
navigation: {
title: 'History',
action: <FilterNotesHistory date={date} />,
},
} satisfies LoaderData;
} finally {
notesHistoryQueryPromise.unsubscribe();
}
};

export const Component: FC = () => {
Expand Down
22 changes: 14 additions & 8 deletions src/frontend/src/pages/ui/IndexPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,21 @@ const getFallbackDate = (): string =>
export const loader: LoaderFunction = async ({ request }) => {
const url = new URL(request.url);
const date = url.searchParams.get('date') ?? getFallbackDate();
await store.dispatch(noteApi.endpoints.notes.initiate({ date }));
const notesQueryPromise = store.dispatch(noteApi.endpoints.notes.initiate({ date }));

return {
date,
navigation: {
title: <SelectDate currentDate={new Date(date)} />,
action: <MealsListTotalCalories date={date} />,
},
} satisfies LoaderData;
try {
await notesQueryPromise;

return {
date,
navigation: {
title: <SelectDate currentDate={new Date(date)} />,
action: <MealsListTotalCalories date={date} />,
},
} satisfies LoaderData;
} finally {
notesQueryPromise.unsubscribe();
}
};

export const Component: FC = () => {
Expand Down
16 changes: 11 additions & 5 deletions src/frontend/src/pages/ui/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@ import { AppName, Center } from '@/shared/ui';
import { ok } from '../lib';

export const loader: LoaderFunction = async () => {
const getAuthStatusQuery = await store.dispatch(
const authStatusQueryPromise = store.dispatch(
authApi.endpoints.getStatus.initiate({}, { forceRefetch: true }),
);

if (getAuthStatusQuery.data?.isAuthenticated) {
return redirect('/');
}
try {
const authStatusQuery = await authStatusQueryPromise;

if (authStatusQuery.data?.isAuthenticated) {
return redirect('/');
}

return ok();
return ok();
} finally {
authStatusQueryPromise.unsubscribe();
}
};

export const action: ActionFunction = async ({ request }) => {
Expand Down
12 changes: 9 additions & 3 deletions src/frontend/src/pages/ui/ProductsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ import { Products } from '@/features/products';
import { ok } from '../lib';

export const loader: LoaderFunction = async () => {
const getProductsRequest = productLib.mapToGetProductsRequest(store.getState().products.filter);
await store.dispatch(productApi.endpoints.getProducts.initiate(getProductsRequest));
return ok();
const request = productLib.mapToGetProductsRequest(store.getState().products.filter);
const productsQueryPromise = store.dispatch(productApi.endpoints.getProducts.initiate(request));

try {
await productsQueryPromise;
return ok();
} finally {
productsQueryPromise.unsubscribe();
}
};

export const Component: FC = () => <Products />;

0 comments on commit 0c4a6ff

Please sign in to comment.