diff --git a/.gitignore b/.gitignore index 4bd539d..3a6cce9 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ yarn-error.log* # Misc .DS_Store out + +.yarn +.idea diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index e9adee3..6573d01 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/README.md b/README.md index d6cb0ae..af66d12 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,25 @@ -# carbon-offset-app +# Order custom actions template This project was bootstrapped with [Create Wix App](https://www.npmjs.com/package/@wix/create-app). Read more about it in the [Wix CLI for Apps documentation](https://dev.wix.com/docs/build-apps/developer-tools/cli/get-started/about-the-wix-cli-for-apps). +This template demonstrate the usage of the Ecom orderes SDK, the addition of a menu item to the Order page in the Business Manager and an integration with WhatsUp web. + +### Using the Ecom orders SDK +The Ecom orders SDK is a powerful tool that allows you to interact with the orders in the store. +
In this template we use the SDK to get the order details and the order items. +//https://dev.wix.com/docs/sdk/backend-modules/ecom/orders/get-order + +### Adding a menu item to the Order page in the Business Manager +The Business Manager is the place where the store owner can manage his store. +In this template we add a menu item to the Order page in the Business Manager. +
The file (/src/dashboard/menu-plugins/my-plugin/plugin.json) demionstrate how to add a menu item to the Order page in the Business Manager. + +### Integration with WhatsUp web +WhatsUp web is a popular messaging app that allows you to send messages directly to your customers. +In this template we integrate with WhatsUp web and send an up sale message to the customer. +
the message includes the selected product details and a link to the store product with a coupon code. ## Setup 🔧 ##### Install dependencies: diff --git a/src/assets/checkout.png b/src/assets/checkout.png deleted file mode 100644 index 39b5afc..0000000 Binary files a/src/assets/checkout.png and /dev/null differ diff --git a/src/backend/api/checkout/api.ts b/src/backend/api/checkout/api.ts deleted file mode 100644 index 9659921..0000000 --- a/src/backend/api/checkout/api.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { upsertDataToCollection, safelyGetItemFromCollection } from '../../database'; -import { CHECKOUT_COLLECTION_ID } from '../../consts'; - -export async function GET(req: Request) { - const purchaseFlowId = new URL(req.url).searchParams.get('purchaseFlowId') as string; - const checkoutData = await safelyGetItemFromCollection({ - itemId: purchaseFlowId, - dataCollectionId: CHECKOUT_COLLECTION_ID, - }); - - return new Response(JSON.stringify(checkoutData ?? {})); -}; - -export async function POST(req: Request) { - const { purchaseFlowId, checkoutId, shouldAdd } = await req.json(); - - try { - await upsertDataToCollection({ - dataCollectionId: CHECKOUT_COLLECTION_ID, - item: { - _id: purchaseFlowId, - data: { - checkoutId, - shouldAdd, - }, - }, - }); - - // - return new Response('Success'); - } catch (error) { - console.log(error) - return new Response('Error') - }; -}; diff --git a/src/backend/api/order/api.ts b/src/backend/api/order/api.ts new file mode 100644 index 0000000..0196fc5 --- /dev/null +++ b/src/backend/api/order/api.ts @@ -0,0 +1,10 @@ +import { orders } from '@wix/ecom'; + +export async function GET(req: Request) { + const id = new URL(req.url).searchParams.get('orderId') as string; + + // https://dev.wix.com/docs/sdk/backend-modules/ecom/orders/get-order + const order = await orders.getOrder(id); + + return Response.json(order); +}; \ No newline at end of file diff --git a/src/backend/api/settings/api.ts b/src/backend/api/settings/api.ts deleted file mode 100644 index 4cc8b3f..0000000 --- a/src/backend/api/settings/api.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { getDataFromCollection, upsertDataToCollection } from '../../database'; -import { SETTINGS_COLLECTION_ID, DEFAULT_SETTING } from '../../consts'; -import type { Settings } from '../../../types'; - -export async function GET(req: Request) { - const settingsCollection = await getDataFromCollection({ - dataCollectionId: SETTINGS_COLLECTION_ID, - }); - - const settingsData = settingsCollection.items[0]?.data as Settings; - const settings: Settings = { - title: settingsData?.title || DEFAULT_SETTING.title, - amount: settingsData?.amount || DEFAULT_SETTING.amount, - color: settingsData?.color || DEFAULT_SETTING.color, - iconColor: settingsData?.iconColor || DEFAULT_SETTING.iconColor, - }; - - return new Response(JSON.stringify(settings)); -}; - -export async function POST(req: Request) { - const settingsData = await req.json() as Settings; - - try { - await upsertDataToCollection({ - dataCollectionId: SETTINGS_COLLECTION_ID, - item: { - // Wix data collection can be initialized as a "single item" that has the same ID - _id: 'SETTINGS', - data: settingsData, - }, - }); - - return new Response('Success'); - } catch (error) { - return new Response('Error'); - }; -}; diff --git a/src/backend/consts.ts b/src/backend/consts.ts deleted file mode 100644 index cc981d0..0000000 --- a/src/backend/consts.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Settings } from "../types"; - -// Update according to your app's needed collections -export const SETTINGS_COLLECTION_ID = 'carbon-offset-settings'; -export const CHECKOUT_COLLECTION_ID = 'carbon-offset-checkout'; -export const DEFAULT_SETTING: Settings = { - title: 'Make it carbon neutral', - amount: 2, - color: '#000000', - iconColor: '#000000', -}; diff --git a/src/backend/database.ts b/src/backend/database.ts deleted file mode 100644 index 687c959..0000000 --- a/src/backend/database.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { items } from '@wix/data'; -import { auth } from '@wix/essentials'; - -// Exposing utility functions over Wix Data APIs for easier usage and replacement of database - -type DataItem = { - _id?: string; - data: Record; -}; - -export const getDataFromCollection = async ({ - dataCollectionId -}: { dataCollectionId: string }) => { - const data = await auth.elevate(items.queryDataItems)({ - dataCollectionId, - }).find(); - - return data; -}; - -export const safelyGetItemFromCollection = async ({ - dataCollectionId, - itemId -}: { dataCollectionId: string; itemId: string }) => { - try { - const { data } = await auth.elevate(items.getDataItem)( - itemId, - { dataCollectionId }, - ); - - return data; - } catch (error) { - // Wix data's "getDataItem" API throws exception when item with id does not exist - } -}; - -export const upsertDataToCollection = async ({ - dataCollectionId, - item -}: { dataCollectionId: string; item: DataItem }) => { - const collection = await getDataFromCollection({ dataCollectionId }); - const existsInCollection = item._id && collection.items.find(existingItem => existingItem._id === item._id); - - if (item._id && existsInCollection) { - await auth.elevate(items.updateDataItem)(item._id, { - dataCollectionId, - dataItem: { - data: { - _id: item._id, - ...item.data - }, - }, - }); - } else { - await auth.elevate(items.insertDataItem)({ - dataCollectionId, - dataItem: { - _id: item._id ?? undefined, - data: { - _id: item._id ?? undefined, - ...item.data - }, - }, - }); - }; -}; diff --git a/src/backend/events/cleanup/event.ts b/src/backend/events/cleanup/event.ts deleted file mode 100644 index 57644bb..0000000 --- a/src/backend/events/cleanup/event.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { auth } from '@wix/essentials'; -import { items } from '@wix/data'; -import { checkout } from '@wix/ecom'; -import { CHECKOUT_COLLECTION_ID } from '../../consts'; - -checkout.onCheckoutCompleted(({ data }) => { - auth.elevate(items.removeDataItem)( - data.checkout?.purchaseFlowId ?? '', - { dataCollectionId: CHECKOUT_COLLECTION_ID }, - ); -}); diff --git a/src/backend/events/installation/event.ts b/src/backend/events/installation/event.ts deleted file mode 100644 index 1d9142d..0000000 --- a/src/backend/events/installation/event.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { auth } from '@wix/essentials'; -import { collections } from '@wix/data'; -import { appInstances } from '@wix/app-management'; -import { CHECKOUT_COLLECTION_ID, SETTINGS_COLLECTION_ID } from '../../consts'; - -appInstances.onAppInstanceInstalled(() => { - auth.elevate(collections.createDataCollection)({ - _id: SETTINGS_COLLECTION_ID, - displayName: "Carbon Offset Settings", - fields: [ - { key: 'title', type: collections.Type.TEXT }, - { key: 'amount', type: collections.Type.NUMBER }, - { key: 'color', type: collections.Type.TEXT }, - { key: 'iconColor', type: collections.Type.TEXT }, - ], - permissions: { - // Make sure to change the permissions according to the actual usage of your collection - insert: collections.Role.ANYONE, - read: collections.Role.ANYONE, - remove: collections.Role.ANYONE, - update: collections.Role.ANYONE, - }, - // Plugin for single item collection - plugins: [{ - type: collections.PluginType.SINGLE_ITEM, - singleItemOptions: { - singleItemId: "SETTINGS" - }, - }], - }); - - auth.elevate(collections.createDataCollection)({ - _id: CHECKOUT_COLLECTION_ID, - displayName: "Carbon Offset Checkout", - fields: [ - // In this case, checkoutId is stored as an "added" field that is not neccessarry - // the actual _id for each item of this collection will be purchaseFlowId for easy fetching - { key: 'checkoutId', type: collections.Type.TEXT }, - { key: 'shouldAdd', type: collections.Type.BOOLEAN }, - ], - permissions: { - // Make sure to change the permissions according to the actual usage of your collection - insert: collections.Role.ANYONE, - read: collections.Role.ANYONE, - remove: collections.Role.ANYONE, - update: collections.Role.ANYONE, - }, - }); -}); diff --git a/src/backend/service-plugins/ecom-additional-fees/carbon-offset/plugin.json b/src/backend/service-plugins/ecom-additional-fees/carbon-offset/plugin.json deleted file mode 100644 index 99a8d01..0000000 --- a/src/backend/service-plugins/ecom-additional-fees/carbon-offset/plugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://dev.wix.com/wix-cli/schemas/ecom-additional-fees.json", - "id": "4b15f857-4e87-4cec-bdf3-0b501b063b30", - "name": "carbon-offset" -} diff --git a/src/backend/service-plugins/ecom-additional-fees/carbon-offset/plugin.ts b/src/backend/service-plugins/ecom-additional-fees/carbon-offset/plugin.ts deleted file mode 100644 index f771d58..0000000 --- a/src/backend/service-plugins/ecom-additional-fees/carbon-offset/plugin.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { auth } from '@wix/essentials'; -import { items } from '@wix/data'; -import { additionalFees } from '@wix/ecom/service-plugins/context'; -import { CHECKOUT_COLLECTION_ID, SETTINGS_COLLECTION_ID, DEFAULT_SETTING } from '../../../consts'; -import type { Settings } from '../../../../types'; - -const getCheckoutDataFromCollection = async (purchaseFlowId: string) => { - try { - const { data } = await auth.elevate(items.getDataItem)( - purchaseFlowId, - { dataCollectionId: CHECKOUT_COLLECTION_ID }, - ); - - return data; - } catch (error) { - // Wix data's "getDataItem" API throws exception when item with id does not exist - } -}; - -const getSettingsDataFromCollection = async () => { - return auth.elevate(items.queryDataItems)({ - dataCollectionId: SETTINGS_COLLECTION_ID, - }).find() -}; - -additionalFees.provideHandlers({ - calculateAdditionalFees: async ({ request, metadata }) => { - const [checkoutData, settingsCollection] = await Promise.all([ - getCheckoutDataFromCollection(request.purchaseFlowId ?? ''), - getSettingsDataFromCollection(), - ]); - - if (checkoutData?.shouldAdd) { - const settingsData = settingsCollection.items[0]?.data as Settings; - - return { - additionalFees: [{ - name: 'Carbon Offset', - code: 'carbon-offset-fee', - price: `${settingsData.amount ?? DEFAULT_SETTING.amount}`, - }], - currency: metadata.currency!, - }; - } else { - return { - additionalFees: [], - currency: metadata.currency!, - }; - }; - }, -}); diff --git a/src/components/carbon-offset.tsx b/src/components/carbon-offset.tsx deleted file mode 100644 index 3dec0e1..0000000 --- a/src/components/carbon-offset.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { type FC } from 'react'; -import { httpClient } from '@wix/essentials'; -import { LeafIcon } from './leaf-icon'; -import type { Settings } from '../types' - -type Props = { - settings: Settings; - purchaseFlowId?: string; - checkoutId?: string; - checked?: boolean; - refreshCheckout?: () => void; -}; - -export const CarbonOffset: FC = ({ - settings, - purchaseFlowId, - checkoutId, - checked = false, - refreshCheckout, -}) => { - return ( -
-
- { - // We are using the same component both for rendering on site and for previewing in dashboard - // so we make sure it does not do anything when changing / clicking things in preview - if (purchaseFlowId) { - await httpClient.fetchWithAuth(`${import.meta.env.BASE_API_URL}/checkout`, { - method: 'POST', - body: JSON.stringify({ - purchaseFlowId, - checkoutId, - shouldAdd: e.target.checked, - }), - }); - - // Known Issue: Refresh Checkout is not yet implemented in Custom Element plugins (Wix CLI) - // to workaround it after using the plugin in the site - go out of the checkout and back again - // in order for it to reload and call the relevant SPIs with you updated configurations - refreshCheckout?.(); - }; - }} - /> -

- {settings.title} -

-
-
- -

- {`$${settings.amount}`} -

-
-
- ); -}; diff --git a/src/components/leaf-icon.tsx b/src/components/leaf-icon.tsx deleted file mode 100644 index cc46e0e..0000000 --- a/src/components/leaf-icon.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, { type FC } from 'react'; - -type Props = { - color: string; -}; - -export const LeafIcon: FC = ({ - color, -}) => { - return ( - - - - ); -}; diff --git a/src/components/main-button.tsx b/src/components/main-button.tsx deleted file mode 100644 index a2395c5..0000000 --- a/src/components/main-button.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { type FC } from 'react'; -import { httpClient } from '@wix/essentials'; -import { dashboard } from '@wix/dashboard'; -import { Button } from '@wix/design-system'; -import { GetStarted } from '@wix/wix-ui-icons-common'; -import { id as PLUGIN_ID } from '../site/plugins/custom-elements/carbon-offset/plugin.json'; -import type { Settings } from '../types'; -import '@wix/design-system/styles.global.css'; - -const WIX_ECOMMERCE_APP_ID = '1380b703-ce81-ff05-f115-39571d94dfcd'; -const CHECKOUT_PAGE_ID = '14fd5970-8072-c276-1246-058b79e70c1a'; - -export const MainButton: FC = (settings) => { - return ( - - ); -}; diff --git a/src/components/plugin-preview.tsx b/src/components/plugin-preview.tsx deleted file mode 100644 index 9a18c8a..0000000 --- a/src/components/plugin-preview.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { type FC } from 'react'; -import { CarbonOffset } from './carbon-offset'; -import { Box } from '@wix/design-system'; -import checkoutScreenshot from '../assets/checkout.png'; -import type { Settings } from '../types'; -import '@wix/design-system/styles.global.css'; - -export const PluginPreview: FC = (settings) => { - return ( - - - - - - - - ); -}; diff --git a/src/components/plugin-skeleton/plugin-skeleton.module.css b/src/components/plugin-skeleton/plugin-skeleton.module.css deleted file mode 100644 index f765bc7..0000000 --- a/src/components/plugin-skeleton/plugin-skeleton.module.css +++ /dev/null @@ -1,10 +0,0 @@ -.skeleton { - background: linear-gradient(90deg, #ddda 30%, #eaeaeaaa, #ddda 70%) right / 300% 100%; - animation: skeleton-loading 1.5s linear infinite; -} - -@keyframes skeleton-loading { - to { - background-position: left; - } -} diff --git a/src/components/plugin-skeleton/plugin-skeleton.tsx b/src/components/plugin-skeleton/plugin-skeleton.tsx deleted file mode 100644 index af795cc..0000000 --- a/src/components/plugin-skeleton/plugin-skeleton.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React, { type FC } from 'react'; -import styles from './plugin-skeleton.module.css'; - -export const PluginSkeleton: FC = () => { - return ( -
- ); -}; diff --git a/src/components/settings-form.tsx b/src/components/settings-form.tsx deleted file mode 100644 index 662b2c5..0000000 --- a/src/components/settings-form.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React, { type FC } from 'react'; -import { - Box, - ColorInput, - FormField, - Input, - NumberInput -} from '@wix/design-system'; -import type { Settings } from '../types'; -import '@wix/design-system/styles.global.css'; - -type Props = { - settings: Settings; - setSettings: (settings: Settings) => void; -}; - -export const SettingsForm: FC = ({ - settings, - setSettings, -}) => { - return ( - - - - } - min={1} - onChange={(val) => setSettings({ - ...settings, - amount: val ?? 1, - })} - /> - - - setSettings({ - ...settings, - title: val.target.value - })} - /> - - - setSettings({ - ...settings, - color: val.toString(), - })} - /> - - - setSettings({ - ...settings, - iconColor: val.toString(), - })} - /> - - - - ); -}; diff --git a/src/dashboard/menu-plugins/my-plugin/plugin.json b/src/dashboard/menu-plugins/my-plugin/plugin.json new file mode 100644 index 0000000..02096f9 --- /dev/null +++ b/src/dashboard/menu-plugins/my-plugin/plugin.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://dev.wix.com/wix-cli/schemas/dashboard-menu-plugin.json", + "id": "440141de-a590-45f6-87a3-0640a8d845ca", + "title": "Whats Upsell", + "iconKey": "Whatsapp", + "extends": "7c82ce37-3890-4613-9364-03a363c92ebe", + "action": { + "openModal": { + "componentId": "3259acd9-9b12-4f5d-9ace-737a5eb73876", + "componentParams": {} + } + } +} diff --git a/src/dashboard/modals/whatup-modals/modal.json b/src/dashboard/modals/whatup-modals/modal.json new file mode 100644 index 0000000..954327d --- /dev/null +++ b/src/dashboard/modals/whatup-modals/modal.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://dev.wix.com/wix-cli/schemas/dashboard-modal.json", + "id": "3259acd9-9b12-4f5d-9ace-737a5eb73876", + "title": "Upsell with whatsup", + "width": 600, + "height": 900 +} diff --git a/src/dashboard/modals/whatup-modals/modal.tsx b/src/dashboard/modals/whatup-modals/modal.tsx new file mode 100644 index 0000000..b9e63e3 --- /dev/null +++ b/src/dashboard/modals/whatup-modals/modal.tsx @@ -0,0 +1,158 @@ +import React, { type FC, useEffect, useState } from 'react'; +import { dashboard } from '@wix/dashboard'; +import { httpClient } from "@wix/essentials"; +import { media } from "@wix/sdk"; +import { orders } from '@wix/ecom'; +import { + WixDesignSystemProvider, + Text, + Box, + Image, + CustomModalLayout, + RadioGroup, + InputArea, + FormField, + Divider, + Loader, + SectionHelper, +} from '@wix/design-system'; +import '@wix/design-system/styles.global.css'; +import { height, width, title } from './modal.json'; +import { getContactNameFromOrder, getPhoneNumberFromOrder } from "../../../utils/get-details-from-order"; +import { generateWhatsappLink, generateWhatsappUpsellMessage } from '../../../utils/whatsapp-link-generator'; + +type Product = { + id: string; + value: string; + image: string; +}; + +const Modal: FC<{ orderId: string }> = ({ orderId }) => { + const [loading, setLoading] = useState(true); + const [selectedProduct, setSelectedProduct] = useState(); + const [productOptionsList, setProductOptionsList] = useState([]); + const [phoneNumber, setPhoneNumber] = useState(''); + const [contactName, setContactName] = useState(''); + const [controlledWhatsappMessage, setControlledWhatsappMessage] = useState( + selectedProduct ? generateWhatsappUpsellMessage(contactName, selectedProduct.value) : '' + ); + + useEffect(() => { + const fetchProducts = async () => { + try { + const response = await httpClient.fetchWithAuth(`${import.meta.env.BASE_API_URL}/order?orderId=${orderId}`); + const order: orders.Order = await response.json(); + const mappedOptions = (order.lineItems ?? []).map(lineItem => { + return { + id: lineItem.catalogReference?.catalogItemId ?? '', + value: lineItem.productName?.original ?? '', + image: lineItem.image ?? '', + }; + }); + + setProductOptionsList(mappedOptions); + setPhoneNumber(getPhoneNumberFromOrder(order)); + setContactName(getContactNameFromOrder(order)); + setSelectedProduct(mappedOptions?.[0]); + setLoading(false); + } catch (error) { + dashboard.showToast({ + message: 'Failed to Update Settings', + type: 'error', + }); + }; + }; + + fetchProducts(); + }, []); + + return ( + + dashboard.closeModal()} + secondaryButtonText="Cancel" + secondaryButtonOnClick={() => dashboard.closeModal()} + primaryButtonText="Send Message" + primaryButtonProps={{ disabled: !phoneNumber }} + primaryButtonOnClick={() => { + dashboard.closeModal(); + + if (selectedProduct) { + const whatsappLink = generateWhatsappLink( + contactName, + selectedProduct.value, + phoneNumber, + controlledWhatsappMessage + ); + + window.open(whatsappLink); + }; + }} + content={ + + {loading ? : ( + <> + {!phoneNumber && ( + + Please add a phone number to the contact to send a message + + )} + + { + const product = productOptionsList.find(product => { + return product.id === id; + }); + + if (product) { + setSelectedProduct(product); + }; + }} + > + {productOptionsList.map((product) => { + return ( + + + + {product.value} + + + ) + })} + + + + + setControlledWhatsappMessage(e.target.value)} + /> + + + )} + + } + /> + + ); +}; + +export default Modal; diff --git a/src/dashboard/pages/page.json b/src/dashboard/pages/page.json deleted file mode 100644 index 19aa23a..0000000 --- a/src/dashboard/pages/page.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://dev.wix.com/wix-cli/schemas/dashboard-page.json", - "id": "7a09bd5e-9992-4ad8-b3d2-e179774468ff", - "title": "Carbon Offset" -} diff --git a/src/dashboard/pages/page.tsx b/src/dashboard/pages/page.tsx deleted file mode 100644 index f85bb85..0000000 --- a/src/dashboard/pages/page.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { useEffect, useState, type FC } from 'react'; -import { httpClient } from '@wix/essentials'; -import { - Box, - Card, - Cell, - Layout, - Loader, - Page, - WixDesignSystemProvider, -} from '@wix/design-system'; -import { MainButton } from '../../components/main-button'; -import { PluginPreview } from '../../components/plugin-preview'; -import { SettingsForm } from '../../components/settings-form'; -import type { Settings } from '../../types'; -import '@wix/design-system/styles.global.css'; - -const Index: FC = () => { - const [settings, setSettings] = useState() - - useEffect(() => { - const fetchSettings = async () => { - const res = await httpClient.fetchWithAuth(`${import.meta.env.BASE_API_URL}/settings`); - const data: Settings = (await res.json()); - - setSettings(data); - }; - - fetchSettings(); - }, []); - - return ( - - {!settings ? ( - - - - ) : ( - - - } - /> - - - - - - - - - - - - - - - - - - - - - - - - )} - - ); -}; - -export default Index; diff --git a/src/site/plugins/custom-elements/carbon-offset/panel.tsx b/src/site/plugins/custom-elements/carbon-offset/panel.tsx deleted file mode 100644 index 16bc3ce..0000000 --- a/src/site/plugins/custom-elements/carbon-offset/panel.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -// Checkout plugin does not use a panel -// Currently keep this file as it is -const Panel = () => { - return ( - <> - ); -}; - -export default Panel; diff --git a/src/site/plugins/custom-elements/carbon-offset/plugin.json b/src/site/plugins/custom-elements/carbon-offset/plugin.json deleted file mode 100644 index 8e818c8..0000000 --- a/src/site/plugins/custom-elements/carbon-offset/plugin.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "id": "a37dc989-b6bc-43fd-8786-fe54bacd862e", - "referenceComponentId": "8a82aa10-badb-435b-9c93-8c094247cb7a", - "marketData": { - "name": "Carbon Offset", - "logoUrl": "https://images.vexels.com/media/users/3/142789/isolated/lists/2bfb04ad814c4995f0c537c68db5cd0b-multicolor-swirls-circle-logo.png" - }, - "placements": [ - { - "appDefinitionId": "1380b703-ce81-ff05-f115-39571d94dfcd", - "widgetId": "14fd5970-8072-c276-1246-058b79e70c1a", - "slotId": "checkout:summary:totalsBreakdown:before" - } - ] -} diff --git a/src/site/plugins/custom-elements/carbon-offset/plugin.module.css b/src/site/plugins/custom-elements/carbon-offset/plugin.module.css deleted file mode 100644 index 9c670fe..0000000 --- a/src/site/plugins/custom-elements/carbon-offset/plugin.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.root { - width: 100%; - height: 100%; -} diff --git a/src/site/plugins/custom-elements/carbon-offset/plugin.tsx b/src/site/plugins/custom-elements/carbon-offset/plugin.tsx deleted file mode 100644 index 155d25e..0000000 --- a/src/site/plugins/custom-elements/carbon-offset/plugin.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { useEffect, useState, useMemo, type FC } from 'react'; -import ReactDOM from 'react-dom'; -import reactToWebComponent from 'react-to-webcomponent'; -import { httpClient } from '@wix/essentials'; -import { checkout } from '@wix/ecom'; -import { CarbonOffset } from '../../../../components/carbon-offset'; -import { PluginSkeleton } from '../../../../components/plugin-skeleton/plugin-skeleton'; -import type { Settings } from '../../../../types'; - -type Props = { - checkoutId: string; -}; - -type CallbackFunction = () => void; - -let refreshCheckout: CallbackFunction = () => { - console.log("Checkout Refreshed"); -}; - -const CustomElement: FC = (props) => { - const [settings, setSettings] = useState(); - const [checked, setChecked] = useState(false); - const [purchaseFlowId, setPurchaseFlowId] = useState(''); - - const checkoutId = useMemo(() => { - return props.checkoutId && props.checkoutId.replaceAll('"', ''); - }, [props.checkoutId]); - - useEffect(() => { - const fetchSettings = async () => { - const settingsRes = await httpClient.fetchWithAuth(`${import.meta.env.BASE_API_URL}/settings`); - return settingsRes.json(); - }; - - const fetchData = async () => { - const [settingsData, { purchaseFlowId }] = await Promise.all([fetchSettings(), checkout.getCheckout(checkoutId)]); - const checkoutRes = await httpClient.fetchWithAuth(`${import.meta.env.BASE_API_URL}/checkout?purchaseFlowId=${purchaseFlowId}`); - const checkoutData = await checkoutRes.json(); - - setSettings(settingsData); - setPurchaseFlowId(purchaseFlowId ?? ''); - setChecked(checkoutData.shouldAdd ?? false); - }; - - if (checkoutId) { - fetchData(); - }; - }, [checkoutId]); - - return ( - <> - {!settings ? ( - - ) : ( - - )} - - ); -}; - -const customElement = reactToWebComponent( - CustomElement, - React, - ReactDOM as any, - { - props: { - checkoutId: 'string', - }, - } -); - -// Not yet implemented -customElement.prototype.onRefreshCheckout = (callback: CallbackFunction) => { - refreshCheckout = callback; -}; - -export default customElement; diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 3c6392b..0000000 --- a/src/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type Settings = { - amount: number; - title: string; - color: string; - iconColor: string; -}; diff --git a/src/utils/get-details-from-order.ts b/src/utils/get-details-from-order.ts new file mode 100644 index 0000000..a466c6e --- /dev/null +++ b/src/utils/get-details-from-order.ts @@ -0,0 +1,17 @@ +import { orders } from '@wix/ecom'; + +export function getPhoneNumberFromOrder(order: orders.Order) { + const phoneNumber = order.billingInfo?.contactDetails?.phone ?? order.recipientInfo?.contactDetails?.phone; + if (!phoneNumber) return ''; + + // Remove all dashes from the phone number + const sanitizedPhoneNumber = phoneNumber.replaceAll('-', ''); + return sanitizedPhoneNumber.startsWith('972') ? sanitizedPhoneNumber : `972${sanitizedPhoneNumber}`; +}; + +export function getContactNameFromOrder(order: orders.Order) { + const firstName = order.billingInfo?.contactDetails?.firstName ?? order.recipientInfo?.contactDetails?.firstName; + const lastName = order.billingInfo?.contactDetails?.lastName ?? order.recipientInfo?.contactDetails?.lastName; + + return [firstName, lastName].filter(Boolean).join(' ') || 'dear user'; +}; \ No newline at end of file diff --git a/src/utils/whatsapp-link-generator.ts b/src/utils/whatsapp-link-generator.ts new file mode 100644 index 0000000..1fc9af4 --- /dev/null +++ b/src/utils/whatsapp-link-generator.ts @@ -0,0 +1,27 @@ +const generateProductLink = (productName: string) => { + const productRoute = productName.replaceAll(' ', '-').toLowerCase(); + + // This is a sample url, you should replace it with your own site url + // TODO: dynamically get site URL + const siteUrl = "https://etaybarzilay.wixstudio.io/order-custom-actions"; + return `${siteUrl}/product-page/${productRoute}`; +}; + +export const generateWhatsappLink = (contactName: string, productName: string, phone: string, controlledMessage?: string) => { + let message = controlledMessage || generateWhatsappUpsellMessage(contactName, productName); + const whatsappLink = `https://wa.me/${phone}?text=${encodeURIComponent(message)}`; + + + return whatsappLink; +}; + +export const generateWhatsappUpsellMessage = (contactName: string, productName: string) => { + return ` + *Hey ${contactName}!*\n + Hope you're still enjoying your ${productName}! =]\n Need a new one?\n + Grab it now. Just tap here to order: ${generateProductLink(productName)}\n + Don't miss out! + `; +}; + + diff --git a/wix.config.json b/wix.config.json index ec5f50a..7883628 100644 --- a/wix.config.json +++ b/wix.config.json @@ -1,5 +1,5 @@ { "$schema": "https://dev.wix.com/wix-cli/schemas/wix-config.json", - "appId": "a21a2d2f-e642-4195-adcc-ccdd389ba3c0", - "projectId": "standalone-carbon-offset" + "appId": "9f95a5e9-9723-4183-8025-3e15bcad4960", + "projectId": "custom-order-action-template" }