1
- // components/generics-template.tsx
2
1
import React , { useState , useEffect } from 'react' ;
3
2
import { Switch } from '@/components/ui/switch' ;
4
3
import { useToast } from '@/hooks/use-toast' ;
5
4
import { Dialog , DialogContent } from '@/components/ui/dialog' ;
6
5
import Image from 'next/image' ;
6
+ import TemplatePreviewCard from '@/components/global/generic-template-card/template-preview-card' ;
7
+
7
8
8
9
interface Button {
9
10
type : 'web_url' | 'postback' ;
@@ -26,12 +27,18 @@ export const GenericTemplate = ({
26
27
} : GenericTemplateProps ) => {
27
28
const [ isPreviewOpen , setIsPreviewOpen ] = useState ( false ) ;
28
29
const [ showForm , setShowForm ] = useState ( true ) ;
29
- const [ template , setTemplate ] = useState ( {
30
+ const [ template , setTemplate ] = useState < {
31
+ title : string ;
32
+ subtitle : string ;
33
+ imageUrl : string ;
34
+ defaultAction : string ;
35
+ buttons : Button [ ] ;
36
+ } > ( {
30
37
title : '' ,
31
38
subtitle : '' ,
32
39
imageUrl : '' ,
33
40
defaultAction : '' ,
34
- buttons : [ ] as Button [ ]
41
+ buttons : [ ] // Ensure buttons is initialized as an empty array
35
42
} ) ;
36
43
const [ isSaving , setIsSaving ] = useState ( false ) ;
37
44
const { toast } = useToast ( ) ;
@@ -43,15 +50,6 @@ export const GenericTemplate = ({
43
50
44
51
const fetchTemplate = async ( ) => {
45
52
try {
46
- const cachedTemplate = sessionStorage . getItem ( `template-${ automationId } ` ) ;
47
- if ( cachedTemplate ) {
48
- const parsedTemplate = JSON . parse ( cachedTemplate ) ;
49
- setTemplate ( parsedTemplate ) ;
50
- setShowForm ( false ) ;
51
- onTemplateCreated ( ) ;
52
- return ;
53
- }
54
-
55
53
const response = await fetch ( `/api/templates/${ automationId } ` , { signal } ) ;
56
54
const data = await response . json ( ) ;
57
55
@@ -60,13 +58,17 @@ export const GenericTemplate = ({
60
58
setTemplate ( data . template ) ;
61
59
setShowForm ( false ) ;
62
60
onTemplateCreated ( ) ;
63
- } else if ( ! hasTemplates ) {
61
+ } else {
62
+ // Clear cached template if it doesn't exist in database
63
+ sessionStorage . removeItem ( `template-${ automationId } ` ) ;
64
64
setShowForm ( true ) ;
65
- toast ( {
66
- title : "No Template Found" ,
67
- description : "Create Generic Template from the Drawer" ,
68
- variant : "destructive"
69
- } ) ;
65
+ if ( ! hasTemplates ) {
66
+ toast ( {
67
+ title : "No Template Found" ,
68
+ description : "Create Generic Template from the Drawer" ,
69
+ variant : "destructive"
70
+ } ) ;
71
+ }
70
72
}
71
73
} catch ( error ) {
72
74
if ( error instanceof Error && error . name !== 'AbortError' ) {
@@ -87,6 +89,7 @@ export const GenericTemplate = ({
87
89
88
90
const isValid = template . title . length > 0 &&
89
91
template . imageUrl . length > 0 &&
92
+ Array . isArray ( template . buttons ) &&
90
93
template . buttons . length > 0 &&
91
94
template . buttons . every ( button =>
92
95
button . title . length > 0 &&
@@ -124,52 +127,57 @@ export const GenericTemplate = ({
124
127
} ) ) ;
125
128
} ;
126
129
127
- const handleCreateTemplate = async ( ) => {
128
- if ( ! isValid ) {
129
- toast ( {
130
- title : "Missing Required Fields" ,
131
- description : "Please fill in title, image URL, and at least one valid button" ,
132
- variant : "destructive"
133
- } ) ;
134
- return ;
135
- }
136
-
137
- setIsSaving ( true ) ;
138
- try {
139
- const response = await fetch ( '/api/templates' , {
140
- method : 'POST' ,
141
- headers : {
142
- 'Content-Type' : 'application/json' ,
143
- 'Accept' : 'application/json'
144
- } ,
145
- body : JSON . stringify ( {
146
- automationId,
147
- template : {
148
- ...template ,
149
- buttons : template . buttons . map ( btn => ( {
150
- ...btn ,
151
- title : btn . title . substring ( 0 , 20 )
152
- } ) )
153
- }
154
- } )
155
- } ) ;
130
+ const handleCreateTemplate = async ( ) => {
131
+ if ( ! isValid ) {
132
+ toast ( {
133
+ title : "Missing Required Fields" ,
134
+ description : "Please fill in title, image URL, and at least one valid button" ,
135
+ variant : "destructive"
136
+ } ) ;
137
+ return ;
138
+ }
156
139
157
- const data = await response . json ( ) ;
158
- if ( ! response . ok ) throw new Error ( data . message || 'Failed to save template' ) ;
140
+ setIsSaving ( true ) ;
141
+ try {
142
+ const response = await fetch ( '/api/templates' , {
143
+ method : 'POST' ,
144
+ headers : {
145
+ 'Content-Type' : 'application/json' ,
146
+ 'Accept' : 'application/json'
147
+ } ,
148
+ body : JSON . stringify ( {
149
+ automationId,
150
+ template : {
151
+ title : template . title ,
152
+ subtitle : template . subtitle ,
153
+ imageUrl : template . imageUrl ,
154
+ defaultAction : template . defaultAction ,
155
+ buttons : template . buttons . map ( btn => ( {
156
+ ...btn ,
157
+ title : btn . title . substring ( 0 , 20 )
158
+ } ) )
159
+ }
160
+ } )
161
+ } ) ;
159
162
160
- toast ( { title : "Success" , description : "Template saved successfully" } ) ;
161
- setShowForm ( false ) ;
162
- onTemplateCreated ( ) ;
163
- } catch ( error ) {
164
- toast ( {
165
- title : "Error" ,
166
- description : error instanceof Error ? error . message : "Failed to save template" ,
167
- variant : "destructive"
168
- } ) ;
169
- } finally {
170
- setIsSaving ( false ) ;
171
- }
172
- } ;
163
+ const data = await response . json ( ) ;
164
+ if ( ! response . ok ) throw new Error ( data . message || 'Failed to save template' ) ;
165
+
166
+ // Save to session storage and update UI
167
+ sessionStorage . setItem ( `template-${ automationId } ` , JSON . stringify ( data ) ) ;
168
+ toast ( { title : "Success" , description : "Template saved successfully" } ) ;
169
+ setShowForm ( false ) ;
170
+ onTemplateCreated ( ) ;
171
+ } catch ( error ) {
172
+ toast ( {
173
+ title : "Error" ,
174
+ description : error instanceof Error ? error . message : "Failed to save template" ,
175
+ variant : "destructive"
176
+ } ) ;
177
+ } finally {
178
+ setIsSaving ( false ) ;
179
+ }
180
+ } ;
173
181
174
182
if ( ! showForm ) {
175
183
return (
@@ -206,7 +214,7 @@ export const GenericTemplate = ({
206
214
</ p >
207
215
) }
208
216
< div className = "space-y-2 pt-3" >
209
- { template . buttons . map ( ( button , index ) => (
217
+ { Array . isArray ( template . buttons ) && template . buttons . map ( ( button , index ) => (
210
218
< button
211
219
key = { index }
212
220
className = "w-full p-3 text-center border-2 rounded-lg text-[#768ADD] border-[#768ADD] hover:bg-[#768ADD]/10 transition-all duration-200 font-medium text-sm"
@@ -290,12 +298,12 @@ export const GenericTemplate = ({
290
298
< div >
291
299
< label className = "block text-sm font-medium text-gray-700 mb-2" > Buttons (Max 3) *</ label >
292
300
< div className = "space-y-3 max-h-[200px] min-h-[60px] overflow-y-auto scrollbar-thin scrollbar-track-gray-100" >
293
- { template . buttons . map ( ( button , btnIndex ) => (
301
+ { Array . isArray ( template . buttons ) && template . buttons . map ( ( button , btnIndex ) => (
294
302
< div key = { btnIndex } className = "flex flex-col gap-2 w-full" >
295
303
< select
296
304
className = "w-full p-2 border border-gray-200 rounded-md !bg-white !text-black focus:outline-none"
297
305
value = { button . type }
298
- onChange = { ( e ) => handleButtonChange ( btnIndex , 'type' , e . target . value ) }
306
+ onChange = { ( e ) => handleButtonChange ( btnIndex , 'type' , e . target . value as 'web_url' | 'postback' ) }
299
307
>
300
308
< option value = "web_url" > Web URL</ option >
301
309
< option value = "postback" > Postback</ option >
@@ -363,52 +371,7 @@ export const GenericTemplate = ({
363
371
364
372
< Dialog open = { isPreviewOpen } onOpenChange = { setIsPreviewOpen } >
365
373
< DialogContent className = "bg-white p-0 overflow-hidden max-w-sm rounded-lg shadow-lg" >
366
- < div className = "flex flex-col" >
367
- { template . imageUrl ? (
368
- < div className = "relative w-full h-48 overflow-hidden rounded-t-lg" >
369
- < Image
370
- src = { template . imageUrl }
371
- alt = { template . title }
372
- fill
373
- className = "object-cover transition-transform hover:scale-105"
374
- sizes = "(max-width: 768px) 100vw, 400px"
375
- />
376
- </ div >
377
- ) : (
378
- < div className = "w-full h-48 bg-gray-100 rounded-t-lg flex items-center justify-center" >
379
- < div className = "text-gray-400 text-center p-4" >
380
- < p className = "text-sm" > Preview your template</ p >
381
- < p className = "text-xs mt-1" > Add an image URL to see it here</ p >
382
- </ div >
383
- </ div >
384
- ) }
385
- < div className = "p-6 space-y-3" >
386
- < h3 className = "font-semibold text-xl text-gray-900" >
387
- { template . title || 'Template Title' }
388
- </ h3 >
389
- { template . subtitle && (
390
- < p className = "text-gray-600 text-sm leading-relaxed" >
391
- { template . subtitle }
392
- </ p >
393
- ) }
394
- < div className = "space-y-2 pt-3" >
395
- { template . buttons . length > 0 ? (
396
- template . buttons . map ( ( button , index ) => (
397
- < button
398
- key = { index }
399
- className = "w-full p-3 text-center border-2 rounded-lg text-[#768ADD] border-[#768ADD] hover:bg-[#768ADD]/10 transition-all duration-200 font-medium text-sm"
400
- >
401
- { button . title || 'Button Text' }
402
- </ button >
403
- ) )
404
- ) : (
405
- < div className = "text-center text-gray-400 text-sm" >
406
- < p > Add buttons to see them here</ p >
407
- </ div >
408
- ) }
409
- </ div >
410
- </ div >
411
- </ div >
374
+ < TemplatePreviewCard template = { template } />
412
375
</ DialogContent >
413
376
</ Dialog >
414
377
</ div >
0 commit comments