PluginProbe ʕ •ᴥ•ʔ
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI / 3.6.0
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI v3.6.0
3.6.0 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 2 months ago CreateWithAI.tsx 5 days ago Main.tsx 2 months ago PluginStatus.tsx 2 months ago Sidebar.tsx 2 months ago TemplateList.tsx 5 days ago TemplatesSkeleton.tsx 5 months ago
TemplateList.tsx
897 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 (min-width: 1600px)': {
326 gridTemplateColumns: 'repeat(4, 1fr)',
327 },
328 '@media (max-width: 640px)': {
329 gridTemplateColumns: '1fr',
330 },
331 }}
332 >
333 {templates.map((template) => {
334 const isHovered = hoverCardId === template.id;
335 return (
336 <Box
337 key={template.slug}
338 borderRadius="12px"
339 border="1px solid #e2e8f0"
340 overflow="hidden"
341 position="relative"
342 onMouseEnter={() => setHoverCardId(template.id)}
343 onMouseLeave={() => setHoverCardId(null)}
344 bg="white"
345 display="flex"
346 flexDirection="column"
347 transition="all 0.25s"
348 _hover={{
349 borderColor: 'rgba(117,69,187,0.4)',
350 boxShadow: '0 8px 24px -12px rgba(117,69,187,0.18)',
351 transform: 'translateY(-2px)',
352 }}
353 >
354 {/* Image area */}
355 <Box
356 position="relative"
357 borderBottom="1px solid #e2e8f0"
358 pt="20px"
359 px="20px"
360 pb="0"
361 display="flex"
362 alignItems="flex-start"
363 justifyContent="center"
364 overflow="hidden"
365 background="linear-gradient(129deg, #F3F2F8 2.83%, #F7F5F9 110.96%)"
366 minH="160px"
367 >
368 {/* Image wrapper — white card with shadow */}
369 <Box
370 position="relative"
371 w="100%"
372 bg="white"
373 borderRadius="8px 8px 0 0"
374 border="1px solid #e2e8f0"
375 borderBottom="none"
376 overflow="hidden"
377 boxShadow="0 6px 24px 0 #E5E1EF"
378 >
379 {/* Pro badge inside image wrapper */}
380 {template.isPro && (
381 <Box
382 as="span"
383 position="absolute"
384 top="10px"
385 right="10px"
386 fontSize="10px"
387 fontWeight="700"
388 textTransform="uppercase"
389 letterSpacing="0.06em"
390 color="#7545BB"
391 bg="#f3eefc"
392 border="1px solid #e6ddf6"
393 px="8px"
394 py="2px"
395 borderRadius="4px"
396 zIndex={2}
397 display="inline-flex"
398 alignItems="center"
399 >
400 {__('Pro', 'everest-forms')}
401 </Box>
402 )}
403 <Image
404 src={template.imageUrl}
405 alt={template.title}
406 display="block"
407 w="100%"
408 h="auto"
409 objectFit="cover"
410 objectPosition="top"
411 borderRadius="6px"
412 loading="lazy"
413 />
414 </Box>
415
416 {/* Hover overlay — dark gradient */}
417 <Box
418 position="absolute"
419 inset="0"
420 bgGradient="linear(to-b, rgba(14,14,14,0.2), rgba(14,14,14,0.4), rgba(14,14,14,0.6))"
421 opacity={isHovered ? 1 : 0}
422 transition="opacity 0.3s"
423 display="flex"
424 flexDirection="column"
425 alignItems="center"
426 justifyContent="center"
427 gap="8px"
428 px="20px"
429 zIndex={2}
430 >
431 {/* Favorite button */}
432 <Box
433 as="button"
434 onClick={(e) => {
435 e.stopPropagation();
436 handleFavoriteToggle(template.slug);
437 }}
438 aria-label={`Toggle favorite for ${template.title}`}
439 position="absolute"
440 top="12px"
441 right="12px"
442 w="28px"
443 h="28px"
444 display="inline-flex"
445 alignItems="center"
446 justifyContent="center"
447 borderRadius="full"
448 bg={
449 favorites.includes(template.slug)
450 ? 'white'
451 : 'rgba(255,255,255,0.15)'
452 }
453 backdropFilter="blur(4px)"
454 color={
455 favorites.includes(template.slug) ? 'red.500' : 'white'
456 }
457 border="none"
458 cursor="pointer"
459 _hover={{
460 bg: 'white',
461 color: 'red.500',
462 }}
463 transition="all 0.2s"
464 >
465 <Icon
466 as={
467 favorites.includes(template.slug)
468 ? FaHeart
469 : FaRegHeart
470 }
471 boxSize="3.5"
472 />
473 </Box>
474
475 {/* Use Template button */}
476 <Button
477 w="170px"
478 h="36px"
479 borderRadius="8px"
480 bg="#7545BB"
481 color="white"
482 fontSize="14px"
483 fontWeight="500"
484 boxShadow="0 6px 18px -6px rgba(117,69,187,0.55)"
485 _hover={{ bg: 'rgba(117,69,187,0.9)' }}
486 opacity={isHovered ? 1 : 0}
487 transform={
488 isHovered ? 'translateY(0)' : 'translateY(8px)'
489 }
490 transition="all 0.3s"
491 onClick={() => handleTemplateClick(template)}
492 >
493 {__('Use this template', 'everest-forms')}
494 </Button>
495
496 {/* Preview button */}
497 {template.preview_link && (
498 <Button
499 w="170px"
500 h="36px"
501 borderRadius="8px"
502 bg="transparent"
503 border="1px solid rgba(255,255,255,0.4)"
504 color="white"
505 fontSize="14px"
506 fontWeight="500"
507 _hover={{ bg: 'rgba(255,255,255,0.1)' }}
508 opacity={isHovered ? 1 : 0}
509 transform={
510 isHovered ? 'translateY(0)' : 'translateY(8px)'
511 }
512 transition="all 0.3s 0.06s"
513 onClick={() =>
514 window.open(template.preview_link, '_blank')
515 }
516 >
517 {__('Preview', 'everest-forms')}
518 </Button>
519 )}
520 </Box>
521 </Box>
522
523 {/* Card info */}
524 <Box p="20px" flex="1" display="flex" flexDirection="column">
525 <Text
526 className="template-title"
527 fontSize="14px"
528 fontWeight="600"
529 color="#0e0e0e"
530 mb="4px"
531 margin="0 0 4px 0"
532 transition="color 0.2s"
533 sx={{ '.template-card:hover &': { color: '#7545BB' } }}
534 >
535 {template.title}
536 </Text>
537 <Text
538 fontSize="12px"
539 color="#6b6b6b"
540 lineHeight="1.6"
541 margin="0"
542 flex="1"
543 >
544 {template.description}
545 </Text>
546 </Box>
547 </Box>
548 );
549 })}
550 </Box>
551 ) : (
552 <Box
553 display="flex"
554 flexDirection="column"
555 justifyContent="center"
556 alignItems="center"
557 minH="400px"
558 width="100%"
559 >
560 <Image
561 src={notFoundImage}
562 alt={__('Not Found', 'everest-forms')}
563 boxSize="260px"
564 objectFit="cover"
565 />
566 <Text mt={4} fontSize="lg" fontWeight="bold" textAlign="center">
567 {__('No Templates Found', 'everest-forms')}
568 </Text>
569 <Text margin={0} fontSize="sm" textAlign="center" color="gray.600">
570 {__(
571 "Sorry, we didn't find any templates that match your criteria",
572 'everest-forms',
573 )}
574 </Text>
575 </Box>
576 )}
577
578 {/* ── Premium / locked template modal ────────────────────────────── */}
579 <Modal
580 isCentered
581 isOpen={isPluginModalOpen}
582 onClose={closePluginModal}
583 size="md"
584 >
585 <ModalOverlay bg="rgba(0,0,0,0.35)" backdropFilter="blur(2px)" />
586 <ModalContent
587 borderRadius="16px"
588 p="0"
589 overflow="hidden"
590 boxShadow="0 8px 32px rgba(0,0,0,0.1)"
591 >
592 <ModalHeader p="0">
593 <Flex
594 align="center"
595 gap="12px"
596 px="24px"
597 pt="22px"
598 pb="16px"
599 borderBottom="1px solid #f1f5f9"
600 >
601 <Box
602 w="36px"
603 h="36px"
604 borderRadius="8px"
605 bg="rgba(117,69,187,0.1)"
606 display="flex"
607 alignItems="center"
608 justifyContent="center"
609 flexShrink={0}
610 >
611 <Icon as={LuSparkles} boxSize="16px" color="#7545BB" />
612 </Box>
613 <Box flex="1" minW="0">
614 <Text
615 fontSize="15px"
616 fontWeight="600"
617 color="#0e0e0e"
618 m="0"
619 noOfLines={1}
620 >
621 {lockedTemplateName}
622 </Text>
623 <Text fontSize="12px" color="#9ca3af" m="0">
624 {__('Premium Template', 'everest-forms')}
625 </Text>
626 </Box>
627 </Flex>
628 </ModalHeader>
629 <ModalCloseButton
630 top="14px"
631 right="16px"
632 size="sm"
633 borderRadius="6px"
634 _hover={{ bg: '#f1f5f9' }}
635 />
636
637 <ModalBody px="24px" py="20px">
638 <Text fontSize="13px" color="#6b7280" lineHeight="1.65" m="0">
639 {__(
640 'This template requires a premium plan. Upgrade to unlock all premium templates and features.',
641 'everest-forms',
642 )}
643 </Text>
644 </ModalBody>
645
646 <Box px="24px" pb="22px">
647 <Flex gap="10px">
648 <Box
649 as="button"
650 flex="1"
651 h="38px"
652 borderRadius="8px"
653 border="1px solid #e2e8f0"
654 bg="white"
655 color="#374151"
656 fontSize="13px"
657 fontWeight="500"
658 cursor="pointer"
659 onClick={closePluginModal}
660 _hover={{ bg: '#f8fafc' }}
661 transition="background 0.2s"
662 >
663 {__('Cancel', 'everest-forms')}
664 </Box>
665 <Box
666 as="a"
667 href="https://everestforms.net/upgrade/?utm_medium=evf-template-page&utm_source=evf-free&utm_campaign=template-premium-popup"
668 target="_blank"
669 rel="noopener noreferrer"
670 flex="1"
671 h="38px"
672 borderRadius="8px"
673 bg="#7545BB"
674 color="white"
675 fontSize="13px"
676 fontWeight="500"
677 cursor="pointer"
678 display="flex"
679 alignItems="center"
680 justifyContent="center"
681 gap="6px"
682 _hover={{ bg: '#6a3daa', color: 'white' }}
683 transition="background 0.2s"
684 textDecoration="none"
685 >
686 {__('Upgrade Plan', 'everest-forms')}
687 </Box>
688 </Flex>
689 </Box>
690 </ModalContent>
691 </Modal>
692
693 {/* ── Template addon / choose modal ───────────────────────────────── */}
694 <Modal isCentered isOpen={isOpen} onClose={onClose} size="md">
695 <ModalOverlay bg="rgba(0,0,0,0.4)" backdropFilter="blur(2px)" />
696 <ModalContent
697 borderRadius="16px"
698 p="0"
699 overflow="hidden"
700 boxShadow="0 20px 60px rgba(0,0,0,0.12)"
701 >
702 {/* Header */}
703 <ModalHeader
704 px="24px"
705 pt="22px"
706 pb="16px"
707 borderBottom="1px solid #f1f5f9"
708 p="0"
709 >
710 <Flex
711 align="center"
712 gap="12px"
713 px="24px"
714 pt="22px"
715 pb="16px"
716 borderBottom="1px solid #f1f5f9"
717 >
718 <Box
719 w="36px"
720 h="36px"
721 borderRadius="8px"
722 bg="rgba(117,69,187,0.1)"
723 display="flex"
724 alignItems="center"
725 justifyContent="center"
726 flexShrink={0}
727 >
728 <Icon as={LuSparkles} boxSize="16px" color="#7545BB" />
729 </Box>
730 <Box flex="1" minW="0">
731 <Text
732 fontSize="15px"
733 fontWeight="600"
734 color="#0e0e0e"
735 m="0"
736 noOfLines={1}
737 >
738 {previewTemplate?.title}
739 </Text>
740 <Text fontSize="12px" color="#9ca3af" m="0">
741 {modalState === 'addons'
742 ? __('Required addons', 'everest-forms')
743 : __('Ready to use', 'everest-forms')}
744 </Text>
745 </Box>
746 </Flex>
747 </ModalHeader>
748 <ModalCloseButton
749 top="14px"
750 right="16px"
751 size="sm"
752 borderRadius="6px"
753 _hover={{ bg: '#f1f5f9' }}
754 />
755
756 <ModalBody px="24px" py="20px">
757 {/* ── Addons state ── */}
758 {modalState === 'addons' && (
759 <VStack align="stretch" spacing="0">
760 <HStack spacing="8px" mb="16px">
761 <Box
762 w="4px"
763 h="4px"
764 borderRadius="full"
765 bg="#7545BB"
766 flexShrink={0}
767 mt="1px"
768 />
769 <Text fontSize="13px" color="#6b7280" m="0" lineHeight="1.55">
770 {__(
771 'This template requires the following addons to be installed and activated:',
772 'everest-forms',
773 )}
774 </Text>
775 </HStack>
776 <PluginStatus
777 requiredPlugins={requiredPlugins}
778 onActivateAndContinue={handleAddonsReady}
779 />
780 </VStack>
781 )}
782
783 {/* ── Choose state ── */}
784 {modalState === 'choose' && (
785 <VStack align="stretch" spacing="16px">
786 {/* Primary: Use this template */}
787 <Box
788 as="button"
789 w="100%"
790 h="44px"
791 display="inline-flex"
792 alignItems="center"
793 justifyContent="center"
794 gap="8px"
795 borderRadius="10px"
796 bg="#7545BB"
797 color="white"
798 fontSize="15px"
799 fontWeight="600"
800 border="none"
801 cursor={isCreating ? 'not-allowed' : 'pointer'}
802 opacity={isCreating ? 0.7 : 1}
803 onClick={!isCreating ? handleCreateForm : undefined}
804 transition="background 0.2s, opacity 0.2s"
805 _hover={!isCreating ? { bg: '#6a3daa' } : {}}
806 >
807 {isCreating ? (
808 <Box
809 w="16px"
810 h="16px"
811 border="2px solid rgba(255,255,255,0.4)"
812 borderTopColor="white"
813 borderRadius="full"
814 sx={{
815 animation: 'spin 0.7s linear infinite',
816 '@keyframes spin': {
817 from: { transform: 'rotate(0deg)' },
818 to: { transform: 'rotate(360deg)' },
819 },
820 }}
821 />
822 ) : (
823 <Icon as={FiArrowRight} boxSize="4" />
824 )}
825 <Text margin="0" color="white">
826 {isCreating
827 ? __('Creating…', 'everest-forms')
828 : __('Use this template', 'everest-forms')}
829 </Text>
830 </Box>
831
832 {/* OR divider */}
833 <Flex align="center" gap="12px">
834 <Box flex="1" h="1px" bg="#e2e8f0" />
835 <Text
836 fontSize="12px"
837 fontWeight="500"
838 color="#9ca3af"
839 m="0"
840 textTransform="uppercase"
841 letterSpacing="0.05em"
842 >
843 {__('or', 'everest-forms')}
844 </Text>
845 <Box flex="1" h="1px" bg="#e2e8f0" />
846 </Flex>
847
848 {/* Secondary: Edit with AI */}
849 <Box
850 as="button"
851 w="100%"
852 display="inline-flex"
853 alignItems="center"
854 justifyContent="center"
855 gap="8px"
856 py="10px"
857 borderRadius="10px"
858 bg="transparent"
859 border="none"
860 color="#7545BB"
861 fontSize="14px"
862 fontWeight="500"
863 disabled={!AI_ENABLED}
864 title={
865 AI_ENABLED
866 ? undefined
867 : __('Not available on local sites', 'everest-forms')
868 }
869 cursor={
870 !AI_ENABLED || aiCreatingSlug ? 'not-allowed' : 'pointer'
871 }
872 opacity={!AI_ENABLED ? 0.6 : aiCreatingSlug ? 0.7 : 1}
873 onClick={() => {
874 if (AI_ENABLED && !aiCreatingSlug && previewTemplate)
875 handleEditWithAI(previewTemplate);
876 }}
877 transition="color 0.2s"
878 _hover={AI_ENABLED ? { color: '#6a3daa' } : {}}
879 >
880 <Icon as={LuSparkles} boxSize="4" />
881 <Text margin="0">
882 {aiCreatingSlug
883 ? __('Loading…', 'everest-forms')
884 : __('Edit with AI', 'everest-forms')}
885 </Text>
886 </Box>
887 </VStack>
888 )}
889 </ModalBody>
890 </ModalContent>
891 </Modal>
892 </Box>
893 );
894 };
895
896 export default TemplateList;
897