@imposium-hub/components 2.5.17 → 2.6.0-0
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/dist/cjs/Util.js +1 -2
- package/dist/cjs/Util.js.map +1 -1
- package/dist/cjs/components/app-wrapper/AppWrapper.d.ts +14 -22
- package/dist/cjs/components/app-wrapper/AppWrapper.js +170 -216
- package/dist/cjs/components/app-wrapper/AppWrapper.js.map +1 -1
- package/dist/cjs/components/app-wrapper/AppWrapperV2.d.ts +3 -9
- package/dist/cjs/components/app-wrapper/AppWrapperV2.js +119 -45
- package/dist/cjs/components/app-wrapper/AppWrapperV2.js.map +1 -1
- package/dist/cjs/components/app-wrapper/AppWrapperV3.d.ts +1 -1
- package/dist/cjs/components/app-wrapper/AppWrapperV3.js +7 -44
- package/dist/cjs/components/app-wrapper/AppWrapperV3.js.map +1 -1
- package/dist/cjs/components/compositions/TextLayer.js +0 -1
- package/dist/cjs/components/compositions/TextLayer.js.map +1 -1
- package/dist/cjs/components/header/Header.d.ts +6 -5
- package/dist/cjs/components/header/Header.js +46 -62
- package/dist/cjs/components/header/Header.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -7
- package/dist/cjs/index.js +5 -17
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/redux/actions/asset-uploads.js +0 -1
- package/dist/cjs/redux/actions/asset-uploads.js.map +1 -1
- package/dist/cjs/services/API.d.ts +6 -0
- package/dist/cjs/services/API.js +38 -9
- package/dist/cjs/services/API.js.map +1 -1
- package/dist/cjs/services/Session.d.ts +0 -11
- package/dist/cjs/services/Session.js +3 -126
- package/dist/cjs/services/Session.js.map +1 -1
- package/dist/esm/Util.js +1 -2
- package/dist/esm/Util.js.map +1 -1
- package/dist/esm/components/app-wrapper/AppWrapper.d.ts +14 -22
- package/dist/esm/components/app-wrapper/AppWrapper.js +75 -182
- package/dist/esm/components/app-wrapper/AppWrapper.js.map +1 -1
- package/dist/esm/components/app-wrapper/AppWrapperV2.d.ts +3 -9
- package/dist/esm/components/app-wrapper/AppWrapperV2.js +30 -33
- package/dist/esm/components/app-wrapper/AppWrapperV2.js.map +1 -1
- package/dist/esm/components/app-wrapper/AppWrapperV3.d.ts +1 -1
- package/dist/esm/components/app-wrapper/AppWrapperV3.js +5 -42
- package/dist/esm/components/app-wrapper/AppWrapperV3.js.map +1 -1
- package/dist/esm/components/compositions/TextLayer.js +0 -1
- package/dist/esm/components/compositions/TextLayer.js.map +1 -1
- package/dist/esm/components/header/Header.d.ts +6 -5
- package/dist/esm/components/header/Header.js +46 -60
- package/dist/esm/components/header/Header.js.map +1 -1
- package/dist/esm/index.d.ts +2 -7
- package/dist/esm/index.js +2 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/redux/actions/asset-uploads.js +0 -1
- package/dist/esm/redux/actions/asset-uploads.js.map +1 -1
- package/dist/esm/services/API.d.ts +6 -0
- package/dist/esm/services/API.js +37 -9
- package/dist/esm/services/API.js.map +1 -1
- package/dist/esm/services/Session.d.ts +0 -11
- package/dist/esm/services/Session.js +3 -84
- package/dist/esm/services/Session.js.map +1 -1
- package/package.json +3 -2
- package/src/Util.ts +1 -2
- package/src/components/app-wrapper/AppWrapper.tsx +131 -272
- package/src/components/compositions/TextLayer.tsx +0 -1
- package/src/components/header/Header.tsx +55 -80
- package/src/index.ts +1 -14
- package/src/redux/actions/asset-uploads.ts +0 -1
- package/src/services/API.ts +48 -9
- package/src/components/auth-gate/AuthGate.tsx +0 -84
- package/src/redux/actions/auth.ts +0 -30
- package/src/redux/reducers/auth.ts +0 -33
- package/src/services/Auth0.ts +0 -82
- package/src/services/Session.ts +0 -153
|
@@ -1,308 +1,167 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import NoAccess from '../no-access/NoAccess';
|
|
3
2
|
import Header from '../header/Header';
|
|
4
|
-
import SessionService from '../../services/Session';
|
|
5
|
-
import AuthService, { IIdentity } from '../../services/Auth0';
|
|
6
|
-
import { connect } from 'react-redux';
|
|
7
|
-
import { bindActionCreators } from 'redux';
|
|
8
|
-
import { login, clearCachedAuth } from '../../redux/actions/auth';
|
|
9
3
|
import { validateAccessLevel } from '../../Util';
|
|
10
|
-
import { setAccessData } from '../../redux/actions/access';
|
|
11
4
|
import { ConfirmModal } from '../confirm-modal/ConfirmModal';
|
|
5
|
+
import API, { IImposiumAPI } from '../../services/API';
|
|
6
|
+
import { useAuth0 } from '@auth0/auth0-react';
|
|
12
7
|
|
|
13
8
|
export interface IAppWrapperProps {
|
|
14
|
-
children: React.ReactChildren;
|
|
15
|
-
auth0Domain: string;
|
|
16
|
-
auth0ClientId: string;
|
|
17
|
-
organizationId: string;
|
|
18
9
|
baseUrl: string;
|
|
19
|
-
|
|
10
|
+
api: IImposiumAPI;
|
|
11
|
+
children?: any;
|
|
12
|
+
organizationId: string;
|
|
20
13
|
serviceId: number;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
clearCachedAuth: () => any;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface IAppWrapperState {
|
|
34
|
-
blockRender: boolean;
|
|
35
|
-
restrictAccess: boolean;
|
|
14
|
+
setAccessData(getAccessData): void;
|
|
15
|
+
onAuthenticated: (token: string, activeOrgId: string, activeStoryId: string) => any;
|
|
16
|
+
onStoryChange(storyId: string): any;
|
|
17
|
+
onOrganizationChange(orgId: string, storyId: string): any;
|
|
18
|
+
storyId?: string;
|
|
19
|
+
CrMLink?: string;
|
|
20
|
+
hideStoryPicker?: boolean;
|
|
21
|
+
hideDocs?: boolean;
|
|
22
|
+
hideOrgPicker?: boolean;
|
|
36
23
|
}
|
|
37
24
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
type='request'
|
|
43
|
-
/>
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
private static readonly RESTRICTED_BLOCK: JSX.Element = (
|
|
47
|
-
<NoAccess
|
|
48
|
-
key='restriction-msg'
|
|
49
|
-
type='restrict'
|
|
50
|
-
/>
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
constructor(p: IAppWrapperProps) {
|
|
54
|
-
super(p);
|
|
55
|
-
|
|
56
|
-
this.state = {
|
|
57
|
-
blockRender: true,
|
|
58
|
-
restrictAccess: false
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public componentDidMount = (): void => {
|
|
63
|
-
const { auth0ClientId, auth0Domain } = this.props;
|
|
64
|
-
|
|
65
|
-
AuthService.bindToClient(auth0ClientId, auth0Domain);
|
|
66
|
-
|
|
67
|
-
const validAccessLevel = validateAccessLevel(
|
|
68
|
-
this.props.organizationId,
|
|
69
|
-
this.props.serviceId,
|
|
70
|
-
this.props.access
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
if (!this.state.restrictAccess && !validAccessLevel) {
|
|
74
|
-
this.setState({ restrictAccess: true });
|
|
75
|
-
}
|
|
25
|
+
const APP_WRAPPER_ERROR_STATES = {
|
|
26
|
+
UNAUTHORIZED_ORG: 'UNAUTHORIZED_ORG',
|
|
27
|
+
UNAUTHORIZED_APP: 'UNAUTHORIZED_APP'
|
|
28
|
+
};
|
|
76
29
|
|
|
77
|
-
|
|
78
|
-
|
|
30
|
+
export const AppWrapper = (props: IAppWrapperProps) => {
|
|
31
|
+
const [blockRender, setBlockRender] = React.useState(true);
|
|
32
|
+
const {
|
|
33
|
+
children,
|
|
34
|
+
organizationId,
|
|
35
|
+
storyId,
|
|
36
|
+
hideStoryPicker,
|
|
37
|
+
hideDocs,
|
|
38
|
+
hideOrgPicker,
|
|
39
|
+
CrMLink,
|
|
40
|
+
baseUrl,
|
|
41
|
+
api,
|
|
42
|
+
serviceId
|
|
43
|
+
} = props;
|
|
44
|
+
const { isAuthenticated, isLoading, getAccessTokenSilently, loginWithRedirect, logout } =
|
|
45
|
+
useAuth0();
|
|
46
|
+
const [errorState, setErrorState] = React.useState(null);
|
|
47
|
+
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
void doCheckSession(true);
|
|
50
|
+
}, [isAuthenticated, isLoading]);
|
|
51
|
+
|
|
52
|
+
const getCachedStoryAndOrgForService = (freshAccess, sId) => {
|
|
53
|
+
const service = freshAccess.services.find((s) => s.id === sId);
|
|
54
|
+
if (service) {
|
|
55
|
+
return {
|
|
56
|
+
story_id: service.story_id,
|
|
57
|
+
organization_id: service.organization_id
|
|
58
|
+
};
|
|
79
59
|
}
|
|
80
|
-
|
|
81
|
-
// Always check the session
|
|
82
|
-
this.doCheckSession(true);
|
|
83
60
|
};
|
|
84
61
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*/
|
|
88
|
-
public componentDidUpdate = (prevProps): void => {
|
|
89
|
-
const { auth } = this.props;
|
|
90
|
-
let validAccessLevel: boolean;
|
|
91
|
-
|
|
92
|
-
if (auth) {
|
|
93
|
-
const {
|
|
94
|
-
idTokenPayload: { exp }
|
|
95
|
-
} = auth;
|
|
96
|
-
const validSession: boolean = AuthService.checkExpiry(exp);
|
|
62
|
+
const propagateCreds = (freshAccess, token): void => {
|
|
63
|
+
const { storyId: initialStoryId, organizationId: initialOrganizationId } = props;
|
|
97
64
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.props.organizationId !== prevProps.organizationId ||
|
|
106
|
-
this.props.access !== prevProps.access
|
|
107
|
-
) {
|
|
108
|
-
validAccessLevel = validateAccessLevel(
|
|
109
|
-
this.props.organizationId,
|
|
110
|
-
this.props.serviceId,
|
|
111
|
-
this.props.access
|
|
65
|
+
// If an org and story ID was passed in, we're following a deeplink. Verify we have access to it, and if so, propagate those IDs and set them on the service
|
|
66
|
+
if (initialOrganizationId && initialStoryId) {
|
|
67
|
+
// Validate the user has access to the orgId and storyID passed in, if not, show the "Unauthorized" interface
|
|
68
|
+
const validAccessLevel = validateAccessLevel(
|
|
69
|
+
initialOrganizationId,
|
|
70
|
+
serviceId,
|
|
71
|
+
freshAccess
|
|
112
72
|
);
|
|
113
73
|
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (this.state.restrictAccess && validAccessLevel) {
|
|
119
|
-
this.setState({ restrictAccess: false });
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
/*
|
|
125
|
-
Emit creds to React parent
|
|
126
|
-
*/
|
|
127
|
-
private propagateCreds = (freshAccess, freshIdentity): void => {
|
|
128
|
-
const { storyId, organizationId, onAuthenticated } = this.props;
|
|
129
|
-
|
|
130
|
-
let initialStoryId = storyId;
|
|
131
|
-
let initialOrganizationId = organizationId;
|
|
132
|
-
|
|
133
|
-
const firstOrg = freshAccess.organizations[0];
|
|
134
|
-
|
|
135
|
-
const getIDsFromSession = () => {
|
|
136
|
-
const session = SessionService.getSession();
|
|
137
|
-
if (session && session.story_id && session.organization_id) {
|
|
138
|
-
const org = freshAccess.organizations.find(
|
|
139
|
-
(o: any) => o.id === session.organization_id
|
|
140
|
-
);
|
|
141
|
-
if (org) {
|
|
142
|
-
const story = org.stories.find((s: any) => s.id === session.story_id);
|
|
143
|
-
if (story) {
|
|
144
|
-
return { orgId: org.id, storyId: story.id };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return null;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
// If the org ID is not provided, but the storyID is provided, find the org ID if we can, if not, use the first org ID found and we'll show the story not found interface
|
|
152
|
-
if (!initialOrganizationId && initialStoryId) {
|
|
153
|
-
const org = freshAccess.organizations.find((o: any) =>
|
|
154
|
-
o.stories.find((s: any) => s.id === initialStoryId)
|
|
155
|
-
);
|
|
156
|
-
if (org) {
|
|
157
|
-
initialOrganizationId = org.id;
|
|
158
|
-
} else if (firstOrg) {
|
|
159
|
-
initialOrganizationId = firstOrg.id;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (!initialStoryId) {
|
|
164
|
-
// If storyID is not provided, try to use the one from the previous session
|
|
165
|
-
const sessionIds = getIDsFromSession();
|
|
166
|
-
if (sessionIds) {
|
|
167
|
-
initialOrganizationId = sessionIds.orgId;
|
|
168
|
-
initialStoryId = sessionIds.storyId;
|
|
74
|
+
if (validAccessLevel) {
|
|
75
|
+
props.onAuthenticated(token, initialOrganizationId, initialStoryId);
|
|
76
|
+
setBlockRender(false);
|
|
169
77
|
} else {
|
|
170
|
-
|
|
171
|
-
if (firstOrg) {
|
|
172
|
-
initialOrganizationId = firstOrg.id;
|
|
173
|
-
const story = firstOrg.stories[0];
|
|
174
|
-
if (story) {
|
|
175
|
-
initialStoryId = story.id;
|
|
176
|
-
} else {
|
|
177
|
-
initialStoryId = null;
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
initialOrganizationId = null;
|
|
181
|
-
initialStoryId = null;
|
|
182
|
-
}
|
|
78
|
+
setErrorState(APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_APP);
|
|
183
79
|
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// If at least one org was found, propagate that
|
|
187
|
-
if (initialOrganizationId) {
|
|
188
|
-
SessionService.buildFreshSession(freshIdentity, initialOrganizationId, initialStoryId);
|
|
189
|
-
onAuthenticated(initialOrganizationId, initialStoryId);
|
|
190
|
-
|
|
191
|
-
if (this.state.blockRender) {
|
|
192
|
-
this.setState({ blockRender: false });
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// If not, show the no-orgs interface
|
|
196
|
-
} else {
|
|
197
|
-
this.setState({ restrictAccess: true, blockRender: false });
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
80
|
|
|
201
|
-
|
|
202
|
-
Bust cached creds if exist, invoke auth failure handler
|
|
203
|
-
*/
|
|
204
|
-
private handleCheckSessionFailure = (e: Error): void => {
|
|
205
|
-
if (this.props.storyId) {
|
|
206
|
-
SessionService.cacheStoryId(this.props.storyId);
|
|
81
|
+
// If no orgID or storyID was passed in from the URL use the cached orgID and story ID on the service
|
|
207
82
|
} else {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
SessionService.clearCachedOrgId();
|
|
83
|
+
const { story_id, organization_id } = getCachedStoryAndOrgForService(
|
|
84
|
+
freshAccess,
|
|
85
|
+
serviceId
|
|
86
|
+
);
|
|
87
|
+
props.onAuthenticated(token, organization_id, story_id);
|
|
88
|
+
setBlockRender(false);
|
|
215
89
|
}
|
|
216
|
-
|
|
217
|
-
this.props.clearCachedAuth();
|
|
218
|
-
this.props.onAuthenticationFailure(e);
|
|
219
90
|
};
|
|
220
91
|
|
|
221
92
|
/*
|
|
222
93
|
Check auth0 session, pull Imposium access creds on success and initialize app-wrapper
|
|
223
94
|
*/
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (blockRender) {
|
|
228
|
-
this.setState({ blockRender: true });
|
|
95
|
+
const doCheckSession = async (doBlockRender: boolean = false) => {
|
|
96
|
+
if (doBlockRender) {
|
|
97
|
+
setBlockRender(true);
|
|
229
98
|
}
|
|
230
|
-
AuthService.checkSession()
|
|
231
|
-
.then((freshIdentity: IIdentity) => {
|
|
232
|
-
const orgId = this.props.organizationId
|
|
233
|
-
? this.props.organizationId
|
|
234
|
-
: SessionService.getCachedOrgId();
|
|
235
|
-
SessionService.getAccessData(freshIdentity.accessToken, baseUrl, false, orgId)
|
|
236
|
-
.then((freshAccess: any) => {
|
|
237
|
-
this.props.setAccessData(freshAccess);
|
|
238
|
-
this.props.login(freshIdentity);
|
|
239
|
-
this.propagateCreds(freshAccess, freshIdentity);
|
|
240
|
-
})
|
|
241
|
-
.catch((e: Error) => {
|
|
242
|
-
this.setState({ restrictAccess: true, blockRender: false });
|
|
243
|
-
});
|
|
244
|
-
})
|
|
245
|
-
.catch((e: Error) => {
|
|
246
|
-
this.handleCheckSessionFailure(e);
|
|
247
|
-
});
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
public render = (): JSX.Element => {
|
|
251
|
-
const { blockRender, restrictAccess } = this.state;
|
|
252
|
-
const { children, showRequestAccess, allowUnauthenticatedRender } = this.props;
|
|
253
|
-
|
|
254
|
-
let innerContent: any;
|
|
255
99
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
type='no-orgs'
|
|
266
|
-
/>
|
|
267
|
-
) : showRequestAccess ? (
|
|
268
|
-
AppWrapper.RESTRICTED_REQUEST
|
|
269
|
-
) : (
|
|
270
|
-
AppWrapper.RESTRICTED_BLOCK
|
|
271
|
-
);
|
|
272
|
-
innerContent = [
|
|
273
|
-
typeof children[0] === 'object' && children[0].type === Header && children[0],
|
|
274
|
-
errorIndicator
|
|
275
|
-
];
|
|
276
|
-
}
|
|
100
|
+
// If we're authenticated, get the access token, pull the access data, and propagate back to the parent component
|
|
101
|
+
if (isAuthenticated) {
|
|
102
|
+
let token;
|
|
103
|
+
try {
|
|
104
|
+
token = await getAccessTokenSilently();
|
|
105
|
+
} catch (e) {
|
|
106
|
+
// Trigger a logout if we can't get the access token
|
|
107
|
+
onLogout();
|
|
108
|
+
}
|
|
277
109
|
|
|
278
|
-
|
|
279
|
-
|
|
110
|
+
const orgId = organizationId ? organizationId : null;
|
|
111
|
+
|
|
112
|
+
const tempAPI = new API(baseUrl, token, orgId);
|
|
113
|
+
tempAPI
|
|
114
|
+
.getAccessData(false)
|
|
115
|
+
.then((freshAccess: any) => {
|
|
116
|
+
props.setAccessData(freshAccess);
|
|
117
|
+
propagateCreds(freshAccess, token);
|
|
118
|
+
})
|
|
119
|
+
.catch((e: Error) => {
|
|
120
|
+
setErrorState(APP_WRAPPER_ERROR_STATES.UNAUTHORIZED_ORG);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// If we're not loading, and we're not authenticated, call login, and cache story + org ID from URL if it's there
|
|
124
|
+
} else if (!isLoading && !isAuthenticated) {
|
|
125
|
+
void loginWithRedirect({
|
|
126
|
+
appState: { returnTo: window.location.href }
|
|
127
|
+
});
|
|
280
128
|
}
|
|
129
|
+
};
|
|
281
130
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
className='app'>
|
|
286
|
-
{innerContent}
|
|
287
|
-
<ConfirmModal />
|
|
288
|
-
</div>
|
|
289
|
-
);
|
|
131
|
+
const onLogout = () => {
|
|
132
|
+
setBlockRender(true);
|
|
133
|
+
void logout({ logoutParams: { returnTo: window.location.origin } });
|
|
290
134
|
};
|
|
291
|
-
}
|
|
292
135
|
|
|
293
|
-
|
|
294
|
-
return bindActionCreators(
|
|
295
|
-
{
|
|
296
|
-
login,
|
|
297
|
-
setAccessData,
|
|
298
|
-
clearCachedAuth
|
|
299
|
-
},
|
|
300
|
-
dispatch
|
|
301
|
-
);
|
|
302
|
-
};
|
|
136
|
+
let innerContent: any;
|
|
303
137
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
138
|
+
if (errorState) {
|
|
139
|
+
innerContent = errorState;
|
|
140
|
+
} else if (!blockRender) {
|
|
141
|
+
innerContent = children;
|
|
142
|
+
}
|
|
307
143
|
|
|
308
|
-
|
|
144
|
+
return (
|
|
145
|
+
<div
|
|
146
|
+
id='app'
|
|
147
|
+
className='app'>
|
|
148
|
+
<Header
|
|
149
|
+
onLogout={onLogout}
|
|
150
|
+
activeServiceId={serviceId}
|
|
151
|
+
baseUrl={baseUrl}
|
|
152
|
+
api={api}
|
|
153
|
+
activeOrganization={organizationId}
|
|
154
|
+
activeStory={storyId}
|
|
155
|
+
hideStoryPicker={hideStoryPicker}
|
|
156
|
+
hideDocs={hideDocs}
|
|
157
|
+
showFTLogo={false}
|
|
158
|
+
hideOrgPicker={hideOrgPicker}
|
|
159
|
+
CrMLink={CrMLink}
|
|
160
|
+
onStoryChange={props.onStoryChange}
|
|
161
|
+
onOrganizationChange={props.onOrganizationChange}
|
|
162
|
+
/>
|
|
163
|
+
{innerContent}
|
|
164
|
+
<ConfirmModal />
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
};
|