1
+ /**
2
+ * Copyright 2024 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // [START add_ons_preview_link]
17
+ // [START add_ons_case_preview_link]
18
+
19
+ /**
20
+ * Entry point for a support case link preview.
21
+ *
22
+ * @param {!Object } event The event object.
23
+ * @return {!Card } The resulting preview link card.
24
+ */
25
+ function caseLinkPreview ( event ) {
26
+
27
+ // If the event object URL matches a specified pattern for support case links.
28
+ if ( event . docs . matchedUrl . url ) {
29
+
30
+ // Uses the event object to parse the URL and identify the case details.
31
+ const caseDetails = parseQuery ( event . docs . matchedUrl . url ) ;
32
+
33
+ // Builds a preview card with the case name, and description
34
+ const caseHeader = CardService . newCardHeader ( )
35
+ . setTitle ( `Case ${ caseDetails [ "name" ] [ 0 ] } ` ) ;
36
+ const caseDescription = CardService . newTextParagraph ( )
37
+ . setText ( caseDetails [ "description" ] [ 0 ] ) ;
38
+
39
+ // Returns the card.
40
+ // Uses the text from the card's header for the title of the smart chip.
41
+ return CardService . newCardBuilder ( )
42
+ . setHeader ( caseHeader )
43
+ . addSection ( CardService . newCardSection ( ) . addWidget ( caseDescription ) )
44
+ . build ( ) ;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Extracts the URL parameters from the given URL.
50
+ *
51
+ * @param {!string } url The URL to parse.
52
+ * @return {!Map } A map with the extracted URL parameters.
53
+ */
54
+ function parseQuery ( url ) {
55
+ const query = url . split ( "?" ) [ 1 ] ;
56
+ if ( query ) {
57
+ return query . split ( "&" )
58
+ . reduce ( function ( o , e ) {
59
+ var temp = e . split ( "=" ) ;
60
+ var key = temp [ 0 ] . trim ( ) ;
61
+ var value = temp [ 1 ] . trim ( ) ;
62
+ value = isNaN ( value ) ? value : Number ( value ) ;
63
+ if ( o [ key ] ) {
64
+ o [ key ] . push ( value ) ;
65
+ } else {
66
+ o [ key ] = [ value ] ;
67
+ }
68
+ return o ;
69
+ } , { } ) ;
70
+ }
71
+ return null ;
72
+ }
73
+
74
+ // [END add_ons_case_preview_link]
75
+ // [END add_ons_preview_link]
76
+
77
+ // [START add_ons_3p_resources]
78
+ // [START add_ons_3p_resources_create_case_card]
79
+
80
+ /**
81
+ * Produces a support case creation form card.
82
+ *
83
+ * @param {!Object } event The event object.
84
+ * @param {!Object= } errors An optional map of per-field error messages.
85
+ * @param {boolean } isUpdate Whether to return the form as an update card navigation.
86
+ * @return {!Card|!ActionResponse } The resulting card or action response.
87
+ */
88
+ function createCaseInputCard ( event , errors , isUpdate ) {
89
+
90
+ const cardHeader = CardService . newCardHeader ( )
91
+ . setTitle ( 'Create a support case' )
92
+
93
+ const cardSectionTextInput1 = CardService . newTextInput ( )
94
+ . setFieldName ( 'name' )
95
+ . setTitle ( 'Name' )
96
+ . setMultiline ( false ) ;
97
+
98
+ const cardSectionTextInput2 = CardService . newTextInput ( )
99
+ . setFieldName ( 'description' )
100
+ . setTitle ( 'Description' )
101
+ . setMultiline ( true ) ;
102
+
103
+ const cardSectionSelectionInput1 = CardService . newSelectionInput ( )
104
+ . setFieldName ( 'priority' )
105
+ . setTitle ( 'Priority' )
106
+ . setType ( CardService . SelectionInputType . DROPDOWN )
107
+ . addItem ( 'P0' , 'P0' , false )
108
+ . addItem ( 'P1' , 'P1' , false )
109
+ . addItem ( 'P2' , 'P2' , false )
110
+ . addItem ( 'P3' , 'P3' , false ) ;
111
+
112
+ const cardSectionSelectionInput2 = CardService . newSelectionInput ( )
113
+ . setFieldName ( 'impact' )
114
+ . setTitle ( 'Impact' )
115
+ . setType ( CardService . SelectionInputType . CHECK_BOX )
116
+ . addItem ( 'Blocks a critical customer operation' , 'Blocks a critical customer operation' , false ) ;
117
+
118
+ const cardSectionButtonListButtonAction = CardService . newAction ( )
119
+ . setPersistValues ( true )
120
+ . setFunctionName ( 'submitCaseCreationForm' )
121
+ . setParameters ( { } ) ;
122
+
123
+ const cardSectionButtonListButton = CardService . newTextButton ( )
124
+ . setText ( 'Create' )
125
+ . setTextButtonStyle ( CardService . TextButtonStyle . TEXT )
126
+ . setOnClickAction ( cardSectionButtonListButtonAction ) ;
127
+
128
+ const cardSectionButtonList = CardService . newButtonSet ( )
129
+ . addButton ( cardSectionButtonListButton ) ;
130
+
131
+ // Builds the form inputs with error texts for invalid values.
132
+ const cardSection = CardService . newCardSection ( ) ;
133
+ if ( errors ?. name ) {
134
+ cardSection . addWidget ( createErrorTextParagraph ( errors . name ) ) ;
135
+ }
136
+ cardSection . addWidget ( cardSectionTextInput1 ) ;
137
+ if ( errors ?. description ) {
138
+ cardSection . addWidget ( createErrorTextParagraph ( errors . description ) ) ;
139
+ }
140
+ cardSection . addWidget ( cardSectionTextInput2 ) ;
141
+ if ( errors ?. priority ) {
142
+ cardSection . addWidget ( createErrorTextParagraph ( errors . priority ) ) ;
143
+ }
144
+ cardSection . addWidget ( cardSectionSelectionInput1 ) ;
145
+ if ( errors ?. impact ) {
146
+ cardSection . addWidget ( createErrorTextParagraph ( errors . impact ) ) ;
147
+ }
148
+
149
+ cardSection . addWidget ( cardSectionSelectionInput2 ) ;
150
+ cardSection . addWidget ( cardSectionButtonList ) ;
151
+
152
+ const card = CardService . newCardBuilder ( )
153
+ . setHeader ( cardHeader )
154
+ . addSection ( cardSection )
155
+ . build ( ) ;
156
+
157
+ if ( isUpdate ) {
158
+ return CardService . newActionResponseBuilder ( )
159
+ . setNavigation ( CardService . newNavigation ( ) . updateCard ( card ) )
160
+ . build ( ) ;
161
+ } else {
162
+ return card ;
163
+ }
164
+ }
165
+
166
+ // [END add_ons_3p_resources_create_case_card]
167
+ // [START add_ons_3p_resources_submit_create_case]
168
+
169
+ /**
170
+ * Submits the creation form. If valid, returns a render action
171
+ * that inserts a new link into the document. If invalid, returns an
172
+ * update card navigation that re-renders the creation form with error messages.
173
+ *
174
+ * @param {!Object } event The event object with form input values.
175
+ * @return {!ActionResponse|!SubmitFormResponse } The resulting response.
176
+ */
177
+ function submitCaseCreationForm ( event ) {
178
+ const caseDetails = {
179
+ name : event . formInput . name ,
180
+ description : event . formInput . description ,
181
+ priority : event . formInput . priority ,
182
+ impact : ! ! event . formInput . impact ,
183
+ } ;
184
+
185
+ const errors = validateFormInputs ( caseDetails ) ;
186
+ if ( Object . keys ( errors ) . length > 0 ) {
187
+ return createCaseInputCard ( event , errors , /* isUpdate= */ true ) ;
188
+ } else {
189
+ const title = `Case ${ caseDetails . name } ` ;
190
+ // Adds the case details as parameters to the generated link URL.
191
+ const url = 'https://example.com/support/cases/?' + generateQuery ( caseDetails ) ;
192
+ return createLinkRenderAction ( title , url ) ;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Build a query path with URL parameters.
198
+ *
199
+ * @param {!Map } parameters A map with the URL parameters.
200
+ * @return {!string } The resulting query path.
201
+ */
202
+ function generateQuery ( parameters ) {
203
+ return Object . entries ( parameters ) . flatMap ( ( [ k , v ] ) =>
204
+ Array . isArray ( v ) ? v . map ( e => `${ k } =${ encodeURIComponent ( e ) } ` ) : `${ k } =${ encodeURIComponent ( v ) } `
205
+ ) . join ( "&" ) ;
206
+ }
207
+
208
+ // [END add_ons_3p_resources_submit_create_case]
209
+ // [START add_ons_3p_resources_validate_inputs]
210
+
211
+ /**
212
+ * Validates case creation form input values.
213
+ *
214
+ * @param {!Object } caseDetails The values of each form input submitted by the user.
215
+ * @return {!Object } A map from field name to error message. An empty object
216
+ * represents a valid form submission.
217
+ */
218
+ function validateFormInputs ( caseDetails ) {
219
+ const errors = { } ;
220
+ if ( ! caseDetails . name ) {
221
+ errors . name = 'You must provide a name' ;
222
+ }
223
+ if ( ! caseDetails . description ) {
224
+ errors . description = 'You must provide a description' ;
225
+ }
226
+ if ( ! caseDetails . priority ) {
227
+ errors . priority = 'You must provide a priority' ;
228
+ }
229
+ if ( caseDetails . impact && caseDetails . priority !== 'P0' && caseDetails . priority !== 'P1' ) {
230
+ errors . impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1' ;
231
+ }
232
+
233
+ return errors ;
234
+ }
235
+
236
+ /**
237
+ * Returns a text paragraph with red text indicating a form field validation error.
238
+ *
239
+ * @param {string } errorMessage A description of input value error.
240
+ * @return {!TextParagraph } The resulting text paragraph.
241
+ */
242
+ function createErrorTextParagraph ( errorMessage ) {
243
+ return CardService . newTextParagraph ( )
244
+ . setText ( '<font color=\"#BA0300\"><b>Error:</b> ' + errorMessage + '</font>' ) ;
245
+ }
246
+
247
+ // [END add_ons_3p_resources_validate_inputs]
248
+ // [START add_ons_3p_resources_link_render_action]
249
+
250
+ /**
251
+ * Returns a submit form response that inserts a link into the document.
252
+ *
253
+ * @param {string } title The title of the link to insert.
254
+ * @param {string } url The URL of the link to insert.
255
+ * @return {!SubmitFormResponse } The resulting submit form response.
256
+ */
257
+ function createLinkRenderAction ( title , url ) {
258
+ return {
259
+ renderActions : {
260
+ action : {
261
+ links : [ {
262
+ title : title ,
263
+ url : url
264
+ } ]
265
+ }
266
+ }
267
+ } ;
268
+ }
269
+
270
+ // [END add_ons_3p_resources_link_render_action]
271
+ // [END add_ons_3p_resources]
0 commit comments