@griddo/ax 11.10.23 → 11.10.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/griddo-config/index.js +1 -0
- package/package.json +2 -2
- package/src/Style/index.tsx +10 -8
- package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +10 -8
- package/src/__tests__/components/ConfigPanel/Form/ConnectedField/PageConnectedField/PageConnectedField.test.tsx +86 -8
- package/src/__tests__/components/SideModal/SideModal.test.tsx +64 -0
- package/src/__tests__/hooks/useSchemas.test.tsx +224 -0
- package/src/__tests__/services/SchemasService.test.ts +135 -0
- package/src/api/schemas.tsx +11 -1
- package/src/components/ConfigPanel/GlobalPageForm/index.tsx +11 -17
- package/src/components/Fields/Wysiwyg/helpers.tsx +14 -4
- package/src/components/Loader/components/Dots.js +4 -5
- package/src/components/PageFinder/index.tsx +15 -9
- package/src/components/ResizePanel/index.tsx +13 -3
- package/src/components/ResizePanel/style.tsx +2 -9
- package/src/containers/App/actions.tsx +96 -24
- package/src/containers/App/constants.tsx +7 -0
- package/src/containers/App/interfaces.tsx +26 -8
- package/src/containers/App/reducer.tsx +24 -9
- package/src/containers/Sites/actions.tsx +49 -49
- package/src/forms/editor.tsx +4 -3
- package/src/helpers/index.tsx +76 -94
- package/src/helpers/schemas.tsx +144 -36
- package/src/helpers/structuredData.tsx +36 -7
- package/src/helpers/themes.tsx +26 -8
- package/src/hooks/index.tsx +5 -0
- package/src/hooks/useSchemas.ts +151 -0
- package/src/locales/en-US.ts +1 -0
- package/src/locales/es-ES.ts +1 -0
- package/src/modules/Analytics/GroupPanel/index.tsx +9 -6
- package/src/modules/Analytics/GroupPanel/utils.tsx +12 -28
- package/src/modules/Content/PageItem/index.tsx +33 -36
- package/src/modules/Content/index.tsx +34 -30
- package/src/modules/Content/utils.tsx +16 -12
- package/src/modules/Forms/FormEditor/index.tsx +13 -14
- package/src/modules/FramePreview/index.tsx +8 -8
- package/src/modules/GlobalEditor/index.tsx +57 -42
- package/src/modules/MediaGallery/ImageModal/index.tsx +15 -9
- package/src/modules/MediaGallery/ImageModal/style.tsx +16 -1
- package/src/modules/PublicPreview/index.tsx +4 -3
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/index.tsx +4 -5
- package/src/modules/Settings/Globals/index.tsx +10 -11
- package/src/modules/Sites/index.tsx +13 -5
- package/src/modules/StructuredData/Form/index.tsx +25 -29
- package/src/modules/StructuredData/StructuredDataList/OptionTable/index.tsx +15 -6
- package/src/modules/StructuredData/StructuredDataList/index.tsx +22 -14
- package/src/modules/StructuredData/StructuredDataList/utils.tsx +12 -11
- package/src/schemas/index.tsx +5 -4
- package/src/schemas/pages/Page.ts +308 -0
- package/src/schemas/pages/index.ts +9 -0
- package/src/services/SchemasService.ts +240 -0
- package/src/services/index.ts +9 -0
- package/src/types/index.tsx +48 -39
- package/tsconfig.paths.json +1 -0
- package/src/schemas/pages/Page.tsx +0 -301
- package/src/schemas/pages/index.tsx +0 -5
- /package/src/schemas/pages/{FormPage.tsx → FormPage.ts} +0 -0
- /package/src/schemas/pages/{GlobalPage.tsx → GlobalPage.ts} +0 -0
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { config } from "components";
|
|
3
|
-
|
|
4
1
|
import { Icon, NoteField, Tabs } from "@ax/components";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { useGlobalPermission, useThemes } from "@ax/hooks";
|
|
3
|
+
import type { INotification, ISchema } from "@ax/types";
|
|
7
4
|
|
|
8
5
|
import ConnectedField from "../Form/ConnectedField";
|
|
9
6
|
|
|
10
7
|
import * as S from "./style";
|
|
11
8
|
|
|
12
|
-
const themes = config.schemas.config.themes;
|
|
13
|
-
|
|
14
9
|
const noteText = "This is Global content and you cannot edit it here. To do so, you must go to the Global page";
|
|
15
10
|
const noteTitle = "Global content";
|
|
16
11
|
const errorText = "You don't have the permissions to edit the original content.";
|
|
@@ -19,6 +14,7 @@ const GlobalPageForm = (props: IGlobalPageFormProps): JSX.Element => {
|
|
|
19
14
|
const { selectedTab, setSelectedTab, schema, pageTitle, setHistoryPush, actions, header, footer } = props;
|
|
20
15
|
const tabs = ["content", "config"];
|
|
21
16
|
|
|
17
|
+
const { themes } = useThemes();
|
|
22
18
|
const isAllowedToEditGlobalData = useGlobalPermission("global.globalData.editAllGlobalData");
|
|
23
19
|
|
|
24
20
|
const handleGetGlobalPage = async () => {
|
|
@@ -30,7 +26,7 @@ const GlobalPageForm = (props: IGlobalPageFormProps): JSX.Element => {
|
|
|
30
26
|
if (isAllowedToEditGlobalData) {
|
|
31
27
|
await handleGetGlobalPage();
|
|
32
28
|
const path = "/data/pages/editor";
|
|
33
|
-
setHistoryPush
|
|
29
|
+
setHistoryPush?.(path, true);
|
|
34
30
|
} else {
|
|
35
31
|
actions.setNotificationAction({
|
|
36
32
|
type: "error",
|
|
@@ -47,6 +43,8 @@ const GlobalPageForm = (props: IGlobalPageFormProps): JSX.Element => {
|
|
|
47
43
|
options: { excludeDetailPages: true },
|
|
48
44
|
};
|
|
49
45
|
|
|
46
|
+
const themeOptions = themes ?? [];
|
|
47
|
+
|
|
50
48
|
const themeField = {
|
|
51
49
|
title: "Page Options",
|
|
52
50
|
type: "FieldGroup",
|
|
@@ -57,7 +55,7 @@ const GlobalPageForm = (props: IGlobalPageFormProps): JSX.Element => {
|
|
|
57
55
|
title: "Theme",
|
|
58
56
|
type: "Select",
|
|
59
57
|
key: "theme",
|
|
60
|
-
options:
|
|
58
|
+
options: themeOptions,
|
|
61
59
|
helptext: "It affects the whole page: Header, Content, and footer.",
|
|
62
60
|
},
|
|
63
61
|
{
|
|
@@ -83,14 +81,14 @@ const GlobalPageForm = (props: IGlobalPageFormProps): JSX.Element => {
|
|
|
83
81
|
title: "Header Theme",
|
|
84
82
|
type: "Select",
|
|
85
83
|
key: "headerTheme",
|
|
86
|
-
options:
|
|
84
|
+
options: themeOptions,
|
|
87
85
|
condition: true,
|
|
88
86
|
},
|
|
89
87
|
{
|
|
90
88
|
title: "Footer Theme",
|
|
91
89
|
type: "Select",
|
|
92
90
|
key: "footerTheme",
|
|
93
|
-
options:
|
|
91
|
+
options: themeOptions,
|
|
94
92
|
condition: true,
|
|
95
93
|
},
|
|
96
94
|
],
|
|
@@ -98,12 +96,8 @@ const GlobalPageForm = (props: IGlobalPageFormProps): JSX.Element => {
|
|
|
98
96
|
],
|
|
99
97
|
};
|
|
100
98
|
|
|
101
|
-
const handleRestoreHeader = () =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const handleRestoreFooter = () =>
|
|
105
|
-
actions.restorePageNavigationAction && actions.restorePageNavigationAction("footer");
|
|
106
|
-
|
|
99
|
+
const handleRestoreHeader = () => actions.restorePageNavigationAction?.("header");
|
|
100
|
+
const handleRestoreFooter = () => actions.restorePageNavigationAction?.("footer");
|
|
107
101
|
return (
|
|
108
102
|
<section data-testid="global-page-section">
|
|
109
103
|
<Tabs tabs={tabs} active={selectedTab} setSelectedTab={setSelectedTab} />
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
/** biome-ignore-all lint/a11y/useValidAnchor: TODO: fix this */
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { schemasService } from "@ax/services";
|
|
4
|
+
|
|
3
5
|
import { renderToString } from "react-dom/server";
|
|
4
|
-
import { config } from "components";
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* NOTA DE MIGRACIÓN: Antes, la configuración de rich text (`richTextConfig`)
|
|
9
|
+
* venía de un archivo estático (import de "components"). Ahora, se obtiene de
|
|
10
|
+
* forma centralizada usando el singleton `schemasService`.
|
|
11
|
+
*/
|
|
6
12
|
const getRichTextConfig = (): IRichTextConfig | null => {
|
|
7
|
-
|
|
13
|
+
if (!schemasService.isLoaded()) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return (schemasService.getSchemas().config.richTextConfig as IRichTextConfig) || null;
|
|
8
17
|
};
|
|
9
18
|
|
|
10
19
|
const parseClassNames = (styles: { label: string; className: string }[]) =>
|
|
11
20
|
styles.reduce(
|
|
21
|
+
// biome-ignore lint/performance/noAccumulatingSpread: TODO: fix this
|
|
12
22
|
(acc, current: { label: string; className: string }) => ({ ...acc, [current.className]: current.label }),
|
|
13
23
|
{},
|
|
14
24
|
);
|
|
@@ -85,4 +95,4 @@ const getLanguageMenuHtml = (languages: { name: string; iso: string; featured?:
|
|
|
85
95
|
);
|
|
86
96
|
};
|
|
87
97
|
|
|
88
|
-
export { getRichTextConfig, parseClassNames, getLanguageMenuHtml };
|
|
98
|
+
export { getRichTextConfig, parseClassNames, getLanguageMenuHtml, type IRichTextConfig };
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** biome-ignore-all lint/a11y/noSvgWithoutTitle: not necessary */
|
|
3
2
|
function SvgDots(props) {
|
|
4
3
|
return (
|
|
5
4
|
<svg data-testid="dots-svg" viewBox="0 0 100 100" {...props}>
|
|
6
|
-
<circle fill="#5057FF" cx={
|
|
5
|
+
<circle fill="#5057FF" cx={30} cy={50} r={6}>
|
|
7
6
|
<animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin={0.1} />
|
|
8
7
|
</circle>
|
|
9
|
-
<circle fill="#5057FF" cx={
|
|
8
|
+
<circle fill="#5057FF" cx={50} cy={50} r={6}>
|
|
10
9
|
<animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin={0.2} />
|
|
11
10
|
</circle>
|
|
12
|
-
<circle fill="#5057FF" cx={
|
|
11
|
+
<circle fill="#5057FF" cx={70} cy={50} r={6}>
|
|
13
12
|
<animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin={0.3} />
|
|
14
13
|
</circle>
|
|
15
14
|
</svg>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { IContentType, IGetSitePagesParams, IGlobalLanguage, IPage, IRootState, ISite } from "@ax/types";
|
|
5
|
-
import { getStructuredDataTitle, isReqOk } from "@ax/helpers";
|
|
6
4
|
import { sites, structuredData } from "@ax/api";
|
|
7
|
-
import {
|
|
5
|
+
import { AsyncSelect, Button, Loader, Pagination, SearchField, Select } from "@ax/components";
|
|
6
|
+
import { getStructuredDataTitle, isReqOk } from "@ax/helpers";
|
|
7
|
+
import { LOCALE } from "@ax/locales";
|
|
8
|
+
import type { IContentType, IGetSitePagesParams, IGlobalLanguage, IPage, IRootState, ISite } from "@ax/types";
|
|
9
|
+
|
|
8
10
|
import SelectionListItem from "./SelectionListItem";
|
|
9
11
|
|
|
10
12
|
import * as S from "./style";
|
|
@@ -150,7 +152,12 @@ const PageFinder = (props: IPageFinderProps): JSX.Element => {
|
|
|
150
152
|
const { value, isChecked } = item;
|
|
151
153
|
|
|
152
154
|
let newSelection: IPage[] = [...selectedPages];
|
|
153
|
-
|
|
155
|
+
if (isChecked) {
|
|
156
|
+
newSelection.push(value);
|
|
157
|
+
} else {
|
|
158
|
+
newSelection = newSelection.filter((page: IPage) => page.id !== value.id);
|
|
159
|
+
}
|
|
160
|
+
|
|
154
161
|
const newSelectionIDs = newSelection.map((page: IPage) => page.id);
|
|
155
162
|
|
|
156
163
|
setSelectedPages(newSelection);
|
|
@@ -171,8 +178,7 @@ const PageFinder = (props: IPageFinderProps): JSX.Element => {
|
|
|
171
178
|
};
|
|
172
179
|
|
|
173
180
|
const getPageList = (items: IPage[]) =>
|
|
174
|
-
items
|
|
175
|
-
items.map((page: IPage) => (
|
|
181
|
+
items?.map((page: IPage) => (
|
|
176
182
|
<SelectionListItem
|
|
177
183
|
option={page}
|
|
178
184
|
key={page.id}
|
|
@@ -221,7 +227,7 @@ const PageFinder = (props: IPageFinderProps): JSX.Element => {
|
|
|
221
227
|
)}
|
|
222
228
|
</S.LanguageSiteFilters>
|
|
223
229
|
<S.ContentTypeFilter>
|
|
224
|
-
<S.Text>
|
|
230
|
+
<S.Text>{LOCALE.common.filterByPageType}</S.Text>
|
|
225
231
|
<Select
|
|
226
232
|
name="select"
|
|
227
233
|
options={contentTypeOptions}
|
|
@@ -293,7 +299,7 @@ interface IPageFinderProps {
|
|
|
293
299
|
}
|
|
294
300
|
|
|
295
301
|
const mapStateToProps = (state: IRootState) => ({
|
|
296
|
-
currentSiteID: state.sites.currentSiteInfo
|
|
302
|
+
currentSiteID: state.sites.currentSiteInfo?.id || null,
|
|
297
303
|
allSites: state.sites.sites,
|
|
298
304
|
lang: state.app.lang,
|
|
299
305
|
globalLangs: state.app.globalLangs,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
2
3
|
import ResizeHandle from "./ResizeHandle";
|
|
3
4
|
|
|
4
5
|
import * as S from "./style";
|
|
@@ -11,6 +12,11 @@ const ResizePanel = (props: IResizePanelProps): JSX.Element => {
|
|
|
11
12
|
const [rwidth, setRwidth] = useState(MIN_WIDTH);
|
|
12
13
|
const rightPanelRef = useRef<HTMLDivElement>(null);
|
|
13
14
|
|
|
15
|
+
const calculateFixedPanelMinWidth = (currentWidth: number) => {
|
|
16
|
+
const minWidth = 1280 - currentWidth - 32;
|
|
17
|
+
return minWidth < 500 ? "500px" : `${minWidth}px`;
|
|
18
|
+
};
|
|
19
|
+
|
|
14
20
|
useEffect(() => {
|
|
15
21
|
requestAnimationFrame(() => {
|
|
16
22
|
if (rightPanelRef.current) {
|
|
@@ -27,7 +33,11 @@ const ResizePanel = (props: IResizePanelProps): JSX.Element => {
|
|
|
27
33
|
<>
|
|
28
34
|
<S.LeftPanel data-testid="left-panel">
|
|
29
35
|
{fixed ? (
|
|
30
|
-
<S.FixedPanel
|
|
36
|
+
<S.FixedPanel
|
|
37
|
+
full={full}
|
|
38
|
+
data-testid="fixed-panel"
|
|
39
|
+
style={{ minWidth: rwidth ? calculateFixedPanelMinWidth(rwidth) : "auto" }}
|
|
40
|
+
>
|
|
31
41
|
{leftPanel}
|
|
32
42
|
</S.FixedPanel>
|
|
33
43
|
) : (
|
|
@@ -35,7 +45,7 @@ const ResizePanel = (props: IResizePanelProps): JSX.Element => {
|
|
|
35
45
|
)}
|
|
36
46
|
</S.LeftPanel>
|
|
37
47
|
<ResizeHandle onMouseMove={resize} />
|
|
38
|
-
<S.RightPanel
|
|
48
|
+
<S.RightPanel ref={rightPanelRef} data-testid="right-panel" style={{ width: rwidth ? `${rwidth}px` : "auto" }}>
|
|
39
49
|
{rightPanel}
|
|
40
50
|
</S.RightPanel>
|
|
41
51
|
</>
|
|
@@ -1,35 +1,28 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
2
|
|
|
3
|
-
const getWidth = (width: number, space: string) => {
|
|
4
|
-
const minWidth = 1280 - width - parseInt(space) * 2;
|
|
5
|
-
return minWidth < 500 ? `500px` : `${minWidth}px`;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
3
|
const LeftPanel = styled.section`
|
|
9
4
|
position: relative;
|
|
10
5
|
flex-grow: 1;
|
|
11
6
|
min-width: 500px;
|
|
12
7
|
`;
|
|
13
8
|
|
|
14
|
-
const RightPanel = styled.section
|
|
9
|
+
const RightPanel = styled.section`
|
|
15
10
|
display: flex;
|
|
16
11
|
position: relative;
|
|
17
12
|
padding: ${(p) => `0 ${p.theme.spacing.m} ${p.theme.spacing.m} ${p.theme.spacing.m}`};
|
|
18
13
|
flex-shrink: 0;
|
|
19
|
-
width: ${(p) => p.width}px;
|
|
20
14
|
min-width: 344px;
|
|
21
15
|
max-width: ${(p) => `calc(100% - 500px - ${p.theme.spacing.m})`};
|
|
22
16
|
flex-direction: column;
|
|
23
17
|
`;
|
|
24
18
|
|
|
25
|
-
const FixedPanel = styled.div<{
|
|
19
|
+
const FixedPanel = styled.div<{ full: boolean }>`
|
|
26
20
|
position: sticky;
|
|
27
21
|
top: ${(p) => (p.full ? p.theme.spacing.xl : `calc(${p.theme.spacing.xl} + ${p.theme.spacing.m})`)};
|
|
28
22
|
height: ${(p) =>
|
|
29
23
|
p.full
|
|
30
24
|
? `calc(100vh - ${p.theme.spacing.xl})`
|
|
31
25
|
: `calc(100vh - (${p.theme.spacing.xl} + (${p.theme.spacing.m} * 2)) - (${p.theme.spacing.m} + (${p.theme.spacing.xs} * 2)))`};
|
|
32
|
-
min-width: ${(p) => getWidth(p.width, p.theme.spacing.m)};
|
|
33
26
|
margin-left: ${(p) => (p.full ? "0" : p.theme.spacing.m)};
|
|
34
27
|
`;
|
|
35
28
|
|
|
@@ -1,42 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import differenceInSeconds from "date-fns/differenceInSeconds";
|
|
3
|
-
|
|
1
|
+
import { global, languages, schemas as schemasApi } from "@ax/api";
|
|
4
2
|
import { isReqOk } from "@ax/helpers";
|
|
5
|
-
import {
|
|
6
|
-
import { IRootState } from "@ax/types";
|
|
3
|
+
import { schemasService } from "@ax/services";
|
|
4
|
+
import type { IRootState } from "@ax/types";
|
|
7
5
|
|
|
8
|
-
import
|
|
6
|
+
import differenceInSeconds from "date-fns/differenceInSeconds";
|
|
7
|
+
import type { Dispatch } from "redux";
|
|
9
8
|
|
|
9
|
+
import history from "../../routes/history";
|
|
10
10
|
import {
|
|
11
|
-
SET_ERROR,
|
|
12
|
-
SET_IS_LOADING,
|
|
13
|
-
SET_IS_SAVING,
|
|
14
11
|
IS_LOGGING_IN,
|
|
15
|
-
|
|
12
|
+
LOAD_SCHEMAS_ERROR,
|
|
13
|
+
LOAD_SCHEMAS_START,
|
|
14
|
+
LOAD_SCHEMAS_SUCCESS,
|
|
16
15
|
LOGOUT,
|
|
17
|
-
|
|
16
|
+
SET_ERROR,
|
|
18
17
|
SET_GLOBAL_LANGUAGES,
|
|
19
18
|
SET_GLOBAL_SETTINGS,
|
|
20
|
-
SET_USER,
|
|
21
|
-
SET_SESSION_STARTED_AT,
|
|
22
19
|
SET_HAS_ANIMATION,
|
|
20
|
+
SET_IS_LOADING,
|
|
21
|
+
SET_IS_SAVING,
|
|
22
|
+
SET_LANGUAGE,
|
|
23
|
+
SET_SESSION_STARTED_AT,
|
|
24
|
+
SET_TOKEN,
|
|
25
|
+
SET_USER,
|
|
23
26
|
} from "./constants";
|
|
24
|
-
|
|
25
|
-
import {
|
|
26
|
-
ISetIsLoading,
|
|
27
|
-
ISetIsSaving,
|
|
28
|
-
ISetErrorAction,
|
|
27
|
+
import type {
|
|
29
28
|
IIsLoggingInAction,
|
|
30
|
-
|
|
29
|
+
ILoadSchemasErrorAction,
|
|
30
|
+
ILoadSchemasStartAction,
|
|
31
|
+
ILoadSchemasSuccessAction,
|
|
31
32
|
ILogoutAction,
|
|
32
|
-
|
|
33
|
+
ISetErrorAction,
|
|
33
34
|
ISetGlobalLanguages,
|
|
34
35
|
ISetGlobalSettings,
|
|
35
|
-
ISetUserAction,
|
|
36
|
-
ISetSessionStartedAtAction,
|
|
37
36
|
ISetHasAnimation,
|
|
37
|
+
ISetIsLoading,
|
|
38
|
+
ISetIsSaving,
|
|
39
|
+
ISetLanguageAction,
|
|
40
|
+
ISetSessionStartedAtAction,
|
|
41
|
+
ISetTokenAction,
|
|
42
|
+
ISetUserAction,
|
|
38
43
|
} from "./interfaces";
|
|
39
|
-
import { IError, IUser } from "./reducer";
|
|
44
|
+
import type { IError, IUser } from "./reducer";
|
|
40
45
|
|
|
41
46
|
function setIsLoading(isLoading: boolean): ISetIsLoading {
|
|
42
47
|
return { type: SET_IS_LOADING, payload: { isLoading } };
|
|
@@ -210,7 +215,7 @@ function loginSSO(): (dispatch: Dispatch) => Promise<string | null> {
|
|
|
210
215
|
} else {
|
|
211
216
|
return null;
|
|
212
217
|
}
|
|
213
|
-
} catch (
|
|
218
|
+
} catch (_error) {
|
|
214
219
|
return null;
|
|
215
220
|
}
|
|
216
221
|
};
|
|
@@ -248,6 +253,72 @@ function checkUserSession(): (dispatch: Dispatch, getState: () => IRootState) =>
|
|
|
248
253
|
};
|
|
249
254
|
}
|
|
250
255
|
|
|
256
|
+
function loadSchemasStart(): ILoadSchemasStartAction {
|
|
257
|
+
return { type: LOAD_SCHEMAS_START };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function loadSchemasSuccess(): ILoadSchemasSuccessAction {
|
|
261
|
+
return { type: LOAD_SCHEMAS_SUCCESS };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function loadSchemasError(schemasError: string): ILoadSchemasErrorAction {
|
|
265
|
+
return { type: LOAD_SCHEMAS_ERROR, payload: { schemasError } };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Carga los schemas de la instancia desde la API.
|
|
270
|
+
*
|
|
271
|
+
* Esta función thunk comprueba primero si ya se está realizando una carga (`schemasLoading`)
|
|
272
|
+
* o si el servicio Singleton `schemasService` ya tiene schemas cargados para evitar peticiones duplicadas.
|
|
273
|
+
*
|
|
274
|
+
* - Si ya está cargando (loading), retorna `false`.
|
|
275
|
+
* - Si ya están cargados (loaded), retorna `true`.
|
|
276
|
+
* - Si no, realiza la petición al API y almacena los schemas globales en el servicio (singleton) SchemasService.
|
|
277
|
+
* - En caso de error, despacha la acción de error correspondiente y retorna `false`.
|
|
278
|
+
*
|
|
279
|
+
* @function
|
|
280
|
+
* @returns {Function} Thunk Redux que recibe `dispatch` y `getState` y retorna una Promesa<boolean>:
|
|
281
|
+
* - `true` si los schemas ya estaban cargados o se han cargado exitosamente.
|
|
282
|
+
* - `false` si la carga está en curso o ha fallado.
|
|
283
|
+
*/
|
|
284
|
+
function loadSchemas(): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
|
|
285
|
+
return async (dispatch, getState) => {
|
|
286
|
+
const {
|
|
287
|
+
app: { schemasLoading },
|
|
288
|
+
} = getState();
|
|
289
|
+
|
|
290
|
+
// Avoid duplicate requests
|
|
291
|
+
if (schemasLoading) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Skip if already loaded
|
|
296
|
+
if (schemasService.isLoaded()) {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
dispatch(loadSchemasStart());
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
const response = await schemasApi.getSchemas();
|
|
304
|
+
|
|
305
|
+
if (isReqOk(response?.status)) {
|
|
306
|
+
schemasService.setSchemas(response.data);
|
|
307
|
+
dispatch(loadSchemasSuccess());
|
|
308
|
+
return true;
|
|
309
|
+
} else {
|
|
310
|
+
const errorMsg = response?.data?.message || "Failed to load schemas";
|
|
311
|
+
dispatch(loadSchemasError(errorMsg));
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
} catch (error) {
|
|
315
|
+
const errorMsg = error instanceof Error ? error.message : "Failed to load schemas";
|
|
316
|
+
dispatch(loadSchemasError(errorMsg));
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
251
322
|
export {
|
|
252
323
|
setError,
|
|
253
324
|
resetError,
|
|
@@ -267,4 +338,5 @@ export {
|
|
|
267
338
|
checkUserSession,
|
|
268
339
|
setHasAnimation,
|
|
269
340
|
loginSSO,
|
|
341
|
+
loadSchemas,
|
|
270
342
|
};
|
|
@@ -13,6 +13,10 @@ const SET_USER = `${NAME}/SET_USER`;
|
|
|
13
13
|
const SET_SESSION_STARTED_AT = `${NAME}/SET_SESSION_STARTED_AT`;
|
|
14
14
|
const SET_HAS_ANIMATION = `${NAME}/SET_HAS_ANIMATION`;
|
|
15
15
|
|
|
16
|
+
const LOAD_SCHEMAS_START = `${NAME}/LOAD_SCHEMAS_START`;
|
|
17
|
+
const LOAD_SCHEMAS_SUCCESS = `${NAME}/LOAD_SCHEMAS_SUCCESS`;
|
|
18
|
+
const LOAD_SCHEMAS_ERROR = `${NAME}/LOAD_SCHEMAS_ERROR`;
|
|
19
|
+
|
|
16
20
|
export {
|
|
17
21
|
SET_ERROR,
|
|
18
22
|
SET_IS_LOADING,
|
|
@@ -26,4 +30,7 @@ export {
|
|
|
26
30
|
SET_USER,
|
|
27
31
|
SET_SESSION_STARTED_AT,
|
|
28
32
|
SET_HAS_ANIMATION,
|
|
33
|
+
LOAD_SCHEMAS_START,
|
|
34
|
+
LOAD_SCHEMAS_SUCCESS,
|
|
35
|
+
LOAD_SCHEMAS_ERROR,
|
|
29
36
|
};
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
IS_LOGGING_IN,
|
|
3
|
+
LOAD_SCHEMAS_ERROR,
|
|
4
|
+
LOAD_SCHEMAS_START,
|
|
5
|
+
LOAD_SCHEMAS_SUCCESS,
|
|
3
6
|
LOGOUT,
|
|
4
7
|
SET_ERROR,
|
|
5
|
-
|
|
8
|
+
SET_GLOBAL_LANGUAGES,
|
|
9
|
+
SET_GLOBAL_SETTINGS,
|
|
10
|
+
SET_HAS_ANIMATION,
|
|
6
11
|
SET_IS_LOADING,
|
|
7
12
|
SET_IS_SAVING,
|
|
8
13
|
SET_LANGUAGE,
|
|
9
|
-
SET_GLOBAL_LANGUAGES,
|
|
10
|
-
SET_GLOBAL_SETTINGS,
|
|
11
|
-
SET_USER,
|
|
12
14
|
SET_SESSION_STARTED_AT,
|
|
13
|
-
|
|
15
|
+
SET_TOKEN,
|
|
16
|
+
SET_USER,
|
|
14
17
|
} from "./constants";
|
|
15
|
-
import { IUser } from "./reducer";
|
|
18
|
+
import type { IUser } from "./reducer";
|
|
16
19
|
|
|
17
20
|
export interface ISetTokenAction {
|
|
18
21
|
type: typeof SET_TOKEN;
|
|
@@ -76,6 +79,21 @@ export interface ISetHasAnimation {
|
|
|
76
79
|
payload: { hasAnimation: boolean };
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
export interface ILoadSchemasStartAction {
|
|
83
|
+
type: typeof LOAD_SCHEMAS_START;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ILoadSchemasSuccessAction {
|
|
87
|
+
type: typeof LOAD_SCHEMAS_SUCCESS;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ILoadSchemasErrorAction {
|
|
91
|
+
type: typeof LOAD_SCHEMAS_ERROR;
|
|
92
|
+
payload: { schemasError: string };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type SchemasActionsCreators = ILoadSchemasStartAction | ILoadSchemasSuccessAction | ILoadSchemasErrorAction;
|
|
96
|
+
|
|
79
97
|
export type AppActionsCreators = ISetIsLoading & ISetIsSaving;
|
|
80
98
|
|
|
81
99
|
export type AuthActionsCreators = ISetTokenAction &
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
+
import type { ILanguage } from "@ax/types";
|
|
2
|
+
|
|
1
3
|
import { REHYDRATE } from "redux-persist/lib/constants";
|
|
4
|
+
|
|
2
5
|
import {
|
|
3
|
-
SET_ERROR,
|
|
4
|
-
SET_IS_LOADING,
|
|
5
|
-
SET_IS_SAVING,
|
|
6
|
-
SET_TOKEN,
|
|
7
6
|
IS_LOGGING_IN,
|
|
7
|
+
LOAD_SCHEMAS_ERROR,
|
|
8
|
+
LOAD_SCHEMAS_START,
|
|
9
|
+
LOAD_SCHEMAS_SUCCESS,
|
|
8
10
|
LOGOUT,
|
|
9
|
-
|
|
11
|
+
SET_ERROR,
|
|
10
12
|
SET_GLOBAL_LANGUAGES,
|
|
11
13
|
SET_GLOBAL_SETTINGS,
|
|
12
|
-
SET_USER,
|
|
13
|
-
SET_SESSION_STARTED_AT,
|
|
14
14
|
SET_HAS_ANIMATION,
|
|
15
|
+
SET_IS_LOADING,
|
|
16
|
+
SET_IS_SAVING,
|
|
17
|
+
SET_LANGUAGE,
|
|
18
|
+
SET_SESSION_STARTED_AT,
|
|
19
|
+
SET_TOKEN,
|
|
20
|
+
SET_USER,
|
|
15
21
|
} from "./constants";
|
|
16
|
-
import { ILanguage } from "@ax/types";
|
|
17
22
|
|
|
18
23
|
export interface IAppState {
|
|
19
24
|
isRehydrated: boolean;
|
|
@@ -28,6 +33,8 @@ export interface IAppState {
|
|
|
28
33
|
globalSettings: IGlobalSettings;
|
|
29
34
|
sessionStartedAt: null | Date;
|
|
30
35
|
hasAnimation: boolean;
|
|
36
|
+
schemasLoading: boolean;
|
|
37
|
+
schemasError: string | null;
|
|
31
38
|
}
|
|
32
39
|
export interface IError {
|
|
33
40
|
code?: number;
|
|
@@ -92,12 +99,14 @@ export const initialState = {
|
|
|
92
99
|
},
|
|
93
100
|
sessionStartedAt: null,
|
|
94
101
|
hasAnimation: false,
|
|
102
|
+
schemasLoading: false,
|
|
103
|
+
schemasError: null,
|
|
95
104
|
};
|
|
96
105
|
|
|
97
106
|
export function reducer(state = initialState, action: any): IAppState {
|
|
98
107
|
switch (action.type) {
|
|
99
108
|
case REHYDRATE:
|
|
100
|
-
return { ...state, isRehydrated: true };
|
|
109
|
+
return { ...state, isRehydrated: true, schemasLoading: false, schemasError: null };
|
|
101
110
|
case SET_TOKEN:
|
|
102
111
|
case SET_IS_LOADING:
|
|
103
112
|
case SET_IS_SAVING:
|
|
@@ -112,6 +121,12 @@ export function reducer(state = initialState, action: any): IAppState {
|
|
|
112
121
|
return { ...state, ...action.payload };
|
|
113
122
|
case SET_ERROR:
|
|
114
123
|
return { ...state, error: { ...action.payload } };
|
|
124
|
+
case LOAD_SCHEMAS_START:
|
|
125
|
+
return { ...state, schemasLoading: true, schemasError: null };
|
|
126
|
+
case LOAD_SCHEMAS_SUCCESS:
|
|
127
|
+
return { ...state, schemasLoading: false, schemasError: null };
|
|
128
|
+
case LOAD_SCHEMAS_ERROR:
|
|
129
|
+
return { ...state, schemasLoading: false, schemasError: action.payload.schemasError };
|
|
115
130
|
default:
|
|
116
131
|
return state;
|
|
117
132
|
}
|