@blocklet/launcher-workflow 2.4.4 → 2.4.5
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/es/components/in-progress-session.js +1 -1
- package/es/components/launch-serverless/allocate.js +181 -0
- package/es/components/launch-serverless/install.js +203 -0
- package/es/components/launch-serverless/shared/base-serverless-layout.js +53 -0
- package/es/components/launch-serverless/shared/common-components.js +605 -0
- package/es/components/launch-serverless/shared/loading-display-layout.js +122 -0
- package/es/components/launch-serverless/shared/retry-error-message.js +45 -0
- package/es/components/launch-serverless/start-app.js +356 -0
- package/es/contexts/request.js +2 -2
- package/es/hooks/use-serial-polling.js +43 -0
- package/es/install.js +28 -0
- package/es/launch.js +1 -1
- package/es/locales/en.js +71 -14
- package/es/locales/zh.js +68 -12
- package/es/paid.js +1 -1
- package/es/prepare.js +1 -1
- package/es/start-app.js +28 -0
- package/es/util.js +181 -2
- package/lib/components/in-progress-session.js +3 -3
- package/lib/components/launch-serverless/allocate.js +198 -0
- package/lib/components/launch-serverless/install.js +223 -0
- package/lib/components/launch-serverless/shared/base-serverless-layout.js +59 -0
- package/lib/components/launch-serverless/shared/common-components.js +635 -0
- package/lib/components/launch-serverless/shared/loading-display-layout.js +131 -0
- package/lib/components/launch-serverless/shared/retry-error-message.js +52 -0
- package/lib/components/launch-serverless/start-app.js +369 -0
- package/lib/contexts/request.js +2 -2
- package/lib/hooks/use-serial-polling.js +49 -0
- package/lib/install.js +35 -0
- package/lib/launch.js +2 -2
- package/lib/locales/en.js +71 -14
- package/lib/locales/zh.js +68 -12
- package/lib/paid.js +2 -2
- package/lib/prepare.js +2 -2
- package/lib/start-app.js +35 -0
- package/lib/util.js +214 -11
- package/package.json +8 -5
- package/es/components/launch-serverless.js +0 -115
- package/lib/components/launch-serverless.js +0 -89
|
@@ -5,7 +5,7 @@ import Box from '@mui/material/Box';
|
|
|
5
5
|
import Link from '@mui/material/Link';
|
|
6
6
|
import { useState } from 'react';
|
|
7
7
|
import useAsync from 'react-use/lib/useAsync';
|
|
8
|
-
import joinURL from '
|
|
8
|
+
import { joinURL } from 'ufo';
|
|
9
9
|
import { useLocaleContext } from '../contexts/locale';
|
|
10
10
|
import useRequest from '../contexts/request';
|
|
11
11
|
import { useWorkflowContext } from '../contexts/workflow';
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { useSetState } from 'ahooks';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import React, { useEffect, useMemo, useRef } from 'react';
|
|
4
|
+
import { useNavigate } from 'react-router-dom';
|
|
5
|
+
import { LAUNCH_STATUS } from '@blocklet/launcher-util/lib/constant';
|
|
6
|
+
import { useLocaleContext } from '../../contexts/locale';
|
|
7
|
+
import useRequest from '../../contexts/request';
|
|
8
|
+
import BaseServerlessLayout from './shared/base-serverless-layout';
|
|
9
|
+
import { useDisplayProgress, useElapsedTime, useFormatTime } from './shared/common-components';
|
|
10
|
+
import LoadingDisplayLayout from './shared/loading-display-layout';
|
|
11
|
+
import RetryErrorMessage from './shared/retry-error-message';
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
export default function LaunchServerless({
|
|
14
|
+
sessionId
|
|
15
|
+
}) {
|
|
16
|
+
const {
|
|
17
|
+
t
|
|
18
|
+
} = useLocaleContext();
|
|
19
|
+
const {
|
|
20
|
+
api
|
|
21
|
+
} = useRequest();
|
|
22
|
+
const navigate = useNavigate();
|
|
23
|
+
const timerRef = useRef({});
|
|
24
|
+
const [state, setState] = useSetState({
|
|
25
|
+
allocating: true,
|
|
26
|
+
allocated: false,
|
|
27
|
+
error: '',
|
|
28
|
+
launchSession: null
|
|
29
|
+
});
|
|
30
|
+
const formatTime = useFormatTime();
|
|
31
|
+
const time = useElapsedTime(0);
|
|
32
|
+
const actions = useMemo(() => {
|
|
33
|
+
// 基础步骤时间
|
|
34
|
+
const steps = [{
|
|
35
|
+
message: t('launch.waiting.starting'),
|
|
36
|
+
time: 1
|
|
37
|
+
}, {
|
|
38
|
+
message: t('launch.waiting.securing'),
|
|
39
|
+
time: 1
|
|
40
|
+
}, {
|
|
41
|
+
message: t('launch.waiting.prepare'),
|
|
42
|
+
time: 1
|
|
43
|
+
}, {
|
|
44
|
+
message: t('launch.waiting.waiting'),
|
|
45
|
+
time: 1
|
|
46
|
+
}, {
|
|
47
|
+
message: t('launch.waiting.done'),
|
|
48
|
+
time: 1
|
|
49
|
+
}];
|
|
50
|
+
|
|
51
|
+
// 计算总时间
|
|
52
|
+
const totalTime = steps.reduce((acc, step) => acc + step.time, 0);
|
|
53
|
+
|
|
54
|
+
// 计算每个步骤的进度区间
|
|
55
|
+
let currentProgress = 0;
|
|
56
|
+
return steps.map(step => {
|
|
57
|
+
const progressPercent = step.time / totalTime * 100;
|
|
58
|
+
const range = [Math.round(currentProgress), Math.round(currentProgress + progressPercent)];
|
|
59
|
+
currentProgress += progressPercent;
|
|
60
|
+
return {
|
|
61
|
+
...step,
|
|
62
|
+
range
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}, [t]);
|
|
66
|
+
const estimatedTime = useMemo(() => {
|
|
67
|
+
return actions.reduce((acc, action) => acc + action.time, 0);
|
|
68
|
+
}, [actions]);
|
|
69
|
+
const displayProgress = useDisplayProgress(0, estimatedTime);
|
|
70
|
+
|
|
71
|
+
// 根据当前进度获取对应的 action
|
|
72
|
+
const getCurrentAction = progress => {
|
|
73
|
+
const action = actions.find(({
|
|
74
|
+
range: [start, end]
|
|
75
|
+
}) => progress >= start && progress < end);
|
|
76
|
+
return action?.message || actions[actions.length - 1].message;
|
|
77
|
+
};
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const fetch = async () => {
|
|
80
|
+
try {
|
|
81
|
+
const {
|
|
82
|
+
data: {
|
|
83
|
+
launch: launchSession
|
|
84
|
+
}
|
|
85
|
+
} = await api.get(`/launches/${sessionId}?health=1`);
|
|
86
|
+
setState({
|
|
87
|
+
launchSession
|
|
88
|
+
});
|
|
89
|
+
if (launchSession.status >= LAUNCH_STATUS.installed) {
|
|
90
|
+
navigate(`/start-app/${sessionId}${window.location.search || ''}`);
|
|
91
|
+
} else if (launchSession.status >= LAUNCH_STATUS.allocated) {
|
|
92
|
+
navigate(`/install/${sessionId}${window.location.search || ''}`);
|
|
93
|
+
} else {
|
|
94
|
+
await Promise.all([api.post('/serverless/allocate', {
|
|
95
|
+
launchId: sessionId
|
|
96
|
+
}), new Promise(resolve => {
|
|
97
|
+
timerRef.current.raceTimer = setTimeout(() => {
|
|
98
|
+
resolve();
|
|
99
|
+
}, estimatedTime * 1000);
|
|
100
|
+
})]);
|
|
101
|
+
setState({
|
|
102
|
+
allocating: false,
|
|
103
|
+
allocated: true
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
setState({
|
|
108
|
+
error: error.message,
|
|
109
|
+
allocating: false
|
|
110
|
+
});
|
|
111
|
+
console.error(error);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
if (window.blocklet.DEVELOPER_WORKFLOW_UI) {
|
|
115
|
+
timerRef.current.mockTimer = setTimeout(() => {
|
|
116
|
+
navigate(`/install/${sessionId}${window.location.search || ''}`);
|
|
117
|
+
}, 5000);
|
|
118
|
+
} else {
|
|
119
|
+
fetch();
|
|
120
|
+
}
|
|
121
|
+
return () => {
|
|
122
|
+
const timers = Object.values(timerRef.current); // eslint-disable-line react-hooks/exhaustive-deps
|
|
123
|
+
timers.forEach(timer => clearTimeout(timer));
|
|
124
|
+
};
|
|
125
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
126
|
+
}, []);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (state.allocated) {
|
|
129
|
+
const timer = setTimeout(() => {
|
|
130
|
+
navigate(`/install/${sessionId}${window.location.search || ''}`);
|
|
131
|
+
}, 1000);
|
|
132
|
+
return () => clearTimeout(timer);
|
|
133
|
+
}
|
|
134
|
+
return () => {};
|
|
135
|
+
}, [state.allocated, sessionId, navigate]);
|
|
136
|
+
const handleRetry = async () => {
|
|
137
|
+
try {
|
|
138
|
+
setState({
|
|
139
|
+
error: '',
|
|
140
|
+
allocating: true
|
|
141
|
+
});
|
|
142
|
+
if (!state.launchSession?.status || state.launchSession.status < LAUNCH_STATUS.allocated) {
|
|
143
|
+
await api.post('/serverless/allocate', {
|
|
144
|
+
launchId: sessionId
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
await api.post('/serverless/install', {
|
|
148
|
+
launchId: sessionId
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
setState({
|
|
152
|
+
allocating: false,
|
|
153
|
+
allocated: true
|
|
154
|
+
});
|
|
155
|
+
} catch (error) {
|
|
156
|
+
setState({
|
|
157
|
+
error: error.message,
|
|
158
|
+
allocating: false
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
return /*#__PURE__*/_jsxs(BaseServerlessLayout, {
|
|
163
|
+
title: t('launch.pageTitle'),
|
|
164
|
+
children: [!state.error && /*#__PURE__*/_jsx(LoadingDisplayLayout, {
|
|
165
|
+
title: t('launch.pageTitle'),
|
|
166
|
+
progress: state.allocated ? 100 : Math.max(0, Math.min(displayProgress, 100)),
|
|
167
|
+
elapsedTime: formatTime(time),
|
|
168
|
+
estimatedTime: formatTime(estimatedTime),
|
|
169
|
+
currentAction: getCurrentAction(displayProgress),
|
|
170
|
+
error: null,
|
|
171
|
+
onRetry: null
|
|
172
|
+
}), state.error && /*#__PURE__*/_jsx(RetryErrorMessage, {
|
|
173
|
+
title: t('prepare.serverless.prepareFailed'),
|
|
174
|
+
onRetry: handleRetry,
|
|
175
|
+
retryText: t('common.retry')
|
|
176
|
+
})]
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
LaunchServerless.propTypes = {
|
|
180
|
+
sessionId: PropTypes.string.isRequired
|
|
181
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { useSetState } from 'ahooks';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import React, { useEffect, useMemo } from 'react';
|
|
4
|
+
import { useNavigate } from 'react-router-dom';
|
|
5
|
+
import { useLocaleContext } from '../../contexts/locale';
|
|
6
|
+
import useRequest from '../../contexts/request';
|
|
7
|
+
import useSerialPolling from '../../hooks/use-serial-polling';
|
|
8
|
+
import BaseServerlessLayout from './shared/base-serverless-layout';
|
|
9
|
+
import { calculateEstimatedTime, useDisplayProgress, useElapsedTime, useFormatTime } from './shared/common-components';
|
|
10
|
+
import LoadingDisplayLayout from './shared/loading-display-layout';
|
|
11
|
+
import RetryErrorMessage from './shared/retry-error-message';
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
const CHECK_INTERVAL = 2000;
|
|
14
|
+
const STATUS = {
|
|
15
|
+
installing: 1,
|
|
16
|
+
installed: 2,
|
|
17
|
+
error: 10
|
|
18
|
+
};
|
|
19
|
+
export default function LaunchServerless({
|
|
20
|
+
sessionId
|
|
21
|
+
}) {
|
|
22
|
+
const {
|
|
23
|
+
t
|
|
24
|
+
} = useLocaleContext();
|
|
25
|
+
const {
|
|
26
|
+
api
|
|
27
|
+
} = useRequest();
|
|
28
|
+
const navigate = useNavigate();
|
|
29
|
+
const [state, setState] = useSetState({
|
|
30
|
+
status: STATUS.installing,
|
|
31
|
+
startTime: Date.now(),
|
|
32
|
+
hasTryInstall: false,
|
|
33
|
+
retryCount: 0,
|
|
34
|
+
error: ''
|
|
35
|
+
});
|
|
36
|
+
const formatTime = useFormatTime();
|
|
37
|
+
|
|
38
|
+
// 定义带进度区间的 actions
|
|
39
|
+
const actions = useMemo(() => {
|
|
40
|
+
// 基础步骤时间
|
|
41
|
+
const steps = [{
|
|
42
|
+
message: t('install.waiting.verifying'),
|
|
43
|
+
time: 2
|
|
44
|
+
}, {
|
|
45
|
+
message: t('install.waiting.downloading'),
|
|
46
|
+
time: 5
|
|
47
|
+
}, {
|
|
48
|
+
message: t('install.waiting.extracting'),
|
|
49
|
+
time: 2
|
|
50
|
+
}, {
|
|
51
|
+
message: t('install.waiting.installing'),
|
|
52
|
+
time: 3
|
|
53
|
+
}, {
|
|
54
|
+
message: t('install.waiting.installed'),
|
|
55
|
+
time: 1
|
|
56
|
+
}];
|
|
57
|
+
|
|
58
|
+
// 计算总时间
|
|
59
|
+
const totalTime = steps.reduce((acc, step) => acc + step.time, 0);
|
|
60
|
+
|
|
61
|
+
// 计算每个步骤的进度区间
|
|
62
|
+
let currentProgress = 0;
|
|
63
|
+
return steps.map(step => {
|
|
64
|
+
const progressPercent = step.time / totalTime * 100;
|
|
65
|
+
const range = [Math.round(currentProgress), Math.round(currentProgress + progressPercent)];
|
|
66
|
+
currentProgress += progressPercent;
|
|
67
|
+
return {
|
|
68
|
+
...step,
|
|
69
|
+
range
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}, [t]);
|
|
73
|
+
|
|
74
|
+
// 根据组件计算预估时间
|
|
75
|
+
const estimatedTime = useMemo(() => {
|
|
76
|
+
const components = window.blockletMeta?.components || [];
|
|
77
|
+
const additionalTime = calculateEstimatedTime(components);
|
|
78
|
+
return actions.reduce((acc, {
|
|
79
|
+
time
|
|
80
|
+
}) => acc + time, 0) + Math.ceil(additionalTime * 0.6);
|
|
81
|
+
}, [actions]);
|
|
82
|
+
|
|
83
|
+
// 根据当前进度获取对应的 action
|
|
84
|
+
const getCurrentAction = progress => {
|
|
85
|
+
const action = actions.find(({
|
|
86
|
+
range: [start, end]
|
|
87
|
+
}) => progress >= start && progress < end);
|
|
88
|
+
return action?.message || actions[actions.length - 1].message;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// 时间管理 - 参考 launch-dedicated.js
|
|
92
|
+
let installStartTime = sessionStorage.getItem(`launcher-install-${sessionId}-time`);
|
|
93
|
+
if (!installStartTime) {
|
|
94
|
+
installStartTime = Date.now();
|
|
95
|
+
sessionStorage.setItem(`launcher-install-${sessionId}-time`, installStartTime);
|
|
96
|
+
}
|
|
97
|
+
const time = useElapsedTime(Math.round((Date.now() - installStartTime) / 1000));
|
|
98
|
+
const startProgress = useMemo(() => {
|
|
99
|
+
return Math.min(time / estimatedTime * 100, 90);
|
|
100
|
+
}, [time, estimatedTime]);
|
|
101
|
+
const displayProgress = useDisplayProgress(startProgress, state.status === STATUS.installed ? Math.min(estimatedTime - time, 2) : estimatedTime);
|
|
102
|
+
const checkLaunchSession = async () => {
|
|
103
|
+
if (window.blocklet.DEVELOPER_WORKFLOW_UI) {
|
|
104
|
+
// 开发模式下的模拟逻辑
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
setState({
|
|
107
|
+
status: STATUS.installed
|
|
108
|
+
});
|
|
109
|
+
sessionStorage.removeItem(`launcher-install-${sessionId}-time`);
|
|
110
|
+
navigate(`/start-app/${sessionId}${window.location.search || ''}`);
|
|
111
|
+
}, 47000);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const {
|
|
116
|
+
data: {
|
|
117
|
+
launch: launchSession
|
|
118
|
+
}
|
|
119
|
+
} = await api.get(`/launches/${sessionId}?health=1`);
|
|
120
|
+
const {
|
|
121
|
+
status
|
|
122
|
+
} = launchSession.metadata;
|
|
123
|
+
const hasInstalled = ['starting', 'installed', 'running', 'stopped'].includes(status);
|
|
124
|
+
if (launchSession.running || hasInstalled) {
|
|
125
|
+
sessionStorage.removeItem(`launcher-install-${sessionId}-time`);
|
|
126
|
+
setState({
|
|
127
|
+
status: STATUS.installed
|
|
128
|
+
});
|
|
129
|
+
if (['installed', 'stopped'].includes(status)) {
|
|
130
|
+
await api.post(`/launches/${sessionId}/start`).catch(error => {
|
|
131
|
+
console.error(error);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
} else if (!state.hasTryInstall && !hasInstalled && Date.now() - state.startTime > 20000) {
|
|
135
|
+
setState({
|
|
136
|
+
hasTryInstall: true
|
|
137
|
+
});
|
|
138
|
+
await api.post('/serverless/install', {
|
|
139
|
+
launchId: sessionId
|
|
140
|
+
}).catch(error => {
|
|
141
|
+
console.error(error);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
setState({
|
|
145
|
+
retryCount: 0
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (state.retryCount < 5) {
|
|
149
|
+
console.warn('check launch session occurred error, retry', state.retryCount, error);
|
|
150
|
+
setState({
|
|
151
|
+
retryCount: state.retryCount + 1
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
sessionStorage.removeItem(`launcher-install-${sessionId}-time`);
|
|
155
|
+
setState({
|
|
156
|
+
error: error.message,
|
|
157
|
+
status: STATUS.error,
|
|
158
|
+
retryCount: 0
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// 串行检查安装状态
|
|
165
|
+
useSerialPolling({
|
|
166
|
+
isEnabled: state.status === STATUS.installing,
|
|
167
|
+
interval: CHECK_INTERVAL,
|
|
168
|
+
onPoll: checkLaunchSession
|
|
169
|
+
});
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
if (state.status === STATUS.installed && displayProgress >= 99) {
|
|
172
|
+
const timer = setTimeout(() => {
|
|
173
|
+
sessionStorage.removeItem(`launcher-install-${sessionId}-time`);
|
|
174
|
+
navigate(`/start-app/${sessionId}${window.location.search || ''}`);
|
|
175
|
+
}, 1500);
|
|
176
|
+
return () => clearTimeout(timer);
|
|
177
|
+
}
|
|
178
|
+
return () => {};
|
|
179
|
+
}, [state.status, sessionId, navigate, displayProgress]);
|
|
180
|
+
const handleRetry = () => {
|
|
181
|
+
sessionStorage.removeItem(`launcher-install-${sessionId}-time`);
|
|
182
|
+
navigate(`/launch/${sessionId}${window.location.search || ''}`);
|
|
183
|
+
};
|
|
184
|
+
return /*#__PURE__*/_jsxs(BaseServerlessLayout, {
|
|
185
|
+
title: t('install.pageTitle'),
|
|
186
|
+
children: [state.status !== STATUS.error && /*#__PURE__*/_jsx(LoadingDisplayLayout, {
|
|
187
|
+
title: t('install.pageTitle'),
|
|
188
|
+
progress: state.status === STATUS.installed && displayProgress >= 99 ? 100 : displayProgress,
|
|
189
|
+
elapsedTime: formatTime(time),
|
|
190
|
+
estimatedTime: formatTime(estimatedTime),
|
|
191
|
+
currentAction: getCurrentAction(displayProgress),
|
|
192
|
+
error: null,
|
|
193
|
+
onRetry: handleRetry
|
|
194
|
+
}), state.status === STATUS.error && /*#__PURE__*/_jsx(RetryErrorMessage, {
|
|
195
|
+
title: t('install.installFailed'),
|
|
196
|
+
onRetry: handleRetry,
|
|
197
|
+
retryText: t('common.retry')
|
|
198
|
+
})]
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
LaunchServerless.propTypes = {
|
|
202
|
+
sessionId: PropTypes.string.isRequired
|
|
203
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Box } from '@mui/material';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 无服务器启动流程的基础布局组件
|
|
7
|
+
* 提取公共的样式和结构,避免重复代码
|
|
8
|
+
*/
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
export default function BaseServerlessLayout({
|
|
11
|
+
title,
|
|
12
|
+
children
|
|
13
|
+
}) {
|
|
14
|
+
return /*#__PURE__*/_jsx(Box, {
|
|
15
|
+
sx: {
|
|
16
|
+
display: 'flex',
|
|
17
|
+
flexDirection: 'column',
|
|
18
|
+
width: '100%',
|
|
19
|
+
height: '100%',
|
|
20
|
+
paddingTop: '100px',
|
|
21
|
+
'& .page-logo': {
|
|
22
|
+
display: 'flex',
|
|
23
|
+
justifyContent: 'center',
|
|
24
|
+
marginTop: {
|
|
25
|
+
xs: '48px',
|
|
26
|
+
md: '62px'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
children: /*#__PURE__*/_jsx(Box, {
|
|
31
|
+
sx: {
|
|
32
|
+
display: 'flex',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
flexDirection: 'column',
|
|
35
|
+
textAlign: 'center',
|
|
36
|
+
'& .loading-description': {
|
|
37
|
+
marginTop: '8px'
|
|
38
|
+
},
|
|
39
|
+
'& .result-title': {
|
|
40
|
+
fontSize: '18px'
|
|
41
|
+
},
|
|
42
|
+
'& .result-title.color-loading': {
|
|
43
|
+
color: 'primary.main'
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
children: children
|
|
47
|
+
})
|
|
48
|
+
}, title);
|
|
49
|
+
}
|
|
50
|
+
BaseServerlessLayout.propTypes = {
|
|
51
|
+
title: PropTypes.string.isRequired,
|
|
52
|
+
children: PropTypes.node.isRequired
|
|
53
|
+
};
|