PluginProbe ʕ •ᴥ•ʔ
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI / 3.5.3
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI v3.5.3
3.5.3 3.5.2 3.5.1 3.5.0 3.4.8 3.4.7 3.4.6 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5.1 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.10 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.6.1 1.6.7 1.7.0 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.2 1.7.2.1 1.7.2.2 1.7.3 1.7.4 1.7.5 1.7.5.1 1.7.5.2 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.8.0 1.8.0.1 1.8.1 1.8.2 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.0.1 1.9.1 1.9.2 1.9.3 1.9.4 1.9.4.1 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.0.1 2.0.1 2.0.2 2.0.3 2.0.3.1 2.0.4 2.0.4.1 2.0.5 2.0.6 2.0.7 2.0.8 2.0.8.1 2.0.9 3.0.0 3.0.0.1 3.0.1 3.0.2 3.0.3 3.0.3.1 3.0.4 3.0.4.1 3.0.4.2 3.0.5 3.0.5.1 3.0.5.2 3.0.6 3.0.6.1 3.0.7.1 3.0.8 3.0.8.1 3.0.9 3.0.9.1 3.0.9.2 3.0.9.3 3.0.9.4 3.0.9.5 3.1.0 3.1.1 3.1.2 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.0 3.4.0 3.4.1 3.4.2 3.4.2.1 3.4.3 3.4.4 3.4.5 trunk 1.0 1.0.1 1.0.2 1.0.3
everest-forms / src / templates / components / TemplateList.tsx
everest-forms / src / templates / components Last commit date
CreateFormCTA.tsx 1 month ago CreateWithAI.tsx 1 month ago Main.tsx 1 month ago PluginStatus.tsx 1 month ago Sidebar.tsx 1 month ago TemplateList.tsx 1 month ago TemplatesSkeleton.tsx 5 months ago
TemplateList.tsx
894 lines
1 import {
2 Box,
3 Button,
4 Flex,
5 HStack,
6 Icon,
7 Image,
8 Modal,
9 ModalBody,
10 ModalCloseButton,
11 ModalContent,
12 ModalHeader,
13 ModalOverlay,
14 Text,
15 useDisclosure,
16 useToast,
17 VStack,
18 } from '@chakra-ui/react';
19 import { useMutation, useQueryClient } from '@tanstack/react-query';
20 import apiFetch from '@wordpress/api-fetch';
21 import { __ } from '@wordpress/i18n';
22 import React, { useEffect, useState } from 'react';
23 import { FaHeart, FaRegHeart } from 'react-icons/fa';
24 import { FiArrowRight } from 'react-icons/fi';
25 import { LuSparkles } from 'react-icons/lu';
26 import notFoundImage from '../images/not-found-image.png';
27 import { templatesScriptData } from '../utils/global';
28 import PluginStatus from './PluginStatus';
29
30 interface Template {
31 id: number;
32 title: string;
33 slug: string;
34 imageUrl: string;
35 description: string;
36 isPro: boolean;
37 preview_link?: string;
38 addons?: { [key: string]: string };
39 categories?: string[];
40 }
41
42 interface TemplateListProps {
43 selectedCategory: string;
44 templates: Template[];
45 onCreateWithAI?: (formId?: number, title?: string) => void;
46 }
47
48 const { restURL, security } = templatesScriptData;
49
50 // "Edit with AI" is disabled (shown greyed-out) on local / development sites where the AI gateway is unavailable.
51 const AI_ENABLED = !!templatesScriptData?.aiEnabled;
52
53 interface CreateTemplateResponse {
54 success: boolean;
55 data?: {
56 id: number;
57 redirect: string;
58 status: number;
59 };
60 message?: string;
61 }
62
63 const TemplateList: React.FC<TemplateListProps> = ({
64 selectedCategory,
65 templates,
66 onCreateWithAI,
67 }) => {
68 const [previewTemplate, setPreviewTemplate] = useState<Template | null>(null);
69 const [selectedTemplateSlug, setSelectedTemplateSlug] = useState<string>('');
70 const [modalState, setModalState] = useState<'addons' | 'choose'>('choose');
71 const { isOpen, onOpen, onClose } = useDisclosure();
72 const [hoverCardId, setHoverCardId] = useState<number | null>(null);
73 const [favorites, setFavorites] = useState<string[]>([]);
74 const [isCreating, setIsCreating] = useState(false);
75 const toast = useToast();
76 const queryClient = useQueryClient();
77 const [isPluginModalOpen, setIsPluginModalOpen] = useState(false);
78 const [lockedTemplateName, setLockedTemplateName] = useState('');
79
80 const openPluginModal = () => setIsPluginModalOpen(true);
81 const closePluginModal = () => setIsPluginModalOpen(false);
82
83 useEffect(() => {
84 const savedFavorites = localStorage.getItem('favorites');
85
86 if (savedFavorites) {
87 setFavorites(JSON.parse(savedFavorites));
88 } else {
89 const fetchFavorites = async () => {
90 try {
91 const response: any = await apiFetch({
92 path: `${restURL}everest-forms/v1/templates/favorite_forms`,
93 method: 'GET',
94 headers: {
95 'X-WP-Nonce': security,
96 },
97 });
98
99 if (response && Array.isArray(response)) {
100 setFavorites(response);
101 localStorage.setItem('favorites', JSON.stringify(response));
102 }
103 } catch (error) {
104 console.error('Error fetching favorites:', error);
105 }
106 };
107
108 fetchFavorites();
109 }
110 }, []);
111
112 const handleTemplateClick = async (template: Template) => {
113 const addonKeys = template.addons ? Object.keys(template.addons) : [];
114
115 try {
116 const response = await apiFetch({
117 path: `${restURL}everest-forms/v1/plugin/upgrade`,
118 method: 'POST',
119 body: JSON.stringify({ requiredPlugins: addonKeys }),
120 headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': security },
121 });
122
123 const { plugin_status } = response as {
124 plugin_status: Record<string, string>;
125 };
126
127 if (!plugin_status) {
128 setLockedTemplateName(template.title);
129 openPluginModal();
130 return;
131 }
132
133 setSelectedTemplateSlug(template.slug);
134 setPreviewTemplate(template);
135
136 // No addons required → go straight to the choose state
137 if (addonKeys.length === 0) {
138 setModalState('choose');
139 } else {
140 setModalState('addons');
141 }
142 onOpen();
143 } catch (error) {
144 toast({
145 title: __('Error', 'everest-forms'),
146 description: __(
147 'An error occurred while checking the plugin status. Please try again.',
148 'everest-forms',
149 ),
150 status: 'error',
151 position: 'bottom-right',
152 duration: 5000,
153 isClosable: true,
154 variant: 'subtle',
155 });
156 }
157 };
158
159 // Called when all addons are active — transition to the choose view
160 const handleAddonsReady = () => {
161 setModalState('choose');
162 };
163
164 // "Edit with AI": create a DRAFT form from the template, then open the AI
165 // preview with it loaded so the user can refine it by prompting.
166 const [aiCreatingSlug, setAiCreatingSlug] = useState('');
167 const handleEditWithAI = async (template: Template) => {
168 if (aiCreatingSlug) return;
169 setAiCreatingSlug(template.slug);
170 try {
171 const response = (await apiFetch({
172 path: `${restURL}everest-forms/v1/templates/create`,
173 method: 'POST',
174 body: JSON.stringify({
175 title: template.title,
176 slug: template.slug,
177 draft: true,
178 }),
179 headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': security },
180 })) as CreateTemplateResponse;
181
182 if (response.success && response.data?.id) {
183 // Navigate into the AI flow with the draft loaded (component unmounts).
184 onCreateWithAI?.(response.data.id, template.title);
185 } else {
186 throw new Error(response.message || 'create_failed');
187 }
188 } catch (error) {
189 setAiCreatingSlug('');
190 toast({
191 title: __('Error', 'everest-forms'),
192 description: __(
193 'Could not start AI editing. Please try again.',
194 'everest-forms',
195 ),
196 status: 'error',
197 position: 'bottom-right',
198 duration: 5000,
199 isClosable: true,
200 variant: 'subtle',
201 });
202 }
203 };
204
205 // Creates the form using the template title as name
206 const handleCreateForm = async () => {
207 if (!previewTemplate) return;
208 setIsCreating(true);
209 try {
210 const response = (await apiFetch({
211 path: `${restURL}everest-forms/v1/templates/create`,
212 method: 'POST',
213 body: JSON.stringify({
214 title: previewTemplate.title,
215 slug: selectedTemplateSlug,
216 }),
217 headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': security },
218 })) as CreateTemplateResponse;
219
220 if (response.success && response.data) {
221 window.location.href = response.data.redirect;
222 } else {
223 setIsCreating(false);
224 toast({
225 title: __('Error', 'everest-forms'),
226 description:
227 response.message ||
228 __('Failed to create form template.', 'everest-forms'),
229 status: 'error',
230 position: 'bottom-right',
231 duration: 5000,
232 isClosable: true,
233 variant: 'subtle',
234 });
235 }
236 } catch (error) {
237 setIsCreating(false);
238 toast({
239 title: __('Error', 'everest-forms'),
240 description: __(
241 'An error occurred while creating the form template.',
242 'everest-forms',
243 ),
244 status: 'error',
245 position: 'bottom-right',
246 duration: 5000,
247 isClosable: true,
248 variant: 'subtle',
249 });
250 }
251 };
252
253 const mutation = useMutation(
254 async (slug: string) => {
255 const newFavorites = favorites.includes(slug)
256 ? favorites.filter((item) => item !== slug)
257 : [...favorites, slug];
258
259 setFavorites(newFavorites);
260 localStorage.setItem('favorites', JSON.stringify(newFavorites));
261
262 await apiFetch({
263 path: `${restURL}everest-forms/v1/templates/favorite`,
264 method: 'POST',
265 body: JSON.stringify({
266 action: newFavorites.includes(slug)
267 ? 'add_favorite'
268 : 'remove_favorite',
269 slug,
270 }),
271 headers: {
272 'Content-Type': 'application/json',
273 'X-WP-Nonce': security,
274 },
275 });
276
277 return newFavorites;
278 },
279 {
280 onError: () => {
281 toast({
282 title: __('Error', 'everest-forms'),
283 description: __(
284 'An error occurred while updating favorites.',
285 'everest-forms',
286 ),
287 status: 'error',
288 position: 'bottom-right',
289 duration: 5000,
290 isClosable: true,
291 variant: 'subtle',
292 });
293 },
294 onSuccess: (newFavorites) => {
295 queryClient.invalidateQueries(['templates']);
296 setFavorites(newFavorites);
297 localStorage.setItem('favorites', JSON.stringify(newFavorites));
298 queryClient.invalidateQueries(['favorites']);
299 },
300 },
301 );
302
303 const handleFavoriteToggle = (slug: string) => {
304 mutation.mutate(slug);
305 };
306
307 const requiredPlugins = previewTemplate?.addons
308 ? Object.entries(previewTemplate.addons).map(([key, value]) => ({
309 key,
310 value,
311 }))
312 : [];
313
314 return (
315 <Box padding="0">
316 {templates?.length ? (
317 <Box
318 sx={{
319 display: 'grid',
320 gridTemplateColumns: 'repeat(2, 1fr)',
321 gap: '16px',
322 '@media (min-width: 1280px)': {
323 gridTemplateColumns: 'repeat(3, 1fr)',
324 },
325 '@media (max-width: 640px)': {
326 gridTemplateColumns: '1fr',
327 },
328 }}
329 >
330 {templates.map((template) => {
331 const isHovered = hoverCardId === template.id;
332 return (
333 <Box
334 key={template.slug}
335 borderRadius="12px"
336 border="1px solid #e2e8f0"
337 overflow="hidden"
338 position="relative"
339 onMouseEnter={() => setHoverCardId(template.id)}
340 onMouseLeave={() => setHoverCardId(null)}
341 bg="white"
342 display="flex"
343 flexDirection="column"
344 transition="all 0.25s"
345 _hover={{
346 borderColor: 'rgba(117,69,187,0.4)',
347 boxShadow: '0 8px 24px -12px rgba(117,69,187,0.18)',
348 transform: 'translateY(-2px)',
349 }}
350 >
351 {/* Image area */}
352 <Box
353 position="relative"
354 borderBottom="1px solid #e2e8f0"
355 pt="20px"
356 px="20px"
357 pb="0"
358 display="flex"
359 alignItems="flex-start"
360 justifyContent="center"
361 overflow="hidden"
362 background="linear-gradient(129deg, #F3F2F8 2.83%, #F7F5F9 110.96%)"
363 minH="160px"
364 >
365 {/* Image wrapper — white card with shadow */}
366 <Box
367 position="relative"
368 w="100%"
369 bg="white"
370 borderRadius="8px 8px 0 0"
371 border="1px solid #e2e8f0"
372 borderBottom="none"
373 overflow="hidden"
374 boxShadow="0 6px 24px 0 #E5E1EF"
375 >
376 {/* Pro badge inside image wrapper */}
377 {template.isPro && (
378 <Box
379 as="span"
380 position="absolute"
381 top="10px"
382 right="10px"
383 fontSize="10px"
384 fontWeight="700"
385 textTransform="uppercase"
386 letterSpacing="0.06em"
387 color="#7545BB"
388 bg="#f3eefc"
389 border="1px solid #e6ddf6"
390 px="8px"
391 py="2px"
392 borderRadius="4px"
393 zIndex={2}
394 display="inline-flex"
395 alignItems="center"
396 >
397 {__('Pro', 'everest-forms')}
398 </Box>
399 )}
400 <Image
401 src={template.imageUrl}
402 alt={template.title}
403 display="block"
404 w="100%"
405 h="auto"
406 objectFit="cover"
407 objectPosition="top"
408 borderRadius="6px"
409 loading="lazy"
410 />
411 </Box>
412
413 {/* Hover overlay — dark gradient */}
414 <Box
415 position="absolute"
416 inset="0"
417 bgGradient="linear(to-b, rgba(14,14,14,0.2), rgba(14,14,14,0.4), rgba(14,14,14,0.6))"
418 opacity={isHovered ? 1 : 0}
419 transition="opacity 0.3s"
420 display="flex"
421 flexDirection="column"
422 alignItems="center"
423 justifyContent="center"
424 gap="8px"
425 px="20px"
426 zIndex={2}
427 >
428 {/* Favorite button */}
429 <Box
430 as="button"
431 onClick={(e) => {
432 e.stopPropagation();
433 handleFavoriteToggle(template.slug);
434 }}
435 aria-label={`Toggle favorite for ${template.title}`}
436 position="absolute"
437 top="12px"
438 right="12px"
439 w="28px"
440 h="28px"
441 display="inline-flex"
442 alignItems="center"
443 justifyContent="center"
444 borderRadius="full"
445 bg={
446 favorites.includes(template.slug)
447 ? 'white'
448 : 'rgba(255,255,255,0.15)'
449 }
450 backdropFilter="blur(4px)"
451 color={
452 favorites.includes(template.slug) ? 'red.500' : 'white'
453 }
454 border="none"
455 cursor="pointer"
456 _hover={{
457 bg: 'white',
458 color: 'red.500',
459 }}
460 transition="all 0.2s"
461 >
462 <Icon
463 as={
464 favorites.includes(template.slug)
465 ? FaHeart
466 : FaRegHeart
467 }
468 boxSize="3.5"
469 />
470 </Box>
471
472 {/* Use Template button */}
473 <Button
474 w="170px"
475 h="36px"
476 borderRadius="8px"
477 bg="#7545BB"
478 color="white"
479 fontSize="14px"
480 fontWeight="500"
481 boxShadow="0 6px 18px -6px rgba(117,69,187,0.55)"
482 _hover={{ bg: 'rgba(117,69,187,0.9)' }}
483 opacity={isHovered ? 1 : 0}
484 transform={
485 isHovered ? 'translateY(0)' : 'translateY(8px)'
486 }
487 transition="all 0.3s"
488 onClick={() => handleTemplateClick(template)}
489 >
490 {__('Use this template', 'everest-forms')}
491 </Button>
492
493 {/* Preview button */}
494 {template.preview_link && (
495 <Button
496 w="170px"
497 h="36px"
498 borderRadius="8px"
499 bg="transparent"
500 border="1px solid rgba(255,255,255,0.4)"
501 color="white"
502 fontSize="14px"
503 fontWeight="500"
504 _hover={{ bg: 'rgba(255,255,255,0.1)' }}
505 opacity={isHovered ? 1 : 0}
506 transform={
507 isHovered ? 'translateY(0)' : 'translateY(8px)'
508 }
509 transition="all 0.3s 0.06s"
510 onClick={() =>
511 window.open(template.preview_link, '_blank')
512 }
513 >
514 {__('Preview', 'everest-forms')}
515 </Button>
516 )}
517 </Box>
518 </Box>
519
520 {/* Card info */}
521 <Box p="20px" flex="1" display="flex" flexDirection="column">
522 <Text
523 className="template-title"
524 fontSize="14px"
525 fontWeight="600"
526 color="#0e0e0e"
527 mb="4px"
528 margin="0 0 4px 0"
529 transition="color 0.2s"
530 sx={{ '.template-card:hover &': { color: '#7545BB' } }}
531 >
532 {template.title}
533 </Text>
534 <Text
535 fontSize="12px"
536 color="#6b6b6b"
537 lineHeight="1.6"
538 margin="0"
539 flex="1"
540 >
541 {template.description}
542 </Text>
543 </Box>
544 </Box>
545 );
546 })}
547 </Box>
548 ) : (
549 <Box
550 display="flex"
551 flexDirection="column"
552 justifyContent="center"
553 alignItems="center"
554 minH="400px"
555 width="100%"
556 >
557 <Image
558 src={notFoundImage}
559 alt={__('Not Found', 'everest-forms')}
560 boxSize="260px"
561 objectFit="cover"
562 />
563 <Text mt={4} fontSize="lg" fontWeight="bold" textAlign="center">
564 {__('No Templates Found', 'everest-forms')}
565 </Text>
566 <Text margin={0} fontSize="sm" textAlign="center" color="gray.600">
567 {__(
568 "Sorry, we didn't find any templates that match your criteria",
569 'everest-forms',
570 )}
571 </Text>
572 </Box>
573 )}
574
575 {/* ── Premium / locked template modal ────────────────────────────── */}
576 <Modal
577 isCentered
578 isOpen={isPluginModalOpen}
579 onClose={closePluginModal}
580 size="md"
581 >
582 <ModalOverlay bg="rgba(0,0,0,0.35)" backdropFilter="blur(2px)" />
583 <ModalContent
584 borderRadius="16px"
585 p="0"
586 overflow="hidden"
587 boxShadow="0 8px 32px rgba(0,0,0,0.1)"
588 >
589 <ModalHeader p="0">
590 <Flex
591 align="center"
592 gap="12px"
593 px="24px"
594 pt="22px"
595 pb="16px"
596 borderBottom="1px solid #f1f5f9"
597 >
598 <Box
599 w="36px"
600 h="36px"
601 borderRadius="8px"
602 bg="rgba(117,69,187,0.1)"
603 display="flex"
604 alignItems="center"
605 justifyContent="center"
606 flexShrink={0}
607 >
608 <Icon as={LuSparkles} boxSize="16px" color="#7545BB" />
609 </Box>
610 <Box flex="1" minW="0">
611 <Text
612 fontSize="15px"
613 fontWeight="600"
614 color="#0e0e0e"
615 m="0"
616 noOfLines={1}
617 >
618 {lockedTemplateName}
619 </Text>
620 <Text fontSize="12px" color="#9ca3af" m="0">
621 {__('Premium Template', 'everest-forms')}
622 </Text>
623 </Box>
624 </Flex>
625 </ModalHeader>
626 <ModalCloseButton
627 top="14px"
628 right="16px"
629 size="sm"
630 borderRadius="6px"
631 _hover={{ bg: '#f1f5f9' }}
632 />
633
634 <ModalBody px="24px" py="20px">
635 <Text fontSize="13px" color="#6b7280" lineHeight="1.65" m="0">
636 {__(
637 'This template requires a premium plan. Upgrade to unlock all premium templates and features.',
638 'everest-forms',
639 )}
640 </Text>
641 </ModalBody>
642
643 <Box px="24px" pb="22px">
644 <Flex gap="10px">
645 <Box
646 as="button"
647 flex="1"
648 h="38px"
649 borderRadius="8px"
650 border="1px solid #e2e8f0"
651 bg="white"
652 color="#374151"
653 fontSize="13px"
654 fontWeight="500"
655 cursor="pointer"
656 onClick={closePluginModal}
657 _hover={{ bg: '#f8fafc' }}
658 transition="background 0.2s"
659 >
660 {__('Cancel', 'everest-forms')}
661 </Box>
662 <Box
663 as="a"
664 href="https://everestforms.net/upgrade/?utm_medium=evf-template-page&utm_source=evf-free&utm_campaign=template-premium-popup"
665 target="_blank"
666 rel="noopener noreferrer"
667 flex="1"
668 h="38px"
669 borderRadius="8px"
670 bg="#7545BB"
671 color="white"
672 fontSize="13px"
673 fontWeight="500"
674 cursor="pointer"
675 display="flex"
676 alignItems="center"
677 justifyContent="center"
678 gap="6px"
679 _hover={{ bg: '#6a3daa', color: 'white' }}
680 transition="background 0.2s"
681 textDecoration="none"
682 >
683 {__('Upgrade Plan', 'everest-forms')}
684 </Box>
685 </Flex>
686 </Box>
687 </ModalContent>
688 </Modal>
689
690 {/* ── Template addon / choose modal ───────────────────────────────── */}
691 <Modal isCentered isOpen={isOpen} onClose={onClose} size="md">
692 <ModalOverlay bg="rgba(0,0,0,0.4)" backdropFilter="blur(2px)" />
693 <ModalContent
694 borderRadius="16px"
695 p="0"
696 overflow="hidden"
697 boxShadow="0 20px 60px rgba(0,0,0,0.12)"
698 >
699 {/* Header */}
700 <ModalHeader
701 px="24px"
702 pt="22px"
703 pb="16px"
704 borderBottom="1px solid #f1f5f9"
705 p="0"
706 >
707 <Flex
708 align="center"
709 gap="12px"
710 px="24px"
711 pt="22px"
712 pb="16px"
713 borderBottom="1px solid #f1f5f9"
714 >
715 <Box
716 w="36px"
717 h="36px"
718 borderRadius="8px"
719 bg="rgba(117,69,187,0.1)"
720 display="flex"
721 alignItems="center"
722 justifyContent="center"
723 flexShrink={0}
724 >
725 <Icon as={LuSparkles} boxSize="16px" color="#7545BB" />
726 </Box>
727 <Box flex="1" minW="0">
728 <Text
729 fontSize="15px"
730 fontWeight="600"
731 color="#0e0e0e"
732 m="0"
733 noOfLines={1}
734 >
735 {previewTemplate?.title}
736 </Text>
737 <Text fontSize="12px" color="#9ca3af" m="0">
738 {modalState === 'addons'
739 ? __('Required addons', 'everest-forms')
740 : __('Ready to use', 'everest-forms')}
741 </Text>
742 </Box>
743 </Flex>
744 </ModalHeader>
745 <ModalCloseButton
746 top="14px"
747 right="16px"
748 size="sm"
749 borderRadius="6px"
750 _hover={{ bg: '#f1f5f9' }}
751 />
752
753 <ModalBody px="24px" py="20px">
754 {/* ── Addons state ── */}
755 {modalState === 'addons' && (
756 <VStack align="stretch" spacing="0">
757 <HStack spacing="8px" mb="16px">
758 <Box
759 w="4px"
760 h="4px"
761 borderRadius="full"
762 bg="#7545BB"
763 flexShrink={0}
764 mt="1px"
765 />
766 <Text fontSize="13px" color="#6b7280" m="0" lineHeight="1.55">
767 {__(
768 'This template requires the following addons to be installed and activated:',
769 'everest-forms',
770 )}
771 </Text>
772 </HStack>
773 <PluginStatus
774 requiredPlugins={requiredPlugins}
775 onActivateAndContinue={handleAddonsReady}
776 />
777 </VStack>
778 )}
779
780 {/* ── Choose state ── */}
781 {modalState === 'choose' && (
782 <VStack align="stretch" spacing="16px">
783 {/* Primary: Use this template */}
784 <Box
785 as="button"
786 w="100%"
787 h="44px"
788 display="inline-flex"
789 alignItems="center"
790 justifyContent="center"
791 gap="8px"
792 borderRadius="10px"
793 bg="#7545BB"
794 color="white"
795 fontSize="15px"
796 fontWeight="600"
797 border="none"
798 cursor={isCreating ? 'not-allowed' : 'pointer'}
799 opacity={isCreating ? 0.7 : 1}
800 onClick={!isCreating ? handleCreateForm : undefined}
801 transition="background 0.2s, opacity 0.2s"
802 _hover={!isCreating ? { bg: '#6a3daa' } : {}}
803 >
804 {isCreating ? (
805 <Box
806 w="16px"
807 h="16px"
808 border="2px solid rgba(255,255,255,0.4)"
809 borderTopColor="white"
810 borderRadius="full"
811 sx={{
812 animation: 'spin 0.7s linear infinite',
813 '@keyframes spin': {
814 from: { transform: 'rotate(0deg)' },
815 to: { transform: 'rotate(360deg)' },
816 },
817 }}
818 />
819 ) : (
820 <Icon as={FiArrowRight} boxSize="4" />
821 )}
822 <Text margin="0" color="white">
823 {isCreating
824 ? __('Creating…', 'everest-forms')
825 : __('Use this template', 'everest-forms')}
826 </Text>
827 </Box>
828
829 {/* OR divider */}
830 <Flex align="center" gap="12px">
831 <Box flex="1" h="1px" bg="#e2e8f0" />
832 <Text
833 fontSize="12px"
834 fontWeight="500"
835 color="#9ca3af"
836 m="0"
837 textTransform="uppercase"
838 letterSpacing="0.05em"
839 >
840 {__('or', 'everest-forms')}
841 </Text>
842 <Box flex="1" h="1px" bg="#e2e8f0" />
843 </Flex>
844
845 {/* Secondary: Edit with AI */}
846 <Box
847 as="button"
848 w="100%"
849 display="inline-flex"
850 alignItems="center"
851 justifyContent="center"
852 gap="8px"
853 py="10px"
854 borderRadius="10px"
855 bg="transparent"
856 border="none"
857 color="#7545BB"
858 fontSize="14px"
859 fontWeight="500"
860 disabled={!AI_ENABLED}
861 title={
862 AI_ENABLED
863 ? undefined
864 : __('Not available on local sites', 'everest-forms')
865 }
866 cursor={
867 !AI_ENABLED || aiCreatingSlug ? 'not-allowed' : 'pointer'
868 }
869 opacity={!AI_ENABLED ? 0.6 : aiCreatingSlug ? 0.7 : 1}
870 onClick={() => {
871 if (AI_ENABLED && !aiCreatingSlug && previewTemplate)
872 handleEditWithAI(previewTemplate);
873 }}
874 transition="color 0.2s"
875 _hover={AI_ENABLED ? { color: '#6a3daa' } : {}}
876 >
877 <Icon as={LuSparkles} boxSize="4" />
878 <Text margin="0">
879 {aiCreatingSlug
880 ? __('Loading…', 'everest-forms')
881 : __('Edit with AI', 'everest-forms')}
882 </Text>
883 </Box>
884 </VStack>
885 )}
886 </ModalBody>
887 </ModalContent>
888 </Modal>
889 </Box>
890 );
891 };
892
893 export default TemplateList;
894