diff --git a/.github/workflows/test-view.yml b/.github/workflows/test-view.yml index e3e0dfea..ba8cdd6a 100644 --- a/.github/workflows/test-view.yml +++ b/.github/workflows/test-view.yml @@ -7,6 +7,7 @@ on: [pull_request] env: NODE_VERSION: 20 + PNPM_VERSION: 9.12.0 jobs: test: @@ -18,6 +19,7 @@ jobs: name: Install pnpm with: run_install: false + version: ${{ env.PNPM_VERSION }} - name: Install Node.js uses: actions/setup-node@v4 @@ -36,6 +38,7 @@ jobs: name: Install pnpm with: run_install: false + version: ${{ env.PNPM_VERSION }} - name: Install Node.js uses: actions/setup-node@v4 diff --git a/package.yaml b/package.yaml index 556c8d95..daa6c488 100644 --- a/package.yaml +++ b/package.yaml @@ -63,3 +63,4 @@ dependencies: react-router-dom: ^6.26.2 recharts: ^2.13.0 zod: ^3.23.8 +packageManager: pnpm@9.12.0 diff --git a/src/react-view/api/generated-models.ts b/src/react-view/api/generated-models.ts index 62d462bb..c63043ce 100644 --- a/src/react-view/api/generated-models.ts +++ b/src/react-view/api/generated-models.ts @@ -20,6 +20,23 @@ export interface paths { patch?: never; trace?: never; }; + "/bank-transactions/{transactionId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** @description Set a comment into the transaction */ + patch: operations["ApiBankTransactions_comment"]; + trace?: never; + }; "/bank-transactions/{transactionId}/label/{labelId}": { parameters: { query?: never; @@ -217,39 +234,42 @@ export interface paths { patch?: never; trace?: never; }; - "/profiles": { + "/rules": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["ApiProfile_get"]; + get: operations["ApiRule_list"]; put?: never; - post: operations["ApiProfile_create"]; + post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/profiles/{id}": { + "/session": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get?: never; + /** @description Check if the user is logged in */ + get: operations["ApiSession_me"]; put?: never; - post?: never; - delete: operations["ApiProfile_delete"]; + /** @description Login */ + post: operations["ApiSession_login"]; + /** @description Logout */ + delete: operations["ApiSession_logout"]; options?: never; head?: never; - patch: operations["ApiProfile_update"]; + patch?: never; trace?: never; }; - "/profiles/{id}/group/{groupId}": { + "/session/me": { parameters: { query?: never; header?: never; @@ -257,28 +277,57 @@ export interface paths { cookie?: never; }; get?: never; + put: operations["ApiSession_updateMe"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/users": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["ApiUser_list"]; put?: never; - post: operations["ApiProfile_addGroup"]; - delete: operations["ApiProfile_deleteGroup"]; + post: operations["ApiUser_create"]; + delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/session": { + "/users/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - /** @description Check if the user is logged in */ - get: operations["ApiSession_me"]; + get?: never; + put: operations["ApiUser_update"]; + post?: never; + delete: operations["ApiUser_delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/users/{id}/group/{groupId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; put?: never; - /** @description Login */ - post: operations["ApiSession_login"]; - /** @description Logout */ - delete: operations["ApiSession_logout"]; + post: operations["ApiUser_addGroup"]; + delete: operations["ApiUser_deleteGroup"]; options?: never; head?: never; patch?: never; @@ -306,24 +355,27 @@ export interface components { email: string; firstName?: string; lastName?: string; - isActive: boolean; - isAdmin: boolean; - defaultGroupId: components["schemas"]["UUID"]; - }; - BaseUserDataUpdate: { - username?: string; - email?: string; - firstName?: string; - lastName?: string; - isActive?: boolean; - isAdmin?: boolean; - defaultGroupId?: components["schemas"]["UUID"]; }; CheckMyProfile: components["schemas"]["GetMyProfile"] | components["schemas"]["NotIdentified"]; + Condition: { + id: components["schemas"]["UUID"]; + operation: components["schemas"]["Operation"]; + value: string; + }; + /** @enum {string} */ + ConditionalRelation: "and" | "notOr"; CreateLabel: { name: string; groupOwnerId: components["schemas"]["UUID"]; }; + CreateUserData: { + username: string; + email: string; + firstName?: string; + lastName?: string; + isActive: boolean; + isAdmin: boolean; + }; /** Format: date-time */ Date: string; /** Format: date */ @@ -446,8 +498,40 @@ export interface components { */ user: "anonymous"; }; + /** @enum {string} */ + Operation: "greater" | "greaterEqual" | "lower" | "lowerEqual" | "contains" | "prefix" | "regularExpression" | "suffix"; + Rule: { + id: components["schemas"]["UUID"]; + parentRule?: components["schemas"]["UUID"]; + groupOwnerId: components["schemas"]["UUID"]; + name: string; + relations: components["schemas"]["ConditionalRelation"]; + conditions: components["schemas"]["Condition"][]; + }; + SetLabelAction: { + label: components["schemas"]["UUID"]; + }; /** Format: uuid */ UUID: string; + UpdateMyProfile: { + username: string; + email: string; + firstName?: string; + lastName?: string; + defaultGroupId: components["schemas"]["UUID"]; + newPassword?: string; + password?: string; + }; + UpdateUserData: { + username: string; + email: string; + firstName?: string; + lastName?: string; + isActive: boolean; + isAdmin: boolean; + password?: string; + defaultGroupId: components["schemas"]["UUID"]; + }; UploadData: { kind: string; /** Format: byte */ @@ -467,10 +551,10 @@ export interface components { email: string; firstName?: string; lastName?: string; - isActive: boolean; isAdmin: boolean; - defaultGroupId: components["schemas"]["UUID"]; + isActive: boolean; groups: components["schemas"]["UserGroup"][]; + defaultGroupId: components["schemas"]["UUID"]; }; }; responses: never; @@ -508,6 +592,43 @@ export interface operations { }; }; }; + ApiBankTransactions_comment: { + parameters: { + query?: never; + header?: never; + path: { + transactionId: components["schemas"]["UUID"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + comment?: string; + }; + }; + }; + responses: { + /** @description The request has succeeded. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BankTransaction"]; + }; + }; + /** @description The server cannot find the requested resource. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFoundBankTransaction"]; + }; + }; + }; + }; ApiBankTransactions_linkLabel: { parameters: { query?: never; @@ -985,12 +1106,29 @@ export interface operations { }; }; }; - ApiProfile_get: { + ApiRule_list: { parameters: { - query?: { - cursor?: string; - limit?: number; + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The request has succeeded. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Rule"][]; + }; }; + }; + }; + ApiSession_me: { + parameters: { + query?: never; header?: never; path?: never; cookie?: never; @@ -1003,15 +1141,21 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": { - results: components["schemas"]["UserProfile"][]; - next?: string; - }; + "application/json": components["schemas"]["CheckMyProfile"]; + }; + }; + /** @description An unexpected error response. */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; }; }; }; }; - ApiProfile_create: { + ApiSession_login: { parameters: { query?: never; header?: never; @@ -1020,7 +1164,7 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["BaseUserData"]; + "application/json": components["schemas"]["UserCredentials"]; }; }; responses: { @@ -1033,15 +1177,22 @@ export interface operations { "application/json": components["schemas"]["UserProfile"]; }; }; + /** @description Access is unauthorized. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InvalidCredentials"]; + }; + }; }; }; - ApiProfile_delete: { + ApiSession_logout: { parameters: { query?: never; header?: never; - path: { - id: components["schemas"]["UUID"]; - }; + path?: never; cookie?: never; }; requestBody?: never; @@ -1057,20 +1208,16 @@ export interface operations { }; }; }; - ApiProfile_update: { + ApiSession_updateMe: { parameters: { query?: never; header?: never; - path: { - id: components["schemas"]["UUID"]; - }; + path?: never; cookie?: never; }; requestBody: { content: { - "application/json": { - body?: components["schemas"]["BaseUserDataUpdate"]; - }; + "application/json": components["schemas"]["UpdateMyProfile"]; }; }; responses: { @@ -1083,55 +1230,153 @@ export interface operations { "application/json": components["schemas"]["UserProfile"]; }; }; + /** @description Unauthorized, usually when using a group that the user doesn't have access to */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; }; }; - ApiProfile_addGroup: { + ApiUser_list: { parameters: { - query?: never; - header?: never; - path: { - id: components["schemas"]["UUID"]; - groupId: components["schemas"]["UUID"]; + query?: { + cursor?: string; + limit?: number; }; + header?: never; + path?: never; cookie?: never; }; requestBody?: never; responses: { - /** @description There is no content to send for this request, but the headers may be useful. */ - 204: { + /** @description The request has succeeded. */ + 200: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": { + results: components["schemas"]["UserProfile"][]; + next?: string; + }; + }; + }; + /** @description Access is unauthorized. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + ApiUser_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + username: string; + email: string; + firstName?: string; + lastName?: string; + isActive: boolean; + isAdmin: boolean; + password: string; + }; + }; + }; + responses: { + /** @description The request has succeeded. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UserProfile"]; + }; + }; + /** @description Access is unauthorized. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; }; }; }; - ApiProfile_deleteGroup: { + ApiUser_update: { parameters: { query?: never; header?: never; path: { id: components["schemas"]["UUID"]; - groupId: components["schemas"]["UUID"]; }; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateUserData"]; + }; + }; responses: { - /** @description There is no content to send for this request, but the headers may be useful. */ - 204: { + /** @description The request has succeeded. */ + 200: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": components["schemas"]["UserProfile"]; + }; + }; + /** @description Unauthorized, usually when using a group that the user doesn't have access to */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Access is unauthorized. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description The server cannot find the requested resource. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; }; }; }; - ApiSession_me: { + ApiUser_delete: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: components["schemas"]["UUID"]; + }; cookie?: never; }; requestBody?: never; @@ -1142,11 +1387,11 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["CheckMyProfile"]; + "application/json": boolean; }; }; - /** @description An unexpected error response. */ - default: { + /** @description Access is unauthorized. */ + 401: { headers: { [name: string]: unknown; }; @@ -1156,18 +1401,17 @@ export interface operations { }; }; }; - ApiSession_login: { + ApiUser_addGroup: { parameters: { query?: never; header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UserCredentials"]; + path: { + id: components["schemas"]["UUID"]; + groupId: components["schemas"]["UUID"]; }; + cookie?: never; }; + requestBody?: never; responses: { /** @description The request has succeeded. */ 200: { @@ -1184,16 +1428,19 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["InvalidCredentials"]; + "application/json": components["schemas"]["Error"]; }; }; }; }; - ApiSession_logout: { + ApiUser_deleteGroup: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: components["schemas"]["UUID"]; + groupId: components["schemas"]["UUID"]; + }; cookie?: never; }; requestBody?: never; @@ -1204,7 +1451,16 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": boolean; + "application/json": components["schemas"]["UserProfile"]; + }; + }; + /** @description Access is unauthorized. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; }; }; }; diff --git a/src/react-view/api/models.ts b/src/react-view/api/models.ts index 422f7975..e27c8cd7 100644 --- a/src/react-view/api/models.ts +++ b/src/react-view/api/models.ts @@ -15,4 +15,5 @@ export type Label = components['schemas']['Label']; export type MyProfile = components['schemas']['GetMyProfile']; export type UserGroup = components['schemas']['UserGroup']; export type UserProfile = components['schemas']['UserProfile']; +export type UpdateMyProfile = components["schemas"]["UpdateMyProfile"] export type SessionInfo = components['schemas']['CheckMyProfile']; diff --git a/src/react-view/api/openapi-react-query.ts b/src/react-view/api/openapi-react-query.ts deleted file mode 100644 index befcdf62..00000000 --- a/src/react-view/api/openapi-react-query.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { - type QueryClient, - type QueryFunctionContext, - useMutation, - type UseMutationOptions, - type UseMutationResult, - useQuery, - type UseQueryOptions, - type UseQueryResult, - useSuspenseQuery, - type UseSuspenseQueryOptions, - type UseSuspenseQueryResult, -} from '@tanstack/react-query'; -import type { Client as FetchClient, ClientMethod, FetchResponse, MaybeOptionalInit } from 'openapi-fetch'; -import type { HttpMethod, MediaType, PathsWithMethod, RequiredKeysOf } from 'openapi-typescript-helpers'; - -type InitWithUnknowns = Init & { [key: string]: unknown }; - -export type QueryKey< - Paths extends Record>, - Method extends HttpMethod, - Path extends PathsWithMethod, -> = readonly [Method, Path, MaybeOptionalInit]; - -export type QueryOptionsFunction>, Media extends MediaType> = < - Method extends HttpMethod, - Path extends PathsWithMethod, - Init extends MaybeOptionalInit, - Response extends Required>, // note: Required is used to avoid repeating NonNullable in UseQuery types - Options extends Omit< - UseQueryOptions>, - 'queryKey' | 'queryFn' - >, ->( - method: Method, - path: Path, - ...[init, options]: RequiredKeysOf extends never - ? [InitWithUnknowns?, Options?] - : [InitWithUnknowns, Options?] -) => UseQueryOptions>; - -export type UseQueryMethod>, Media extends MediaType> = < - Method extends HttpMethod, - Path extends PathsWithMethod, - Init extends MaybeOptionalInit, - Response extends Required>, // note: Required is used to avoid repeating NonNullable in UseQuery types - Options extends Omit< - UseQueryOptions>, - 'queryKey' | 'queryFn' - >, ->( - method: Method, - url: Path, - ...[init, options, queryClient]: RequiredKeysOf extends never - ? [InitWithUnknowns?, Options?, QueryClient?] - : [InitWithUnknowns, Options?, QueryClient?] -) => UseQueryResult; - -export type UseSuspenseQueryMethod>, Media extends MediaType> = < - Method extends HttpMethod, - Path extends PathsWithMethod, - Init extends MaybeOptionalInit, - Response extends Required>, // note: Required is used to avoid repeating NonNullable in UseQuery types - Options extends Omit< - UseSuspenseQueryOptions>, - 'queryKey' | 'queryFn' - >, ->( - method: Method, - url: Path, - ...[init, options, queryClient]: RequiredKeysOf extends never - ? [InitWithUnknowns?, Options?, QueryClient?] - : [InitWithUnknowns, Options?, QueryClient?] -) => UseSuspenseQueryResult; - -export type UseMutationMethod>, Media extends MediaType> = < - Method extends HttpMethod, - Path extends PathsWithMethod, - Init extends MaybeOptionalInit, - Response extends Required>, // note: Required is used to avoid repeating NonNullable in UseQuery types - Options extends Omit, 'mutationKey' | 'mutationFn'>, ->( - method: Method, - url: Path, - options?: Options, - queryClient?: QueryClient, -) => UseMutationResult; - -export interface OpenapiQueryClient { - queryOptions: QueryOptionsFunction; - useQuery: UseQueryMethod; - useSuspenseQuery: UseSuspenseQueryMethod; - useMutation: UseMutationMethod; -} - -// TODO: Add the ability to bring queryClient as argument -export default function createClient( - client: FetchClient, -): OpenapiQueryClient { - const queryFn = async >({ - queryKey: [method, path, init], - signal, - }: QueryFunctionContext>) => { - const mth = method.toUpperCase() as Uppercase; - const fn = client[mth] as ClientMethod; - const { data, error } = await fn(path, { signal, ...(init as any) }); // TODO: find a way to avoid as any - if (error || !data) { - throw error; - } - return data; - }; - - const queryOptions: QueryOptionsFunction = (method, path, ...[init, options]) => ({ - queryKey: [method, path, init as InitWithUnknowns] as const, - queryFn, - ...options, - }); - - return { - queryOptions, - useQuery: (method, path, ...[init, options, queryClient]) => - useQuery(queryOptions(method, path, init as InitWithUnknowns, options), queryClient), - useSuspenseQuery: (method, path, ...[init, options, queryClient]) => - useSuspenseQuery(queryOptions(method, path, init as InitWithUnknowns, options), queryClient), - useMutation: (method, path, options, queryClient) => - useMutation( - { - mutationKey: [method, path], - mutationFn: async init => { - const mth = method.toUpperCase() as Uppercase; - const fn = client[mth] as ClientMethod; - const { data, error } = await fn(path, init); - if (error || !data) { - throw error; - } - return data; - }, - ...options, - }, - queryClient, - ), - }; -} diff --git a/src/react-view/contents/common/uploader-queue.context.tsx b/src/react-view/contents/common/uploader-queue.context.tsx index 33caf81d..5b77000d 100644 --- a/src/react-view/contents/common/uploader-queue.context.tsx +++ b/src/react-view/contents/common/uploader-queue.context.tsx @@ -38,7 +38,7 @@ export const ProvideUploadQueue: React.FC = ({ children }) => setFiles(oldFiles => { const pos = oldFiles.findIndex(({ id }) => fileId === id); if (pos >= 0) { - oldFiles[pos] = { ...oldFiles[pos], ...changeFile }; + oldFiles[pos] = { ...oldFiles[pos], ...changeFile } as IFileData; } return [...oldFiles]; }); @@ -46,7 +46,7 @@ export const ProvideUploadQueue: React.FC = ({ children }) => const submit = async () => { setUploading(true); - for await (const fileData of files) { + for (const fileData of files) { change(fileData.id, { status: FileStatus.uploading }); const response = await sendFile.execute(fileData.kind, fileData.file); diff --git a/src/react-view/contents/graphs/graph-router.tsx b/src/react-view/contents/graphs/graph-router.tsx index eacad863..015dd5a1 100644 --- a/src/react-view/contents/graphs/graph-router.tsx +++ b/src/react-view/contents/graphs/graph-router.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { Route, Routes, useParams } from 'react-router'; -import * as uuid from 'uuid'; import NotFound from '../extra/not-found'; import { EditGraph } from './form/edit-graph.form'; diff --git a/src/react-view/contents/main/index.tsx b/src/react-view/contents/main/index.tsx deleted file mode 100644 index a7e512a3..00000000 --- a/src/react-view/contents/main/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { Grid, ResponsiveContext } from 'grommet'; -import React from 'react'; - -import { LastFiles } from '../document/last-files'; -import { TagsList } from './tags-list'; - -export const MainPage: React.FC = () => { - const size = React.useContext(ResponsiveContext); - return ( - - - - - ); -}; diff --git a/src/react-view/contents/main/tags-list.tsx b/src/react-view/contents/main/tags-list.tsx deleted file mode 100644 index bfacc744..00000000 --- a/src/react-view/contents/main/tags-list.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Box, Button } from 'grommet'; -import React from 'react'; - -import { useGetAvailableTagsQuery } from '../../graphql/generated'; -import { LoadingPage } from '../../utils/ui/loading'; -import { ITagModel, Tag } from '../../utils/ui/tag/tag'; - -interface TagsListProps { - onFilterChange: (tags: ITagModel[]) => void; -} -export const TagsList: React.FC = ({ onFilterChange }) => { - const [tagsQuery] = useGetAvailableTagsQuery(); - const [filters, setFilters] = React.useState([]); - if (tagsQuery.fetching) { - return ; - } else if (tagsQuery.data) { - return ( - - {tagsQuery.data.listTags.map(tag => ( - - ))} - - ); - } - return
Some Error
; -}; diff --git a/src/react-view/contents/profile/edit-profile.tsx b/src/react-view/contents/profile/edit-profile.tsx index 5f97f800..f9850c88 100644 --- a/src/react-view/contents/profile/edit-profile.tsx +++ b/src/react-view/contents/profile/edit-profile.tsx @@ -1,18 +1,24 @@ import { Box, Button, Form, FormField, Heading, TextInput } from 'grommet'; import React from 'react'; +import { useAsyncCallback } from 'react-async-hook'; +import { useApi } from '../../api/client'; +import { UpdateMyProfile } from '../../api/models'; import { useLogger } from '../../utils/logger/logger.context'; -import { useSession } from '../../utils/session/session-context'; +import { useSession, useUserProfileOrThrows } from '../../utils/session/session-context'; export const EditProfile = () => { const { refresh, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - session, } = useSession(); - const [, updateProfile] = usePatchUserInfo(); + const {firstName, lastName, ...profile} = useUserProfileOrThrows() + + const client = useApi() + const updateProfile = useAsyncCallback((data: UpdateMyProfile)=>client.PUT("/session/me", { + body: data + })) const logger = useLogger(); - const [uiProfile, setUiProfile] = React.useState({ + const [uiProfile, setUiProfile] = React.useState({ password: '', firstName: firstName ? firstName : '', lastName: lastName ? lastName : '', @@ -23,23 +29,27 @@ export const EditProfile = () => { return ( Edit profile - + value={uiProfile} onChange={setUiProfile} - onSubmit={async () => { + onSubmit={() => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { newPassword2, ...data } = uiProfile; logger.info('New sent data', { data }); - await updateProfile({ + updateProfile.execute( data, - }); - setUiProfile({ - ...uiProfile, - password: '', - newPassword: '', - newPassword2: '', - }); - await refresh(); + ).then(()=>{ + setUiProfile({ + ...uiProfile, + password: '', + newPassword: '', + newPassword2: '', + }); + refresh(); + }).catch((error:unknown)=> { + logger.error("Error updating the user info", { error }) + }) + }} > diff --git a/src/react-view/contents/transaction-list/transaction-list.tsx b/src/react-view/contents/transaction-list/transaction-list.tsx index 190ae795..b082eb02 100644 --- a/src/react-view/contents/transaction-list/transaction-list.tsx +++ b/src/react-view/contents/transaction-list/transaction-list.tsx @@ -6,17 +6,18 @@ import { useApi } from '../../api/client'; import { useLabelsListContext } from '../common/label.context'; import { BankTransactionEnriched, useTransactionsData } from '../common/transaction.context'; import { TransactionRow } from './transaction-row'; +import { ApiUUID } from '../../api/models'; interface TransactionListFilters { kind?: string; - label?: number; + label?: string; movement?: string; } export const TransactionList: React.FC = () => { const { data: results, replace } = useTransactionsData(); const client = useApi() - const kindRequest = useAsync(()=>client.GET("/imports/parsers")); + const kindRequest = useAsync(()=>client.GET("/imports/parsers"), [client]); const labels = useLabelsListContext(); const [filters, setFilters] = useState({}); @@ -27,7 +28,7 @@ export const TransactionList: React.FC = () => { if (filters.label) { filteredResults = filteredResults.filter(({ labelIds: transactionLabels }) => - transactionLabels.includes(filters.label as number), + transactionLabels.includes(filters.label as ApiUUID), ); } @@ -48,7 +49,7 @@ export const TransactionList: React.FC = () => { {