@postgres.ai/shared 3.5.0-pr-1027.1 → 4.0.0-pr-1028.1

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 (186) hide show
  1. package/components/DestroyCloneModal/index.js +3 -3
  2. package/components/DestroyCloneRestrictionModal/index.js +1 -1
  3. package/components/MenuButton/index.d.ts +2 -0
  4. package/components/MenuButton/index.js +1 -1
  5. package/components/ResetCloneModal/index.js +5 -3
  6. package/icons/ArrowDropDown/index.d.ts +1 -0
  7. package/icons/ArrowDropDown/index.js +1 -1
  8. package/icons/PostgresSQL/index.d.ts +2 -0
  9. package/icons/PostgresSQL/index.js +40 -0
  10. package/package.json +1 -1
  11. package/pages/Branches/Branch/context.d.ts +22 -0
  12. package/pages/Branches/Branch/context.js +3 -0
  13. package/pages/Branches/Branch/index.d.ts +9 -0
  14. package/pages/Branches/Branch/index.js +172 -0
  15. package/pages/Branches/Branch/stores/Main.d.ts +37 -0
  16. package/pages/Branches/Branch/stores/Main.js +90 -0
  17. package/pages/Branches/Branch/useCreatedStores.d.ts +6 -0
  18. package/pages/Branches/Branch/useCreatedStores.js +5 -0
  19. package/pages/Branches/components/BranchesTable/index.d.ts +10 -0
  20. package/pages/Branches/components/BranchesTable/index.js +107 -0
  21. package/pages/Branches/components/Modals/DeleteBranchModal/index.d.ts +11 -0
  22. package/pages/Branches/components/Modals/DeleteBranchModal/index.js +49 -0
  23. package/pages/Branches/components/Modals/types.d.ts +4 -0
  24. package/pages/Branches/components/Modals/types.js +1 -0
  25. package/pages/Branches/index.d.ts +6 -0
  26. package/pages/Branches/index.js +60 -0
  27. package/pages/Clone/context.d.ts +2 -0
  28. package/pages/Clone/index.d.ts +3 -1
  29. package/pages/Clone/index.js +54 -23
  30. package/pages/Clone/stores/Main.d.ts +4 -2
  31. package/pages/Clone/utils/index.d.ts +4 -0
  32. package/pages/Clone/utils/index.js +12 -0
  33. package/pages/CreateBranch/index.d.ts +17 -0
  34. package/pages/CreateBranch/index.js +135 -0
  35. package/pages/CreateBranch/stores/Main.d.ts +43 -0
  36. package/pages/CreateBranch/stores/Main.js +59 -0
  37. package/pages/CreateBranch/useCreatedStores.d.ts +6 -0
  38. package/pages/CreateBranch/useCreatedStores.js +5 -0
  39. package/pages/CreateBranch/useForm.d.ts +51 -0
  40. package/pages/CreateBranch/useForm.js +30 -0
  41. package/pages/CreateBranch/utils/index.d.ts +2 -0
  42. package/pages/CreateBranch/utils/index.js +10 -0
  43. package/pages/CreateClone/index.d.ts +4 -2
  44. package/pages/CreateClone/index.js +92 -40
  45. package/pages/CreateClone/stores/Main.d.ts +25 -2
  46. package/pages/CreateClone/stores/Main.js +26 -2
  47. package/pages/CreateClone/styles.module.scss +47 -4
  48. package/pages/CreateClone/useForm.d.ts +1 -0
  49. package/pages/CreateClone/useForm.js +1 -0
  50. package/pages/CreateClone/utils/index.d.ts +3 -0
  51. package/pages/CreateClone/utils/index.js +17 -0
  52. package/pages/CreateSnapshot/index.d.ts +17 -0
  53. package/pages/CreateSnapshot/index.js +117 -0
  54. package/pages/CreateSnapshot/stores/Main.d.ts +21 -0
  55. package/pages/CreateSnapshot/stores/Main.js +31 -0
  56. package/pages/CreateSnapshot/useCreatedStores.d.ts +6 -0
  57. package/pages/CreateSnapshot/useCreatedStores.js +5 -0
  58. package/pages/CreateSnapshot/useForm.d.ts +53 -0
  59. package/pages/CreateSnapshot/useForm.js +25 -0
  60. package/pages/CreateSnapshot/utils/index.d.ts +1 -0
  61. package/pages/CreateSnapshot/utils/index.js +3 -0
  62. package/pages/Instance/{components → Clones}/ClonesList/MenuCell/index.js +1 -1
  63. package/pages/Instance/{components → Clones}/ClonesList/MenuCell/utils.js +2 -2
  64. package/pages/Instance/Clones/ClonesList/index.js +96 -0
  65. package/pages/Instance/{components → Clones}/ClonesList/styles.module.scss +10 -0
  66. package/pages/Instance/{ClonesModal → Clones/ClonesModal}/index.js +5 -5
  67. package/pages/Instance/Clones/Header/styles.module.scss +4 -1
  68. package/pages/Instance/Clones/index.d.ts +5 -1
  69. package/pages/Instance/Clones/index.js +6 -8
  70. package/pages/{Configuration → Instance/Configuration}/InputWithTooltip/index.js +15 -11
  71. package/pages/{Configuration → Instance/Configuration}/index.d.ts +2 -1
  72. package/pages/{Configuration → Instance/Configuration}/index.js +9 -6
  73. package/pages/{Configuration → Instance/Configuration}/tooltipText.js +1 -1
  74. package/pages/Instance/Info/Disks/Disk/index.js +6 -4
  75. package/pages/Instance/Info/Retrieval/RetrievalModal/index.js +0 -4
  76. package/pages/Instance/Info/Retrieval/index.js +1 -3
  77. package/pages/Instance/Info/Snapshots/Calendar/utils.d.ts +4 -0
  78. package/pages/Instance/Info/Snapshots/index.js +10 -5
  79. package/pages/Instance/Info/Snapshots/utils.d.ts +13 -2
  80. package/pages/Instance/Info/Snapshots/utils.js +16 -6
  81. package/pages/Instance/Info/Status/index.js +2 -2
  82. package/pages/Instance/Info/index.js +2 -1
  83. package/pages/Instance/Snapshots/components/SnapshotHeader/index.d.ts +11 -0
  84. package/pages/Instance/Snapshots/components/SnapshotHeader/index.js +36 -0
  85. package/pages/Instance/Snapshots/components/SnapshotsList/index.d.ts +11 -0
  86. package/pages/Instance/Snapshots/components/SnapshotsList/index.js +157 -0
  87. package/pages/Instance/Snapshots/components/SnapshotsTable/index.d.ts +6 -0
  88. package/pages/Instance/Snapshots/components/SnapshotsTable/index.js +125 -0
  89. package/pages/Instance/Snapshots/index.d.ts +6 -0
  90. package/pages/Instance/Snapshots/index.js +92 -0
  91. package/pages/Instance/Snapshots/utils/index.d.ts +16 -0
  92. package/pages/Instance/Snapshots/utils/index.js +30 -0
  93. package/pages/Instance/SnapshotsModal/index.js +1 -1
  94. package/pages/Instance/Tabs/PlatformTabs.d.ts +10 -0
  95. package/pages/Instance/Tabs/PlatformTabs.js +51 -0
  96. package/pages/Instance/Tabs/index.d.ts +19 -2
  97. package/pages/Instance/Tabs/index.js +71 -36
  98. package/pages/Instance/Tabs/styles.d.ts +1 -0
  99. package/pages/Instance/Tabs/styles.js +62 -0
  100. package/pages/Instance/components/ModalReloadButton/index.js +1 -1
  101. package/pages/Instance/context.d.ts +7 -0
  102. package/pages/Instance/index.js +14 -13
  103. package/pages/Instance/stores/Main.d.ts +36 -10
  104. package/pages/Instance/stores/Main.js +83 -24
  105. package/pages/Instance/styles.scss +1 -4
  106. package/pages/Logs/hooks/useWsScroll.js +6 -8
  107. package/pages/Logs/index.d.ts +2 -1
  108. package/pages/Logs/index.js +42 -31
  109. package/pages/Logs/wsLogs.d.ts +3 -2
  110. package/pages/Logs/wsLogs.js +24 -8
  111. package/pages/Logs/wsSnackbar.js +7 -7
  112. package/pages/Snapshots/Snapshot/DestorySnapshotModal/index.d.ts +12 -0
  113. package/pages/Snapshots/Snapshot/DestorySnapshotModal/index.js +69 -0
  114. package/pages/Snapshots/Snapshot/context.d.ts +23 -0
  115. package/pages/Snapshots/Snapshot/context.js +3 -0
  116. package/pages/Snapshots/Snapshot/index.d.ts +9 -0
  117. package/pages/Snapshots/Snapshot/index.js +171 -0
  118. package/pages/Snapshots/Snapshot/stores/Main.d.ts +33 -0
  119. package/pages/Snapshots/Snapshot/stores/Main.js +71 -0
  120. package/pages/Snapshots/Snapshot/useCreatedStores.d.ts +6 -0
  121. package/pages/Snapshots/Snapshot/useCreatedStores.js +5 -0
  122. package/stores/Snapshots.d.ts +12 -3
  123. package/stores/Snapshots.js +27 -3
  124. package/types/api/endpoints/createBranch.d.ts +12 -0
  125. package/types/api/endpoints/createBranch.js +1 -0
  126. package/types/api/endpoints/createClone.d.ts +1 -0
  127. package/types/api/endpoints/createSnapshot.d.ts +5 -0
  128. package/types/api/endpoints/createSnapshot.js +1 -0
  129. package/types/api/endpoints/deleteBranch.d.ts +4 -0
  130. package/types/api/endpoints/deleteBranch.js +1 -0
  131. package/types/api/endpoints/destroySnapshot.d.ts +4 -0
  132. package/types/api/endpoints/destroySnapshot.js +1 -0
  133. package/types/api/endpoints/getBranchSnapshot.d.ts +5 -0
  134. package/types/api/endpoints/getBranchSnapshot.js +1 -0
  135. package/types/api/endpoints/getBranches.d.ts +18 -0
  136. package/types/api/endpoints/getBranches.js +5 -0
  137. package/types/api/endpoints/getConfig.d.ts +2 -2
  138. package/types/api/endpoints/getEngine.d.ts +1 -1
  139. package/types/api/endpoints/getFullConfig.d.ts +1 -1
  140. package/types/api/endpoints/getSnapshotList.d.ts +10 -0
  141. package/types/api/endpoints/getSnapshotList.js +1 -0
  142. package/types/api/endpoints/getSnapshots.d.ts +1 -0
  143. package/types/api/endpoints/updateConfig.d.ts +1 -1
  144. package/types/api/entities/branchSnapshot.d.ts +6 -0
  145. package/types/api/entities/branchSnapshot.js +1 -0
  146. package/types/api/entities/branchSnapshots.d.ts +15 -0
  147. package/types/api/entities/branchSnapshots.js +7 -0
  148. package/types/api/entities/clone.d.ts +6 -0
  149. package/types/api/entities/config.js +1 -1
  150. package/types/api/entities/createBranch.d.ts +5 -0
  151. package/types/api/entities/createBranch.js +1 -0
  152. package/types/api/entities/createSnapshot.d.ts +5 -0
  153. package/types/api/entities/createSnapshot.js +1 -0
  154. package/types/api/entities/dbSource.d.ts +1 -0
  155. package/types/api/entities/instance.d.ts +5 -0
  156. package/types/api/entities/instanceState.d.ts +5 -0
  157. package/types/api/entities/snapshot.d.ts +8 -0
  158. package/types/api/entities/snapshot.js +1 -1
  159. package/utils/date.d.ts +2 -0
  160. package/utils/date.js +7 -0
  161. package/utils/snapshot.d.ts +5 -2
  162. package/utils/snapshot.js +6 -1
  163. package/pages/Instance/components/ClonesList/index.js +0 -52
  164. /package/pages/Instance/{components → Clones}/ClonesList/ConnectionModal/index.d.ts +0 -0
  165. /package/pages/Instance/{components → Clones}/ClonesList/ConnectionModal/index.js +0 -0
  166. /package/pages/Instance/{components → Clones}/ClonesList/MenuCell/index.d.ts +0 -0
  167. /package/pages/Instance/{components → Clones}/ClonesList/MenuCell/utils.d.ts +0 -0
  168. /package/pages/Instance/{components → Clones}/ClonesList/index.d.ts +0 -0
  169. /package/pages/Instance/{ClonesModal → Clones/ClonesModal}/index.d.ts +0 -0
  170. /package/pages/Instance/{ClonesModal → Clones/ClonesModal}/utils.d.ts +0 -0
  171. /package/pages/Instance/{ClonesModal → Clones/ClonesModal}/utils.js +0 -0
  172. /package/pages/{Configuration → Instance/Configuration}/Header/index.d.ts +0 -0
  173. /package/pages/{Configuration → Instance/Configuration}/Header/index.js +0 -0
  174. /package/pages/{Configuration → Instance/Configuration}/InputWithTooltip/index.d.ts +0 -0
  175. /package/pages/{Configuration → Instance/Configuration}/ResponseMessage/index.d.ts +0 -0
  176. /package/pages/{Configuration → Instance/Configuration}/ResponseMessage/index.js +0 -0
  177. /package/pages/{Configuration → Instance/Configuration}/configOptions.d.ts +0 -0
  178. /package/pages/{Configuration → Instance/Configuration}/configOptions.js +0 -0
  179. /package/pages/{Configuration → Instance/Configuration}/styles.module.scss +0 -0
  180. /package/pages/{Configuration → Instance/Configuration}/tooltipText.d.ts +0 -0
  181. /package/pages/{Configuration → Instance/Configuration}/useForm.d.ts +0 -0
  182. /package/pages/{Configuration → Instance/Configuration}/useForm.js +0 -0
  183. /package/pages/{Configuration → Instance/Configuration}/utils/index.d.ts +0 -0
  184. /package/pages/{Configuration → Instance/Configuration}/utils/index.js +0 -0
  185. /package/pages/Instance/{SnapshotsModal → Snapshots/components/SnapshotsModal}/utils.d.ts +0 -0
  186. /package/pages/Instance/{SnapshotsModal → Snapshots/components/SnapshotsModal}/utils.js +0 -0
@@ -0,0 +1,11 @@
1
+ /// <reference types="react" />
2
+ import { ModalProps } from '@postgres.ai/shared/pages/Branches/components/Modals/types';
3
+ import { DeleteBranch } from '@postgres.ai/shared/types/api/endpoints/deleteBranch';
4
+ interface DeleteBranchModalProps extends ModalProps {
5
+ deleteBranch: DeleteBranch;
6
+ branchName: string;
7
+ instanceId: string;
8
+ afterSubmitClick: () => void;
9
+ }
10
+ export declare const DeleteBranchModal: ({ isOpen, onClose, deleteBranch, branchName, instanceId, afterSubmitClick, }: DeleteBranchModalProps) => JSX.Element;
11
+ export {};
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*--------------------------------------------------------------------------
3
+ * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai
4
+ * All Rights Reserved. Proprietary and confidential.
5
+ * Unauthorized copying of this file, via any medium is strictly prohibited
6
+ *--------------------------------------------------------------------------
7
+ */
8
+ import { useState } from 'react';
9
+ import { makeStyles } from '@material-ui/core';
10
+ import { Modal } from '@postgres.ai/shared/components/Modal';
11
+ import { SimpleModalControls } from '@postgres.ai/shared/components/SimpleModalControls';
12
+ import { ImportantText } from '@postgres.ai/shared/components/ImportantText';
13
+ import { Text } from '@postgres.ai/shared/components/Text';
14
+ const useStyles = makeStyles({
15
+ errorMessage: {
16
+ color: 'red',
17
+ marginTop: '10px',
18
+ },
19
+ }, { index: 1 });
20
+ export const DeleteBranchModal = ({ isOpen, onClose, deleteBranch, branchName, instanceId, afterSubmitClick, }) => {
21
+ const classes = useStyles();
22
+ const [deleteError, setDeleteError] = useState(null);
23
+ const handleDelete = async () => {
24
+ var _a;
25
+ const deleteRes = await deleteBranch(branchName, instanceId);
26
+ if (deleteRes === null || deleteRes === void 0 ? void 0 : deleteRes.error) {
27
+ setDeleteError((_a = deleteRes.error) === null || _a === void 0 ? void 0 : _a.message);
28
+ }
29
+ else {
30
+ afterSubmitClick();
31
+ }
32
+ };
33
+ const handleClose = () => {
34
+ setDeleteError(null);
35
+ onClose();
36
+ };
37
+ return (_jsxs(Modal, { title: "Confirmation", onClose: handleClose, isOpen: isOpen, size: "xs", children: [_jsxs(Text, { children: ["Are you sure you want to delete branch", ' ', _jsx(ImportantText, { children: branchName }), "? This action cannot be undone."] }), deleteError && _jsx("p", { className: classes.errorMessage, children: deleteError }), _jsx(SimpleModalControls, { items: [
38
+ {
39
+ text: 'Cancel',
40
+ onClick: handleClose,
41
+ },
42
+ {
43
+ text: 'Delete branch',
44
+ variant: 'primary',
45
+ onClick: handleDelete,
46
+ isDisabled: branchName === 'main',
47
+ },
48
+ ] })] }));
49
+ };
@@ -0,0 +1,4 @@
1
+ export interface ModalProps {
2
+ isOpen: boolean;
3
+ onClose: () => void;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface BranchesProps {
3
+ instanceId: string;
4
+ }
5
+ export declare const Branches: React.FC<BranchesProps>;
6
+ export {};
@@ -0,0 +1,60 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*--------------------------------------------------------------------------
3
+ * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai
4
+ * All Rights Reserved. Proprietary and confidential.
5
+ * Unauthorized copying of this file, via any medium is strictly prohibited
6
+ *--------------------------------------------------------------------------
7
+ */
8
+ import { observer } from 'mobx-react-lite';
9
+ import { useHistory } from 'react-router';
10
+ import { makeStyles } from '@material-ui/core';
11
+ import { useEffect, useState } from 'react';
12
+ import { useStores, useHost } from '@postgres.ai/shared/pages/Instance/context';
13
+ import { Button } from '@postgres.ai/shared/components/Button2';
14
+ import { Spinner } from '@postgres.ai/shared/components/Spinner';
15
+ import { ErrorStub } from '@postgres.ai/shared/components/ErrorStub';
16
+ import { BranchesTable } from '@postgres.ai/shared/pages/Branches/components/BranchesTable';
17
+ import { SectionTitle } from '@postgres.ai/shared/components/SectionTitle';
18
+ import { Tooltip } from '@postgres.ai/shared/components/Tooltip';
19
+ import { InfoIcon } from '@postgres.ai/shared/icons/Info';
20
+ const useStyles = makeStyles({
21
+ container: {
22
+ marginTop: '16px',
23
+ },
24
+ infoIcon: {
25
+ height: '12px',
26
+ width: '12px',
27
+ marginLeft: '8px',
28
+ color: '#808080',
29
+ },
30
+ spinner: {
31
+ position: 'absolute',
32
+ right: '50%',
33
+ transform: 'translate(-50%, -50%)',
34
+ },
35
+ }, { index: 1 });
36
+ export const Branches = observer(({ instanceId }) => {
37
+ const host = useHost();
38
+ const stores = useStores();
39
+ const classes = useStyles();
40
+ const history = useHistory();
41
+ const [branches, setBranches] = useState([]);
42
+ const [isLoading, setIsLoading] = useState(true);
43
+ const { instance, getBranches, isBranchesLoading, getBranchesError, deleteBranch, } = stores.main;
44
+ const goToBranchAddPage = () => history.push(host.routes.createBranch());
45
+ const loadBranches = () => {
46
+ getBranches(instanceId)
47
+ .then((response) => {
48
+ response && setBranches(response);
49
+ })
50
+ .finally(() => setIsLoading(false));
51
+ };
52
+ useEffect(() => {
53
+ loadBranches();
54
+ }, []);
55
+ if (!instance && !isBranchesLoading)
56
+ return _jsx(_Fragment, {});
57
+ if (getBranchesError)
58
+ return (_jsx(ErrorStub, { title: getBranchesError === null || getBranchesError === void 0 ? void 0 : getBranchesError.title, message: getBranchesError === null || getBranchesError === void 0 ? void 0 : getBranchesError.message }));
59
+ return (_jsx("div", { className: classes.container, children: isBranchesLoading || isLoading ? (_jsx(Spinner, { size: "lg", className: classes.spinner })) : (_jsxs(_Fragment, { children: [_jsx(SectionTitle, { level: 2, tag: "h2", text: `Branches (${(branches === null || branches === void 0 ? void 0 : branches.length) || 0})`, rightContent: _jsxs(_Fragment, { children: [_jsx(Button, { theme: "primary", isDisabled: !branches.length, onClick: goToBranchAddPage, children: "Create branch" }), !branches.length && (_jsx(Tooltip, { content: "No existing branch", children: _jsx("div", { style: { display: 'flex' }, children: _jsx(InfoIcon, { className: classes.infoIcon }) }) }))] }) }), _jsx(BranchesTable, { branches: branches, branchesRoute: host.routes.branches(), reloadBranches: loadBranches, deleteBranch: deleteBranch, emptyTableText: "This instance has no active branches." })] })) }));
60
+ });
@@ -5,6 +5,8 @@ export declare type Host = {
5
5
  cloneId: string;
6
6
  routes: {
7
7
  instance: () => string;
8
+ snapshot: (snapshotId: string) => string;
9
+ createSnapshot: (cloneId: string) => string;
8
10
  };
9
11
  api: Api;
10
12
  elements: {
@@ -1,6 +1,8 @@
1
1
  /// <reference types="react" />
2
2
  import { Host } from './context';
3
- declare type Props = Host;
3
+ declare type Props = Host & {
4
+ isPlatform?: boolean;
5
+ };
4
6
  export declare const Clone: ((props: Props) => JSX.Element) & {
5
7
  displayName: string;
6
8
  };
@@ -6,10 +6,10 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
6
6
  *--------------------------------------------------------------------------
7
7
  */
8
8
  import { useEffect, useState } from 'react';
9
- import copyToClipboard from 'copy-to-clipboard';
10
9
  import { observer } from 'mobx-react-lite';
11
10
  import { useHistory } from 'react-router-dom';
12
- import { makeStyles, TextField, Button, FormControlLabel, Checkbox, IconButton, } from '@material-ui/core';
11
+ import copyToClipboard from 'copy-to-clipboard';
12
+ import { makeStyles, Button, FormControlLabel, Checkbox, TextField, IconButton, } from '@material-ui/core';
13
13
  import { getSshPortForwardingCommand, getPsqlConnectionStr, getJdbcConnectionStr, } from '@postgres.ai/shared/utils/connection';
14
14
  import { formatBytesIEC } from '@postgres.ai/shared/utils/units';
15
15
  import { ErrorStub } from '@postgres.ai/shared/components/ErrorStub';
@@ -23,28 +23,55 @@ import { Tooltip } from '@postgres.ai/shared/components/Tooltip';
23
23
  import { SectionTitle } from '@postgres.ai/shared/components/SectionTitle';
24
24
  import { icons } from '@postgres.ai/shared/styles/icons';
25
25
  import { styles } from '@postgres.ai/shared/styles/styles';
26
+ import { SyntaxHighlight } from '@postgres.ai/shared/components/SyntaxHighlight';
26
27
  import { Status } from './Status';
27
28
  import { useCreatedStores } from './useCreatedStores';
28
- const textFieldWidth = 400;
29
+ import { getCliDestroyCloneCommand, getCliProtectedCloneCommand, getCliResetCloneCommand, getCreateSnapshotCommand, } from './utils';
30
+ import { InstanceTabs, TABS_INDEX } from "../Instance/Tabs";
31
+ const textFieldWidth = 525;
29
32
  const useStyles = makeStyles((theme) => ({
33
+ wrapper: {
34
+ display: 'flex',
35
+ gap: '40px',
36
+ maxWidth: '1200px',
37
+ fontSize: '14px !important',
38
+ marginTop: '20px',
39
+ '@media (max-width: 1300px)': {
40
+ flexDirection: 'column',
41
+ gap: '20px',
42
+ },
43
+ },
30
44
  title: {
31
- marginTop: '16px',
45
+ marginTop: '8px',
46
+ lineHeight: '26px'
47
+ },
48
+ tooltip: {
49
+ marginTop: '8px',
32
50
  },
33
51
  container: {
34
- maxWidth: '425px',
52
+ maxWidth: textFieldWidth + 25,
35
53
  marginTop: '16px',
36
54
  },
37
55
  text: {
38
56
  marginTop: '4px',
39
57
  },
40
58
  errorStub: {
41
- marginTop: '24px',
59
+ margin: '24px 0',
42
60
  },
43
61
  spinner: {
44
62
  marginLeft: '8px',
45
63
  },
46
64
  summary: {
47
- marginTop: 20,
65
+ flex: '1.5 1 0',
66
+ minWidth: 0,
67
+ },
68
+ snippetContainer: {
69
+ flex: '1 1 0',
70
+ minWidth: 0,
71
+ boxShadow: 'rgba(0, 0, 0, 0.1) 0px 4px 12px',
72
+ padding: '10px 20px 10px 20px',
73
+ height: 'max-content',
74
+ borderRadius: '4px',
48
75
  },
49
76
  paramTitle: {
50
77
  display: 'inline-block',
@@ -71,10 +98,12 @@ const useStyles = makeStyles((theme) => ({
71
98
  },
72
99
  actions: {
73
100
  display: 'flex',
74
- marginRight: '-16px',
101
+ flexWrap: 'wrap',
102
+ rowGap: '16px',
103
+ marginBottom: '20px',
75
104
  },
76
105
  actionButton: {
77
- marginRight: '16px',
106
+ marginRight: '10px',
78
107
  },
79
108
  remark: {
80
109
  fontSize: '12px',
@@ -120,6 +149,9 @@ const useStyles = makeStyles((theme) => ({
120
149
  maxWidth: textFieldWidth,
121
150
  width: '100%',
122
151
  },
152
+ status: {
153
+ maxWidth: `${textFieldWidth}px`,
154
+ },
123
155
  copyButton: {
124
156
  position: 'absolute',
125
157
  top: 16,
@@ -129,12 +161,9 @@ const useStyles = makeStyles((theme) => ({
129
161
  height: 32,
130
162
  padding: 8,
131
163
  },
132
- status: {
133
- maxWidth: `${textFieldWidth}px`,
134
- },
135
164
  }), { index: 1 });
136
165
  export const Clone = observer((props) => {
137
- var _a, _b, _c;
166
+ var _a, _b, _c, _d, _e, _f, _g;
138
167
  const classes = useStyles();
139
168
  const history = useHistory();
140
169
  const stores = useCreatedStores(props);
@@ -146,14 +175,12 @@ export const Clone = observer((props) => {
146
175
  useEffect(() => {
147
176
  stores.main.load(props.instanceId, props.cloneId);
148
177
  }, []);
149
- const { instance, clone, isResettingClone, isDestroyingClone, isReloading, isUpdatingClone, isCloneStable, } = stores.main;
150
- const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0;}' }), props.elements.breadcrumbs, _jsx(SectionTitle, { className: classes.title, tag: "h1", level: 1, text: `Clone ${props.cloneId}` })] }));
151
- // Getting instance error.
152
- if (stores.main.instanceError)
153
- return (_jsxs(_Fragment, { children: [headRendered, _jsx(ErrorStub, { title: stores.main.instanceError.title, message: stores.main.instanceError.message })] }));
154
- // Getting clone error.
155
- if (stores.main.cloneError)
156
- return (_jsxs(_Fragment, { children: [headRendered, _jsx(ErrorStub, { ...stores.main.cloneError })] }));
178
+ const { instance, snapshots, clone, isResettingClone, isDestroyingClone, isReloading, isUpdatingClone, isCloneStable, } = stores.main;
179
+ const headRendered = (_jsxs(_Fragment, { children: [_jsx("style", { children: 'p { margin: 0;}' }), props.elements.breadcrumbs, _jsx(SectionTitle, { className: classes.title, tag: "h1", level: 1, text: `Clone ${props.cloneId}`, children: _jsx(InstanceTabs, { tab: TABS_INDEX.CLONES, isPlatform: props.isPlatform, instanceId: props.instanceId, hasLogs: props.api.initWS !== undefined }) })] }));
180
+ const cloneErrorMessage = ((_a = stores.main.instanceError) === null || _a === void 0 ? void 0 : _a.message) || ((_b = stores.main.cloneError) === null || _b === void 0 ? void 0 : _b.message);
181
+ const cloneErrorTitle = ((_c = stores.main.instanceError) === null || _c === void 0 ? void 0 : _c.title) || ((_d = stores.main.cloneError) === null || _d === void 0 ? void 0 : _d.title);
182
+ if (cloneErrorMessage && cloneErrorTitle)
183
+ return (_jsxs(_Fragment, { children: [headRendered, _jsx(ErrorStub, { title: cloneErrorTitle, message: cloneErrorMessage })] }));
157
184
  // Initial getting data spinner.
158
185
  if (!instance || !clone) {
159
186
  return (_jsxs(_Fragment, { children: [headRendered, _jsx(PageSpinner, {})] }));
@@ -175,6 +202,9 @@ export const Clone = observer((props) => {
175
202
  if (isSuccess)
176
203
  history.push(props.routes.instance());
177
204
  };
205
+ const createSnapshot = async () => {
206
+ history.push(props.routes.createSnapshot(props.cloneId));
207
+ };
178
208
  // Clone reload.
179
209
  const reloadClone = () => stores.main.reload();
180
210
  // Data protection.
@@ -190,7 +220,8 @@ export const Clone = observer((props) => {
190
220
  isReloading ||
191
221
  isUpdatingClone ||
192
222
  !isCloneStable;
193
- return (_jsxs(_Fragment, { children: [headRendered, _jsxs("div", { className: classes.container, children: [_jsxs("div", { className: classes.actions, children: [_jsxs(Button, { variant: "contained", color: "primary", onClick: requestResetClone, disabled: isDisabledControls, title: 'Reset clone', className: classes.actionButton, children: ["Reset clone", isResettingClone && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "contained", color: "primary", onClick: requestDestroyClone, disabled: isDisabledControls, title: 'Destroy this clone', className: classes.actionButton, children: ["Destroy clone", isDestroyingClone && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "outlined", color: "secondary", onClick: reloadClone, disabled: isDisabledControls || isReloading, title: 'Refresh clone information', className: classes.actionButton, children: ["Reload info", isReloading && _jsx(Spinner, { size: "sm", className: classes.spinner })] })] }), stores.main.resetCloneError && (_jsx(ErrorStub, { title: "Resetting error", message: stores.main.resetCloneError, className: classes.errorStub })), stores.main.destroyCloneError && (_jsx(ErrorStub, { title: "Destroying error", message: stores.main.destroyCloneError, className: classes.errorStub })), _jsxs("div", { className: classes.summary, children: [_jsxs("div", { children: [_jsx("p", { children: _jsx("strong", { children: "Created" }) }), _jsx("p", { className: classes.text, children: clone.createdAt })] }), _jsx("br", {}), _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: "Data state at" }), "\u00A0", _jsx(Tooltip, { content: _jsxs(_Fragment, { children: [_jsx("strong", { children: "Data state time" }), " is a time at which data is\u00A0 recovered for this clone."] }), children: icons.infoIcon })] }), _jsx("p", { className: classes.text, children: (_a = clone.snapshot) === null || _a === void 0 ? void 0 : _a.dataStateAt })] }), _jsx("br", {}), _jsxs("div", { children: [_jsx("p", { children: _jsx("strong", { children: "Status" }) }), _jsx(Status, { rawClone: clone, className: classes.status })] }), _jsx("br", {}), _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: "Summary" }), "\u00A0", _jsx(Tooltip, { content: _jsxs(_Fragment, { children: [_jsx("strong", { children: "Logical data size" }), " is a logical size of files in PGDATA which is being thin-cloned.", _jsx("br", {}), _jsx("br", {}), _jsx("strong", { children: "Physical data diff size" }), " is an actual size of a\u00A0 thin clone. On creation there is no diff between clone\u2019s\u00A0 and initial data state, all data blocks match. During work\u00A0 with the clone data diverges and data diff size increases.", _jsx("br", {}), _jsx("br", {}), _jsx("strong", { children: "Clone creation time" }), " is time which was\u00A0 spent to provision the clone."] }), children: icons.infoIcon })] }), _jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Logical data size:" }), ((_b = instance.state) === null || _b === void 0 ? void 0 : _b.dataSize)
223
+ return (_jsxs(_Fragment, { children: [headRendered, _jsxs("div", { className: classes.wrapper, children: [_jsxs("div", { className: classes.summary, children: [_jsxs("div", { className: classes.actions, children: [_jsxs(Button, { variant: "contained", color: "primary", onClick: requestResetClone, disabled: isDisabledControls, title: 'Reset clone', className: classes.actionButton, children: ["Reset clone", isResettingClone && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "contained", color: "primary", onClick: requestDestroyClone, disabled: isDisabledControls, title: 'Delete this clone', className: classes.actionButton, children: ["Delete clone", isDestroyingClone && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "contained", color: "primary", onClick: createSnapshot, disabled: isDisabledControls, title: 'Delete this clone', className: classes.actionButton, children: ["Create snapshot", snapshots.snapshotDataLoading && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), _jsxs(Button, { variant: "outlined", color: "secondary", onClick: reloadClone, disabled: isDisabledControls, title: 'Refresh clone information', className: classes.actionButton, children: ["Reload info", isReloading && _jsx(Spinner, { size: "sm", className: classes.spinner })] })] }), stores.main.destroyCloneError ||
224
+ (stores.main.resetCloneError && (_jsx(ErrorStub, { title: 'Resetting error', message: stores.main.resetCloneError, className: classes.errorStub }))), _jsxs("div", { children: [_jsx("p", { children: _jsx("strong", { children: "Branch" }) }), _jsx("p", { className: classes.text, children: clone.branch })] }), _jsxs("div", { className: classes.title, children: [_jsx("p", { children: _jsx("strong", { children: "Created" }) }), _jsx("p", { className: classes.text, children: clone.createdAt })] }), _jsx("br", {}), _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: "Data state at" }), "\u00A0", _jsx(Tooltip, { content: _jsxs(_Fragment, { children: [_jsx("strong", { children: "Data state time" }), " is a time at which data is\u00A0 recovered for this clone."] }), children: icons.infoIcon })] }), _jsx("p", { className: classes.text, children: (_e = clone.snapshot) === null || _e === void 0 ? void 0 : _e.dataStateAt })] }), _jsx("br", {}), _jsxs("div", { children: [_jsx("p", { children: _jsx("strong", { children: "Status" }) }), _jsx(Status, { rawClone: clone, className: classes.status })] }), _jsx("br", {}), _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: "Summary" }), "\u00A0", _jsx(Tooltip, { content: _jsxs(_Fragment, { children: [_jsx("strong", { children: "Logical data size" }), " is a logical size of files in PGDATA which is being thin-cloned.", _jsx("br", {}), _jsx("br", {}), _jsx("strong", { children: "Physical data diff size" }), " is an actual size of a\u00A0 thin clone. On creation there is no diff between clone\u2019s\u00A0 and initial data state, all data blocks match. During work\u00A0 with the clone data diverges and data diff size increases.", _jsx("br", {}), _jsx("br", {}), _jsx("strong", { children: "Clone creation time" }), " is time which was\u00A0 spent to provision the clone."] }), children: icons.infoIcon })] }), _jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Logical data size:" }), ((_f = instance.state) === null || _f === void 0 ? void 0 : _f.dataSize)
194
225
  ? formatBytesIEC(instance.state.dataSize)
195
226
  : '-'] }), _jsxs("p", { className: classes.text, children: [_jsx("span", { className: classes.paramTitle, children: "Physical data diff size:" }), clone.metadata.cloneDiffSize
196
227
  ? formatBytesIEC(clone.metadata.cloneDiffSize)
@@ -217,5 +248,5 @@ export const Clone = observer((props) => {
217
248
  style: styles.inputFieldLabel,
218
249
  }, FormHelperTextProps: {
219
250
  style: styles.inputFieldHelper,
220
- } }), _jsx(IconButton, { className: classes.copyButton, "aria-label": "Copy", onClick: () => copyToClipboard(jdbcConnectionStr), children: icons.copyIcon })] }), "\u00A0", _jsx(Tooltip, { content: _jsx(_Fragment, { children: "Used to connect to Postgres using JDBC. Change DBNAME to\u00A0 name of the database you want to connect, change DBPASSWORD\u00A0 to the password you\u2019ve used on clone creation." }), children: _jsx("span", { className: classes.textFieldInfo, children: icons.infoIcon }) })] }))] })), _jsx("br", {}), _jsx("div", { className: classes.fieldBlock, children: _jsxs("span", { className: classes.remark, children: ["Password was set during clone creation. It\u2019s not being stored.", _jsx("br", {}), "You would need to recreate a clone if the password is lost."] }) }), _jsx("br", {}), _jsx("p", { children: _jsx("strong", { children: "Protection" }) }), _jsxs("p", { children: [_jsx(FormControlLabel, { className: classes.checkboxLabel, control: _jsx(Checkbox, { checked: clone.protected, onChange: toggleDataProtection, name: "protected", disabled: isDisabledControls }), label: "Enable deletion protection" }), _jsx("br", {}), _jsxs("span", { className: classes.remark, children: ["When enabled no one can delete this clone and automated deletion is also disabled.", _jsx("br", {}), "Please be careful: abandoned clones with this checkbox enabled may cause out-of-disk-space events. Check disk space on daily basis and delete this clone once the work is done."] })] }), stores.main.updateCloneError && (_jsx(ErrorStub, { title: "Updating error", message: stores.main.updateCloneError, className: classes.errorStub }))] }), _jsxs(_Fragment, { children: [_jsx(DestroyCloneRestrictionModal, { isOpen: isOpenRestrictionModal, onClose: () => setIsOpenRestrictionModal(false), cloneId: clone.id }), _jsx(DestroyCloneModal, { isOpen: isOpenDestroyModal, onClose: () => setIsOpenDestroyModal(false), cloneId: clone.id, onDestroyClone: destroyClone }), _jsx(ResetCloneModal, { isOpen: isOpenResetModal, onClose: () => setIsOpenResetModal(false), clone: clone, snapshots: stores.main.snapshots.data, onResetClone: resetClone, version: (_c = instance.state) === null || _c === void 0 ? void 0 : _c.engine.version })] })] })] }));
251
+ } }), _jsx(IconButton, { className: classes.copyButton, "aria-label": "Copy", onClick: () => copyToClipboard(jdbcConnectionStr), children: icons.copyIcon })] }), "\u00A0", _jsx(Tooltip, { content: _jsx(_Fragment, { children: "Used to connect to Postgres using JDBC. Change DBNAME to\u00A0 name of the database you want to connect, change DBPASSWORD\u00A0 to the password you\u2019ve used on clone creation." }), children: _jsx("span", { className: classes.textFieldInfo, children: icons.infoIcon }) })] }))] })), _jsx("br", {}), _jsx("div", { className: classes.fieldBlock, children: _jsxs("span", { className: classes.remark, children: ["Password was set during clone creation. It\u2019s not being stored.", _jsx("br", {}), "You would need to recreate a clone if the password is lost."] }) }), _jsx("br", {}), _jsx("p", { children: _jsx("strong", { children: "Protection" }) }), _jsxs("p", { children: [_jsx(FormControlLabel, { className: classes.checkboxLabel, control: _jsx(Checkbox, { checked: clone.protected, onChange: toggleDataProtection, name: "protected", disabled: isDisabledControls }), label: "Enable deletion protection" }), _jsx("br", {}), _jsxs("span", { className: classes.remark, children: ["When enabled, no one can delete this clone and automated deletion is also disabled.", _jsx("br", {}), "Please be careful: abandoned clones with this checkbox enabled may cause out-of-disk-space events. Check disk space on daily basis and delete this clone once the work is done."] })] }), stores.main.updateCloneError && (_jsx(ErrorStub, { title: "Updating error", message: stores.main.updateCloneError, className: classes.errorStub }))] }), _jsxs("div", { className: classes.snippetContainer, children: [_jsx(SectionTitle, { tag: "h2", level: 2, text: 'Reset clone using CLI' }), _jsx("p", { className: classes.tooltip, children: "You can reset the clone using CLI using the following command:" }), _jsx(SyntaxHighlight, { content: getCliResetCloneCommand(props.cloneId) }), _jsx(SectionTitle, { className: classes.title, tag: "h2", level: 2, text: 'Delete clone using CLI' }), _jsx("p", { className: classes.tooltip, children: "You can delete the clone using CLI using the following command:" }), _jsx(SyntaxHighlight, { content: getCliDestroyCloneCommand(props.cloneId) }), _jsx(SectionTitle, { className: classes.title, tag: "h2", level: 2, text: 'Toggle deletion protection using CLI' }), _jsx("p", { className: classes.tooltip, children: "You can toggle deletion protection using CLI for this clone using the following command:" }), _jsx(SyntaxHighlight, { content: getCliProtectedCloneCommand(true) }), _jsx(SyntaxHighlight, { content: getCliProtectedCloneCommand(false) }), _jsx(SectionTitle, { className: classes.title, tag: "h2", level: 2, text: 'Create snapshot for this clone using CLI' }), _jsx("p", { className: classes.tooltip, children: "You can create a snapshot for this clone using CLI using the following command:" }), _jsx(SyntaxHighlight, { content: getCreateSnapshotCommand(props.cloneId) })] }), _jsxs(_Fragment, { children: [_jsx(DestroyCloneRestrictionModal, { isOpen: isOpenRestrictionModal, onClose: () => setIsOpenRestrictionModal(false), cloneId: clone.id }), _jsx(DestroyCloneModal, { isOpen: isOpenDestroyModal, onClose: () => setIsOpenDestroyModal(false), cloneId: clone.id, onDestroyClone: destroyClone }), _jsx(ResetCloneModal, { isOpen: isOpenResetModal, onClose: () => setIsOpenResetModal(false), clone: clone, snapshots: snapshots.data, onResetClone: resetClone, version: (_g = instance.state) === null || _g === void 0 ? void 0 : _g.engine.version })] })] })] }));
221
252
  });
@@ -6,12 +6,14 @@ import { UpdateClone } from '@postgres.ai/shared/types/api/endpoints/updateClone
6
6
  import { SnapshotsStore, SnapshotsApi } from '@postgres.ai/shared/stores/Snapshots';
7
7
  import { Clone } from '@postgres.ai/shared/types/api/entities/clone';
8
8
  import { Instance } from '@postgres.ai/shared/types/api/entities/instance';
9
+ import { InitWS } from '@postgres.ai/shared/types/api/endpoints/initWS';
9
10
  export declare type Api = SnapshotsApi & {
10
11
  getInstance: GetInstance;
11
12
  getClone: GetClone;
12
13
  resetClone: ResetClone;
13
14
  destroyClone: DestroyClone;
14
15
  updateClone: UpdateClone;
16
+ initWS: InitWS;
15
17
  };
16
18
  declare type Error = {
17
19
  title?: string;
@@ -34,8 +36,8 @@ export declare class MainStore {
34
36
  private readonly api;
35
37
  constructor(api: Api);
36
38
  get isCloneStable(): boolean;
37
- load: (instanceId: string, cloneId: string) => Promise<boolean>;
38
- reload: () => Promise<boolean>;
39
+ load: (instanceId: string, cloneId: string) => Promise<boolean | undefined>;
40
+ reload: () => Promise<boolean | undefined>;
39
41
  private loadInstance;
40
42
  private loadClone;
41
43
  resetClone: (snapshotId: string) => Promise<boolean>;
@@ -0,0 +1,4 @@
1
+ export declare const getCliResetCloneCommand: (cloneId: string) => string;
2
+ export declare const getCliDestroyCloneCommand: (cloneId: string) => string;
3
+ export declare const getCliProtectedCloneCommand: (enabled: boolean) => string;
4
+ export declare const getCreateSnapshotCommand: (cloneId: string) => string;
@@ -0,0 +1,12 @@
1
+ export const getCliResetCloneCommand = (cloneId) => {
2
+ return `dblab clone reset ${cloneId ? cloneId : `<CLONE_ID>`}`;
3
+ };
4
+ export const getCliDestroyCloneCommand = (cloneId) => {
5
+ return `dblab clone destroy ${cloneId ? cloneId : `<CLONE_ID>`}`;
6
+ };
7
+ export const getCliProtectedCloneCommand = (enabled) => {
8
+ return `dblab clone update --protected ${enabled ? '' : 'false'}`;
9
+ };
10
+ export const getCreateSnapshotCommand = (cloneId) => {
11
+ return `dblab branch snapshot --clone-id ${cloneId}`;
12
+ };
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { MainStoreApi } from './stores/Main';
3
+ interface CreateBranchProps {
4
+ instanceId: string;
5
+ api: MainStoreApi;
6
+ routes: {
7
+ branch: (branchName: string) => string;
8
+ };
9
+ elements: {
10
+ breadcrumbs: React.ReactNode;
11
+ };
12
+ isPlatform?: boolean;
13
+ }
14
+ export declare const CreateBranchPage: (({ instanceId, api, elements, routes, isPlatform }: CreateBranchProps) => JSX.Element) & {
15
+ displayName: string;
16
+ };
17
+ export {};
@@ -0,0 +1,135 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /*--------------------------------------------------------------------------
3
+ * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai
4
+ * All Rights Reserved. Proprietary and confidential.
5
+ * Unauthorized copying of this file, via any medium is strictly prohibited
6
+ *--------------------------------------------------------------------------
7
+ */
8
+ import cn from 'classnames';
9
+ import { useHistory } from 'react-router';
10
+ import { observer } from 'mobx-react-lite';
11
+ import { useEffect, useState } from 'react';
12
+ import { TextField, makeStyles } from '@material-ui/core';
13
+ import { Button } from '@postgres.ai/shared/components/Button';
14
+ import { ResponseMessage } from '@postgres.ai/shared/pages/Instance/Configuration/ResponseMessage';
15
+ import { Select } from '@postgres.ai/shared/components/Select';
16
+ import { SectionTitle } from '@postgres.ai/shared/components/SectionTitle';
17
+ import { ErrorStub } from '@postgres.ai/shared/components/ErrorStub';
18
+ import { SyntaxHighlight } from '@postgres.ai/shared/components/SyntaxHighlight';
19
+ import { StubSpinner } from '@postgres.ai/shared/components/StubSpinnerFlex';
20
+ import { Spinner } from '@postgres.ai/shared/components/Spinner';
21
+ import { useForm } from './useForm';
22
+ import { useCreatedStores } from './useCreatedStores';
23
+ import { getCliBranchListCommand, getCliCreateBranchCommand } from './utils';
24
+ import { InstanceTabs, TABS_INDEX } from "../Instance/Tabs";
25
+ const useStyles = makeStyles({
26
+ wrapper: {
27
+ display: 'flex',
28
+ gap: '60px',
29
+ maxWidth: '1200px',
30
+ fontSize: '14px',
31
+ marginTop: '20px',
32
+ '@media (max-width: 1300px)': {
33
+ flexDirection: 'column',
34
+ gap: '20px',
35
+ },
36
+ },
37
+ container: {
38
+ maxWidth: '100%',
39
+ flex: '1 1 0',
40
+ minWidth: 0,
41
+ '& p,span': {
42
+ fontSize: 14,
43
+ },
44
+ },
45
+ snippetContainer: {
46
+ flex: '1 1 0',
47
+ minWidth: 0,
48
+ boxShadow: 'rgba(0, 0, 0, 0.1) 0px 4px 12px',
49
+ padding: '10px 20px 10px 20px',
50
+ height: 'max-content',
51
+ borderRadius: '4px',
52
+ },
53
+ marginBottom: {
54
+ marginBottom: '8px',
55
+ },
56
+ marginBottom2x: {
57
+ marginBottom: '16px',
58
+ },
59
+ marginTop: {
60
+ marginTop: '8px',
61
+ },
62
+ title: {
63
+ marginTop: '8px',
64
+ lineHeight: '26px'
65
+ },
66
+ form: {
67
+ marginTop: '16px',
68
+ },
69
+ spinner: {
70
+ marginLeft: '8px',
71
+ color: '#fff',
72
+ },
73
+ snapshotOverflow: {
74
+ width: '100%',
75
+ wordWrap: 'break-word',
76
+ whiteSpace: 'initial',
77
+ },
78
+ }, { index: 1 });
79
+ export const CreateBranchPage = observer(({ instanceId, api, elements, routes, isPlatform }) => {
80
+ const stores = useCreatedStores(api);
81
+ const classes = useStyles();
82
+ const history = useHistory();
83
+ const [branchSnapshots, setBranchSnapshots] = useState([]);
84
+ const { load, branchesList, getBranchesError, createBranch, createBranchError, isBranchesLoading, isCreatingBranch, getSnapshots, snapshotsError, } = stores.main;
85
+ const handleSubmit = async (values) => {
86
+ await createBranch({
87
+ ...values,
88
+ instanceId,
89
+ }).then((branch) => {
90
+ if (branch && (branch === null || branch === void 0 ? void 0 : branch.name)) {
91
+ history.push(routes.branch(branch.name));
92
+ }
93
+ });
94
+ };
95
+ const fetchSnapshots = async (branchName) => {
96
+ await getSnapshots(instanceId, branchName).then((response) => {
97
+ var _a;
98
+ if (response) {
99
+ setBranchSnapshots(response);
100
+ formik.setFieldValue('snapshotID', (_a = response[0]) === null || _a === void 0 ? void 0 : _a.id);
101
+ }
102
+ });
103
+ };
104
+ const handleParentBranchChange = async (e) => {
105
+ const branchName = e.target.value;
106
+ formik.setFieldValue('baseBranch', branchName);
107
+ await fetchSnapshots(branchName);
108
+ };
109
+ const [{ formik }] = useForm(handleSubmit);
110
+ useEffect(() => {
111
+ load(instanceId);
112
+ fetchSnapshots(formik.values.baseBranch);
113
+ }, [formik.values.baseBranch]);
114
+ if (isBranchesLoading) {
115
+ return _jsx(StubSpinner, {});
116
+ }
117
+ return (_jsxs(_Fragment, { children: [elements.breadcrumbs, _jsx(SectionTitle, { tag: "h1", level: 1, text: "Create branch", className: classes.title, children: _jsx(InstanceTabs, { tab: TABS_INDEX.BRANCHES, isPlatform: isPlatform, instanceId: instanceId, hasLogs: api.initWS !== undefined }) }), _jsxs("div", { className: classes.wrapper, children: [_jsxs("div", { className: classes.container, children: [(snapshotsError || getBranchesError) && (_jsx("div", { className: classes.marginTop, children: _jsx(ErrorStub, { message: (snapshotsError === null || snapshotsError === void 0 ? void 0 : snapshotsError.message) || (getBranchesError === null || getBranchesError === void 0 ? void 0 : getBranchesError.message) }) })), _jsxs("div", { className: classes.form, children: [_jsx(TextField, { label: "Branch name", variant: "outlined", required: true, fullWidth: true, size: "small", InputLabelProps: {
118
+ shrink: true,
119
+ }, value: formik.values.branchName, error: Boolean(formik.errors.branchName), className: classes.marginBottom, onChange: (e) => formik.setFieldValue('branchName', e.target.value) }), _jsx("p", { className: cn(classes.marginTop, classes.marginBottom), children: "Choose an existing branch. The new branch will initially point at the same snapshot as the parent branch but going further, their evolution paths will be independent - new snapshots can be created for both branches." }), _jsx(Select, { fullWidth: true, label: "Parent branch", value: formik.values.baseBranch, disabled: !branchesList || formik.isSubmitting, onChange: handleParentBranchChange, error: Boolean(formik.errors.baseBranch), items: branchesList
120
+ ? branchesList.map((branch) => {
121
+ return {
122
+ value: branch.name,
123
+ children: branch.name,
124
+ };
125
+ })
126
+ : [] }), _jsx("strong", { children: "Snapshot ID" }), _jsx("p", { className: cn(classes.marginTop, classes.marginBottom), children: "Choose an existing snapshot. This snapshot will be memorized as a forking point for the new branch; it cannot be deleted while the branch exists." }), _jsx(Select, { fullWidth: true, className: classes.marginBottom2x, label: "Snapshot ID", value: formik.values.snapshotID, disabled: !branchesList || formik.isSubmitting, onChange: (e) => formik.setFieldValue('snapshotID', e.target.value), error: Boolean(formik.errors.baseBranch), items: branchSnapshots
127
+ ? branchSnapshots.map((snapshot, i) => {
128
+ const isLatest = i === 0;
129
+ return {
130
+ value: snapshot.id,
131
+ children: (_jsxs("div", { children: [_jsxs("strong", { className: classes.snapshotOverflow, children: [snapshot === null || snapshot === void 0 ? void 0 : snapshot.id, " ", isLatest && _jsx("span", { children: "Latest" })] }), (snapshot === null || snapshot === void 0 ? void 0 : snapshot.dataStateAt) && (_jsxs("p", { children: ["Data state at: ", snapshot === null || snapshot === void 0 ? void 0 : snapshot.dataStateAt] }))] })),
132
+ };
133
+ })
134
+ : [] }), _jsxs(Button, { variant: "primary", size: "medium", className: classes.marginTop, onClick: formik.submitForm, children: ["Create branch", isCreatingBranch && (_jsx(Spinner, { size: "sm", className: classes.spinner }))] }), createBranchError && (_jsx(ResponseMessage, { type: 'error', message: createBranchError }))] })] }), ' ', _jsxs("div", { className: classes.snippetContainer, children: [_jsx(SectionTitle, { tag: "h1", level: 1, text: "The same using CLI" }), _jsx("p", { className: classes.marginTop, children: "Alternatively, you can create a new branch using CLI. Fill the form, copy the command below and paste it into your terminal." }), _jsx(SyntaxHighlight, { content: getCliCreateBranchCommand(formik.values.branchName, formik.values.baseBranch) }), _jsx(SectionTitle, { className: classes.marginTop, tag: "h2", level: 2, text: 'Get branches using CLI' }), _jsx("p", { className: classes.marginTop, children: "You can get a list of all branches using CLI. Copy the command below and paste it into your terminal." }), _jsx(SyntaxHighlight, { content: getCliBranchListCommand() })] })] })] }));
135
+ });
@@ -0,0 +1,43 @@
1
+ import { GetBranches } from '@postgres.ai/shared/types/api/endpoints/getBranches';
2
+ import { CreateBranch, CreateBranchFormValues } from '@postgres.ai/shared/types/api/endpoints/createBranch';
3
+ import { Branch } from '@postgres.ai/shared/types/api/endpoints/getBranches';
4
+ import { GetSnapshots } from '@postgres.ai/shared/types/api/endpoints/getSnapshots';
5
+ import { InitWS } from '@postgres.ai/shared/types/api/endpoints/initWS';
6
+ declare type Error = {
7
+ title?: string;
8
+ message: string;
9
+ };
10
+ export declare type MainStoreApi = {
11
+ getBranches: GetBranches;
12
+ createBranch: CreateBranch;
13
+ getSnapshots: GetSnapshots;
14
+ initWS: InitWS;
15
+ };
16
+ export declare class MainStore {
17
+ snapshotsError: Error | null;
18
+ getBranchesError: Error | null;
19
+ createBranchError: string | null;
20
+ isBranchesLoading: boolean;
21
+ isCreatingBranch: boolean;
22
+ branchesList: Branch[];
23
+ private readonly api;
24
+ constructor(api: MainStoreApi);
25
+ load: (instanceId: string) => Promise<void>;
26
+ createBranch: (values: CreateBranchFormValues) => Promise<import("../../../types/api/entities/createBranch").CreateBranchDTO | null | undefined>;
27
+ getBranches: (instanceId: string) => Promise<Branch[] | null | undefined>;
28
+ getSnapshots: (instanceId: string, branchName?: string) => Promise<{
29
+ createdAtDate: Date;
30
+ dataStateAtDate: Date;
31
+ numClones: string | number;
32
+ clones: string[];
33
+ createdAt: string;
34
+ dataStateAt: string;
35
+ id: string;
36
+ pool: string;
37
+ physicalSize: number;
38
+ logicalSize: number;
39
+ message: string;
40
+ branch: string;
41
+ }[] | null | undefined>;
42
+ }
43
+ export {};