@griddo/ax 11.10.50 → 11.10.51

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "11.10.50",
4
+ "version": "11.10.51",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Diego M. Béjar <diego.bejar@secuoyas.com>",
@@ -217,5 +217,5 @@
217
217
  "publishConfig": {
218
218
  "access": "public"
219
219
  },
220
- "gitHead": "436955fcbd3b425d3281aab388824c46bc41cc44"
220
+ "gitHead": "9d1f19c90a45a5f03e5a2aaa6af48c244cac5b8f"
221
221
  }
@@ -0,0 +1,234 @@
1
+ import { Router } from "react-router-dom";
2
+
3
+ import { parseTheme } from "@ax/helpers";
4
+ import Social from "@ax/modules/Settings/Social";
5
+ import history from "@ax/routes/history";
6
+ import globalTheme from "@ax/themes/theme.json";
7
+
8
+ import configureStore from "redux-mock-store";
9
+ import thunk from "redux-thunk";
10
+ import { ThemeProvider } from "styled-components";
11
+
12
+ import { act, cleanup, render, screen } from "../../../../../config/jest/test-utils";
13
+
14
+ afterEach(cleanup);
15
+
16
+ const mockComponent = () => null;
17
+
18
+ const mockNavItems = [
19
+ { title: "Social", path: "/settings/social", component: mockComponent },
20
+ { title: "Other", path: "/settings/other", component: mockComponent },
21
+ ];
22
+
23
+ const mockCurrentNavItem = { title: "Social", path: "/settings/social", component: mockComponent };
24
+
25
+ const createMockStore = (overrides = {}) => {
26
+ const defaultStore = {
27
+ app: { isSaving: false },
28
+ social: {},
29
+ sites: { currentSiteInfo: { id: 1 } },
30
+ users: { currentPermissions: [] },
31
+ ...overrides,
32
+ };
33
+ const middlewares: any = [thunk];
34
+ const mockStore = configureStore(middlewares);
35
+ return mockStore(defaultStore);
36
+ };
37
+
38
+ describe("Social Settings Component", () => {
39
+ it("should render the Social settings component", async () => {
40
+ const store = createMockStore();
41
+
42
+ await act(async () => {
43
+ render(
44
+ <Router history={history}>
45
+ <ThemeProvider theme={parseTheme(globalTheme)}>
46
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
47
+ </ThemeProvider>
48
+ </Router>,
49
+ { store },
50
+ );
51
+ });
52
+
53
+ // Check that the title is rendered
54
+ expect(screen.getByRole("heading", { name: "Social" })).toBeTruthy();
55
+ });
56
+
57
+ it("should render social fields from state", async () => {
58
+ const store = createMockStore({
59
+ social: {
60
+ instagram: "https://instagram.com/user",
61
+ twitter: "https://twitter.com/user",
62
+ },
63
+ });
64
+
65
+ await act(async () => {
66
+ render(
67
+ <Router history={history}>
68
+ <ThemeProvider theme={parseTheme(globalTheme)}>
69
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
70
+ </ThemeProvider>
71
+ </Router>,
72
+ { store },
73
+ );
74
+ });
75
+
76
+ expect(await screen.findByDisplayValue("https://instagram.com/user")).toBeTruthy();
77
+ expect(screen.getByDisplayValue("https://twitter.com/user")).toBeTruthy();
78
+ });
79
+
80
+ it("should render hardcoded socials when they have values", async () => {
81
+ const store = createMockStore({
82
+ social: {
83
+ instagram: "https://instagram.com/user",
84
+ facebook: "",
85
+ },
86
+ });
87
+
88
+ await act(async () => {
89
+ render(
90
+ <Router history={history}>
91
+ <ThemeProvider theme={parseTheme(globalTheme)}>
92
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
93
+ </ThemeProvider>
94
+ </Router>,
95
+ { store },
96
+ );
97
+ });
98
+
99
+ // Check that fields with values from social state are rendered
100
+ expect(screen.getByDisplayValue("https://instagram.com/user")).toBeTruthy();
101
+ });
102
+
103
+ it("should have Instagram field visible", async () => {
104
+ const store = createMockStore({
105
+ social: {
106
+ instagram: "https://instagram.com/user",
107
+ },
108
+ });
109
+
110
+ await act(async () => {
111
+ render(
112
+ <Router history={history}>
113
+ <ThemeProvider theme={parseTheme(globalTheme)}>
114
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
115
+ </ThemeProvider>
116
+ </Router>,
117
+ { store },
118
+ );
119
+ });
120
+
121
+ // Verify Instagram field is rendered with correct value
122
+ const instagramValue = screen.getByDisplayValue("https://instagram.com/user");
123
+ expect(instagramValue).toBeTruthy();
124
+ });
125
+
126
+ it("should show button with correct state when form is not dirty", async () => {
127
+ const store = createMockStore({
128
+ social: {
129
+ instagram: "https://instagram.com/user",
130
+ },
131
+ });
132
+
133
+ await act(async () => {
134
+ render(
135
+ <Router history={history}>
136
+ <ThemeProvider theme={parseTheme(globalTheme)}>
137
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
138
+ </ThemeProvider>
139
+ </Router>,
140
+ { store },
141
+ );
142
+ });
143
+
144
+ // Should have a button (the save button) that is disabled when not dirty
145
+ const buttons = screen.getAllByRole("button");
146
+ const saveButton = buttons.find((btn) => btn.textContent?.includes("Save"));
147
+ expect(saveButton).toBeTruthy();
148
+ expect(saveButton?.hasAttribute("disabled")).toBe(true);
149
+ });
150
+
151
+ it("should display saving state", async () => {
152
+ const store = createMockStore({
153
+ app: { isSaving: true },
154
+ });
155
+
156
+ await act(async () => {
157
+ render(
158
+ <Router history={history}>
159
+ <ThemeProvider theme={parseTheme(globalTheme)}>
160
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
161
+ </ThemeProvider>
162
+ </Router>,
163
+ { store },
164
+ );
165
+ });
166
+
167
+ const saveButton = screen.getByRole("button", { name: /saving/i });
168
+ expect(saveButton.textContent).toBe("Saving");
169
+ expect(saveButton.hasAttribute("disabled")).toBe(true);
170
+ });
171
+
172
+ it("should render instance socials from config", async () => {
173
+ const store = createMockStore({
174
+ social: {
175
+ customSocial: "https://example.com",
176
+ },
177
+ });
178
+
179
+ await act(async () => {
180
+ render(
181
+ <Router history={history}>
182
+ <ThemeProvider theme={parseTheme(globalTheme)}>
183
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
184
+ </ThemeProvider>
185
+ </Router>,
186
+ { store },
187
+ );
188
+ });
189
+
190
+ // The component should render without errors even if config.schemas.config.socials is empty
191
+ expect(screen.getByRole("heading", { name: "Social" })).toBeTruthy();
192
+ });
193
+
194
+ it("should not duplicate socials between instance and hardcoded", async () => {
195
+ const store = createMockStore({
196
+ social: {
197
+ instagram: "https://instagram.com/user",
198
+ },
199
+ });
200
+
201
+ await act(async () => {
202
+ render(
203
+ <Router history={history}>
204
+ <ThemeProvider theme={parseTheme(globalTheme)}>
205
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
206
+ </ThemeProvider>
207
+ </Router>,
208
+ { store },
209
+ );
210
+ });
211
+
212
+ // Instagram should only appear once (from instance or hardcoded, not both)
213
+ const instagramLabels = screen.queryAllByText(/instagram/i);
214
+ expect(instagramLabels.length).toBeGreaterThanOrEqual(1);
215
+ });
216
+
217
+ it("should handle nav menu clicks with dispatch", async () => {
218
+ const store = createMockStore();
219
+
220
+ await act(async () => {
221
+ render(
222
+ <Router history={history}>
223
+ <ThemeProvider theme={parseTheme(globalTheme)}>
224
+ <Social navItems={mockNavItems} currentNavItem={mockCurrentNavItem} />
225
+ </ThemeProvider>
226
+ </Router>,
227
+ { store },
228
+ );
229
+ });
230
+
231
+ // Component should render the Nav component which handles menu clicks
232
+ expect(screen.getByRole("heading", { name: "Social" })).toBeTruthy();
233
+ });
234
+ });
@@ -51,6 +51,7 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
51
51
 
52
52
  const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
53
53
  id: editorID,
54
+ disabled: !!disabled,
54
55
  });
55
56
 
56
57
  const whiteListFirstItem: string | undefined = whiteList?.[0];
@@ -1,12 +1,12 @@
1
- import React, { useEffect, useState, forwardRef } from "react";
2
-
3
- import { isValidDate, stringToDate, getStringifyDateRange, getRange, isValidDateRange } from "@ax/helpers";
1
+ import type React from "react";
2
+ import { forwardRef, useEffect, useState } from "react";
4
3
 
5
4
  import { Icon, IconAction } from "@ax/components";
5
+ import { getRange, getStringifyDateRange, isValidDate, isValidDateRange, stringToDate } from "@ax/helpers";
6
6
 
7
7
  import * as S from "./style";
8
8
 
9
- const DatePickerInput = (props: IDatePickerProps, ref: any): JSX.Element => {
9
+ const DatePickerInput = (props: IDatePickerProps): JSX.Element => {
10
10
  const { dates, onClick, handleChange, isOpen, error, disabled, handleValidation, validators } = props;
11
11
  const { start, end } = dates;
12
12
  const placeholder = "dd/mm/yyyy";
@@ -14,7 +14,8 @@ const DatePickerInput = (props: IDatePickerProps, ref: any): JSX.Element => {
14
14
  const currentDate = getStringifyDateRange(start, end);
15
15
 
16
16
  const [value, setValue] = useState(currentDate);
17
- useEffect(() => setValue(currentDate), [dates, currentDate]);
17
+
18
+ useEffect(() => setValue(currentDate), [currentDate]);
18
19
 
19
20
  const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
20
21
  const inputValue = e.target.value;
@@ -36,10 +37,22 @@ const DatePickerInput = (props: IDatePickerProps, ref: any): JSX.Element => {
36
37
  }
37
38
  };
38
39
 
40
+ const handleOnKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
41
+ const allowed = /^[0-9/\- ]$/;
42
+ const isControlKey = e.key.length > 1; // Backspace, Delete, ArrowLeft, Tab, etc.
43
+
44
+ // Detecta si se está usando Ctrl (Windows) o Cmd (Mac) para permitir atajos
45
+ const isKeyboardShortcut = e.ctrlKey || e.metaKey;
46
+
47
+ if (!isControlKey && !isKeyboardShortcut && !allowed.test(e.key)) {
48
+ e.preventDefault();
49
+ }
50
+ };
51
+
39
52
  const handleOnBlur = (e: React.FocusEvent<HTMLInputElement>) => {
40
53
  const inputValue = e.target.value;
41
54
 
42
- handleValidation && handleValidation(inputValue, validators);
55
+ handleValidation?.(inputValue, validators);
43
56
  };
44
57
 
45
58
  const CalendarIcon = <IconAction icon="calendar" onClick={onClick} disabled={disabled} />;
@@ -55,6 +68,7 @@ const DatePickerInput = (props: IDatePickerProps, ref: any): JSX.Element => {
55
68
  value={value || ""}
56
69
  placeholder={placeholder}
57
70
  onChange={handleOnChange}
71
+ onKeyDown={handleOnKeyDown}
58
72
  onBlur={handleOnBlur}
59
73
  disabled={disabled}
60
74
  />
@@ -37,24 +37,32 @@ const useEqualStructured = (component: any) => {
37
37
  /**
38
38
  * Stores the provided value (object or string) and returns the previous value,
39
39
  * updating it only when a "saved" state is detected (either from the external state
40
- * manager, or explicitly via the second argument). Useful for detecting changes
41
- * and implementing dirty checks.
40
+ * manager, or explicitly via options). Useful for detecting changes and implementing dirty checks.
42
41
  *
43
42
  * @param value - The current value to track.
44
- * @param isSaved - Optional flag to indicate if the value has been saved/reset externally.
43
+ * @param options - Optional configuration object.
44
+ * @param options.isSaved - Flag to indicate if the value has been saved/reset externally.
45
+ * @param options.force - Force update of the stored value on next render.
45
46
  * @returns The previously saved value, or undefined for first render.
46
47
  */
47
- const usePrevious = (value: Record<string, unknown> | string, isSaved?: boolean) => {
48
+ const usePrevious = (
49
+ value: Record<string, unknown> | string,
50
+ options?: {
51
+ isSaved?: boolean;
52
+ force?: boolean;
53
+ },
54
+ ) => {
55
+ const { isSaved, force } = options || {};
48
56
  const valueStr = value && JSON.stringify(value);
49
57
  const ref = useRef<Record<string, unknown> | string | undefined>(undefined);
50
58
  const isSavedData = getIsSavedData();
51
59
 
52
60
  useEffect(() => {
53
61
  const currentValue = valueStr && JSON.parse(valueStr);
54
- if (!ref.current || isEmptyObj(ref.current)) {
62
+ if (!ref.current || isEmptyObj(ref.current) || force) {
55
63
  ref.current = currentValue;
56
64
  }
57
- }, [valueStr]);
65
+ }, [valueStr, force]);
58
66
 
59
67
  // biome-ignore lint/correctness/useExhaustiveDependencies: TODO fix this
60
68
  useEffect(() => {
@@ -73,8 +81,9 @@ const useIsDirty = (
73
81
  const [isDirty, setIsDirty] = useState(false);
74
82
  const [isSaved, setIsSaved] = useState(false);
75
83
  const [isResetting, setIsResetting] = useState(false);
84
+ const [force, setForce] = useState(false);
76
85
 
77
- const prevContent = usePrevious(updatedValues, isSaved);
86
+ const prevContent = usePrevious(updatedValues, { isSaved, force });
78
87
 
79
88
  const hasChanged = (): boolean => {
80
89
  if (prevContent && updatedValues) {
@@ -90,10 +99,11 @@ const useIsDirty = (
90
99
  return false;
91
100
  };
92
101
 
93
- const resetDirty = (reseting = true) => {
102
+ const resetDirty = (reseting = true, force = false) => {
94
103
  setIsDirty(false);
95
104
  setIsSaved(true);
96
105
  reseting && setIsResetting(true);
106
+ setForce(force);
97
107
  };
98
108
 
99
109
  // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
@@ -156,4 +166,4 @@ const useShouldBeSaved = (form: Record<string, unknown> | IUser | FormContent) =
156
166
  return { isDirty, setIsDirty };
157
167
  };
158
168
 
159
- export { useDebounce, useEqualStructured, usePrevious, useIsDirty, useShouldBeSaved };
169
+ export { useDebounce, useEqualStructured, useIsDirty, usePrevious, useShouldBeSaved };
@@ -483,7 +483,7 @@ const PageEditor = (props: IProps) => {
483
483
 
484
484
  const toggleDraftPage = async () => {
485
485
  const { getPage } = props;
486
- resetDirty();
486
+ resetDirty(undefined, true);
487
487
  const pageID = isDraft ? editorContent.draftFromPage : editorContent.haveDraftPage;
488
488
  await getPage(pageID);
489
489
  };
@@ -1,54 +1,102 @@
1
- import { INavItem, IRootState, ISocialState } from "@ax/types";
2
- import React, { useEffect, useState } from "react";
3
- import { connect } from "react-redux";
4
-
1
+ //
2
+ // Refactor: Migrado de connect() HOC a hooks de Redux
3
+ //
4
+ // ANTES: Patrón connect() HOC
5
+ // ├─ interface IProps {
6
+ // │ ├─ isSaving, socialInfo (state)
7
+ // │ └─ saveSocial, getSocial, setHistoryPush (dispatch)
8
+ // │
9
+ // ├─ mapStateToProps: Selectores del estado
10
+ // ├─ mapDispatchToProps: Acciones despachadas
11
+ // └─ export default connect(mapStateToProps, mapDispatchToProps)(Social)
12
+ // └─ El HOC inyecta props automáticamente
13
+ //
14
+ // AHORA: Hooks de Redux
15
+ // ├─ interface IProps { navItems, currentNavItem } (solo props propias)
16
+ // ├─ useDispatch() → acceso directo al dispatch
17
+ // ├─ useSelector() → selectores granulares dentro del componente
18
+ // │ ├─ state.app.isSaving
19
+ // │ └─ state.social
20
+ // └─ export default Social (sin HOC)
21
+ //
22
+ // CAMBIOS ESPECÍFICOS:
23
+ // • Removida toda la lógica de mapeo de props Redux
24
+ // • dispatch() llamado directamente en setRoute() y handleMenuClick()
25
+ // • Mejor legibilidad: el estado necesario está cerca de donde se usa
26
+ // • Menor acoplamiento: el componente no depende de una estructura de props fija
27
+ //
28
+
29
+ import { useEffect, useState } from "react";
30
+ import { useDispatch, useSelector } from "react-redux";
31
+
32
+ import { ErrorToast, FieldsBehavior, MainWrapper, Nav } from "@ax/components";
33
+ import { appActions } from "@ax/containers/App";
34
+ import { socialActions } from "@ax/containers/Settings/Social";
5
35
  import { RouteLeavingGuard } from "@ax/guards";
36
+ import { capitalize } from "@ax/helpers";
6
37
  import { useIsDirty } from "@ax/hooks";
38
+ import type { INavItem, IRootState } from "@ax/types";
7
39
 
8
- import { appActions } from "@ax/containers/App";
9
- import { FieldsBehavior, MainWrapper, ErrorToast, Nav } from "@ax/components";
10
- import { socialActions } from "@ax/containers/Settings/Social";
40
+ import { config } from "components";
41
+ import type { AnyAction } from "redux";
42
+ import type { ThunkDispatch } from "redux-thunk";
11
43
 
12
44
  import * as S from "./style";
13
45
 
14
- const Social = (props: IProps): JSX.Element => {
15
- const { isSaving, navItems, currentNavItem, socialInfo, saveSocial, getSocial, setHistoryPush } = props;
16
-
17
- const title = "Social";
18
-
19
- const socialFields: { name: string; title: string }[] = [
20
- { name: "instagram", title: "Instagram URL" },
21
- { name: "linkedIn", title: "LinkedIn URL" },
22
- { name: "facebook", title: "Facebook URL" },
23
- { name: "twitter", title: "X/Twitter URL" },
24
- { name: "youtube", title: "YouTube URL" },
25
- { name: "flickr", title: "Flickr URL" },
26
- { name: "tiktok", title: "Tiktok URL" },
27
- { name: "snapchat", title: "Snapchat URL" },
28
- { name: "newsletter", title: "Newsletter URL" },
29
- { name: "podcast", title: "Podcast URL" },
30
- ];
46
+ interface IProps {
47
+ navItems: INavItem[];
48
+ currentNavItem: INavItem;
49
+ }
31
50
 
32
- const [socialForm, setSocialForm] = useState<Record<string, string>>({});
33
- const changeSocialForm = (field: string, value: string) => setSocialForm((state) => ({ ...state, [field]: value }));
51
+ const instanceSocials: string[] = config.schemas.config.socials || [];
52
+ const hardcodedSocials = [
53
+ { name: "instagram", title: "Instagram URL" },
54
+ { name: "linkedIn", title: "LinkedIn URL" },
55
+ { name: "facebook", title: "Facebook URL" },
56
+ { name: "twitter", title: "X/Twitter URL" },
57
+ { name: "youtube", title: "YouTube URL" },
58
+ { name: "flickr", title: "Flickr URL" },
59
+ { name: "tiktok", title: "Tiktok URL" },
60
+ { name: "snapchat", title: "Snapchat URL" },
61
+ { name: "newsletter", title: "Newsletter URL" },
62
+ { name: "podcast", title: "Podcast URL" },
63
+ ];
34
64
 
65
+ const Social = (props: IProps): JSX.Element => {
66
+ const { navItems, currentNavItem } = props;
67
+ const dispatch = useDispatch<ThunkDispatch<IRootState, undefined, AnyAction>>();
68
+ const isSaving = useSelector((state: IRootState) => state.app.isSaving);
69
+ const socialInfo = useSelector((state: IRootState) => state.social);
70
+ const [socialForm, setSocialForm] = useState<Record<string, string>>({});
71
+ const [serverFields, setServerFields] = useState<Set<string>>(new Set());
35
72
  const { isDirty, resetDirty, setIsDirty } = useIsDirty(socialForm);
36
73
 
37
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
74
+ const socialFields = [
75
+ ...instanceSocials.map((name) => ({ name, title: `${capitalize(name)} URL` })),
76
+ ...hardcodedSocials.filter(
77
+ (s) => !instanceSocials.includes(s.name) && (serverFields.has(s.name) || !!socialForm[s.name]),
78
+ ),
79
+ ];
80
+
81
+ const changeSocialForm = (field: string, value: string) => {
82
+ setSocialForm((state) => ({ ...state, [field]: value }));
83
+ };
84
+
85
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Can't add resetDirty to dependencies because it causes an infinite loop
38
86
  useEffect(() => {
39
- getSocial();
87
+ dispatch(socialActions.getSocial());
40
88
  resetDirty();
41
- }, []);
89
+ }, [dispatch]);
42
90
 
43
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
44
91
  useEffect(() => {
45
92
  setSocialForm({ ...socialInfo });
93
+ setServerFields(new Set(Object.keys(socialInfo).filter((k) => !!socialInfo[k])));
46
94
  setIsDirty(false);
47
- }, [socialInfo]);
95
+ }, [socialInfo, setIsDirty]);
48
96
 
49
97
  const saveForm = async () => {
50
- const isSaved = await saveSocial(socialForm);
51
- if (isSaved) setIsDirty(false);
98
+ const isSaved = await dispatch(socialActions.saveSocial(socialForm));
99
+ if (isSaved) resetDirty();
52
100
  };
53
101
 
54
102
  const rightButtonProps = {
@@ -57,7 +105,10 @@ const Social = (props: IProps): JSX.Element => {
57
105
  action: saveForm,
58
106
  };
59
107
 
60
- const setRoute = (path: string) => setHistoryPush(path);
108
+ const setRoute = (path: string) => {
109
+ dispatch(appActions.setHistoryPush(path));
110
+ };
111
+
61
112
  const modalText = (
62
113
  <>
63
114
  Some settings <strong>are not saved</strong>.{" "}
@@ -65,13 +116,13 @@ const Social = (props: IProps): JSX.Element => {
65
116
  );
66
117
 
67
118
  const handleMenuClick = (path: string) => {
68
- setHistoryPush(path);
119
+ dispatch(appActions.setHistoryPush(path));
69
120
  };
70
121
 
71
122
  return (
72
123
  <>
73
124
  <RouteLeavingGuard when={isDirty} action={setRoute} text={modalText} />
74
- <MainWrapper backLink={false} title={title} rightButton={rightButtonProps}>
125
+ <MainWrapper backLink={false} title="Social" rightButton={rightButtonProps}>
75
126
  <S.Wrapper>
76
127
  <Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
77
128
  <S.ContentWrapper>
@@ -95,25 +146,4 @@ const Social = (props: IProps): JSX.Element => {
95
146
  );
96
147
  };
97
148
 
98
- interface IProps {
99
- isSaving: boolean;
100
- socialInfo: ISocialState;
101
- navItems: INavItem[];
102
- currentNavItem: INavItem;
103
- saveSocial: (form: Record<string, string>) => Promise<boolean>;
104
- getSocial(): void;
105
- setHistoryPush(path: string, isEditor?: boolean): void;
106
- }
107
-
108
- const mapDispatchToProps = {
109
- setHistoryPush: appActions.setHistoryPush,
110
- saveSocial: socialActions.saveSocial,
111
- getSocial: socialActions.getSocial,
112
- };
113
-
114
- const mapStateToProps = (state: IRootState) => ({
115
- isSaving: state.app.isSaving,
116
- socialInfo: state.social,
117
- });
118
-
119
- export default connect(mapStateToProps, mapDispatchToProps)(Social);
149
+ export default Social;