PluginProbe ʕ •ᴥ•ʔ
Presto Player / 4.3.2
Presto Player v4.3.2
4.3.2 4.3.1 4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / src / admin / dashboard / components / Popup / EmailFormPopup.js
presto-player / src / admin / dashboard / components / Popup Last commit date
ConfirmPopup.js 2 months ago EmailFormPopup.js 2 months ago EmailFormPopupUtils.js 2 months ago
EmailFormPopup.js
199 lines
1 /**
2 * Add/Edit email submission popup: email, status, visibility (public/private only), published date/time.
3 * Visibility drives effectiveStatus (private => post status 'private'); password protection was removed.
4 */
5 // eslint-disable-next-line import/no-extraneous-dependencies
6 import React, { useState, useEffect } from 'react';
7 import { __ } from '@wordpress/i18n';
8 import { Dialog, Button, Container, Input, Loader, Select, Tooltip } from '@bsf/force-ui';
9 import { Info, Settings } from 'lucide-react';
10 import PostScheduleField from '../PostScheduleField';
11 import {
12 DEFAULT_STATE,
13 getInitialState,
14 getOptionLabel,
15 toPublishedDateTime,
16 STATUS_OPTIONS,
17 VISIBILITY_OPTIONS,
18 } from './EmailFormPopupUtils';
19
20 function OptionSelect( { options, value, onChange, label, tooltip } ) {
21 return (
22 <div>
23 { label && (
24 <div className="flex items-center gap-1 mb-1">
25 <span className="text-sm font-medium text-field-label">{ label }</span>
26 { tooltip && (
27 <Tooltip content={ tooltip } placement="right">
28 <Info className="size-3.5 text-icon-secondary cursor-help shrink-0" />
29 </Tooltip>
30 ) }
31 </div>
32 ) }
33 <Select by="value" size="md" value={ { value, label: getOptionLabel( options, value ) } } onChange={ ( o ) => o && onChange( o.value ) }>
34 <Select.Button render={ ( v ) => v?.label } />
35 <Select.Options>
36 { options.map( ( o ) => <Select.Option key={ o.value } value={ { value: o.value, label: o.label } }>{ o.label }</Select.Option> ) }
37 </Select.Options>
38 </Select>
39 </div>
40 );
41 }
42
43 const EmailFormPopup = ( { open, onClose, initialData, onSuccess } ) => {
44 const [ email, setEmail ] = useState( '' );
45 const [ status, setStatus ] = useState( 'publish' );
46 const [ visibility, setVisibility ] = useState( 'public' );
47 const [ publishedDate, setPublishedDate ] = useState( null );
48 const [ publishedTimeValue, setPublishedTimeValue ] = useState( '' );
49 const [ publishedPeriod, setPublishedPeriod ] = useState( '-1' );
50 const [ saving, setSaving ] = useState( false );
51 const [ errorMessage, setErrorMessage ] = useState( '' );
52 // Tracks whether the user touched the date/time/period picker. Without this,
53 // every save round-trips the time through the 10-min picker granularity and
54 // drifts the stored post_date by up to 10 minutes — losing precision on edits
55 // that didn't intend to change the timestamp at all.
56 const [ dateTouched, setDateTouched ] = useState( false );
57
58 const isEditMode = !! initialData?.id;
59
60 useEffect( () => {
61 if ( open ) {
62 const s = getInitialState( initialData );
63 setEmail( s.email );
64 setStatus( s.status );
65 setVisibility( s.visibility );
66 setPublishedDate( s.publishedDate );
67 setPublishedTimeValue( s.publishedTimeValue );
68 setPublishedPeriod( s.publishedPeriod );
69 setErrorMessage( '' );
70 setSaving( false );
71 setDateTouched( false );
72 }
73 }, [ open, initialData ] );
74
75 const resetAndClose = () => {
76 if ( saving ) return;
77 setEmail( DEFAULT_STATE.email );
78 setStatus( DEFAULT_STATE.status );
79 setVisibility( DEFAULT_STATE.visibility );
80 setPublishedDate( null );
81 setPublishedTimeValue( '' );
82 setPublishedPeriod( '-1' );
83 setErrorMessage( '' );
84 setDateTouched( false );
85 onClose();
86 };
87
88 // Client-side hint only; server validates with is_email() and is authoritative.
89 const isValidEmail = ( v ) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test( ( v || '' ).trim() );
90
91 const handleSave = async () => {
92 const trimmed = ( email || '' ).trim();
93 setErrorMessage( '' );
94 if ( ! trimmed ) {
95 setErrorMessage( __( 'Please enter an email address.', 'presto-player' ) );
96 return;
97 }
98 if ( ! isValidEmail( trimmed ) ) {
99 setErrorMessage( __( 'Please enter a valid email address.', 'presto-player' ) );
100 return;
101 }
102 setSaving( true );
103 try {
104 const effectiveStatus = visibility === 'private' ? 'private' : status;
105 const payload = {
106 ...( isEditMode && { id: initialData.id, email: trimmed, video_title: initialData.video_title, preset_name: initialData.preset_name } ),
107 ...( ! isEditMode && { email: trimmed } ),
108 status: effectiveStatus,
109 visibility,
110 ...( ( dateTouched || ! isEditMode ) && {
111 date: toPublishedDateTime( publishedDate, publishedTimeValue, publishedPeriod ),
112 } ),
113 };
114 const result = onSuccess?.( payload );
115 if ( result?.then ) await result;
116 } catch ( err ) {
117 setErrorMessage( err?.message || __( 'Something went wrong.', 'presto-player' ) );
118 } finally {
119 setSaving( false );
120 }
121 };
122
123 return (
124 <Dialog design="simple" exitOnEsc scrollLock open={ open } setOpen={ ( v ) => ! v && resetAndClose() }>
125 <Dialog.Backdrop />
126 <Dialog.Panel className="max-w-2xl w-full overflow-visible">
127 <Dialog.Header>
128 <div className="flex items-center justify-between">
129 <div className="flex items-center gap-2">
130 <Settings size={ 20 } />
131 <Dialog.Title>
132 { __( 'Post Settings', 'presto-player' ) }
133 </Dialog.Title>
134 </div>
135 <Dialog.CloseButton onClick={ resetAndClose } />
136 </div>
137 </Dialog.Header>
138
139 <Dialog.Body>
140 <Container containerType="flex" direction="column">
141 <Container.Item>
142 <Input
143 type="email"
144 size="md"
145 label={ __( 'Email', 'presto-player' ) }
146 value={ email }
147 onChange={ ( value ) => setEmail( value ) }
148 placeholder={ __( 'Enter email address…', 'presto-player' ) }
149 className="w-full"
150 />
151 { errorMessage && (
152 <p className="text-sm text-text-error mt-1" role="alert">{ errorMessage }</p>
153 ) }
154 </Container.Item>
155
156 <Container.Item>
157 <OptionSelect
158 options={ STATUS_OPTIONS }
159 value={ status }
160 onChange={ setStatus }
161 label={ __( 'Status', 'presto-player' ) }
162 tooltip={ __( 'Controls the publication state — Published (live), Pending Review (awaiting approval), or Draft (not live yet).', 'presto-player' ) }
163 />
164 </Container.Item>
165
166 <Container.Item>
167 <OptionSelect
168 options={ VISIBILITY_OPTIONS }
169 value={ visibility }
170 onChange={ setVisibility }
171 label={ __( 'Visibility', 'presto-player' ) }
172 tooltip={ __( 'Controls who can access this submission — Public (everyone) or Private (admins only). Setting Private overrides the Status.', 'presto-player' ) }
173 />
174 </Container.Item>
175
176 <PostScheduleField
177 date={ publishedDate }
178 time={ publishedTimeValue }
179 period={ publishedPeriod }
180 setDate={ ( v ) => { setDateTouched( true ); setPublishedDate( v ); } }
181 setTime={ ( v ) => { setDateTouched( true ); setPublishedTimeValue( v ); } }
182 setPeriod={ ( v ) => { setDateTouched( true ); setPublishedPeriod( v ); } }
183 />
184 </Container>
185 </Dialog.Body>
186
187 <Dialog.Footer className="pt-0 justify-between">
188 <Button size="md" variant="outline" onClick={ resetAndClose } disabled={ saving }>{ __( 'Cancel', 'presto-player' ) }</Button>
189 <Button size="md" disabled={ saving } onClick={ handleSave } iconPosition="right" icon={ saving && <Loader icon={ null } size="sm" variant="primary" /> }>
190 { __( 'Save Settings', 'presto-player' ) }
191 </Button>
192 </Dialog.Footer>
193 </Dialog.Panel>
194 </Dialog>
195 );
196 };
197
198 export default EmailFormPopup;
199