forked from extern/bruno
feat: golden edition modal
This commit is contained in:
parent
2608ec035e
commit
0db6103b69
@ -0,0 +1,20 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const StyledWrapper = styled.div`
|
||||||
|
color: ${(props) => props.theme.text};
|
||||||
|
.collection-options {
|
||||||
|
svg {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default StyledWrapper;
|
177
packages/bruno-app/src/components/Sidebar/GoldenEdition/index.js
Normal file
177
packages/bruno-app/src/components/Sidebar/GoldenEdition/index.js
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import Modal from 'components/Modal/index';
|
||||||
|
import { PostHog } from 'posthog-node';
|
||||||
|
import { uuid } from 'utils/common';
|
||||||
|
import { IconHeart, IconUser, IconUsers } from '@tabler/icons';
|
||||||
|
import platformLib from 'platform';
|
||||||
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
|
let posthogClient = null;
|
||||||
|
const posthogApiKey = 'phc_7gtqSrrdZRohiozPMLIacjzgHbUlhalW1Bu16uYijMR';
|
||||||
|
const getPosthogClient = () => {
|
||||||
|
if (posthogClient) {
|
||||||
|
return posthogClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
posthogClient = new PostHog(posthogApiKey);
|
||||||
|
return posthogClient;
|
||||||
|
};
|
||||||
|
const getAnonymousTrackingId = () => {
|
||||||
|
let id = localStorage.getItem('bruno.anonymousTrackingId');
|
||||||
|
|
||||||
|
if (!id || !id.length || id.length !== 21) {
|
||||||
|
id = uuid();
|
||||||
|
localStorage.setItem('bruno.anonymousTrackingId', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
const HeartIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor"
|
||||||
|
className="flex-shrink-0 w-5 h-4 text-yellow-600"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<path fillRule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CheckIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className="flex-shrink-0 w-5 h-5 text-green-500"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const GoldenEdition = ({ onClose }) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const anonymousId = getAnonymousTrackingId();
|
||||||
|
const client = getPosthogClient();
|
||||||
|
client.capture({
|
||||||
|
distinctId: anonymousId,
|
||||||
|
event: 'golden-edition-modal-opened',
|
||||||
|
properties: {
|
||||||
|
os: platformLib.os.family
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const goldenEditionBuyClick = () => {
|
||||||
|
const anonymousId = getAnonymousTrackingId();
|
||||||
|
const client = getPosthogClient();
|
||||||
|
client.capture({
|
||||||
|
distinctId: anonymousId,
|
||||||
|
event: 'golden-edition-buy-clicked',
|
||||||
|
properties: {
|
||||||
|
os: platformLib.os.family
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const goldenEditon = [
|
||||||
|
'Inbuilt Bru File Explorer',
|
||||||
|
'Visual Git (Like Gitlens for Vscode)',
|
||||||
|
'GRPC, Websocket, SocketIO, MQTT',
|
||||||
|
'Intergration with Secret Managers',
|
||||||
|
'Load Data from File for Collection Run',
|
||||||
|
'Developer Tools',
|
||||||
|
'OpenAPI Designer',
|
||||||
|
'Performance/Load Testing',
|
||||||
|
'Inbuilt Terminal',
|
||||||
|
'Custom Themes'
|
||||||
|
];
|
||||||
|
|
||||||
|
const [pricingOption, setPricingOption] = useState('individuals');
|
||||||
|
|
||||||
|
const handlePricingOptionChange = (option) => {
|
||||||
|
setPricingOption(option);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledWrapper>
|
||||||
|
<Modal size="sm" title={'Golden Edition'} handleCancel={onClose} hideFooter={true}>
|
||||||
|
<div className="flex flex-col text-gray-900 bg-white w-full">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-semibold">Golden Edition</h3>
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
goldenEditionBuyClick();
|
||||||
|
window.open('https://www.usebruno.com/pricing', '_blank');
|
||||||
|
}}
|
||||||
|
target="_blank"
|
||||||
|
className="flex text-white bg-yellow-600 hover:bg-yellow-700 font-medium rounded-lg text-sm px-4 py-2 text-center cursor-pointer"
|
||||||
|
>
|
||||||
|
<IconHeart size={18} strokeWidth={1.5} />{' '}
|
||||||
|
<span className="ml-2">{pricingOption === 'individuals' ? 'Buy' : 'Subscribe'}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{pricingOption === 'individuals' ? (
|
||||||
|
<div>
|
||||||
|
<div className="my-4">
|
||||||
|
<span className="text-3xl font-extrabold">$19</span>
|
||||||
|
</div>
|
||||||
|
<p className="bg-yellow-200 rounded-md px-2 py-1 mb-2 inline-flex text-sm">One Time Payment</p>
|
||||||
|
<p className="text-sm">perpetual license for 2 devices, with 2 years of updates</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<div className="my-4">
|
||||||
|
<span className="text-3xl font-extrabold">$2</span>
|
||||||
|
</div>
|
||||||
|
<p>/user/month</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className="flex items-center justify-between my-8 w-40 bg-gray-200 rounded-full p-1"
|
||||||
|
style={{ width: '24rem' }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`cursor-pointer w-1/2 h-8 flex items-center justify-center rounded-full ${
|
||||||
|
pricingOption === 'individuals' ? 'bg-white text-gray-900 font-medium' : 'text-gray-500'
|
||||||
|
}`}
|
||||||
|
onClick={() => handlePricingOptionChange('individuals')}
|
||||||
|
>
|
||||||
|
<IconUser className="text-gray-500 mr-2 icon" size={16} strokeWidth={1.5} /> Individuals
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`cursor-pointer w-1/2 h-8 flex items-center justify-center rounded-full ${
|
||||||
|
pricingOption === 'organizations' ? 'bg-white text-gray-900 font-medium' : 'text-gray-500'
|
||||||
|
}`}
|
||||||
|
onClick={() => handlePricingOptionChange('organizations')}
|
||||||
|
>
|
||||||
|
<IconUsers className="text-gray-500 mr-2 icon" size={16} strokeWidth={1.5} /> Organizations
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul role="list" className="space-y-3 text-left">
|
||||||
|
<li className="flex items-center space-x-3">
|
||||||
|
<HeartIcon />
|
||||||
|
<span>Support Bruno's Development</span>
|
||||||
|
</li>
|
||||||
|
{goldenEditon.map((item, index) => (
|
||||||
|
<li className="flex items-center space-x-3" key={index}>
|
||||||
|
<CheckIcon />
|
||||||
|
<span>{item}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GoldenEdition;
|
@ -4,19 +4,21 @@ import StyledWrapper from './StyledWrapper';
|
|||||||
import GitHubButton from 'react-github-btn';
|
import GitHubButton from 'react-github-btn';
|
||||||
import Preferences from 'components/Preferences';
|
import Preferences from 'components/Preferences';
|
||||||
import Cookies from 'components/Cookies';
|
import Cookies from 'components/Cookies';
|
||||||
|
import GoldenEdition from './GoldenEdition';
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { IconSettings, IconCookie } from '@tabler/icons';
|
import { IconSettings, IconCookie, IconHeart } from '@tabler/icons';
|
||||||
import { updateLeftSidebarWidth, updateIsDragging, showPreferences } from 'providers/ReduxStore/slices/app';
|
import { updateLeftSidebarWidth, updateIsDragging, showPreferences } from 'providers/ReduxStore/slices/app';
|
||||||
import { useTheme } from 'providers/Theme';
|
import { useTheme } from 'providers/Theme';
|
||||||
|
|
||||||
const MIN_LEFT_SIDEBAR_WIDTH = 222;
|
const MIN_LEFT_SIDEBAR_WIDTH = 221;
|
||||||
const MAX_LEFT_SIDEBAR_WIDTH = 600;
|
const MAX_LEFT_SIDEBAR_WIDTH = 600;
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
const leftSidebarWidth = useSelector((state) => state.app.leftSidebarWidth);
|
const leftSidebarWidth = useSelector((state) => state.app.leftSidebarWidth);
|
||||||
const preferencesOpen = useSelector((state) => state.app.showPreferences);
|
const preferencesOpen = useSelector((state) => state.app.showPreferences);
|
||||||
|
const [goldenEditonOpen, setGoldenEditonOpen] = useState(false);
|
||||||
|
|
||||||
const [asideWidth, setAsideWidth] = useState(leftSidebarWidth);
|
const [asideWidth, setAsideWidth] = useState(leftSidebarWidth);
|
||||||
const [cookiesOpen, setCookiesOpen] = useState(false);
|
const [cookiesOpen, setCookiesOpen] = useState(false);
|
||||||
@ -79,6 +81,7 @@ const Sidebar = () => {
|
|||||||
return (
|
return (
|
||||||
<StyledWrapper className="flex relative h-screen">
|
<StyledWrapper className="flex relative h-screen">
|
||||||
<aside>
|
<aside>
|
||||||
|
{goldenEditonOpen && <GoldenEdition onClose={() => setGoldenEditonOpen(false)} />}
|
||||||
<div className="flex flex-row h-screen w-full">
|
<div className="flex flex-row h-screen w-full">
|
||||||
{preferencesOpen && <Preferences onClose={() => dispatch(showPreferences(false))} />}
|
{preferencesOpen && <Preferences onClose={() => dispatch(showPreferences(false))} />}
|
||||||
{cookiesOpen && <Cookies onClose={() => setCookiesOpen(false)} />}
|
{cookiesOpen && <Cookies onClose={() => setCookiesOpen(false)} />}
|
||||||
@ -103,6 +106,12 @@ const Sidebar = () => {
|
|||||||
className="mr-2 hover:text-gray-700"
|
className="mr-2 hover:text-gray-700"
|
||||||
onClick={() => setCookiesOpen(true)}
|
onClick={() => setCookiesOpen(true)}
|
||||||
/>
|
/>
|
||||||
|
<IconHeart
|
||||||
|
size={18}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className="mr-2 hover:text-gray-700"
|
||||||
|
onClick={() => setGoldenEditonOpen(true)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="pl-1" style={{ position: 'relative', top: '3px' }}>
|
<div className="pl-1" style={{ position: 'relative', top: '3px' }}>
|
||||||
{/* This will get moved to home page */}
|
{/* This will get moved to home page */}
|
||||||
|
@ -107,7 +107,7 @@ app.on('ready', async () => {
|
|||||||
|
|
||||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||||
require('electron').shell.openExternal(details.url);
|
require('electron').shell.openExternal(details.url);
|
||||||
return { action: 'allow' };
|
return { action: 'deny' };
|
||||||
});
|
});
|
||||||
|
|
||||||
// register all ipc handlers
|
// register all ipc handlers
|
||||||
|
Loading…
Reference in New Issue
Block a user