R: Refactoring folder structure: introducing features
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run

This commit is contained in:
Ivan 2025-02-10 01:32:55 +03:00
parent 80473b0b82
commit 17b94b5e9a
435 changed files with 1786 additions and 1746 deletions

View File

@ -1,16 +1,16 @@
import { Suspense } from 'react';
import { Outlet } from 'react-router';
import ConceptToaster from '@/app/ConceptToaster';
import Footer from '@/app/Footer';
import Navigation from '@/app/Navigation';
import Loader from '@/components/ui/Loader';
import { ModalLoader } from '@/components/ui/Modal';
import { Loader } from '@/components/Loader';
import { ModalLoader } from '@/components/Modal';
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
import { globals } from '@/utils/constants';
import { Footer } from './Footer';
import { GlobalDialogs } from './GlobalDialogs';
import ConceptToaster from './GlobalToaster';
import { GlobalTooltips } from './GlobalTooltips';
import { Navigation } from './Navigation';
import { NavigationState } from './Navigation/NavigationContext';
function ApplicationLayout() {

View File

@ -1,9 +1,9 @@
import { useNavigate, useRouteError } from 'react-router';
import InfoError from '@/components/info/InfoError';
import { Button } from '@/components/ui/Control';
import { Button } from '@/components/Control';
import { InfoError } from '@/components/InfoError';
function ErrorFallback() {
export function ErrorFallback() {
const error = useRouteError();
const router = useNavigate();
@ -18,5 +18,3 @@ function ErrorFallback() {
</div>
);
}
export default ErrorFallback;

View File

@ -1,9 +1,9 @@
import clsx from 'clsx';
import { TextURL } from '@/components/ui/Control';
import { TextURL } from '@/components/Control';
import { external_urls } from '@/utils/constants';
function Footer() {
export function Footer() {
return (
<footer
className={clsx(
@ -25,5 +25,3 @@ function Footer() {
</footer>
);
}
export default Footer;

View File

@ -2,32 +2,31 @@
import React from 'react';
import { DialogType } from '@/models/miscellaneous';
import { useDialogsStore } from '@/stores/dialogs';
import { DialogType, useDialogsStore } from '@/stores/dialogs';
const DlgChangeInputSchema = React.lazy(() => import('@/dialogs/DlgChangeInputSchema'));
const DlgChangeLocation = React.lazy(() => import('@/dialogs/DlgChangeLocation'));
const DlgCloneLibraryItem = React.lazy(() => import('@/dialogs/DlgCloneLibraryItem'));
const DlgCreateCst = React.lazy(() => import('@/dialogs/DlgCreateCst'));
const DlgCreateOperation = React.lazy(() => import('@/dialogs/DlgCreateOperation'));
const DlgCreateVersion = React.lazy(() => import('@/dialogs/DlgCreateVersion'));
const DlgCstTemplate = React.lazy(() => import('@/dialogs/DlgCstTemplate'));
const DlgDeleteCst = React.lazy(() => import('@/dialogs/DlgDeleteCst'));
const DlgDeleteOperation = React.lazy(() => import('@/dialogs/DlgDeleteOperation'));
const DlgEditEditors = React.lazy(() => import('@/dialogs/DlgEditEditors'));
const DlgEditOperation = React.lazy(() => import('@/dialogs/DlgEditOperation'));
const DlgEditReference = React.lazy(() => import('@/dialogs/DlgEditReference'));
const DlgEditVersions = React.lazy(() => import('@/dialogs/DlgEditVersions'));
const DlgEditWordForms = React.lazy(() => import('@/dialogs/DlgEditWordForms'));
const DlgGraphParams = React.lazy(() => import('@/dialogs/DlgGraphParams'));
const DlgInlineSynthesis = React.lazy(() => import('@/dialogs/DlgInlineSynthesis'));
const DlgRelocateConstituents = React.lazy(() => import('@/dialogs/DlgRelocateConstituents'));
const DlgRenameCst = React.lazy(() => import('@/dialogs/DlgRenameCst'));
const DlgShowAST = React.lazy(() => import('@/dialogs/DlgShowAST'));
const DlgShowQR = React.lazy(() => import('@/dialogs/DlgShowQR'));
const DlgShowTypeGraph = React.lazy(() => import('@/dialogs/DlgShowTypeGraph'));
const DlgSubstituteCst = React.lazy(() => import('@/dialogs/DlgSubstituteCst'));
const DlgUploadRSForm = React.lazy(() => import('@/dialogs/DlgUploadRSForm'));
const DlgChangeInputSchema = React.lazy(() => import('@/features/oss/dialogs/DlgChangeInputSchema'));
const DlgChangeLocation = React.lazy(() => import('@/features/library/dialogs/DlgChangeLocation'));
const DlgCloneLibraryItem = React.lazy(() => import('@/features/rsform/dialogs/DlgCloneLibraryItem'));
const DlgCreateCst = React.lazy(() => import('@/features/rsform/dialogs/DlgCreateCst'));
const DlgCreateOperation = React.lazy(() => import('@/features/oss/dialogs/DlgCreateOperation'));
const DlgCreateVersion = React.lazy(() => import('@/features/rsform/dialogs/DlgCreateVersion'));
const DlgCstTemplate = React.lazy(() => import('@/features/rsform/dialogs/DlgCstTemplate'));
const DlgDeleteCst = React.lazy(() => import('@/features/rsform/dialogs/DlgDeleteCst'));
const DlgDeleteOperation = React.lazy(() => import('@/features/oss/dialogs/DlgDeleteOperation'));
const DlgEditEditors = React.lazy(() => import('@/features/rsform/dialogs/DlgEditEditors'));
const DlgEditOperation = React.lazy(() => import('@/features/oss/dialogs/DlgEditOperation'));
const DlgEditReference = React.lazy(() => import('@/features/rsform/dialogs/DlgEditReference'));
const DlgEditVersions = React.lazy(() => import('@/features/rsform/dialogs/DlgEditVersions'));
const DlgEditWordForms = React.lazy(() => import('@/features/rsform/dialogs/DlgEditWordForms'));
const DlgGraphParams = React.lazy(() => import('@/features/rsform/dialogs/DlgGraphParams'));
const DlgInlineSynthesis = React.lazy(() => import('@/features/rsform/dialogs/DlgInlineSynthesis'));
const DlgRelocateConstituents = React.lazy(() => import('@/features/oss/dialogs/DlgRelocateConstituents'));
const DlgRenameCst = React.lazy(() => import('@/features/rsform/dialogs/DlgRenameCst'));
const DlgShowAST = React.lazy(() => import('@/features/rsform/dialogs/DlgShowAST'));
const DlgShowQR = React.lazy(() => import('@/features/rsform/dialogs/DlgShowQR'));
const DlgShowTypeGraph = React.lazy(() => import('@/features/rsform/dialogs/DlgShowTypeGraph'));
const DlgSubstituteCst = React.lazy(() => import('@/features/rsform/dialogs/DlgSubstituteCst'));
const DlgUploadRSForm = React.lazy(() => import('@/features/rsform/dialogs/DlgUploadRSForm'));
export const GlobalDialogs = () => {
const active = useDialogsStore(state => state.active);

View File

@ -6,7 +6,6 @@ interface ToasterThemedProps extends Omit<ToastContainerProps, 'theme'> {}
function ToasterThemed(props: ToasterThemedProps) {
const darkMode = usePreferencesStore(state => state.darkMode);
return <ToastContainer theme={darkMode ? 'dark' : 'light'} {...props} />;
}

View File

@ -1,8 +1,8 @@
'use client';
import InfoConstituenta from '@/components/info/InfoConstituenta';
import { Tooltip } from '@/components/ui/Container';
import Loader from '@/components/ui/Loader';
import { Tooltip } from '@/components/Container';
import { Loader } from '@/components/Loader';
import InfoConstituenta from '@/features/rsform/components/InfoConstituenta';
import { useTooltipsStore } from '@/stores/tooltips';
import { globals } from '@/utils/constants';

View File

@ -1,6 +1,5 @@
import clsx from 'clsx';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
import { CProps } from '@/components/props';
import useWindowSize from '@/hooks/useWindowSize';
@ -10,10 +9,11 @@ import { PARAMETER } from '@/utils/constants';
import { urls } from '../urls';
import Logo from './Logo';
import NavigationButton from './NavigationButton';
import { useConceptNavigation } from './NavigationContext';
import ToggleNavigation from './ToggleNavigation';
import UserMenu from './UserMenu';
function Navigation() {
export function Navigation() {
const router = useConceptNavigation();
const size = useWindowSize();
const noNavigationAnimation = useAppLayoutStore(state => state.noNavigationAnimation);
@ -65,5 +65,3 @@ function Navigation() {
</nav>
);
}
export default Navigation;

View File

@ -1,5 +1,5 @@
import { useAuthSuspense } from '@/backend/auth/useAuth';
import { IconLogin, IconUser2 } from '@/components/Icons';
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
import { usePreferencesStore } from '@/stores/preferences';
import NavigationButton from './NavigationButton';

View File

@ -1,6 +1,4 @@
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { useAuthSuspense } from '@/backend/auth/useAuth';
import { useLogout } from '@/backend/auth/useLogout';
import { Dropdown, DropdownButton } from '@/components/Dropdown';
import {
IconAdmin,
IconAdminOff,
@ -16,10 +14,12 @@ import {
IconUser
} from '@/components/Icons';
import { CProps } from '@/components/props';
import { Dropdown, DropdownButton } from '@/components/ui/Dropdown';
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
import { useLogout } from '@/features/auth/backend/useLogout';
import { usePreferencesStore } from '@/stores/preferences';
import { urls } from '../urls';
import { useConceptNavigation } from './NavigationContext';
interface UserDropdownProps {
isOpen: boolean;

View File

@ -1,10 +1,10 @@
import { Suspense } from 'react';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { useDropdown } from '@/components/ui/Dropdown';
import Loader from '@/components/ui/Loader';
import { useDropdown } from '@/components/Dropdown';
import { Loader } from '@/components/Loader';
import { urls } from '../urls';
import { useConceptNavigation } from './NavigationContext';
import UserButton from './UserButton';
import UserDropdown from './UserDropdown';

View File

@ -1 +1 @@
export { default } from './Navigation';
export { Navigation } from './Navigation';

View File

@ -1,19 +1,19 @@
import { createBrowserRouter } from 'react-router';
import { prefetchAuth } from '@/backend/auth/useAuth';
import { prefetchLibrary } from '@/backend/library/useLibrary';
import { prefetchOSS } from '@/backend/oss/useOSS';
import { prefetchRSForm } from '@/backend/rsform/useRSForm';
import { prefetchProfile } from '@/backend/users/useProfile';
import { prefetchUsers } from '@/backend/users/useUsers';
import Loader from '@/components/ui/Loader';
import CreateItemPage from '@/pages/CreateItemPage';
import HomePage from '@/pages/HomePage';
import LoginPage from '@/pages/LoginPage';
import NotFoundPage from '@/pages/NotFoundPage';
import { Loader } from '@/components/Loader';
import { prefetchAuth } from '@/features/auth/backend/useAuth';
import LoginPage from '@/features/auth/pages/LoginPage';
import HomePage from '@/features/home/HomePage';
import NotFoundPage from '@/features/home/NotFoundPage';
import { prefetchLibrary } from '@/features/library/backend/useLibrary';
import CreateItemPage from '@/features/library/pages/CreateItemPage';
import { prefetchOSS } from '@/features/oss/backend/useOSS';
import { prefetchRSForm } from '@/features/rsform/backend/useRSForm';
import { prefetchProfile } from '@/features/users/backend/useProfile';
import { prefetchUsers } from '@/features/users/backend/useUsers';
import ApplicationLayout from './ApplicationLayout';
import ErrorFallback from './ErrorFallback';
import { ErrorFallback } from './ErrorFallback';
import { routes } from './urls';
export const Router = createBrowserRouter([
@ -38,25 +38,25 @@ export const Router = createBrowserRouter([
},
{
path: routes.signup,
lazy: () => import('@/pages/RegisterPage')
lazy: () => import('@/features/users/pages/RegisterPage')
},
{
path: routes.profile,
loader: prefetchProfile,
lazy: () => import('@/pages/UserProfilePage')
lazy: () => import('@/features/users/pages/UserProfilePage')
},
{
path: routes.restore_password,
lazy: () => import('@/pages/RestorePasswordPage')
lazy: () => import('@/features/auth/pages/RestorePasswordPage')
},
{
path: routes.password_change,
lazy: () => import('@/pages/PasswordChangePage')
lazy: () => import('@/features/auth/pages/PasswordChangePage')
},
{
path: routes.library,
loader: () => Promise.allSettled([prefetchLibrary(), prefetchUsers()]),
lazy: () => import('@/pages/LibraryPage')
lazy: () => import('@/features/library/pages/LibraryPage')
},
{
path: routes.create_schema,
@ -65,24 +65,24 @@ export const Router = createBrowserRouter([
{
path: `${routes.rsforms}/:id`,
loader: data => prefetchRSForm(parseRSFormURL(data.params.id, data.request.url)),
lazy: () => import('@/pages/RSFormPage')
lazy: () => import('@/features/rsform/pages/RSFormPage')
},
{
path: `${routes.oss}/:id`,
loader: data => prefetchOSS(parseOssURL(data.params.id)),
lazy: () => import('@/pages/OssPage')
lazy: () => import('@/features/oss/pages/OssPage')
},
{
path: routes.manuals,
lazy: () => import('@/pages/ManualsPage')
lazy: () => import('@/features/help/pages/ManualsPage')
},
{
path: `${routes.icons}`,
lazy: () => import('@/pages/IconsPage')
lazy: () => import('@/features/home/IconsPage')
},
{
path: `${routes.database_schema}`,
lazy: () => import('@/pages/DatabaseSchemaPage')
lazy: () => import('@/features/home/DatabaseSchemaPage')
}
]
}

View File

@ -1,3 +1,6 @@
export { useConceptNavigation } from './Navigation/NavigationContext';
export { useBlockNavigation } from './Navigation/NavigationContext';
export { urls } from './urls';
import { RouterProvider } from 'react-router';
import { Router } from './Router';

View File

@ -1,13 +1,15 @@
/**
* Module: generic API for backend REST communications using axios library.
*/
import axios from 'axios';
import { AxiosError, AxiosRequestConfig } from 'axios';
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
import { toast } from 'react-toastify';
import { buildConstants } from '@/utils/buildConstants';
import { extractErrorMessage } from '@/utils/utils';
export { AxiosError } from 'axios';
export const isAxiosError = axios.isAxiosError;
const defaultOptions = {
xsrfCookieName: 'csrftoken',
xsrfHeaderName: 'x-csrftoken',

View File

@ -1,6 +1,6 @@
import { QueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { AxiosError } from './apiTransport';
import { DELAYS } from './configuration';
declare module '@tanstack/react-query' {

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { CProps } from '../props';
interface DividerProps extends CProps.Styling {
/** Indicates whether the divider is vertical. */

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { CProps } from '../props';
/**
* `flex` column container.

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { CProps } from '../props';
interface OverlayProps extends CProps.Styling {
/** Id of the overlay. */

View File

@ -1,8 +1,9 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { globals } from '@/utils/constants';
import { CProps } from '../props';
interface ButtonProps extends CProps.Control, CProps.Colors, CProps.Button {
/** Icon to display first. */
icon?: React.ReactNode;

View File

@ -1,8 +1,9 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { globals } from '@/utils/constants';
import { CProps } from '../props';
interface MiniButtonProps extends CProps.Button {
/** Button type. */
type?: 'button' | 'submit';

View File

@ -1,8 +1,9 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { globals } from '@/utils/constants';
import { CProps } from '../props';
interface SelectorButtonProps extends CProps.Button {
/** Text to display in the button. */
text?: string;

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { CProps } from '../props';
interface SubmitButtonProps extends CProps.Button {
/** Text to display in the button. */

View File

@ -1,5 +1,4 @@
export { Button } from './Button';
export { LinkTopic } from './LinkTopic';
export { MiniButton } from './MiniButton';
export { SelectorButton } from './SelectorButton';
export { SubmitButton } from './SubmitButton';

View File

@ -17,8 +17,7 @@ import {
} from '@tanstack/react-table';
import { useMemo, useState } from 'react';
import { CProps } from '@/components/props';
import { CProps } from '../props';
import DefaultNoData from './DefaultNoData';
import PaginationTools from './PaginationTools';
import TableBody from './TableBody';

View File

@ -5,9 +5,10 @@ import { Table } from '@tanstack/react-table';
import clsx from 'clsx';
import { useCallback } from 'react';
import { IconPageFirst, IconPageLast, IconPageLeft, IconPageRight } from '@/components/Icons';
import { prefixes } from '@/utils/constants';
import { IconPageFirst, IconPageLast, IconPageLeft, IconPageRight } from '../Icons';
interface PaginationToolsProps<TData> {
id?: string;
table: Table<TData>;

View File

@ -2,7 +2,7 @@
import { Table } from '@tanstack/react-table';
import { CheckboxTristate } from '@/components/ui/Input';
import { CheckboxTristate } from '../Input';
interface SelectAllProps<TData> {
table: Table<TData>;

View File

@ -2,7 +2,7 @@
import { Row } from '@tanstack/react-table';
import { Checkbox } from '@/components/ui/Input';
import { Checkbox } from '../Input';
interface SelectRowProps<TData> {
row: Row<TData>;

View File

@ -2,7 +2,7 @@
import { Column } from '@tanstack/react-table';
import { IconSortAsc, IconSortDesc } from '@/components/Icons';
import { IconSortAsc, IconSortDesc } from '../Icons';
interface SortingIconProps<TData> {
column: Column<TData>;

View File

@ -3,8 +3,7 @@
import { Cell, flexRender, Row, Table } from '@tanstack/react-table';
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { CProps } from '../props';
import { IConditionalStyle } from '.';
import SelectRow from './SelectRow';

View File

@ -1,6 +1,6 @@
import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library';
import { CstMatchMode, DependencyMode } from '@/models/miscellaneous';
import { CstType, ExpressionStatus } from '@/models/rsform';
import { AccessPolicy, LibraryItemType, LocationHead } from '@/features/library/models/library';
import { CstType, ExpressionStatus } from '@/features/rsform/models/rsform';
import { CstMatchMode, DependencyMode } from '@/features/rsform/stores/cstSearch';
import {
IconAlias,

View File

@ -1,8 +1,9 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { PARAMETER } from '@/utils/constants';
import { CProps } from '../props';
interface DropdownProps extends CProps.Styling {
/** Indicates whether the dropdown should stretch to the left. */
stretchLeft?: boolean;

View File

@ -1,9 +1,10 @@
import axios, { type AxiosError } from 'axios';
import clsx from 'clsx';
import { PrettyJson } from '@/components/ui/View';
import { AxiosError, isAxiosError } from '@/backend/apiTransport';
import { isResponseHtml } from '@/utils/utils';
import { PrettyJson } from './View';
export type ErrorData = string | Error | AxiosError | undefined | null;
interface InfoErrorProps {
@ -15,7 +16,7 @@ function DescribeError({ error }: { error: ErrorData }) {
return <p>Ошибки отсутствуют</p>;
} else if (typeof error === 'string') {
return <p>{error}</p>;
} else if (!axios.isAxiosError(error)) {
} else if (!isAxiosError(error)) {
return (
<div className='mt-6'>
<p>
@ -76,7 +77,7 @@ function DescribeError({ error }: { error: ErrorData }) {
);
}
function InfoError({ error }: InfoErrorProps) {
export function InfoError({ error }: InfoErrorProps) {
return (
<div
className={clsx(
@ -97,5 +98,3 @@ function InfoError({ error }: InfoErrorProps) {
</div>
);
}
export default InfoError;

View File

@ -1,9 +1,10 @@
import clsx from 'clsx';
import { CheckboxChecked } from '@/components/Icons';
import { CProps } from '@/components/props';
import { globals } from '@/utils/constants';
import { CheckboxChecked } from '../Icons';
import { CProps } from '../props';
export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick' | 'onChange'> {
/** Label to display next to the checkbox. */
label?: string;

View File

@ -1,9 +1,9 @@
import clsx from 'clsx';
import { CheckboxChecked, CheckboxNull } from '@/components/Icons';
import { CProps } from '@/components/props';
import { globals } from '@/utils/constants';
import { CheckboxChecked, CheckboxNull } from '../Icons';
import { CProps } from '../props';
import { CheckboxProps } from './Checkbox';
export interface CheckboxTristateProps extends Omit<CheckboxProps, 'value' | 'onChange'> {

View File

@ -1,7 +1,7 @@
import clsx from 'clsx';
import { FieldError, GlobalError } from 'react-hook-form';
import { CProps } from '@/components/props';
import { CProps } from '../props';
interface ErrorFieldProps extends CProps.Styling {
error?: FieldError | GlobalError;

View File

@ -3,10 +3,9 @@
import clsx from 'clsx';
import { useRef, useState } from 'react';
import { IconUpload } from '@/components/Icons';
import { CProps } from '@/components/props';
import { Button } from '../Control';
import { IconUpload } from '../Icons';
import { CProps } from '../props';
import { Label } from './Label';
interface FileInputProps extends Omit<CProps.Input, 'accept' | 'type'> {

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { CProps } from '../props';
interface LabelProps extends CProps.Label {
/** Text to display. */

View File

@ -9,10 +9,11 @@ import Select, {
StylesConfig
} from 'react-select';
import { IconClose, IconDropArrow, IconDropArrowUp } from '@/components/Icons';
import useWindowSize from '@/hooks/useWindowSize';
import { APP_COLORS, SELECT_THEME } from '@/styling/color';
import { IconClose, IconDropArrow, IconDropArrowUp } from '../Icons';
function DropdownIndicator<Option, Group extends GroupBase<Option> = GroupBase<Option>>(
props: DropdownIndicatorProps<Option, true, Group>
) {

View File

@ -9,10 +9,11 @@ import Select, {
StylesConfig
} from 'react-select';
import { IconClose, IconDropArrow, IconDropArrowUp } from '@/components/Icons';
import useWindowSize from '@/hooks/useWindowSize';
import { APP_COLORS, SELECT_THEME } from '@/styling/color';
import { IconClose, IconDropArrow, IconDropArrowUp } from '../Icons';
function DropdownIndicator<Option, Group extends GroupBase<Option> = GroupBase<Option>>(
props: DropdownIndicatorProps<Option, false, Group>
) {

View File

@ -1,12 +1,12 @@
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { IconDropArrow, IconPageRight } from '@/components/Icons';
import { CProps } from '@/components/props';
import { globals, PARAMETER } from '@/utils/constants';
import { Overlay } from '../Container';
import { MiniButton } from '../Control';
import { IconDropArrow, IconPageRight } from '../Icons';
import { CProps } from '../props';
interface SelectTreeProps<ItemType> extends CProps.Styling {
/** Current value. */

View File

@ -1,8 +1,7 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { Label } from '@/components/ui/Input/Label';
import { Label } from '../Input/Label';
import { CProps } from '../props';
import { ErrorField } from './ErrorField';
export interface TextAreaProps extends CProps.Editor, CProps.ErrorProcessing, CProps.Colors, CProps.TextArea {

View File

@ -1,8 +1,7 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { Label } from '@/components/ui/Input/Label';
import { Label } from '../Input/Label';
import { CProps } from '../props';
import { ErrorField } from './ErrorField';
interface TextInputProps extends CProps.Editor, CProps.ErrorProcessing, CProps.Colors, CProps.Input {

View File

@ -54,7 +54,7 @@ const animatePulse = (startBig: boolean, duration: string) => {
/**
* Displays animated loader.
*/
function Loader({ scale = 5, circular }: LoaderProps) {
export function Loader({ scale = 5, circular }: LoaderProps) {
if (circular) {
return (
<div className='flex justify-center' aria-label='three-circles-loading' aria-busy='true' role='progressbar'>
@ -89,5 +89,3 @@ function Loader({ scale = 5, circular }: LoaderProps) {
);
}
}
export default Loader;

View File

@ -2,16 +2,16 @@
import clsx from 'clsx';
import { IconClose } from '@/components/Icons';
import BadgeHelp from '@/components/info/BadgeHelp';
import { CProps } from '@/components/props';
import { HelpTopic } from '@/features/help/models/helpTopic';
import useEscapeKey from '@/hooks/useEscapeKey';
import { HelpTopic } from '@/models/miscellaneous';
import { useDialogsStore } from '@/stores/dialogs';
import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/labels';
import { Button, MiniButton, SubmitButton } from '../Control';
import { IconClose } from '../Icons';
import { CProps } from '../props';
import BadgeHelp from '../shared/BadgeHelp';
import { ModalBackdrop } from './ModalBackdrop';
export interface ModalProps extends CProps.Styling {

View File

@ -2,7 +2,7 @@
import clsx from 'clsx';
import Loader from '@/components/ui/Loader';
import { Loader } from '@/components/Loader';
export function ModalLoader() {
return (

View File

@ -2,14 +2,14 @@
import clsx from 'clsx';
import { IconClose } from '@/components/Icons';
import BadgeHelp from '@/components/info/BadgeHelp';
import useEscapeKey from '@/hooks/useEscapeKey';
import { useDialogsStore } from '@/stores/dialogs';
import { PARAMETER } from '@/utils/constants';
import { prepareTooltip } from '@/utils/labels';
import { Button, MiniButton } from '../Control';
import { IconClose } from '../Icons';
import BadgeHelp from '../shared/BadgeHelp';
import { ModalBackdrop } from './ModalBackdrop';
import { ModalProps } from './ModalForm';

View File

@ -2,9 +2,10 @@ import clsx from 'clsx';
import type { TabProps as TabPropsImpl } from 'react-tabs';
import { Tab as TabImpl } from 'react-tabs';
import { CProps } from '@/components/props';
import { globals } from '@/utils/constants';
import { CProps } from '../props';
interface TabLabelProps extends Omit<TabPropsImpl, 'children'>, CProps.Titled {
/** Label to display in the tab. */
label?: string;

View File

@ -1,37 +0,0 @@
'use client';
import clsx from 'clsx';
import { CProps } from '@/components/props';
import WordformButton from '@/dialogs/DlgEditReference/WordformButton';
import { Grammeme } from '@/models/language';
import { prefixes } from '@/utils/constants';
import { DefaultWordForms, IGrammemeOption, SelectorGrammemes } from '@/utils/selectors';
interface SelectWordFormProps extends CProps.Styling {
value: IGrammemeOption[];
onChange: React.Dispatch<React.SetStateAction<IGrammemeOption[]>>;
}
function SelectWordForm({ value, onChange, className, ...restProps }: SelectWordFormProps) {
function handleSelect(grams: Grammeme[]) {
onChange(SelectorGrammemes.filter(({ value }) => grams.includes(value as Grammeme)));
}
return (
<div className={clsx('text-xs sm:text-sm', className)} {...restProps}>
{DefaultWordForms.slice(0, 12).map((data, index) => (
<WordformButton
key={`${prefixes.wordform_list}${index}`}
text={data.text}
example={data.example}
grams={data.grams}
isSelected={data.grams.every(gram => value.find(item => (item.value as Grammeme) === gram))}
onSelectGrams={handleSelect}
/>
))}
</div>
);
}
export default SelectWordForm;

View File

@ -1,14 +1,14 @@
import React, { Suspense } from 'react';
import { PlacesType, Tooltip } from '@/components/Container';
import { TextURL } from '@/components/Control';
import { IconHelp } from '@/components/Icons';
import { Loader } from '@/components/Loader';
import { CProps } from '@/components/props';
import { PlacesType, Tooltip } from '@/components/ui/Container';
import { TextURL } from '@/components/ui/Control';
import Loader from '@/components/ui/Loader';
import { HelpTopic } from '@/models/miscellaneous';
import { HelpTopic } from '@/features/help/models/helpTopic';
import { usePreferencesStore } from '@/stores/preferences';
const TopicPage = React.lazy(() => import('@/pages/ManualsPage/TopicPage'));
const TopicPage = React.lazy(() => import('@/features/help/pages/ManualsPage/TopicPage'));
interface BadgeHelpProps extends CProps.Styling {
/** Topic to display in a tooltip. */

View File

@ -1,11 +1,10 @@
import clsx from 'clsx';
import { Overlay } from '@/components/Container';
import { IconSearch } from '@/components/Icons';
import { TextInput } from '@/components/Input';
import { CProps } from '@/components/props';
import { Overlay } from './Container';
import { TextInput } from './Input';
interface SearchBarProps extends CProps.Styling {
/** Id of the search bar. */
id?: string;

View File

@ -3,9 +3,20 @@ import { z } from 'zod';
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
import { DELAYS } from '@/backend/configuration';
import { ICurrentUser } from '@/models/user';
import { LibraryItemID } from '@/features/library/models/library';
import { UserID } from '@/features/users/models/user';
import { errors, information } from '@/utils/labels';
/**
* Represents CurrentUser information.
*/
export interface ICurrentUser {
id: UserID | null;
username: string;
is_staff: boolean;
editor: LibraryItemID[];
}
/**
* Represents login data, used to authenticate users.
*/

View File

@ -1,6 +1,7 @@
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { queryClient } from '../queryClient';
import { queryClient } from '@/backend/queryClient';
import { authApi } from './api';
export function useAuth() {

View File

@ -1,9 +1,8 @@
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls';
import { useAuthSuspense } from '@/backend/auth/useAuth';
import { useLogout } from '@/backend/auth/useLogout';
import { urls, useConceptNavigation } from '@/app';
import { TextURL } from '@/components/Control';
import { TextURL } from './ui/Control';
import { useAuthSuspense } from '../backend/useAuth';
import { useLogout } from '../backend/useLogout';
function ExpectedAnonymous() {
const { user } = useAuthSuspense();

View File

@ -1,8 +1,8 @@
'use client';
import { useAuthSuspense } from '@/backend/auth/useAuth';
import { TextURL } from '@/components/Control';
import { TextURL } from './ui/Control';
import { useAuthSuspense } from '../backend/useAuth';
function RequireAuth({ children }: React.PropsWithChildren) {
const { isAnonymous } = useAuthSuspense();

View File

@ -1,22 +1,22 @@
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import axios from 'axios';
import clsx from 'clsx';
import { useForm } from 'react-hook-form';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls';
import { IUserLoginDTO, UserLoginSchema } from '@/backend/auth/api';
import { useAuthSuspense } from '@/backend/auth/useAuth';
import { useLogin } from '@/backend/auth/useLogin';
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
import { ErrorData } from '@/components/info/InfoError';
import { SubmitButton, TextURL } from '@/components/ui/Control';
import { TextInput } from '@/components/ui/Input';
import { urls, useConceptNavigation } from '@/app';
import { isAxiosError } from '@/backend/apiTransport';
import { SubmitButton, TextURL } from '@/components/Control';
import { ErrorData } from '@/components/InfoError';
import { TextInput } from '@/components/Input';
import useQueryStrings from '@/hooks/useQueryStrings';
import { resources } from '@/utils/constants';
import { IUserLoginDTO, UserLoginSchema } from '../backend/api';
import { useAuthSuspense } from '../backend/useAuth';
import { useLogin } from '../backend/useLogin';
import ExpectedAnonymous from '../components/ExpectedAnonymous';
function LoginPage() {
const router = useConceptNavigation();
const query = useQueryStrings();
@ -97,7 +97,7 @@ export default LoginPage;
// ====== Internals =========
function ServerError({ error }: { error: ErrorData }): React.ReactElement | null {
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
if (isAxiosError(error) && error.response && error.response.status === 400) {
return (
<div className='text-sm select-text text-warn-600'>
На Портале отсутствует такое сочетание имени пользователя и пароля

View File

@ -1,18 +1,18 @@
'use client';
import axios from 'axios';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
import { urls } from '@/app/urls';
import { useResetPassword } from '@/backend/auth/useResetPassword';
import InfoError, { ErrorData } from '@/components/info/InfoError';
import { SubmitButton } from '@/components/ui/Control';
import { TextInput } from '@/components/ui/Input';
import Loader from '@/components/ui/Loader';
import { urls, useConceptNavigation } from '@/app';
import { isAxiosError } from '@/backend/apiTransport';
import { SubmitButton } from '@/components/Control';
import { ErrorData, InfoError } from '@/components/InfoError';
import { TextInput } from '@/components/Input';
import { Loader } from '@/components/Loader';
import useQueryStrings from '@/hooks/useQueryStrings';
import { useResetPassword } from '../backend/useResetPassword';
export function Component() {
const router = useConceptNavigation();
const token = useQueryStrings().get('token') ?? '';
@ -95,7 +95,7 @@ export function Component() {
// ====== Internals =========
function ServerError({ error }: { error: ErrorData }): React.ReactElement {
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
if (isAxiosError(error) && error.response && error.response.status === 404) {
return <div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данная ссылка не действительна</div>;
}
return <InfoError error={error} />;

View File

@ -1,13 +1,14 @@
'use client';
import axios from 'axios';
import clsx from 'clsx';
import { useState } from 'react';
import { useRequestPasswordReset } from '@/backend/auth/useRequestPasswordReset';
import { ErrorData } from '@/components/info/InfoError';
import { SubmitButton, TextURL } from '@/components/ui/Control';
import { TextInput } from '@/components/ui/Input';
import { isAxiosError } from '@/backend/apiTransport';
import { SubmitButton, TextURL } from '@/components/Control';
import { ErrorData } from '@/components/InfoError';
import { TextInput } from '@/components/Input';
import { useRequestPasswordReset } from '../backend/useRequestPasswordReset';
export function Component() {
const { requestPasswordReset, isPending, error: serverError, reset: clearServerError } = useRequestPasswordReset();
@ -61,7 +62,7 @@ export function Component() {
// ====== Internals =========
function ServerError({ error }: { error: ErrorData }): React.ReactElement {
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
if (isAxiosError(error) && error.response && error.response.status === 400) {
return (
<div className='mx-auto mt-6 text-sm select-text text-warn-600'>Данный email не используется на Портале.</div>
);

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { CstClass } from '@/models/rsform';
import { CstClass } from '@/features/rsform/models/rsform';
import { colorBgCstClass } from '@/styling/color';
import { prefixes } from '@/utils/constants';
import { describeCstClass, labelCstClass } from '@/utils/labels';

View File

@ -1,6 +1,6 @@
import clsx from 'clsx';
import { ExpressionStatus } from '@/models/rsform';
import { ExpressionStatus } from '@/features/rsform/models/rsform';
import { colorBgCstStatus } from '@/styling/color';
import { prefixes } from '@/utils/constants';
import { describeExpressionStatus, labelExpressionStatus } from '@/utils/labels';

View File

@ -1,7 +1,7 @@
import { urls } from '@/app/urls';
import { HelpTopic } from '@/models/miscellaneous';
import { urls } from '@/app';
import { TextURL } from '@/components/Control';
import { TextURL } from './TextURL';
import { HelpTopic } from '../models/helpTopic';
interface TextURLProps {
/** Text to display. */

View File

@ -1,13 +1,14 @@
import { HelpTopic, topicParent } from '@/models/miscellaneous';
import { prefixes } from '@/utils/constants';
import TopicItem from './TopicItem';
import { topicParent } from '../models/helpTopic';
import { HelpTopic } from '../models/helpTopic';
import { TopicItem } from './TopicItem';
interface SubtopicsProps {
headTopic: HelpTopic;
}
function Subtopics({ headTopic }: SubtopicsProps) {
export function Subtopics({ headTopic }: SubtopicsProps) {
return (
<>
<h2>Содержание раздела</h2>
@ -19,5 +20,3 @@ function Subtopics({ headTopic }: SubtopicsProps) {
</>
);
}
export default Subtopics;

View File

@ -1,17 +1,16 @@
import { LinkTopic } from '@/components/ui/Control';
import { HelpTopic } from '@/models/miscellaneous';
import { describeHelpTopic, labelHelpTopic, removeTags } from '@/utils/labels';
import { LinkTopic } from '../components/LinkTopic';
import { HelpTopic } from '../models/helpTopic';
interface TopicItemProps {
topic: HelpTopic;
}
function TopicItem({ topic }: TopicItemProps) {
export function TopicItem({ topic }: TopicItemProps) {
return (
<li>
<LinkTopic text={labelHelpTopic(topic)} topic={topic} /> {removeTags(describeHelpTopic(topic))}
</li>
);
}
export default TopicItem;

View File

@ -1,8 +1,8 @@
import { TextURL } from '@/components/ui/Control';
import { HelpTopic } from '@/models/miscellaneous';
import { TextURL } from '@/components/Control';
import { external_urls } from '@/utils/constants';
import Subtopics from '../Subtopics';
import { Subtopics } from '../components/Subtopics';
import { HelpTopic } from '../models/helpTopic';
function HelpConceptSystem() {
return (

View File

@ -1,4 +1,4 @@
import { TextURL } from '@/components/ui/Control';
import { TextURL } from '@/components/Control';
import { external_urls, PARAMETER } from '@/utils/constants';
function HelpExteor() {

View File

@ -1,6 +1,5 @@
import { HelpTopic } from '@/models/miscellaneous';
import Subtopics from '../Subtopics';
import { Subtopics } from '../components/Subtopics';
import { HelpTopic } from '../models/helpTopic';
function HelpInfo() {
return (

View File

@ -8,9 +8,9 @@ import {
IconPin,
IconUser2
} from '@/components/Icons';
import { HelpTopic } from '@/models/miscellaneous';
import Subtopics from '../Subtopics';
import { Subtopics } from '../components/Subtopics';
import { HelpTopic } from '../models/helpTopic';
function HelpInterface() {
return (

View File

@ -1,8 +1,9 @@
import { LinkTopic, TextURL } from '@/components/ui/Control';
import { HelpTopic } from '@/models/miscellaneous';
import { TextURL } from '@/components/Control';
import { external_urls, prefixes } from '@/utils/constants';
import TopicItem from '../TopicItem';
import { LinkTopic } from '../components/LinkTopic';
import { TopicItem } from '../components/TopicItem';
import { HelpTopic } from '../models/helpTopic';
function HelpMain() {
return (

View File

@ -1,9 +1,9 @@
import { EmbedYoutube } from '@/components/ui/View';
import { EmbedYoutube } from '@/components/View';
import useWindowSize from '@/hooks/useWindowSize';
import { HelpTopic } from '@/models/miscellaneous';
import { external_urls, youtube } from '@/utils/constants';
import Subtopics from '../Subtopics';
import { Subtopics } from '../components/Subtopics';
import { HelpTopic } from '../models/helpTopic';
function HelpRSLang() {
const windowSize = useWindowSize();

Some files were not shown because too many files have changed in this diff Show More