@griddo/ax 12.6.1 → 12.6.2

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.
Files changed (91) hide show
  1. package/config/griddo-config/index.js +0 -1
  2. package/package.json +4 -3
  3. package/src/GlobalStore.tsx +35 -91
  4. package/src/__tests__/hooks/heartbeat.test.tsx +88 -0
  5. package/src/components/MainWrapper/index.tsx +2 -14
  6. package/src/containers/ActivityLog/{thunks.ts → actions.tsx} +89 -104
  7. package/src/containers/ActivityLog/constants.tsx +6 -0
  8. package/src/containers/ActivityLog/index.tsx +3 -16
  9. package/src/containers/ActivityLog/interfaces.tsx +12 -0
  10. package/src/containers/ActivityLog/reducer.tsx +24 -0
  11. package/src/containers/Analytics/actions.tsx +109 -0
  12. package/src/containers/Analytics/constants.tsx +5 -0
  13. package/src/containers/Analytics/index.tsx +3 -14
  14. package/src/containers/Analytics/interfaces.tsx +9 -0
  15. package/src/containers/Analytics/reducer.tsx +28 -0
  16. package/src/containers/Domains/actions.tsx +58 -0
  17. package/src/containers/Domains/constants.tsx +5 -0
  18. package/src/containers/Domains/index.tsx +3 -12
  19. package/src/containers/Domains/interfaces.tsx +9 -0
  20. package/src/containers/Domains/reducer.tsx +22 -0
  21. package/src/containers/Redirects/actions.tsx +190 -0
  22. package/src/containers/Redirects/constants.tsx +17 -1
  23. package/src/containers/Redirects/index.tsx +3 -14
  24. package/src/containers/Redirects/interfaces.tsx +23 -0
  25. package/src/containers/Redirects/reducer.tsx +35 -0
  26. package/src/containers/Settings/Languages/actions.tsx +88 -0
  27. package/src/containers/Settings/Languages/constants.tsx +6 -0
  28. package/src/containers/Settings/Languages/index.tsx +3 -15
  29. package/src/containers/Settings/Languages/reducer.tsx +31 -0
  30. package/src/containers/Settings/Social/actions.tsx +63 -0
  31. package/src/containers/Settings/Social/constants.tsx +3 -0
  32. package/src/containers/Settings/Social/index.tsx +3 -11
  33. package/src/containers/Settings/Social/interfaces.tsx +9 -0
  34. package/src/containers/Settings/Social/reducer.tsx +18 -0
  35. package/src/containers/Sites/actions.tsx +2 -6
  36. package/src/hooks/heartbeat.ts +32 -0
  37. package/src/hooks/index.tsx +2 -0
  38. package/src/index.tsx +6 -7
  39. package/src/modules/ActivityLog/ItemLog/EventItem/index.tsx +27 -13
  40. package/src/modules/ActivityLog/ItemLogUser/UserItem/EventItem/index.tsx +27 -13
  41. package/src/modules/ActivityLog/index.tsx +51 -22
  42. package/src/modules/Analytics/index.tsx +42 -33
  43. package/src/modules/App/Routing/index.tsx +4 -5
  44. package/src/modules/FileDrive/index.tsx +1 -1
  45. package/src/modules/GlobalEditor/index.tsx +6 -8
  46. package/src/modules/GlobalSettings/Robots/index.tsx +47 -24
  47. package/src/modules/MediaGallery/index.tsx +1 -1
  48. package/src/modules/PageEditor/index.tsx +5 -9
  49. package/src/modules/Redirects/RedirectItem/index.tsx +22 -9
  50. package/src/modules/Redirects/RedirectPanel/index.tsx +14 -13
  51. package/src/modules/Redirects/index.tsx +69 -26
  52. package/src/modules/Settings/ContentTypes/DataPacks/index.tsx +3 -4
  53. package/src/modules/Settings/Languages/LanguagePanel/Form/ConnectedField/index.tsx +18 -11
  54. package/src/modules/Settings/Languages/LanguagePanel/Form/index.tsx +9 -7
  55. package/src/modules/Settings/Languages/LanguagePanel/index.tsx +44 -38
  56. package/src/modules/Settings/Languages/Table/Header/index.tsx +19 -12
  57. package/src/modules/Settings/Languages/Table/Item/index.tsx +36 -21
  58. package/src/modules/Settings/Languages/Table/index.tsx +14 -6
  59. package/src/modules/Settings/Languages/index.tsx +30 -18
  60. package/src/modules/Settings/SeoAnalyticsSettings/Analytics/index.tsx +52 -42
  61. package/src/modules/Settings/Social/index.tsx +12 -17
  62. package/src/modules/StructuredData/StructuredDataList/index.tsx +2 -3
  63. package/src/types/index.tsx +6 -5
  64. package/tsconfig.paths.json +0 -1
  65. package/src/__tests__/modules/ActivityLog/ActivityLog.uc.test.tsx +0 -132
  66. package/src/__tests__/modules/Analytics/Analytics.uc.test.tsx +0 -168
  67. package/src/__tests__/modules/GlobalSettings/Robots/Robots.test.tsx +0 -110
  68. package/src/__tests__/modules/GlobalSettings/Robots/Robots.uc.test.tsx +0 -125
  69. package/src/__tests__/modules/Redirects/Redirects.uc.test.tsx +0 -164
  70. package/src/__tests__/modules/Settings/Languages/Languages.uc.test.tsx +0 -213
  71. package/src/__tests__/modules/Settings/Social/Social.uc.test.tsx +0 -171
  72. package/src/__tests__/store/activityLog.test.ts +0 -301
  73. package/src/__tests__/store/analytics.test.ts +0 -134
  74. package/src/__tests__/store/domains.test.ts +0 -107
  75. package/src/__tests__/store/languages.test.ts +0 -129
  76. package/src/__tests__/store/persist.test.ts +0 -47
  77. package/src/__tests__/store/redirects.test.ts +0 -313
  78. package/src/__tests__/store/social.test.ts +0 -165
  79. package/src/containers/ActivityLog/slice.ts +0 -45
  80. package/src/containers/Analytics/slice.ts +0 -48
  81. package/src/containers/Analytics/thunks.ts +0 -103
  82. package/src/containers/Domains/slice.ts +0 -37
  83. package/src/containers/Domains/thunks.ts +0 -60
  84. package/src/containers/Redirects/slice.ts +0 -60
  85. package/src/containers/Redirects/thunks.ts +0 -196
  86. package/src/containers/Settings/Languages/slice.ts +0 -48
  87. package/src/containers/Settings/Languages/thunks.ts +0 -83
  88. package/src/containers/Settings/Social/slice.ts +0 -40
  89. package/src/containers/Settings/Social/thunks.ts +0 -68
  90. package/src/store/hooks.ts +0 -37
  91. package/src/store/index.ts +0 -2
@@ -1,196 +0,0 @@
1
- import { redirects } from "@ax/api";
2
- import { appActions } from "@ax/containers/App";
3
- import { handleRequest } from "@ax/helpers";
4
- import type { IRedirect, IRootState } from "@ax/types";
5
-
6
- import { createAsyncThunk } from "@reduxjs/toolkit";
7
- import type { Dispatch } from "redux";
8
-
9
- import { DEFAULT_PARAMS, ERROR_CODE } from "./constants";
10
- import type { IRedirectsState } from "./slice";
11
- import { setImports, setRedirects, setTotalImports, setTotalItems } from "./slice";
12
-
13
- /**
14
- * Read and write side of UC-AX-manage-redirects, as createAsyncThunk.
15
- *
16
- * They keep calling `handleRequest` inside for the same reason the Languages pilot does:
17
- * that helper owns the loading-flag and error-channel conventions of the whole codebase —
18
- * see docs/RTK-MIGRATION.md §5.
19
- *
20
- * This area is where the **callback-as-thunk-argument** mine of §5 finally goes off.
21
- * `addRedirect` used to take an `errorAction: () => void` that it called on a 409, and
22
- * createAsyncThunk keeps its argument in `meta.arg` of every action it dispatches — a
23
- * function there trips the `serializableCheck` that GlobalStore deliberately leaves on.
24
- * So the 409 now comes back as **data** (`isOriginTaken`) and the screen decides what to
25
- * open, which is where a decision about modals belonged in the first place.
26
- *
27
- * As with the other areas, the legacy `try/catch` whose body was `console.log(e)` is gone:
28
- * createAsyncThunk turns a throw into a `rejected` action, so the failure is observable.
29
- */
30
- const onError = (response: unknown, dispatch: Dispatch) => appActions.handleError(response)(dispatch);
31
-
32
- interface IMessageResponse {
33
- data: { message?: string | string[] };
34
- }
35
-
36
- // The two bulk endpoints answer with one message per offending row, and the screen reports
37
- // the count instead of the messages. Same wording as before, verb apart.
38
- const onCountedError = (response: IMessageResponse, dispatch: Dispatch, verb: "delete" | "import") => {
39
- const { message } = response.data;
40
- const isMultiple = Array.isArray(message) && message.length > 1;
41
- const msg = isMultiple ? `The ${verb} action failed due to ${message.length} errors.` : undefined;
42
- appActions.handleError(response, isMultiple, msg)(dispatch);
43
- };
44
-
45
- // Every write reloads the list, and the filter it reloads with depends on scope: the loaded
46
- // site if there is one, otherwise whatever filter the caller passed. Kept as a pure helper
47
- // so each thunk dispatches the reload with its own correctly typed `dispatch`.
48
- const scopedFilter = (getState: () => unknown, filter?: string) => {
49
- const { currentSiteInfo } = (getState() as IRootState).sites;
50
- return currentSiteInfo?.id ? `&sites=${currentSiteInfo.id}` : filter;
51
- };
52
-
53
- export interface IRedirectsQuery {
54
- page: number;
55
- itemsPerPage: number;
56
- pagination: boolean;
57
- query?: string;
58
- filterBy?: string;
59
- }
60
-
61
- interface IGetRedirectsArgs {
62
- params: IRedirectsQuery;
63
- filters?: string;
64
- }
65
-
66
- export const getRedirects = createAsyncThunk(
67
- "redirects/getRedirects",
68
- async ({ params, filters }: IGetRedirectsArgs, { dispatch }) => {
69
- // Empties the list before asking, as before: the table blanks instead of showing the
70
- // previous page underneath the spinner.
71
- dispatch(setRedirects([]));
72
-
73
- return handleRequest(
74
- async () => redirects.getRedirects(params, filters),
75
- {
76
- handleSuccess: ({ items, totalItems }: { items: IRedirect[]; totalItems: number }) => {
77
- dispatch(setRedirects(items));
78
- dispatch(setTotalItems(totalItems));
79
- },
80
- handleError: (response: unknown) => onError(response, dispatch),
81
- },
82
- [appActions.setIsLoading],
83
- )(dispatch);
84
- },
85
- );
86
-
87
- interface IRedirectForm {
88
- from: string;
89
- to: string | number;
90
- }
91
-
92
- interface IAddRedirectArgs {
93
- redirect: IRedirectForm;
94
- force?: boolean;
95
- filter?: string;
96
- }
97
-
98
- export interface IAddRedirectResult {
99
- saved: boolean;
100
- /** The `from` already has a redirect (409). Not a failure — the screen offers to overwrite. */
101
- isOriginTaken: boolean;
102
- }
103
-
104
- export const addRedirect = createAsyncThunk(
105
- "redirects/addRedirect",
106
- async ({ redirect, force, filter }: IAddRedirectArgs, { dispatch, getState }): Promise<IAddRedirectResult> => {
107
- let isOriginTaken = false;
108
-
109
- const saved = await handleRequest(
110
- async () => redirects.createRedirect(redirect, force),
111
- {
112
- handleSuccess: () =>
113
- dispatch(getRedirects({ params: { ...DEFAULT_PARAMS }, filters: scopedFilter(getState, filter) })),
114
- handleError: (response: { status: number }) => {
115
- if (response.status === ERROR_CODE.isBeingUsed) {
116
- isOriginTaken = true;
117
- return;
118
- }
119
- onError(response, dispatch);
120
- },
121
- },
122
- [],
123
- )(dispatch);
124
-
125
- return { saved, isOriginTaken };
126
- },
127
- );
128
-
129
- interface IUpdateRedirectArgs {
130
- redirect: IRedirectForm;
131
- redirectID: number;
132
- filter?: string;
133
- }
134
-
135
- export const updateRedirect = createAsyncThunk(
136
- "redirects/updateRedirect",
137
- async ({ redirect, redirectID, filter }: IUpdateRedirectArgs, { dispatch, getState }) =>
138
- handleRequest(
139
- async () => redirects.updateRedirect(redirect, redirectID),
140
- {
141
- handleSuccess: () =>
142
- dispatch(getRedirects({ params: { ...DEFAULT_PARAMS }, filters: scopedFilter(getState, filter) })),
143
- handleError: (response: unknown) => onError(response, dispatch),
144
- },
145
- [],
146
- )(dispatch),
147
- );
148
-
149
- interface IDeleteRedirectArgs {
150
- redirectID: number | number[];
151
- filter?: string;
152
- }
153
-
154
- export const deleteRedirect = createAsyncThunk(
155
- "redirects/deleteRedirect",
156
- async ({ redirectID, filter }: IDeleteRedirectArgs, { dispatch, getState }) =>
157
- handleRequest(
158
- async () =>
159
- Array.isArray(redirectID) ? redirects.deleteRedirectsBulk(redirectID) : redirects.deleteRedirect(redirectID),
160
- {
161
- handleSuccess: () =>
162
- dispatch(getRedirects({ params: { ...DEFAULT_PARAMS }, filters: scopedFilter(getState, filter) })),
163
- handleError: (response: IMessageResponse) => onCountedError(response, dispatch, "delete"),
164
- },
165
- [appActions.setIsSaving],
166
- )(dispatch),
167
- );
168
-
169
- interface IImportRedirectsArgs {
170
- rows: IRedirectForm[];
171
- /** First phase: ask the backend to classify the rows without writing anything. */
172
- check: boolean;
173
- }
174
-
175
- export const importRedirects = createAsyncThunk(
176
- "redirects/importRedirects",
177
- async ({ rows, check }: IImportRedirectsArgs, { dispatch }) =>
178
- handleRequest(
179
- async () => (check ? redirects.createRedirectsBulkCheck(rows) : redirects.createRedirectsBulk(rows)),
180
- {
181
- handleSuccess: (data: { total: number } & NonNullable<IRedirectsState["imports"]>) => {
182
- if (check) {
183
- const { total, error, existing, ok } = data;
184
- dispatch(setImports({ error, existing, ok }));
185
- dispatch(setTotalImports(total));
186
- return;
187
- }
188
- // Reloads UNFILTERED, unlike every other write. Kept as-is: after importing a
189
- // batch the user is shown the whole catalogue, not the current site's slice.
190
- dispatch(getRedirects({ params: { ...DEFAULT_PARAMS } }));
191
- },
192
- handleError: (response: IMessageResponse) => onCountedError(response, dispatch, "import"),
193
- },
194
- [],
195
- )(dispatch),
196
- );
@@ -1,48 +0,0 @@
1
- import type { ISiteLanguage } from "@ax/types";
2
-
3
- import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
4
-
5
- /**
6
- * First area migrated to Redux Toolkit (UC-AX-manage-languages).
7
- *
8
- * The slice holds ONLY the language panel's form. The list of a site's languages
9
- * lives in `sites.currentSiteLanguages` and the loading flags in `app`, which is
10
- * why this area is so small — see docs/RTK-MIGRATION.md and the use case notes.
11
- *
12
- * `updateFormValue` and `resetForm` used to be thunks that read `getState()` to
13
- * merge onto the current form; as reducers they express the same thing directly,
14
- * so the public shape (name + argument) is unchanged for callers.
15
- */
16
- export interface ILanguageState {
17
- form: ISiteLanguage;
18
- }
19
-
20
- export const initialState: ILanguageState = {
21
- form: {
22
- language: null,
23
- path: "",
24
- domain: null,
25
- isDefault: false,
26
- },
27
- };
28
-
29
- const languagesSlice = createSlice({
30
- name: "languages",
31
- initialState,
32
- reducers: {
33
- setForm(state, action: PayloadAction<ISiteLanguage>) {
34
- state.form = action.payload;
35
- },
36
- updateFormValue(state, action: PayloadAction<Partial<ISiteLanguage>>) {
37
- state.form = { ...state.form, ...action.payload };
38
- },
39
- resetForm(state) {
40
- state.form = initialState.form;
41
- },
42
- },
43
- });
44
-
45
- export const { setForm, updateFormValue, resetForm } = languagesSlice.actions;
46
-
47
- export { initialState as languagesInitialState };
48
- export const languagesReducer = languagesSlice.reducer;
@@ -1,83 +0,0 @@
1
- import { languages } from "@ax/api";
2
- import { appActions } from "@ax/containers/App";
3
- import { sitesActions } from "@ax/containers/Sites";
4
- import { handleRequest } from "@ax/helpers";
5
- import type { ISiteLanguage } from "@ax/types";
6
-
7
- import { createAsyncThunk } from "@reduxjs/toolkit";
8
- import type { Dispatch } from "redux";
9
-
10
- /**
11
- * Write side of UC-AX-manage-languages, as createAsyncThunk.
12
- *
13
- * They keep calling `handleRequest` inside: that helper is the codebase's
14
- * hand-rolled createAsyncThunk (152 call-sites) and owns the loading-flag and
15
- * error-channel conventions. Replacing it is a cross-cutting job, not this
16
- * area's — see docs/RTK-MIGRATION.md §5.
17
- *
18
- * What does change: the legacy versions wrapped everything in a `try/catch` whose
19
- * body was `console.log(e) // TODO: capturar error bien`, swallowing failures.
20
- * createAsyncThunk turns a throw into a `rejected` action instead, so the failure
21
- * is at least observable in devtools rather than lost.
22
- *
23
- * Reads live in `sitesActions.getSiteLanguages` (the list belongs to `sites`), so
24
- * every write refreshes through it, exactly as before.
25
- */
26
- // Called through the namespaces rather than destructured at module scope: a
27
- // destructured reference is frozen at import time and cannot be spied on, which
28
- // would make the refresh-after-write behaviour untestable.
29
- const refreshList = (siteID: number, dispatch: Dispatch) => sitesActions.getSiteLanguages(siteID)(dispatch);
30
-
31
- const onError = (response: unknown, dispatch: Dispatch) => appActions.handleError(response)(dispatch);
32
-
33
- interface ISiteLanguageArgs {
34
- siteID: number;
35
- data: ISiteLanguage;
36
- }
37
-
38
- interface IDeleteSiteLanguageArgs {
39
- siteID: number;
40
- languageID: number;
41
- }
42
-
43
- export const createSiteLanguage = createAsyncThunk(
44
- "languages/createSiteLanguage",
45
- async ({ siteID, data }: ISiteLanguageArgs, { dispatch }) =>
46
- handleRequest(
47
- async () => languages.createSiteLanguage(siteID, data),
48
- {
49
- handleSuccess: () => refreshList(siteID, dispatch),
50
- handleError: (response: unknown) => onError(response, dispatch),
51
- },
52
- [appActions.setIsSaving],
53
- )(dispatch),
54
- );
55
-
56
- export const updateSiteLanguage = createAsyncThunk(
57
- "languages/updateSiteLanguage",
58
- async ({ siteID, data }: ISiteLanguageArgs, { dispatch }) =>
59
- handleRequest(
60
- async () => languages.updateSiteLanguage(siteID, data),
61
- {
62
- handleSuccess: () => refreshList(siteID, dispatch),
63
- handleError: (response: unknown) => onError(response, dispatch),
64
- },
65
- [appActions.setIsSaving],
66
- )(dispatch),
67
- );
68
-
69
- export const deleteSiteLanguage = createAsyncThunk(
70
- "languages/deleteSiteLanguage",
71
- // setIsLoading, not setIsSaving — kept as-is to preserve behaviour: the list
72
- // view only renders <Loading /> off app.isLoading, so deleting shows the
73
- // spinner while adding/editing does not. Inconsistency noted in the use case.
74
- async ({ siteID, languageID }: IDeleteSiteLanguageArgs, { dispatch }) =>
75
- handleRequest(
76
- async () => languages.deleteSiteLanguage(siteID, languageID),
77
- {
78
- handleSuccess: () => refreshList(siteID, dispatch),
79
- handleError: (response: unknown) => onError(response, dispatch),
80
- },
81
- [appActions.setIsLoading],
82
- )(dispatch),
83
- );
@@ -1,40 +0,0 @@
1
- import type { ISocialState } from "@ax/types";
2
-
3
- import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
4
-
5
- /**
6
- * The `social` area holds one site's social profile URLs (and the two things filed
7
- * alongside them, newsletter and podcast) as a flat `{ network: url }` map —
8
- * UC-AX-configure-social-networks. No types, order or icons: the screen is a plain
9
- * form of URL text fields saved in a single PUT, and the set of fields comes from the
10
- * instance config plus a hardcoded list, not from the state.
11
- *
12
- * `ISocialState` stays in `@ax/types` rather than moving next to the slice as
13
- * `ILanguageState` and `IAnalyticsState` did: seven preview components use it as a
14
- * prop type, so it is a shared domain type that happens to also be this area's state.
15
- *
16
- * STAYS in redux-persist, against the rule of ADR 0002: six page-editor screens read
17
- * `state.social` to feed the preview canvas and none of them fetches — only
18
- * `sites.setSiteInfo` writes it outside this screen, and that does not re-run on a hard
19
- * refresh. The persist was their loader. Leaves in waves 2-4 with those consumers.
20
- */
21
- export const initialState: ISocialState = {};
22
-
23
- const socialSlice = createSlice({
24
- name: "social",
25
- initialState,
26
- reducers: {
27
- // REPLACES, it does not merge — the opposite of its `analytics` sibling. The
28
- // legacy reducer returned `{ ...payload }` and the screen leans on it: since
29
- // "removing" a network means emptying its field and saving, a key the server
30
- // drops has to drop out of the store too.
31
- setSocial(_state, action: PayloadAction<ISocialState>) {
32
- return { ...action.payload };
33
- },
34
- },
35
- });
36
-
37
- export const { setSocial } = socialSlice.actions;
38
-
39
- export { initialState as socialInitialState };
40
- export const socialReducer = socialSlice.reducer;
@@ -1,68 +0,0 @@
1
- import { social } from "@ax/api";
2
- import { appActions } from "@ax/containers/App";
3
- import { handleRequest } from "@ax/helpers";
4
- import type { IRootState, ISocialState } from "@ax/types";
5
-
6
- import { createAsyncThunk } from "@reduxjs/toolkit";
7
- import type { Dispatch } from "redux";
8
-
9
- import { setSocial } from "./slice";
10
-
11
- /**
12
- * Read and write side of UC-AX-configure-social-networks, as createAsyncThunk.
13
- *
14
- * They keep calling `handleRequest` inside for the same reason the Languages pilot
15
- * does: that helper owns the loading-flag and error-channel conventions of the whole
16
- * codebase, and replacing it is a cross-cutting job — see docs/RTK-MIGRATION.md §5.
17
- *
18
- * Both keep reading the site from `sites.currentSiteInfo` instead of taking it as an
19
- * argument, exactly as the legacy thunks did. This area has a single scope — whatever
20
- * site is loaded — so there is nothing to make explicit the way Analytics had to with
21
- * its global scope, and no call site pays the single-argument rule here.
22
- *
23
- * What does change: the legacy versions wrapped everything in a `try/catch` whose
24
- * body was `console.log(e)`, swallowing failures. createAsyncThunk turns a throw into
25
- * a `rejected` action instead, so the failure is observable.
26
- */
27
- // Called through the namespace rather than destructured at module scope: a
28
- // destructured reference is frozen at import time and cannot be spied on.
29
- const onError = (response: unknown, dispatch: Dispatch) => appActions.handleError(response)(dispatch);
30
-
31
- // `currentSiteInfo` is typed `ISite | null`, which the legacy thunks never had to face
32
- // because they read it through an untyped `getState` — on a null site they built the
33
- // url off `undefined` and threw, and their `try/catch` swallowed it. The screen only
34
- // exists inside a loaded site, so the guard below is unreachable in practice; it is
35
- // there so the impossible case cannot turn into a request to `/site/undefined/socials`.
36
- const siteOf = (getState: () => unknown) => (getState() as IRootState).sites.currentSiteInfo;
37
-
38
- export const getSocial = createAsyncThunk<boolean, void>("social/getSocial", async (_arg, { dispatch, getState }) => {
39
- const site = siteOf(getState);
40
- if (!site) return false;
41
-
42
- return handleRequest(
43
- async () => social.getSocial(site.id),
44
- {
45
- handleSuccess: (data: ISocialState) => dispatch(setSocial(data)),
46
- // No error channel on the read side, as before: the failure only reaches the
47
- // console and the form is left empty. Wart documented in the use case.
48
- handleError: () => console.log("Error en getSocial"),
49
- },
50
- [appActions.setIsLoading],
51
- )(dispatch);
52
- });
53
-
54
- export const saveSocial = createAsyncThunk("social/saveSocial", async (form: ISocialState, { dispatch, getState }) => {
55
- const site = siteOf(getState);
56
- if (!site) return false;
57
-
58
- return handleRequest(
59
- // The whole map goes out in one PUT, emptied fields included — that is what
60
- // makes clearing a field remove the network.
61
- async () => social.updateSocial(site.id, form),
62
- {
63
- handleSuccess: (data: ISocialState) => dispatch(setSocial(data)),
64
- handleError: (response: unknown) => onError(response, dispatch),
65
- },
66
- [appActions.setIsSaving],
67
- )(dispatch);
68
- });
@@ -1,37 +0,0 @@
1
- import type { TypedUseSelectorHook } from "react-redux";
2
- import { useDispatch, useSelector } from "react-redux";
3
-
4
- import type { IRootState } from "@ax/types";
5
-
6
- import type { ThunkDispatch } from "@reduxjs/toolkit";
7
- import type { AnyAction } from "redux";
8
-
9
- /**
10
- * Typed hooks — the replacement for `connect` during the RTK migration.
11
- *
12
- * `RootState` is `IRootState` (the hand-written shape the 145 legacy `connect`
13
- * call-sites already use) rather than `ReturnType<store.getState>`: the root
14
- * reducer is still declared as `Reducer<any>`, so deriving the type from the
15
- * store would silently collapse to `any`. Once every area owns a slice, the
16
- * derivation becomes the source of truth and `IRootState` can go.
17
- *
18
- * `AppDispatch` is spelled out as a thunk dispatch for the same reason — it is
19
- * what `configureStore`'s default middleware actually gives us, and writing it
20
- * explicitly keeps `dispatch` from degrading to `any`.
21
- *
22
- * The stack stays on react-redux 7.2.9 + redux 4.2.1 on purpose — RTK 2.9 accepts
23
- * both as peers, so nothing here needs them bumped. Both bumps were tried and
24
- * backed out, each for a measured reason (see docs/RTK-MIGRATION.md §8.3):
25
- * - redux 5 retypes `Dispatch` to `UnknownAction`, which the 15 legacy
26
- * `containers/*\/actions.tsx` action interfaces are not assignable to (361 type
27
- * errors) — and three of those files are ones `main` has already changed.
28
- * - react-redux 8 subscribes via `useSyncExternalStore`, which surfaces a
29
- * pre-existing render loop in `components/TableFilters/CheckGroupFilter`
30
- * (unconditional `setSelectedValue` in an effect keyed on fresh references).
31
- * Hence `AnyAction` rather than redux 5's `UnknownAction`.
32
- */
33
- export type RootState = IRootState;
34
- export type AppDispatch = ThunkDispatch<IRootState, undefined, AnyAction>;
35
-
36
- export const useAppDispatch = () => useDispatch<AppDispatch>();
37
- export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
@@ -1,2 +0,0 @@
1
- export type { AppDispatch, RootState } from "./hooks";
2
- export { useAppDispatch, useAppSelector } from "./hooks";