@postgres.ai/shared 3.5.1-pr-1027.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.
Files changed (202) hide show
  1. package/.gitlab-ci.yml +60 -0
  2. package/components/AlertSnackbar/index.tsx +23 -0
  3. package/components/AlertSnackbar/useAlertSnackbar.tsx +65 -0
  4. package/components/Button/index.tsx +79 -0
  5. package/components/Button2/index.tsx +43 -0
  6. package/components/Button2/styles.module.scss +82 -0
  7. package/components/DestroyCloneModal/index.tsx +56 -0
  8. package/components/DestroyCloneRestrictionModal/index.tsx +50 -0
  9. package/components/ErrorStub/index.tsx +83 -0
  10. package/components/FormattedText/index.tsx +44 -0
  11. package/components/FormattedText/styles.module.scss +34 -0
  12. package/components/GatewayLink/index.tsx +33 -0
  13. package/components/HorizontalScrollContainer/index.tsx +131 -0
  14. package/components/HorizontalScrollContainer/types.ts +12 -0
  15. package/components/HorizontalScrollContainer/utils.ts +16 -0
  16. package/components/ImportantText/index.tsx +29 -0
  17. package/components/Link2/index.tsx +31 -0
  18. package/components/Link2/styles.module.scss +12 -0
  19. package/components/MenuButton/index.tsx +80 -0
  20. package/components/MenuButton/styles.module.scss +42 -0
  21. package/components/Modal/index.tsx +93 -0
  22. package/components/PageSpinner/index.tsx +18 -0
  23. package/components/PageSpinner/styles.module.scss +13 -0
  24. package/components/ResetCloneModal/index.tsx +154 -0
  25. package/components/SectionTitle/index.tsx +74 -0
  26. package/components/Select/index.tsx +42 -0
  27. package/components/SimpleModalControls/index.tsx +56 -0
  28. package/components/Spinner/icon.tsx +29 -0
  29. package/components/Spinner/index.tsx +16 -0
  30. package/components/Spinner/styles.module.scss +33 -0
  31. package/components/Status/index.tsx +61 -0
  32. package/components/Status/styles.module.scss +45 -0
  33. package/components/StubContainer/index.tsx +41 -0
  34. package/components/StubSpinner/index.tsx +49 -0
  35. package/components/StubSpinnerFlex/index.tsx +20 -0
  36. package/components/StubSpinnerFlex/styles.module.scss +20 -0
  37. package/components/SyntaxHighlight/index.tsx +107 -0
  38. package/components/Table/RowMenu/index.tsx +111 -0
  39. package/components/Table/index.tsx +140 -0
  40. package/components/Text/index.tsx +28 -0
  41. package/components/TextField/index.tsx +117 -0
  42. package/components/Tooltip/index.tsx +52 -0
  43. package/config/index.ts +32 -0
  44. package/config/links.ts +6 -0
  45. package/craco.config.js +80 -0
  46. package/helpers/getEntropy.ts +232 -0
  47. package/helpers/localStorage.ts +15 -0
  48. package/helpers/request.ts +47 -0
  49. package/hooks/useWindowDimensions.ts +16 -0
  50. package/icons/ArrowDropDown/index.tsx +29 -0
  51. package/icons/Circle/index.tsx +27 -0
  52. package/icons/External/index.tsx +14 -0
  53. package/icons/Info/index.tsx +12 -0
  54. package/icons/Renewable/index.tsx +65 -0
  55. package/icons/Shield/index.tsx +33 -0
  56. package/icons/Warning/index.tsx +29 -0
  57. package/meta.json +1 -0
  58. package/package.json +55 -0
  59. package/pages/Clone/Status/index.tsx +73 -0
  60. package/pages/Clone/context.ts +22 -0
  61. package/pages/Clone/index.tsx +634 -0
  62. package/pages/Clone/stores/Main.ts +206 -0
  63. package/pages/Clone/useCreatedStores.ts +11 -0
  64. package/pages/Configuration/Header/index.tsx +84 -0
  65. package/pages/Configuration/InputWithTooltip/index.tsx +240 -0
  66. package/pages/Configuration/ResponseMessage/index.tsx +71 -0
  67. package/pages/Configuration/configOptions.ts +60 -0
  68. package/pages/Configuration/index.tsx +1184 -0
  69. package/pages/Configuration/styles.module.scss +122 -0
  70. package/pages/Configuration/tooltipText.tsx +157 -0
  71. package/pages/Configuration/useForm.ts +108 -0
  72. package/pages/Configuration/utils/index.ts +153 -0
  73. package/pages/CreateClone/index.tsx +311 -0
  74. package/pages/CreateClone/stores/Main.ts +107 -0
  75. package/pages/CreateClone/styles.module.scss +71 -0
  76. package/pages/CreateClone/useCreatedStores.ts +11 -0
  77. package/pages/CreateClone/useForm.ts +36 -0
  78. package/pages/Instance/Clones/Header/Item/index.tsx +15 -0
  79. package/pages/Instance/Clones/Header/Item/styles.module.scss +17 -0
  80. package/pages/Instance/Clones/Header/index.tsx +74 -0
  81. package/pages/Instance/Clones/Header/styles.module.scss +11 -0
  82. package/pages/Instance/Clones/index.tsx +135 -0
  83. package/pages/Instance/ClonesModal/index.tsx +71 -0
  84. package/pages/Instance/ClonesModal/utils.ts +21 -0
  85. package/pages/Instance/InactiveInstance/index.tsx +165 -0
  86. package/pages/Instance/InactiveInstance/utils.ts +9 -0
  87. package/pages/Instance/Info/Connection/ConnectModal/Content/index.tsx +176 -0
  88. package/pages/Instance/Info/Connection/ConnectModal/Content/utils.ts +24 -0
  89. package/pages/Instance/Info/Connection/ConnectModal/index.tsx +36 -0
  90. package/pages/Instance/Info/Connection/index.tsx +81 -0
  91. package/pages/Instance/Info/Details/index.tsx +20 -0
  92. package/pages/Instance/Info/Disks/Disk/ActionsMenu/index.tsx +100 -0
  93. package/pages/Instance/Info/Disks/Disk/Marker/index.tsx +26 -0
  94. package/pages/Instance/Info/Disks/Disk/ProgressBar/PointerIcon.tsx +20 -0
  95. package/pages/Instance/Info/Disks/Disk/ProgressBar/index.tsx +73 -0
  96. package/pages/Instance/Info/Disks/Disk/Status/index.tsx +75 -0
  97. package/pages/Instance/Info/Disks/Disk/index.tsx +168 -0
  98. package/pages/Instance/Info/Disks/index.tsx +65 -0
  99. package/pages/Instance/Info/Icons/index.tsx +39 -0
  100. package/pages/Instance/Info/Retrieval/RefreshFailedAlert/index.tsx +32 -0
  101. package/pages/Instance/Info/Retrieval/RefreshFailedAlert/styles.module.scss +33 -0
  102. package/pages/Instance/Info/Retrieval/RetrievalModal/index.tsx +49 -0
  103. package/pages/Instance/Info/Retrieval/RetrievalModal/styles.module.scss +6 -0
  104. package/pages/Instance/Info/Retrieval/RetrievalTable/index.tsx +53 -0
  105. package/pages/Instance/Info/Retrieval/RetrievalTable/styles.module.scss +29 -0
  106. package/pages/Instance/Info/Retrieval/index.tsx +95 -0
  107. package/pages/Instance/Info/Retrieval/utils.ts +10 -0
  108. package/pages/Instance/Info/Snapshots/Calendar/Day/index.tsx +125 -0
  109. package/pages/Instance/Info/Snapshots/Calendar/index.tsx +133 -0
  110. package/pages/Instance/Info/Snapshots/Calendar/utils.ts +74 -0
  111. package/pages/Instance/Info/Snapshots/TimeLine/Day/index.tsx +79 -0
  112. package/pages/Instance/Info/Snapshots/TimeLine/index.tsx +57 -0
  113. package/pages/Instance/Info/Snapshots/index.tsx +97 -0
  114. package/pages/Instance/Info/Snapshots/utils.ts +18 -0
  115. package/pages/Instance/Info/Status/InstanceResponseModal/index.tsx +32 -0
  116. package/pages/Instance/Info/Status/InstanceResponseModal/styles.module.scss +3 -0
  117. package/pages/Instance/Info/Status/index.tsx +85 -0
  118. package/pages/Instance/Info/Status/styles.module.scss +12 -0
  119. package/pages/Instance/Info/Status/utils.ts +24 -0
  120. package/pages/Instance/Info/components/Property/index.tsx +32 -0
  121. package/pages/Instance/Info/components/Property/styles.module.scss +21 -0
  122. package/pages/Instance/Info/components/Section/index.tsx +50 -0
  123. package/pages/Instance/Info/components/ValueStatus/index.tsx +51 -0
  124. package/pages/Instance/Info/index.tsx +129 -0
  125. package/pages/Instance/SnapshotsModal/index.tsx +169 -0
  126. package/pages/Instance/SnapshotsModal/utils.ts +17 -0
  127. package/pages/Instance/Tabs/index.tsx +98 -0
  128. package/pages/Instance/components/ClonesList/ConnectionModal/index.tsx +196 -0
  129. package/pages/Instance/components/ClonesList/MenuCell/index.tsx +98 -0
  130. package/pages/Instance/components/ClonesList/MenuCell/utils.ts +21 -0
  131. package/pages/Instance/components/ClonesList/index.tsx +189 -0
  132. package/pages/Instance/components/ClonesList/styles.module.scss +32 -0
  133. package/pages/Instance/components/ErrorStub/index.tsx +77 -0
  134. package/pages/Instance/components/ModalReloadButton/index.tsx +43 -0
  135. package/pages/Instance/components/Tags/Tag/index.tsx +60 -0
  136. package/pages/Instance/components/Tags/index.tsx +42 -0
  137. package/pages/Instance/context.ts +39 -0
  138. package/pages/Instance/index.tsx +235 -0
  139. package/pages/Instance/stores/ClonesModal.ts +35 -0
  140. package/pages/Instance/stores/Main.ts +335 -0
  141. package/pages/Instance/stores/SnapshotsModal.ts +35 -0
  142. package/pages/Instance/styles.scss +40 -0
  143. package/pages/Instance/useCreatedStores.ts +14 -0
  144. package/pages/Logs/Icons/PlusIcon.tsx +8 -0
  145. package/pages/Logs/constants/index.ts +7 -0
  146. package/pages/Logs/hooks/useWsScroll.tsx +44 -0
  147. package/pages/Logs/index.tsx +267 -0
  148. package/pages/Logs/utils/index.ts +20 -0
  149. package/pages/Logs/wsLogs.ts +110 -0
  150. package/pages/Logs/wsSnackbar.ts +27 -0
  151. package/postgres.ai-shared-3.5.0.tgz +0 -0
  152. package/react-app-env.d.ts +71 -0
  153. package/scripts/copy-assets.js +30 -0
  154. package/scripts/pack.js +70 -0
  155. package/stores/Snapshots.ts +54 -0
  156. package/styles/colors.ts +67 -0
  157. package/styles/global.scss +29 -0
  158. package/styles/icons.tsx +1917 -0
  159. package/styles/mixins.scss +30 -0
  160. package/styles/styles.ts +87 -0
  161. package/styles/theme.ts +53 -0
  162. package/styles/vars.scss +43 -0
  163. package/styles/vars.ts +40 -0
  164. package/tsconfig.build.json +37 -0
  165. package/tsconfig.json +30 -0
  166. package/types/api/endpoints/createClone.ts +10 -0
  167. package/types/api/endpoints/destroyClone.ts +7 -0
  168. package/types/api/endpoints/getClone.ts +6 -0
  169. package/types/api/endpoints/getConfig.ts +6 -0
  170. package/types/api/endpoints/getEngine.ts +13 -0
  171. package/types/api/endpoints/getFullConfig.ts +4 -0
  172. package/types/api/endpoints/getInstance.ts +6 -0
  173. package/types/api/endpoints/getInstanceRetrieval.ts +6 -0
  174. package/types/api/endpoints/getSeImages.ts +22 -0
  175. package/types/api/endpoints/getSnapshots.ts +6 -0
  176. package/types/api/endpoints/getWSToken.ts +6 -0
  177. package/types/api/endpoints/initWS.ts +1 -0
  178. package/types/api/endpoints/refreshInstance.ts +4 -0
  179. package/types/api/endpoints/resetClone.ts +8 -0
  180. package/types/api/endpoints/testDbSource.ts +48 -0
  181. package/types/api/endpoints/updateClone.ts +10 -0
  182. package/types/api/endpoints/updateConfig.ts +6 -0
  183. package/types/api/entities/clone.ts +42 -0
  184. package/types/api/entities/config.ts +114 -0
  185. package/types/api/entities/dbSource.ts +13 -0
  186. package/types/api/entities/instance.ts +67 -0
  187. package/types/api/entities/instanceRetrieval.ts +46 -0
  188. package/types/api/entities/instanceState.ts +102 -0
  189. package/types/api/entities/pool.ts +27 -0
  190. package/types/api/entities/snapshot.ts +18 -0
  191. package/types/api/entities/wsToken.ts +7 -0
  192. package/types/byte-size/index.d.ts +22 -0
  193. package/utils/api.ts +30 -0
  194. package/utils/clone.ts +31 -0
  195. package/utils/connection.ts +38 -0
  196. package/utils/date.ts +87 -0
  197. package/utils/instance.ts +10 -0
  198. package/utils/numbers.ts +11 -0
  199. package/utils/react.ts +10 -0
  200. package/utils/snapshot.ts +4 -0
  201. package/utils/strings.ts +11 -0
  202. package/utils/units.ts +23 -0
@@ -0,0 +1,32 @@
1
+ import { observer } from 'mobx-react-lite'
2
+
3
+ import { useStores } from '@postgres.ai/shared/pages/Instance/context'
4
+ import { WarningIcon } from '@postgres.ai/shared/icons/Warning'
5
+ import { formatDateStd } from '@postgres.ai/shared/utils/date'
6
+
7
+ import { Property } from '../../components/Property'
8
+
9
+ import styles from './styles.module.scss'
10
+
11
+ export const RefreshFailedAlert = observer(() => {
12
+ const stores = useStores()
13
+
14
+ const refreshFailed =
15
+ stores.main.instance?.state?.retrieving?.alerts?.refreshFailed
16
+ if (!refreshFailed) return null
17
+
18
+ return (
19
+ <div className={styles.root}>
20
+ <div className={styles.header}>
21
+ <h6 className={styles.title}>{refreshFailed.message}</h6>
22
+ <WarningIcon className={styles.icon} />
23
+ </div>
24
+ <Property classes={{ name: styles.propertyName }} name="Last seen">
25
+ {formatDateStd(refreshFailed.lastSeen, { withDistance: true })}
26
+ </Property>
27
+ <Property classes={{ name: styles.propertyName }} name="Count">
28
+ {refreshFailed.count}
29
+ </Property>
30
+ </div>
31
+ )
32
+ })
@@ -0,0 +1,33 @@
1
+ @import '@postgres.ai/shared/styles/vars';
2
+
3
+ .root {
4
+ padding: 8px 10px;
5
+ background: #fff2e5;
6
+ border: 1px solid $color-gray-100;
7
+ margin-top: 12px;
8
+ border-radius: $border-radius--small;
9
+ }
10
+
11
+ .header {
12
+ display: flex;
13
+ justify-content: space-between;
14
+ align-items: center;
15
+ margin-bottom: 8px;
16
+ }
17
+
18
+ .title {
19
+ font-weight: 700;
20
+ font-size: 12px;
21
+ margin-right: 24px;
22
+ margin: 0;
23
+ }
24
+
25
+ .icon {
26
+ width: 10px;
27
+ color: $color-status-warning;
28
+ flex: 0 0 auto;
29
+ }
30
+
31
+ .propertyName {
32
+ flex: 0 0 123px;
33
+ }
@@ -0,0 +1,49 @@
1
+ import { useEffect } from 'react'
2
+
3
+ import { Modal } from '@postgres.ai/shared/components/Modal'
4
+ import { useStores } from '@postgres.ai/shared/pages/Instance/context'
5
+ import { ModalReloadButton } from '@postgres.ai/shared/pages/Instance/components/ModalReloadButton'
6
+ import { ActivityType } from '@postgres.ai/shared/types/api/entities/instanceRetrieval'
7
+ import { RetrievalTable } from '../RetrievalTable'
8
+
9
+ import styles from './styles.module.scss'
10
+
11
+ export const RetrievalModal = ({
12
+ isOpen,
13
+ onClose,
14
+ data,
15
+ }: {
16
+ isOpen: boolean
17
+ onClose: () => void
18
+ data: {
19
+ source: ActivityType[]
20
+ target: ActivityType[]
21
+ }
22
+ }) => {
23
+ const stores = useStores()
24
+ const { isReloadingInstanceRetrieval, reloadInstanceRetrieval } = stores.main
25
+
26
+ useEffect(() => {
27
+ reloadInstanceRetrieval()
28
+ },[])
29
+
30
+ return (
31
+ <Modal
32
+ title="Retrieval activity details"
33
+ isOpen={isOpen}
34
+ onClose={onClose}
35
+ size="md"
36
+ titleRightContent={
37
+ <ModalReloadButton
38
+ isReloading={isReloadingInstanceRetrieval}
39
+ onReload={reloadInstanceRetrieval}
40
+ />
41
+ }
42
+ >
43
+ <div className={styles.tableContainer}>
44
+ <RetrievalTable data={data?.source} activity="source" />
45
+ <RetrievalTable data={data?.target} activity="target" />
46
+ </div>
47
+ </Modal>
48
+ )
49
+ }
@@ -0,0 +1,6 @@
1
+ .tableContainer {
2
+ display: flex;
3
+ width: 100%;
4
+ justify-content: space-between;
5
+ flex-direction: row;
6
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ Table,
3
+ TableHead,
4
+ TableRow,
5
+ TableCell,
6
+ TableBody,
7
+ } from '@material-ui/core'
8
+ import { ActivityType } from '@postgres.ai/shared/types/api/entities/instanceRetrieval'
9
+
10
+ import styles from './styles.module.scss'
11
+
12
+ export const RetrievalTable = ({
13
+ data,
14
+ activity,
15
+ }: {
16
+ data: ActivityType[]
17
+ activity: string
18
+ }) => {
19
+ return (
20
+ <Table>
21
+ <TableHead>
22
+ <TableRow>
23
+ <TableCell className={styles.tableSubtitle}>
24
+ Activity on the {activity}
25
+ </TableCell>
26
+ </TableRow>
27
+ </TableHead>
28
+ <TableBody className={styles.tableBody}>
29
+ {data && data.length > 0 ? (
30
+ data.map((item, index) => (
31
+ <div key={index}>
32
+ {Object.entries(item).map((val, index) => (
33
+ <TableRow key={index} hover className={styles.tableRow}>
34
+ <TableCell>
35
+ {val[0]}: {val[1]}
36
+ </TableCell>
37
+ </TableRow>
38
+ ))}
39
+ </div>
40
+ ))
41
+ ) : (
42
+ <TableBody className={styles.tableBody}>
43
+ <div>
44
+ <TableRow className={styles.tableRow}>
45
+ <TableCell>No activity on the {activity}</TableCell>
46
+ </TableRow>
47
+ </div>
48
+ </TableBody>
49
+ )}
50
+ </TableBody>
51
+ </Table>
52
+ )
53
+ }
@@ -0,0 +1,29 @@
1
+ .tableRow {
2
+ display: inline-table !important;
3
+ width: 100%;
4
+ }
5
+
6
+ .tableBody {
7
+ height: 100%;
8
+
9
+ div:not(:first-child) {
10
+ padding-top: 0.75rem;
11
+ border-top: 1px solid rgba(224, 224, 224, 1);
12
+ }
13
+
14
+ div {
15
+ padding: 0.75rem 0;
16
+ }
17
+
18
+ td {
19
+ border-bottom: 0;
20
+ padding: 0px 18px;
21
+ font-size: 13px;
22
+ font-family: 'Fira Code', monospace;
23
+ }
24
+ }
25
+
26
+ .tableSubtitle {
27
+ font-size: 15px;
28
+ font-weight: bold;
29
+ }
@@ -0,0 +1,95 @@
1
+ import { useState } from 'react'
2
+ import { observer } from 'mobx-react-lite'
3
+ import { makeStyles } from '@material-ui/core'
4
+
5
+ import { useStores } from '@postgres.ai/shared/pages/Instance/context'
6
+ import { Status } from '@postgres.ai/shared/components/Status'
7
+ import { capitalize } from '@postgres.ai/shared/utils/strings'
8
+ import { formatDateStd } from '@postgres.ai/shared/utils/date'
9
+ import { Button } from '@postgres.ai/shared/components/Button2'
10
+ import { Tooltip } from '@postgres.ai/shared/components/Tooltip'
11
+ import { InfoIcon } from '@postgres.ai/shared/icons/Info'
12
+
13
+ import { Section } from '../components/Section'
14
+ import { Property } from '../components/Property'
15
+
16
+ import { RefreshFailedAlert } from './RefreshFailedAlert'
17
+
18
+ import { getTypeByStatus, isRetrievalUnknown } from './utils'
19
+ import { RetrievalModal } from './RetrievalModal'
20
+
21
+ const useStyles = makeStyles(
22
+ () => ({
23
+ infoIcon: {
24
+ height: '12px',
25
+ width: '12px',
26
+ marginLeft: '8px',
27
+ color: '#808080',
28
+ },
29
+ detailsButton: {
30
+ marginLeft: '8px',
31
+ },
32
+ }),
33
+ { index: 1 },
34
+ )
35
+
36
+ export const Retrieval = observer(() => {
37
+ const stores = useStores()
38
+ const classes = useStyles()
39
+ const [isModalOpen, setIsModalOpen] = useState<boolean>(false)
40
+
41
+ const { instance, instanceRetrieval } = stores.main
42
+ if (!instance) return null
43
+
44
+ const retrieving = instance.state?.retrieving
45
+ if (!retrieving) return null
46
+
47
+ if (!instanceRetrieval) return null
48
+ const { mode, status, activity } = instanceRetrieval
49
+ const isVisible = mode !== 'physical' && !isRetrievalUnknown(mode)
50
+ const isActive = mode === 'logical' && status === 'refreshing'
51
+
52
+ return (
53
+ <Section title="Retrieval">
54
+ <Property name="Status">
55
+ <Status type={getTypeByStatus(retrieving.status)}>
56
+ {capitalize(retrieving.status)}
57
+ {isVisible && (
58
+ <>
59
+ <Button
60
+ theme="primary"
61
+ onClick={() => setIsModalOpen(true)}
62
+ isDisabled={!isActive}
63
+ className={classes.detailsButton}
64
+ >
65
+ Show details
66
+ </Button>
67
+ {!isActive && (
68
+ <Tooltip content="No retrieval activity details">
69
+ <InfoIcon className={classes.infoIcon} />
70
+ </Tooltip>
71
+ )}
72
+ </>
73
+ )}
74
+ </Status>
75
+ </Property>
76
+ <Property name="Mode">{retrieving.mode}</Property>
77
+ <Property name="Last refresh">
78
+ {retrieving.lastRefresh
79
+ ? formatDateStd(retrieving.lastRefresh, { withDistance: true })
80
+ : '-'}
81
+ </Property>
82
+ <Property name="Next refresh">
83
+ {retrieving.nextRefresh
84
+ ? formatDateStd(retrieving.nextRefresh, { withDistance: true })
85
+ : 'Not scheduled'}
86
+ </Property>
87
+ <RefreshFailedAlert />
88
+ <RetrievalModal
89
+ data={activity}
90
+ isOpen={isModalOpen}
91
+ onClose={() => setIsModalOpen(false)}
92
+ />
93
+ </Section>
94
+ )
95
+ })
@@ -0,0 +1,10 @@
1
+ export const getTypeByStatus = (status: string | undefined) => {
2
+ if (status === 'finished') return 'ok'
3
+ if (status === 'refreshing') return 'waiting'
4
+ if (status === 'failed') return 'error'
5
+ return 'unknown'
6
+ }
7
+
8
+ export const isRetrievalUnknown = (mode: string | undefined) => {
9
+ return mode === 'unknown' || mode === ''
10
+ }
@@ -0,0 +1,125 @@
1
+ /*--------------------------------------------------------------------------
2
+ * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai
3
+ * All Rights Reserved. Proprietary and confidential.
4
+ * Unauthorized copying of this file, via any medium is strictly prohibited
5
+ *--------------------------------------------------------------------------
6
+ */
7
+
8
+ import { makeStyles } from '@material-ui/core'
9
+ import clsx from 'clsx'
10
+
11
+ import { isSameDayUTC, formatUTC } from '@postgres.ai/shared/utils/date'
12
+ import { Snapshot } from '@postgres.ai/shared/types/api/entities/snapshot'
13
+ import { colors } from '@postgres.ai/shared/styles/colors'
14
+ import { Tooltip } from '@postgres.ai/shared/components/Tooltip'
15
+
16
+ const CELL_SIZE = 28
17
+
18
+ const useStyles = makeStyles(
19
+ {
20
+ root: {
21
+ position: 'relative',
22
+ cursor: 'default',
23
+ flex: `0 0 ${CELL_SIZE}px`,
24
+ background: '#f4f4f4',
25
+ height: `${CELL_SIZE}px`,
26
+ display: 'flex',
27
+ borderRadius: `${CELL_SIZE / 2}px`,
28
+ alignItems: 'center',
29
+ justifyContent: 'center',
30
+ marginTop: '16px',
31
+ fontSize: '12px',
32
+ },
33
+ rootHasSnapshots: {
34
+ background: colors.secondary2.lightLight,
35
+ cursor: 'pointer',
36
+ },
37
+ rootCurrent: {
38
+ border: `1px solid ${colors.secondary2.main}`,
39
+ },
40
+ rootDisabled: {
41
+ opacity: '0.25',
42
+ pointerEvents: 'none',
43
+ },
44
+ itemsCount: {
45
+ top: '-8px',
46
+ right: '-6px',
47
+ position: 'absolute',
48
+ fontSize: '8px',
49
+ backgroundColor: colors.white,
50
+ border: `1px solid ${colors.secondary2.lightLight}`,
51
+ borderRadius: '8px',
52
+ height: '16px',
53
+ padding: '2px',
54
+ minWidth: '16px',
55
+
56
+ display: 'flex',
57
+ alignItems: 'center',
58
+ justifyContent: 'center',
59
+ },
60
+ break: {
61
+ flex: '0 0 100%',
62
+ },
63
+ },
64
+ { index: 1 },
65
+ )
66
+
67
+ type Props = {
68
+ date: Date
69
+ snapshots: Snapshot[]
70
+ isBreak: boolean
71
+ isDisabled: boolean
72
+ onClick: () => void
73
+ }
74
+
75
+ export const Day = (props: Props) => {
76
+ const { date, isBreak, snapshots, isDisabled, onClick } = props
77
+ const classes = useStyles()
78
+
79
+ const today = new Date()
80
+
81
+ const breakRendered = isBreak && <div className={classes.break} />
82
+
83
+ const dayRendered = (
84
+ <div
85
+ className={clsx(
86
+ classes.root,
87
+ snapshots.length && classes.rootHasSnapshots,
88
+ isDisabled && classes.rootDisabled,
89
+ isSameDayUTC(today, date) && classes.rootCurrent,
90
+ )}
91
+ onClick={snapshots.length ? onClick : void 0}
92
+ >
93
+ {!!snapshots.length && (
94
+ <div className={classes.itemsCount}>{snapshots.length}</div>
95
+ )}
96
+ {formatUTC(date, 'd')}
97
+ </div>
98
+ )
99
+
100
+ if (snapshots.length) {
101
+ const contentRendered = (
102
+ <>
103
+ {snapshots.map((snapshot) => {
104
+ return <div key={snapshot.id}>{snapshot.dataStateAt}</div>
105
+ })}
106
+ </>
107
+ )
108
+
109
+ return (
110
+ <>
111
+ <Tooltip content={contentRendered} disableTouchListener>
112
+ {dayRendered}
113
+ </Tooltip>
114
+ {breakRendered}
115
+ </>
116
+ )
117
+ }
118
+
119
+ return (
120
+ <>
121
+ {dayRendered}
122
+ {breakRendered}
123
+ </>
124
+ )
125
+ }
@@ -0,0 +1,133 @@
1
+ /*--------------------------------------------------------------------------
2
+ * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai
3
+ * All Rights Reserved. Proprietary and confidential.
4
+ * Unauthorized copying of this file, via any medium is strictly prohibited
5
+ *--------------------------------------------------------------------------
6
+ */
7
+
8
+ import { useState } from 'react'
9
+ import { makeStyles, IconButton } from '@material-ui/core'
10
+ import { ArrowLeft, ArrowRight } from '@material-ui/icons'
11
+
12
+ import { Snapshot } from '@postgres.ai/shared/types/api/entities/snapshot'
13
+ import { colors } from '@postgres.ai/shared/styles/colors'
14
+ import { formatUTC } from '@postgres.ai/shared/utils/date'
15
+
16
+ import { Day } from './Day'
17
+
18
+ import {
19
+ getPrevMonthStartDate,
20
+ getNextMonthStartDate,
21
+ canGetNextMonthStartDate,
22
+ getMonthStartDate,
23
+ getCalendar,
24
+ } from './utils'
25
+
26
+ const useStyles = makeStyles(
27
+ {
28
+ root: {
29
+ marginTop: '8px',
30
+ },
31
+ header: {
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ justifyContent: 'space-between',
35
+ },
36
+ title: {
37
+ fontWeight: 700,
38
+ fontSize: '12px',
39
+ },
40
+ button: {
41
+ padding: '8px',
42
+
43
+ '&:disabled': {
44
+ pointerEvents: 'auto',
45
+ cursor: 'not-allowed',
46
+ },
47
+ },
48
+ buttonIcon: {
49
+ fontSize: '20px',
50
+ },
51
+ days: {
52
+ display: 'flex',
53
+ flexWrap: 'wrap',
54
+ justifyContent: 'space-between',
55
+ },
56
+ weekDay: {
57
+ fontSize: '12px',
58
+ flex: '0 0 24px',
59
+ textAlign: 'center',
60
+ color: colors.pgaiDarkGray,
61
+ marginTop: '12px',
62
+ },
63
+ },
64
+ { index: 1 },
65
+ )
66
+
67
+ type Props = {
68
+ snapshots: Snapshot[]
69
+ onSelectDate: (date: Date) => void
70
+ }
71
+
72
+ export const Calendar = (props: Props) => {
73
+ const { snapshots, onSelectDate } = props
74
+
75
+ const [monthStartDate, setMonthStartDate] = useState(getMonthStartDate)
76
+
77
+ const classes = useStyles()
78
+
79
+ const nextMonth = () => {
80
+ const nextMonthStartDate = getNextMonthStartDate(monthStartDate)
81
+ setMonthStartDate(nextMonthStartDate)
82
+ }
83
+
84
+ const prevMonth = () => {
85
+ const prevMonthStartDate = getPrevMonthStartDate(monthStartDate)
86
+ setMonthStartDate(prevMonthStartDate)
87
+ }
88
+
89
+ const calendar = getCalendar(monthStartDate, snapshots)
90
+
91
+ return (
92
+ <div className={classes.root}>
93
+ <div className={classes.header}>
94
+ <span className={classes.title}>
95
+ {formatUTC(monthStartDate, 'MMMM y')} UTC
96
+ </span>
97
+ <div>
98
+ <IconButton onClick={prevMonth} className={classes.button}>
99
+ <ArrowLeft className={classes.buttonIcon} />
100
+ </IconButton>
101
+ <IconButton
102
+ onClick={nextMonth}
103
+ disabled={!canGetNextMonthStartDate(monthStartDate)}
104
+ className={classes.button}
105
+ >
106
+ <ArrowRight className={classes.buttonIcon} />
107
+ </IconButton>
108
+ </div>
109
+ </div>
110
+ <div className={classes.days}>
111
+ {calendar.weekDays.map((weekDay) => {
112
+ return (
113
+ <div key={weekDay} className={classes.weekDay}>
114
+ {weekDay}
115
+ </div>
116
+ )
117
+ })}
118
+ </div>
119
+ <div className={classes.days}>
120
+ {calendar.days.map((day) => (
121
+ <Day
122
+ onClick={() => onSelectDate(day.date)}
123
+ isBreak={day.isBreak}
124
+ isDisabled={day.isDisabled}
125
+ date={day.date}
126
+ snapshots={day.snapshots}
127
+ key={day.date.getTime()}
128
+ />
129
+ ))}
130
+ </div>
131
+ </div>
132
+ )
133
+ }
@@ -0,0 +1,74 @@
1
+ /*--------------------------------------------------------------------------
2
+ * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai
3
+ * All Rights Reserved. Proprietary and confidential.
4
+ * Unauthorized copying of this file, via any medium is strictly prohibited
5
+ *--------------------------------------------------------------------------
6
+ */
7
+
8
+ import { daysInWeek } from 'date-fns'
9
+
10
+ import { Snapshot } from '@postgres.ai/shared/types/api/entities/snapshot'
11
+ import { config } from '@postgres.ai/shared/config'
12
+ import {
13
+ startOfMonthUTC,
14
+ isSameDayUTC,
15
+ startOfWeekUTC,
16
+ endOfMonthUTC,
17
+ endOfWeekUTC,
18
+ startOfDayUTC,
19
+ differenceInDaysUTC,
20
+ addDaysUTC,
21
+ addMonthsUTC,
22
+ isSameMonthUTC,
23
+ formatUTC
24
+ } from '@postgres.ai/shared/utils/date'
25
+
26
+ export const getMonthStartDate = () => startOfMonthUTC(new Date())
27
+
28
+ const getEdgeDates = (monthStartDate: Date) => {
29
+ return {
30
+ firstDate: startOfWeekUTC(monthStartDate, { locale: config.dateFnsLocale }),
31
+ lastDate: startOfDayUTC(
32
+ endOfWeekUTC(endOfMonthUTC(monthStartDate), {
33
+ locale: config.dateFnsLocale,
34
+ }),
35
+ ),
36
+ }
37
+ }
38
+
39
+ export const getCalendar = (monthStartDate: Date, snapshots: Snapshot[]) => {
40
+ const { firstDate, lastDate } = getEdgeDates(monthStartDate)
41
+
42
+ const countDays = differenceInDaysUTC(lastDate, firstDate)
43
+
44
+ const days = Array.from({ length: countDays + 1 }, (_, i) => {
45
+ const date = addDaysUTC(firstDate, i)
46
+
47
+ return {
48
+ date,
49
+ snapshots: snapshots.filter((snapshot) =>
50
+ isSameDayUTC(date, snapshot.dataStateAtDate),
51
+ ),
52
+ isBreak: i > 0 && (i + 1) % 7 === 0,
53
+ isDisabled: !isSameMonthUTC(monthStartDate, date),
54
+ }
55
+ })
56
+
57
+ const weekDays = days
58
+ .slice(0, daysInWeek)
59
+ .map((day) => formatUTC(day.date, 'E'))
60
+
61
+ return {
62
+ weekDays,
63
+ days,
64
+ }
65
+ }
66
+
67
+ export const getPrevMonthStartDate = (monthStartDate: Date) =>
68
+ addMonthsUTC(monthStartDate, -1)
69
+
70
+ export const getNextMonthStartDate = (monthStartDate: Date) =>
71
+ addMonthsUTC(monthStartDate, 1)
72
+
73
+ export const canGetNextMonthStartDate = (monthStartDate: Date) =>
74
+ monthStartDate < getMonthStartDate()