11import { Button2 } from "@courselit/components-library" ;
22import { CheckCircled , Sync } from "@courselit/icons" ;
33import { Check , Copy } from "lucide-react" ;
4- import { useState } from "react" ;
4+ import { useState , useEffect , useRef } from "react" ;
55
66export const EmailEditorLayout = ( {
77 children,
@@ -11,68 +11,120 @@ export const EmailEditorLayout = ({
1111 children : React . ReactNode ;
1212 isSaving ?: boolean ;
1313 type ?: "sequence" | "product" ;
14- } ) => (
15- < div className = "flex flex-col h-screen bg-muted/10" >
16- < div className = "flex w-full h-full gap-4 p-4 bg-muted/10" >
17- < div className = "max-w-[220px] flex flex-col" >
18- < div className = "flex items-center mb-4" >
19- < h2 className = "text-base font-semibold text-gray-800" >
20- Variables
21- </ h2 >
22- </ div >
23- < div className = "flex flex-col gap-5 flex-1" >
24- < p className = "text-xs text-muted-foreground mb-1" >
25- You can use the following variables in your content.
26- </ p >
27- < p className = "text-xs text-muted-foreground mb-2" >
28- These will be replaced with the actual data while
29- sending emails.
30- </ p >
31- < div className = "flex flex-col gap-3" >
32- { type === "product" && (
33- < >
34- < VariableDisplay
35- variable = "{{ product.title }}"
36- description = "The title of the product"
37- />
38- < VariableDisplay
39- variable = "{{ product.url }}"
40- description = "The URL of the product"
41- />
42- </ >
14+ } ) => {
15+ const [ showSavedToast , setShowSavedToast ] = useState ( false ) ;
16+ const [ prevIsSaving , setPrevIsSaving ] = useState ( isSaving ) ;
17+ const timerRef = useRef < NodeJS . Timeout | null > ( null ) ;
18+
19+ useEffect ( ( ) => {
20+ // Hide toast when starting a new save operation
21+ if ( ! prevIsSaving && isSaving ) {
22+ setShowSavedToast ( false ) ;
23+ // Clear any existing timer
24+ if ( timerRef . current ) {
25+ clearTimeout ( timerRef . current ) ;
26+ timerRef . current = null ;
27+ }
28+ }
29+
30+ // Show toast when saving changes from true to false (save completed)
31+ if ( prevIsSaving && ! isSaving ) {
32+ setShowSavedToast ( true ) ;
33+ // Clear any existing timer before setting a new one
34+ if ( timerRef . current ) {
35+ clearTimeout ( timerRef . current ) ;
36+ }
37+ timerRef . current = setTimeout ( ( ) => {
38+ setShowSavedToast ( false ) ;
39+ timerRef . current = null ;
40+ } , 5000 ) ;
41+ }
42+ setPrevIsSaving ( isSaving ) ;
43+ } , [ isSaving , prevIsSaving ] ) ;
44+
45+ // Cleanup timer on unmount
46+ useEffect ( ( ) => {
47+ return ( ) => {
48+ if ( timerRef . current ) {
49+ clearTimeout ( timerRef . current ) ;
50+ }
51+ } ;
52+ } , [ ] ) ;
53+
54+ return (
55+ < div className = "flex flex-col h-screen bg-muted/10" >
56+ < div className = "flex w-full h-full gap-4 p-4 bg-muted/10" >
57+ < div className = "max-w-[220px] flex flex-col" >
58+ < div className = "flex items-center mb-4" >
59+ < h2 className = "text-base font-semibold text-gray-800" >
60+ Variables
61+ </ h2 >
62+ </ div >
63+ < div className = "flex flex-col gap-5 flex-1" >
64+ < p className = "text-xs text-muted-foreground mb-1" >
65+ You can use the following variables in your content.
66+ </ p >
67+ < p className = "text-xs text-muted-foreground mb-2" >
68+ These will be replaced with the actual data while
69+ sending emails.
70+ </ p >
71+ < div className = "flex flex-col gap-3" >
72+ { type === "product" && (
73+ < >
74+ < VariableDisplay
75+ variable = "{{ product.title }}"
76+ description = "The title of the product"
77+ />
78+ < VariableDisplay
79+ variable = "{{ product.url }}"
80+ description = "The URL of the product"
81+ />
82+ </ >
83+ ) }
84+ < VariableDisplay
85+ variable = "{{ subscriber.email }}"
86+ description = "The email of the subscriber"
87+ />
88+ < VariableDisplay
89+ variable = "{{ subscriber.name }}"
90+ description = "The name of the subscriber"
91+ />
92+ < VariableDisplay
93+ variable = "{{ address }}"
94+ description = "Your mailing address"
95+ />
96+ < VariableDisplay
97+ variable = "{{ unsubscribe_link }}"
98+ description = "A link to unsubscribe from the marketing emails"
99+ />
100+ </ div >
101+ </ div >
102+ < div className = "flex items-center mt-4 h-6" >
103+ { isSaving && (
104+ < div className = "flex items-center gap-2" >
105+ < Sync className = "h-4 w-4 animate-spin text-muted-foreground" />
106+ < span className = "text-sm text-muted-foreground" >
107+ Saving...
108+ </ span >
109+ </ div >
110+ ) }
111+ { showSavedToast && (
112+ < div className = "flex items-center gap-2 bg-background border px-3 py-1 rounded-md shadow-sm animate-in fade-in-0 duration-300" >
113+ < CheckCircled className = "h-4 w-4 text-primary" />
114+ < span className = "text-sm font-medium text-foreground" >
115+ Changes are saved
116+ </ span >
117+ </ div >
43118 ) }
44- < VariableDisplay
45- variable = "{{ subscriber.email }}"
46- description = "The email of the subscriber"
47- />
48- < VariableDisplay
49- variable = "{{ subscriber.name }}"
50- description = "The name of the subscriber"
51- />
52- < VariableDisplay
53- variable = "{{ address }}"
54- description = "Your mailing address"
55- />
56- < VariableDisplay
57- variable = "{{ unsubscribe_link }}"
58- description = "A link to unsubscribe from the marketing emails"
59- />
60119 </ div >
61120 </ div >
62- < div className = "flex items-center mt-4" >
63- { isSaving ? (
64- < Sync className = "h-4 w-4 animate-spin text-muted-foreground" />
65- ) : (
66- < CheckCircled className = "h-4 w-4 text-green-500" />
67- ) }
121+ < div className = "w-full rounded-xl overflow-hidden border bg-background/98 backdrop-blur supports-[backdrop-filter]:bg-background/80 shadow-sm" >
122+ { children }
68123 </ div >
69124 </ div >
70- < div className = "w-full rounded-xl overflow-hidden border bg-background/98 backdrop-blur supports-[backdrop-filter]:bg-background/80 shadow-sm" >
71- { children }
72- </ div >
73125 </ div >
74- </ div >
75- ) ;
126+ ) ;
127+ } ;
76128
77129export const VariableDisplay = ( {
78130 variable,
0 commit comments