chore: codify React component style

This commit is contained in:
2026-08-25 15:33:20 +03:00
parent 268acdf2e1
commit d10e1dd182
65 changed files with 711 additions and 365 deletions
+12 -6
View File
@@ -2,7 +2,7 @@ import { MantineProvider } from "@mantine/core";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";
import { HttpResponse, http } from "msw";
import type { ReactNode } from "react";
import type { FC, PropsWithChildren } from "react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { server } from "@/test/server";
import { useAuthenticatedQuery } from "./authenticated-query";
@@ -10,11 +10,17 @@ import { apiQueryClient, configureAuthClient } from "./client";
function createWrapper() {
const client = new QueryClient();
return ({ children }: { children: ReactNode }) => (
<MantineProvider>
<QueryClientProvider client={client}>{children}</QueryClientProvider>
</MantineProvider>
);
const TestWrapper: FC<PropsWithChildren> = (props) => {
const { children } = props;
return (
<MantineProvider>
<QueryClientProvider client={client}>{children}</QueryClientProvider>
</MantineProvider>
);
};
return TestWrapper;
}
describe("useAuthenticatedQuery", () => {
+3 -1
View File
@@ -10,7 +10,7 @@ import {
subscribeAuthenticatedSession,
} from "./client";
export function useAuthenticatedQuery<
function useAuthenticatedQuery<
TQueryFnData = unknown,
TError = Error,
TData = TQueryFnData,
@@ -32,3 +32,5 @@ export function useAuthenticatedQuery<
(typeof enabled === "function" ? enabled(query) : (enabled ?? true)),
});
}
export { useAuthenticatedQuery };
+13 -5
View File
@@ -21,16 +21,16 @@ function setAuthenticatedSession(next: boolean) {
for (const listener of authSessionListeners) listener();
}
export function configureAuthClient(handlers: AuthHandlers | null) {
function configureAuthClient(handlers: AuthHandlers | null) {
auth = handlers;
setAuthenticatedSession(Boolean(auth?.accessToken()));
}
export function getAuthenticatedSessionSnapshot() {
function getAuthenticatedSessionSnapshot() {
return hasAuthenticatedSession;
}
export function subscribeAuthenticatedSession(listener: () => void) {
function subscribeAuthenticatedSession(listener: () => void) {
authSessionListeners.add(listener);
return () => authSessionListeners.delete(listener);
}
@@ -71,10 +71,18 @@ async function authenticatedFetch(request: Request): Promise<Response> {
: response;
}
export const apiClient = createClient<paths>({
const apiClient = createClient<paths>({
baseUrl: apiBaseUrl,
credentials: "include",
fetch: authenticatedFetch,
});
export const apiQueryClient = createQueryClient(apiClient);
const apiQueryClient = createQueryClient(apiClient);
export {
apiClient,
apiQueryClient,
configureAuthClient,
getAuthenticatedSessionSnapshot,
subscribeAuthenticatedSession,
};
+3 -1
View File
@@ -1,4 +1,4 @@
export const ru = {
const ru = {
translation: {
appName: "WealthLedger",
navigation: { accounts: "Счета", settings: "Настройки" },
@@ -167,3 +167,5 @@ export const ru = {
},
},
} as const;
export { ru };
+3 -1
View File
@@ -2,8 +2,10 @@ import type { ParseKeys } from "i18next";
import { i18n } from "./i18n";
export function validationMessage(
function validationMessage(
key: ParseKeys,
): (_value: unknown) => { message: string } {
return () => ({ message: i18n.t(key) });
}
export { validationMessage };
+9 -6
View File
@@ -1,13 +1,14 @@
import { AppShell as MantineAppShell, NavLink, Text } from "@mantine/core";
import type { ReactNode } from "react";
import type { FC, ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { Outlet, NavLink as RouterNavLink, useLocation } from "react-router";
export function AppShell({
contentAboveOutlet,
}: {
type AppShellProps = {
contentAboveOutlet?: ReactNode;
}) {
};
const AppShell: FC<AppShellProps> = (props) => {
const { contentAboveOutlet } = props;
const { t } = useTranslation();
const { pathname } = useLocation();
const items = [
@@ -52,4 +53,6 @@ export function AppShell({
</nav>
</MantineAppShell>
);
}
};
export { AppShell };
+5 -10
View File
@@ -1,5 +1,5 @@
import { Button, Loader, Paper, Stack, Text, Title } from "@mantine/core";
import type { ReactNode } from "react";
import type { FC, ReactNode } from "react";
type StateViewProps = {
title: string;
@@ -9,13 +9,8 @@ type StateViewProps = {
error?: boolean;
};
export function StateView({
title,
text,
action,
loading = false,
error = false,
}: StateViewProps) {
const StateView: FC<StateViewProps> = (props) => {
const { title, text, action, loading = false, error = false } = props;
const role = loading ? "status" : error ? "alert" : undefined;
return (
@@ -28,6 +23,6 @@ export function StateView({
</Stack>
</Paper>
);
}
};
export { Button };
export { Button, StateView };