Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/good-pants-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"strapi-plugin-webtools": patch
---

Feature/try webtools inscentives
52 changes: 52 additions & 0 deletions packages/core/admin/components/LockedAddonMenuItem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useState } from 'react';
import { useIntl } from 'react-intl';
import {
SubNavLink,
Tooltip,
} from '@strapi/design-system';
import { Lock } from '@strapi/icons';
import { LockedAddonMenuItemProps } from '../../types/pro-addons';
import TrialModal from '../TrialModal';

const LockedAddonMenuItem: React.FC<LockedAddonMenuItemProps> = ({ addon }) => {
const { formatMessage } = useIntl();
const [isModalOpen, setIsModalOpen] = useState(false);

const handleClick = (e: React.MouseEvent) => {
e.preventDefault();
setIsModalOpen(true);
};

return (
<>
<Tooltip
description={formatMessage({
id: 'webtools.sidebar.locked_addon.tooltip',
defaultMessage: 'Start free trial to unlock',
})}
>
<SubNavLink
onClick={handleClick}
style={{
cursor: 'pointer',
opacity: 0.5,
pointerEvents: 'auto',
}}
>
<span style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Lock width="12px" height="12px" />
{addon.name}
</span>
</SubNavLink>
</Tooltip>

<TrialModal
addon={addon}
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
/>
</>
);
};

export default LockedAddonMenuItem;
71 changes: 71 additions & 0 deletions packages/core/admin/components/TrialCallToAction/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import { useIntl } from 'react-intl';
import {
Box,
Flex,
Typography,
Button,
} from '@strapi/design-system';
import { TRIAL_URL } from '../../constants/pro-addons';

interface TrialCallToActionProps {
variant?: 'banner' | 'card' | 'inline';
}

const TrialCallToAction: React.FC<TrialCallToActionProps> = ({ variant = 'card' }) => {
const { formatMessage } = useIntl();

const content = (
<Flex direction="column" alignItems="center" gap={2}>
<Typography variant="delta" textAlign="center">
{formatMessage({
id: 'webtools.overview.trial_cta.title_short',
defaultMessage: 'Ready to unlock Pro features?',
})}
</Typography>

<Typography variant="omega" textColor="neutral600" textAlign="center">
{formatMessage({
id: 'webtools.overview.trial_cta.subtitle',
defaultMessage: 'Start your free 7-day trial - includes Redirects & Links addons',
})}
</Typography>

<Box marginTop={3}>
<Button
variant="default"
tag="a"
href={TRIAL_URL}
target="_blank"
rel="noopener noreferrer"
>
{formatMessage({
id: 'webtools.overview.trial_cta.button_short',
defaultMessage: 'Start Free Trial',
})}
</Button>
</Box>
</Flex>
);

if (variant === 'inline') {
return content;
}

return (
<Box
hasRadius
background="neutral0"
shadow="tableShadow"
paddingTop={8}
paddingBottom={8}
paddingRight={8}
paddingLeft={8}
style={variant === 'banner' ? { width: '100%' } : undefined}
>
{content}
</Box>
);
};

export default TrialCallToAction;
238 changes: 238 additions & 0 deletions packages/core/admin/components/TrialModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import React from 'react';
import { useIntl } from 'react-intl';
import {
Modal,
Flex,
Typography,
Button,
Box,
Badge,
Grid,
} from '@strapi/design-system';
import {
ArrowRight,
Link as LinkIcon,
ExternalLink,
} from '@strapi/icons';
import { TrialModalProps } from '../../types/pro-addons';
import { TRIAL_URL } from '../../constants/pro-addons';

const iconMap = {
ArrowRight,
Link: LinkIcon,
ArrowsLeftRight: ArrowRight,
};

const TrialModal: React.FC<TrialModalProps> = ({ addon, isOpen, onClose }) => {
const { formatMessage } = useIntl();
const IconComponent = iconMap[addon.icon as keyof typeof iconMap] || ArrowRight;

if (!isOpen) return null;

return (
<Modal.Root open={isOpen} onOpenChange={onClose}>
<Modal.Content style={{ maxWidth: '800px' }}>
<Modal.Header>
<Flex gap={3} alignItems="center">
<Box padding={3} background="primary100" hasRadius>
<IconComponent width="24px" height="24px" />
</Box>
<Flex direction="column" gap={1} alignItems="flex-start">
<Flex alignItems="center" gap={2}>
<Typography variant="beta">
{formatMessage({
id: 'webtools.trial_modal.title',
defaultMessage: 'Unlock {name}',
}, { name: addon.name })}
</Typography>
<Badge size="S">
{formatMessage({
id: 'webtools.overview.addon.pro',
defaultMessage: 'PRO',
})}
</Badge>
</Flex>
<Typography variant="omega" textColor="neutral600">
{addon.tagline}
</Typography>
</Flex>
</Flex>
</Modal.Header>

<Modal.Body>
<Flex direction="column" gap={6}>
{/* Two column layout: Key Benefits | Trial Details */}
<Grid.Root gap={8}>
{/* Left column: Key Benefits */}
<Grid.Item col={6} s={12}>
<Box>
<Typography variant="sigma" textColor="neutral600" marginBottom={3} textTransform="uppercase">
{formatMessage({
id: 'webtools.trial_modal.benefits_title',
defaultMessage: 'Key Benefits',
})}
</Typography>
<Flex direction="column" gap={2} alignItems="flex-start">
{addon.benefits.map((benefit) => (
<Flex key={benefit} gap={2} alignItems="flex-start">
<Box style={{ minWidth: '16px', textAlign: 'left' }}>
<Typography variant="pi" textColor="primary600">
</Typography>
</Box>
<Typography
variant="pi"
textColor="neutral700"
style={{
flex: 1,
lineHeight: '1.6',
hyphens: 'none',
wordBreak: 'normal',
}}
>
{benefit}
</Typography>
</Flex>
))}
</Flex>
</Box>
</Grid.Item>

{/* Right column: Trial Details */}
<Grid.Item col={6} s={12}>
<Box>
<Typography variant="sigma" textColor="neutral600" marginBottom={3} textTransform="uppercase">
{formatMessage({
id: 'webtools.trial_modal.trial_details_title',
defaultMessage: 'Trial Details',
})}
</Typography>
<Flex direction="column" gap={2} alignItems="flex-start">
<Flex gap={2} alignItems="flex-start">
<Box style={{ minWidth: '16px', textAlign: 'left' }}>
<Typography variant="pi" textColor="success600">
</Typography>
</Box>
<Typography variant="pi" textColor="neutral700">
{formatMessage({
id: 'webtools.trial_modal.trial_detail_1',
defaultMessage: '7-day free trial',
})}
</Typography>
</Flex>
<Flex gap={2} alignItems="flex-start">
<Box style={{ minWidth: '16px', textAlign: 'left' }}>
<Typography variant="pi" textColor="success600">
</Typography>
</Box>
<Typography variant="pi" textColor="neutral700">
{formatMessage({
id: 'webtools.trial_modal.trial_detail_2',
defaultMessage: 'Includes Redirects + Links addons',
})}
</Typography>
</Flex>
<Flex gap={2} alignItems="flex-start">
<Box style={{ minWidth: '16px', textAlign: 'left' }}>
<Typography variant="pi" textColor="success600">
</Typography>
</Box>
<Typography variant="pi" textColor="neutral700">
{formatMessage({
id: 'webtools.trial_modal.trial_detail_3',
defaultMessage: 'No credit card required',
})}
</Typography>
</Flex>
<Flex gap={2} alignItems="flex-start">
<Box style={{ minWidth: '16px', textAlign: 'left' }}>
<Typography variant="pi" textColor="success600">
</Typography>
</Box>
<Typography variant="pi" textColor="neutral700">
{formatMessage({
id: 'webtools.trial_modal.trial_detail_4',
defaultMessage: 'Cancel anytime',
})}
</Typography>
</Flex>
</Flex>
</Box>
</Grid.Item>
</Grid.Root>

{/* Testimonial */}
<Box
background="neutral100"
padding={5}
hasRadius
style={{
borderLeft: '3px solid var(--primary-600)',
}}
>
<Typography
variant="omega"
textColor="neutral700"
marginBottom={2}
style={{
lineHeight: '1.6',
hyphens: 'none',
wordBreak: 'normal',
fontStyle: 'italic',
}}
>
{formatMessage({
id: 'webtools.trial_modal.testimonial',
defaultMessage: '"Saved us 20+ hours per project. Essential for our agency workflow."',
})}
</Typography>
<Typography variant="pi" textColor="neutral600" fontWeight="semiBold">
{formatMessage({
id: 'webtools.trial_modal.testimonial_author',
defaultMessage: '— Marcus, Digital Agency Owner',
})}
</Typography>
</Box>
</Flex>
</Modal.Body>

<Modal.Footer>
<Flex justifyContent="space-between" width="100%" gap={3}>
<Button
variant="tertiary"
tag="a"
href={addon.docsUrl}
target="_blank"
rel="noopener noreferrer"
endIcon={<ExternalLink />}
>
{formatMessage({
id: 'webtools.trial_modal.learn_more',
defaultMessage: 'Learn More',
})}
</Button>
<Button
variant="default"
size="L"
tag="a"
href={TRIAL_URL}
target="_blank"
rel="noopener noreferrer"
>
{formatMessage({
id: 'webtools.trial_modal.start_trial',
defaultMessage: 'Start Free Trial',
})}
</Button>
</Flex>
</Modal.Footer>
</Modal.Content>
</Modal.Root>
);
};

export default TrialModal;
Loading
Loading