1
+ import React , { useEffect } from "react" ;
2
+ import ConsolePageTitle from "../../components/ConsolePageTitle" ;
3
+ import Table from '@mui/material/Table' ;
4
+ import TableBody from '@mui/material/TableBody' ;
5
+ import TableCell from '@mui/material/TableCell' ;
6
+ import TableContainer from '@mui/material/TableContainer' ;
7
+ import TableHead from '@mui/material/TableHead' ;
8
+ import TableRow from '@mui/material/TableRow' ;
9
+ import { Grid , Paper , Typography } from "@mui/material" ;
10
+ import Button from "@mui/material/Button" ;
11
+ import Box from "@mui/material/Box/Box" ;
12
+ import { observer } from "mobx-react-lite" ;
13
+ import { consultingStore } from "../../stores/consulting" ;
14
+ import { ConsultingWrapperProps } from "./ConsultingWrapper" ;
15
+ import { makeStyles } from "@material-ui/core" ;
16
+ import { PageSpinner } from "@postgres.ai/shared/components/PageSpinner" ;
17
+ import { ProductCardWrapper } from "../../components/ProductCard/ProductCardWrapper" ;
18
+ import { Link } from "@postgres.ai/shared/components/Link2" ;
19
+ import Permissions from "../../utils/permissions" ;
20
+ import { WarningWrapper } from "../../components/Warning/WarningWrapper" ;
21
+ import { messages } from "../../assets/messages" ;
22
+ import { ConsoleBreadcrumbsWrapper } from "../../components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper" ;
23
+ import { formatPostgresInterval } from "./utils" ;
24
+
25
+
26
+
27
+ const useStyles = makeStyles ( ( theme ) => ( {
28
+ sectionLabel : {
29
+ fontSize : '14px!important' ,
30
+ fontWeight : '700!important' as 'bold' ,
31
+ } ,
32
+ productCardProjects : {
33
+ flex : '1 1 0' ,
34
+ marginRight : '20px' ,
35
+ height : 'maxContent' ,
36
+ gap : 20 ,
37
+ maxHeight : '100%' ,
38
+
39
+ '& svg' : {
40
+ width : '206px' ,
41
+ height : '130px' ,
42
+ } ,
43
+
44
+ [ theme . breakpoints . down ( 'sm' ) ] : {
45
+ flex : '100%' ,
46
+ marginTop : '20px' ,
47
+ minHeight : 'auto !important' ,
48
+
49
+ '&:nth-child(1) svg' : {
50
+ marginBottom : 0 ,
51
+ } ,
52
+
53
+ '&:nth-child(2) svg' : {
54
+ marginBottom : 0 ,
55
+ } ,
56
+ } ,
57
+ } ,
58
+ } ) )
59
+
60
+ export const Consulting = observer ( ( props : ConsultingWrapperProps ) => {
61
+ const { orgId, orgData, match } = props ;
62
+
63
+ const classes = useStyles ( ) ;
64
+
65
+ useEffect ( ( ) => {
66
+ if ( orgId ) {
67
+ consultingStore . getOrgBalance ( orgId ) ;
68
+ consultingStore . getTransactions ( orgId ) ;
69
+ }
70
+ } , [ orgId ] ) ;
71
+
72
+ const breadcrumbs = (
73
+ < ConsoleBreadcrumbsWrapper
74
+ org = { match . params . org }
75
+ breadcrumbs = { [ { name : "Consulting" } ] }
76
+ />
77
+ )
78
+
79
+ if ( consultingStore . loading ) {
80
+ return (
81
+ < Box >
82
+ { breadcrumbs }
83
+ < ConsolePageTitle title = { "Consulting" } />
84
+ < Box sx = { { display : 'flex' , justifyContent : 'center' , alignItems : 'center' , height : '100%' } } >
85
+ < PageSpinner />
86
+ </ Box >
87
+ </ Box >
88
+ )
89
+ }
90
+
91
+ if ( orgData === null || ! Permissions . isAdmin ( orgData ) ) {
92
+ return (
93
+ < Box >
94
+ { breadcrumbs }
95
+ < ConsolePageTitle title = { "Consulting" } />
96
+ < WarningWrapper > { messages . noPermissionPage } </ WarningWrapper >
97
+ </ Box >
98
+ )
99
+ }
100
+
101
+ if ( orgData . consulting_type === null ) {
102
+ return (
103
+ < Box >
104
+ { breadcrumbs }
105
+ < ConsolePageTitle title = { "Consulting" } />
106
+ < Box sx = { { display : 'flex' , justifyContent : 'center' , alignItems : 'center' , height : '100%' } } >
107
+ < ProductCardWrapper
108
+ inline
109
+ className = { classes . productCardProjects }
110
+ title = "Not a customer yet"
111
+ actions = { [
112
+ {
113
+ id : 'learn-more' ,
114
+ content : ( < Link to = "https://postgres.ai/consulting" external target = "_blank" > Learn more</ Link > )
115
+ }
116
+ ] }
117
+ >
118
+ < p >
119
+ Your organization is not a consulting customer yet. To learn more about Postgres.AI consulting, visit this page: < Link to = "https://postgres.ai/consulting" external target = "_blank" > Consulting</ Link > .
120
+ </ p >
121
+ < p >
122
+ Reach out to the team to discuss consulting opportunities: < Link to = "mailto:consulting@postgres.ai" external target = "_blank" > consulting@postgres.ai</ Link > .
123
+ </ p >
124
+ </ ProductCardWrapper >
125
+ </ Box >
126
+ </ Box >
127
+ )
128
+ }
129
+
130
+ return (
131
+ < div >
132
+ { breadcrumbs }
133
+ < ConsolePageTitle title = { "Consulting" } />
134
+ < Grid container spacing = { 3 } >
135
+ { orgData . consulting_type === 'retainer' && < Grid item xs = { 12 } md = { 8 } >
136
+ < Typography variant = "h6" classes = { { root : classes . sectionLabel } } >
137
+ Retainer balance:
138
+ </ Typography >
139
+ < Typography variant = "h5" sx = { { marginTop : 1 } } >
140
+ { formatPostgresInterval ( consultingStore . orgBalance ?. [ 0 ] ?. balance || '00' ) || 0 }
141
+ </ Typography >
142
+ </ Grid > }
143
+ < Grid item xs = { 12 } md = { 8 } >
144
+ < Box >
145
+ < Button variant = "contained" component = "a" href = "https://buy.stripe.com/7sI5odeXt3tB0Eg3cm" target = "_blank" >
146
+ Replenish consulting hours
147
+ </ Button >
148
+ </ Box >
149
+ </ Grid >
150
+ < Grid item xs = { 12 } md = { 8 } >
151
+ < Box >
152
+ < Typography variant = "h6" classes = { { root : classes . sectionLabel } } >
153
+ Issue tracker (GitLab):
154
+ </ Typography >
155
+ < Typography variant = "body1" sx = { { marginTop : 1 , fontSize : 14 } } >
156
+ < Link to = { `https://gitlab.com/postgres-ai/postgresql-consulting/support/${ orgData . alias } ` } external target = "_blank" >
157
+ https://gitlab.com/postgres-ai/postgresql-consulting/support/{ orgData . alias }
158
+ </ Link >
159
+ </ Typography >
160
+ </ Box >
161
+ </ Grid >
162
+ < Grid item xs = { 12 } md = { 8 } >
163
+ < Box >
164
+ < Typography variant = "h6" classes = { { root : classes . sectionLabel } } >
165
+ Book a Zoom call:
166
+ </ Typography >
167
+ < Typography variant = "body1" sx = { { marginTop : 1 , fontSize : 14 } } >
168
+ < Link to = { `https://calend.ly/postgres` } external target = "_blank" >
169
+ https://calend.ly/postgres
170
+ </ Link >
171
+ </ Typography >
172
+ </ Box >
173
+ </ Grid >
174
+ < Grid item xs = { 12 } md = { 8 } >
175
+ < Typography variant = "h6" classes = { { root : classes . sectionLabel } } >
176
+ Activity:
177
+ </ Typography >
178
+ {
179
+ consultingStore . transactions ?. length === 0
180
+ ? < Typography variant = "body1" sx = { { marginTop : 1 } } >
181
+ No activity yet
182
+ </ Typography >
183
+ : < TableContainer component = { Paper } sx = { { marginTop : 1 } } >
184
+ < Table >
185
+ < TableHead >
186
+ < TableRow >
187
+ < TableCell > Action</ TableCell >
188
+ < TableCell > Amount</ TableCell >
189
+ < TableCell > Date</ TableCell >
190
+ < TableCell > Details</ TableCell >
191
+ </ TableRow >
192
+ </ TableHead >
193
+ < TableBody >
194
+ {
195
+ consultingStore . transactions . map ( ( transaction , index ) => {
196
+ return (
197
+ < TableRow key = { index } >
198
+ < TableCell sx = { { whiteSpace : 'nowrap' } } > { transaction . amount . charAt ( 0 ) === '-' ? 'Utilize' : 'Replenish' } </ TableCell >
199
+ < TableCell sx = { { color : transaction . amount . charAt ( 0 ) === '-' ? 'red' : 'green' , whiteSpace : 'nowrap' } } >
200
+ { formatPostgresInterval ( transaction . amount || '00' ) }
201
+ </ TableCell >
202
+ < TableCell sx = { { whiteSpace : 'nowrap' } } > { new Date ( transaction . created_at ) ?. toISOString ( ) ?. split ( 'T' ) ?. [ 0 ] } </ TableCell >
203
+ < TableCell >
204
+ { transaction . issue_id
205
+ ? < Link external to = { `https://gitlab.com/postgres-ai/postgresql-consulting/support/${ orgData . alias } /-/issues/${ transaction . issue_id } ` } target = "_blank" >
206
+ { transaction . description }
207
+ </ Link >
208
+ : transaction . description
209
+ }
210
+ </ TableCell >
211
+ </ TableRow >
212
+ ) ;
213
+ } )
214
+ }
215
+ </ TableBody >
216
+ </ Table >
217
+ </ TableContainer >
218
+ }
219
+ </ Grid >
220
+ </ Grid >
221
+ </ div >
222
+ ) ;
223
+ } ) ;
0 commit comments