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
14 changes: 14 additions & 0 deletions site-popup/template/src/backend/api/instance/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { auth } from "@wix/essentials";
import { appInstances } from "@wix/app-management";

export async function GET() {
try {
const { instance: appInstance } = await auth.elevate(
appInstances.getAppInstance
)();

return Response.json(appInstance);
} catch (error) {
return new Response("Failed to fetch app instance", { status: 500 });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import { DataRangePicker } from './date-range-picker.js';
interface Props {
activationOptions: ActivationOptions;
onChange: (activationOptions: ActivationOptions) => void;
disabled?: boolean;
}

export const ActivationConfiguration: FC<Props> = ({
onChange,
activationOptions,
disabled = false,
}) => {
return (
<Box gap={3} direction="vertical" marginTop={1}>
<RadioGroup
disabled={disabled}
name="Popup Activation"
display="horizontal"
value={activationOptions.activationMode}
Expand All @@ -31,6 +34,7 @@ export const ActivationConfiguration: FC<Props> = ({
</RadioGroup>
{activationOptions.activationMode == 'timed' && (
<DataRangePicker
disabled={disabled}
startDate={Number(
activationOptions.startDate || new Date().getTime()
)}
Expand Down
6 changes: 5 additions & 1 deletion site-popup/template/src/components/date-range-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ interface Props {
startDate: number;
endDate: number;
onChange?: (startDate: number, endDate: number) => void;
disabled?: boolean;
}

export const DataRangePicker: FC<Props> = ({
onChange,
startDate,
endDate,
onChange,
disabled = false,
}) => {
return (
<Layout>
Expand All @@ -21,6 +23,7 @@ export const DataRangePicker: FC<Props> = ({
<DatePicker
width="100%"
placeholderText="Select"
disabled={disabled}
value={new Date(startDate)}
onChange={(value: Date) => onChange?.(value.getTime(), endDate)}
/>
Expand All @@ -35,6 +38,7 @@ export const DataRangePicker: FC<Props> = ({
<DatePicker
width="100%"
placeholderText="Select"
disabled={disabled}
value={new Date(endDate)}
onChange={(value: Date) =>
onChange?.(startDate, value.getTime())
Expand Down
4 changes: 3 additions & 1 deletion site-popup/template/src/components/image-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ interface Props {
imageUrl: string;
imageTitle: string;
onChange: (imageUrl: string, imageTitle: string) => void;
disabled?: boolean;
}

export const ImagePicker: FC<Props> = ({ onChange, imageTitle }) => {
export const ImagePicker: FC<Props> = ({ onChange, imageTitle, disabled = false }) => {
return (
<Box gap={3} verticalAlign="middle" marginTop={1}>
<Input value={imageTitle} disabled />
<TextButton
disabled={disabled}
prefixIcon={<Icons.Edit />}
onClick={() =>
dashboard.openMediaManager().then((response) => {
Expand Down
19 changes: 15 additions & 4 deletions site-popup/template/src/components/site-popup-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ import {
FormField,
FormFieldProps,
} from '@wix/design-system';
import { SitePopupOptions } from '../types.js';
import { ActivationConfiguration } from './activation-configuration.js';
import { ImagePicker } from './image-picker.js';
import { SitePopupOptions } from '../types';
import { ActivationConfiguration } from './activation-configuration';
import { ImagePicker } from './image-picker';
import { SubscriptionBanner } from './subscription-banner';
import { useAppInstance } from '../hooks/instance';

interface Props {
options: SitePopupOptions;
onChange: (options: SitePopupOptions) => void;
}

export const SitePopupSettings: FC<Props> = ({ options, onChange }) => {
const { data: appInstance } = useAppInstance();

const getFieldStatus = (
field: keyof SitePopupOptions
): Partial<FormFieldProps> => {
Expand All @@ -43,14 +47,15 @@ export const SitePopupSettings: FC<Props> = ({ options, onChange }) => {
/>
<Card.Divider />
<Card.Content>
<Box gap={3} direction="vertical">
<Box gap={3} direction="horizontal">
<Box gap={3} direction="vertical" width={'50%'}>
<FormField
labelSize="small"
label="Headline"
{...getFieldStatus('headline')}
>
<Input
disabled={appInstance?.isFree}
placeholder="Sale 20% Off"
value={options?.headline}
onChange={(e) =>
Expand All @@ -72,6 +77,7 @@ export const SitePopupSettings: FC<Props> = ({ options, onChange }) => {
{...getFieldStatus('text')}
>
<InputArea
disabled={appInstance?.isFree}
placeholder="Sign up and get 20% off on our Winter Sale"
value={options?.text}
onChange={(e) =>
Expand All @@ -84,6 +90,7 @@ export const SitePopupSettings: FC<Props> = ({ options, onChange }) => {
</FormField>
<FormField labelSize="small" label="Popup Activation">
<ActivationConfiguration
disabled={appInstance?.isFree}
activationOptions={{
activationMode: options.activationMode,
startDate: options.startDate,
Expand All @@ -99,6 +106,7 @@ export const SitePopupSettings: FC<Props> = ({ options, onChange }) => {
</FormField>
<FormField labelSize="small" label="Popup Image">
<ImagePicker
disabled={appInstance?.isFree}
imageTitle={options.imageTitle}
imageUrl={options.imageUrl}
onChange={(imageUrl, imageTitle) =>
Expand All @@ -111,6 +119,9 @@ export const SitePopupSettings: FC<Props> = ({ options, onChange }) => {
/>
</FormField>
</Box>
<Box gap={3} direction="vertical" width="50%">
<SubscriptionBanner />
</Box>
</Box>
</Card.Content>
</Card>
Expand Down
61 changes: 61 additions & 0 deletions site-popup/template/src/components/subscription-banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Box, Button, MarketingLayout, SectionHelper } from '@wix/design-system';
import React from 'react';
import { useNavigateToPricingPage, useAppInstance } from '../hooks/instance';
import { appInstances } from '@wix/app-management';
import { formatDate } from '../utils/format-date';

export const SubscriptionBanner: React.FC = () => {
const {data: appInstance } = useAppInstance();
const navigateToPricingPage = useNavigateToPricingPage();
const { isFree, freeTrialAvailable, billing } = appInstance ?? {};
const isFreeTrialInProgress = billing?.freeTrialInfo?.status === appInstances.FreeTrialStatus.IN_PROGRESS;

const trialButtons = (
<Box gap={3}>
<Button
skin="premium"
onClick={navigateToPricingPage}
children="Start Free Trial"
/>
<Button
skin="premium"
priority="secondary"
onClick={navigateToPricingPage}
children="See All Plans"
/>
</Box>
);

const plansButtons = (
<Box gap={3}>
<Button
skin="premium"
children="Select Your Plan"
onClick={navigateToPricingPage}
/>
</Box>
);

if (isFree) {
return (
<MarketingLayout
title="You need a subscription"
description="Here is a preview of a site popup. Enroll a premium subscription to setup and configure your site popup."
actions={freeTrialAvailable ? trialButtons : plansButtons}
/>
);
}

if (isFreeTrialInProgress) {
const endDate = new Date(billing.freeTrialInfo?.endDate!);
return (
<SectionHelper
appearance="standard"
title="Free Trial in Progress"
children={`Your free trial is available to ${formatDate(endDate)}.`}
/>
)
}

return null;
};
6 changes: 4 additions & 2 deletions site-popup/template/src/dashboard/pages/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import '@wix/design-system/styles.global.css';
import { withProviders } from '../withProviders';
import { SitePopupSettings } from '../../components/site-popup-settings.js';
import { SitePopupOptions } from '../../types.js';
import { useEmbeds } from '../hooks/wix-embeds.js';
import { useEmbeds } from '../../hooks/wix-embeds.js';
import { Popup } from '../../components/popup/index.js';
import { useAppInstance } from '../../hooks/instance';

const sitePopupDefaultOptions: SitePopupOptions = {
headline: 'Sale 20% Off',
Expand All @@ -22,6 +23,7 @@ function SitePopup() {

const { embedScript, getEmbeddedScript } =
useEmbeds<Partial<SitePopupOptions>>();
const { isLoading: isAppInstanceLoading } = useAppInstance();

const [sitePopupOptions, setSitePopupOptions] = useState<SitePopupOptions>(
sitePopupDefaultOptions
Expand Down Expand Up @@ -53,7 +55,7 @@ function SitePopup() {
}
/>
<Page.Content>
{getEmbeddedScript.isLoading ? (
{getEmbeddedScript.isLoading || isAppInstanceLoading ? (
<Box align="center" verticalAlign="middle" height="50vh">
<Loader text="Loading..." />
</Box>
Expand Down
42 changes: 42 additions & 0 deletions site-popup/template/src/hooks/instance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useCallback } from 'react';
import { appInstances } from '@wix/app-management';
import { useQuery } from '@tanstack/react-query';
import { httpClient } from '@wix/essentials';

/*
This is the URL to the pricing page of the app.
This url looks like this:
https://www.wix.com/apps/upgrade/APPIDHERE?appInstanceId=INSTANCEIDHERE

You can find more information about this here:
https://dev.wix.com/docs/build-apps/launch-your-app/pricing-and-billing/set-up-a-freemium-business-model#step-4--create-an-upgrade-entry-point-to-your-pricing-page
*/
const getPricingPage = (instanceId: string) => `https://www.wix.com/apps/upgrade/<%= devCenter.appId %>?appInstanceId=${instanceId}`

export const QUERY_INSTANCE = 'queryInstance';

export function useAppInstance() {
return useQuery<appInstances.AppInstance>({
queryKey: [QUERY_INSTANCE],
queryFn: async () => {
try {
const response = await httpClient.fetchWithAuth(
`${import.meta.env.BASE_API_URL}/instance`
);
return response.json();
} catch (error) {
console.log("Error fetching instance:", error);
}
},
});
}

export function useNavigateToPricingPage(): () => void {
const { data: appInstance } = useAppInstance();

return useCallback(() => {
if (appInstance?.instanceId) {
window.open(getPricingPage(appInstance?.instanceId), "_blank");
}
}, [appInstance?.instanceId]);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import ReactDOM from 'react-dom';
import React, { useEffect, useState } from 'react';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { Popup } from '../../../../components/popup/index.js';
import { SitePopupOptions } from '../../../../types.js';
import './index.css';
import { site } from '@wix/site-site';
import { useAppInstance } from '../../../../hooks/instance.js';

const PopupOverlay = () => {
const [shown, setShown] = useState<boolean>(false);
const el = document.querySelector('#popup-data') as HTMLElement;
const popupParams = el?.dataset as SitePopupOptions;


const [shown, setShown] = useState<boolean>(false);
const [regionalSettings, setRegionalSettings] = useState<string>('en-us');

const { data: appInstance, isLoading } = useAppInstance();

useEffect(() => {
site.regionalSettings().then(setRegionalSettings);
Expand Down Expand Up @@ -40,6 +44,10 @@ const PopupOverlay = () => {
return false;
};

if (isLoading || appInstance?.isFree) {
return null;
}

return (
<div
className={
Expand All @@ -55,4 +63,9 @@ const PopupOverlay = () => {
);
};

ReactDOM.render(<PopupOverlay />, document.getElementById('site-popup'));
ReactDOM.render(
<QueryClientProvider client={new QueryClient()}>
<PopupOverlay />
</QueryClientProvider>,
document.getElementById('site-popup')
);
11 changes: 11 additions & 0 deletions site-popup/template/src/utils/format-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { i18n } from "@wix/essentials";

export function formatDate(date: Date) {
return date.toLocaleDateString(i18n.getLocale(), {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
}