diff --git a/.env.example b/.env.example index 1c636c5..b4d91c8 100644 --- a/.env.example +++ b/.env.example @@ -2,5 +2,5 @@ APP_DOMAIN=bundles.example.com SHOPIFY_API_KEY=your_client_id SHOPIFY_API_SECRET=your_client_secret -SHOPIFY_APP_HANDLE=bundle-offers +SHOPIFY_APP_HANDLE=offerlyst POSTGRES_PASSWORD=change-me-to-a-long-random-string diff --git a/Dockerfile b/Dockerfile index 861fad1..d0871d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# Bundle Offers web app (admin, app proxy, webhooks, scheduler). The Shopify extensions are deployed +# Offerlyst web app (admin, app proxy, webhooks, scheduler). The Shopify extensions are deployed # separately with `shopify app deploy`. FROM node:22-alpine AS build RUN apk add --no-cache openssl diff --git a/README.md b/README.md index 077ba81..e909113 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Bundle Offers (Shopify app) +# Offerlyst: Bundles & BOGO (Shopify app) + +Brand assets: `brand/` (App Store icon `offerlyst-icon-1200.png`). Public Shopify app that re-creates the ClicksToCart Odoo module `c2c_bundle_offers`: fixed bundles, mix & match and buy X get Y, applied by **one** automatic app discount whose function evaluates every running offer in diff --git a/app/lib/orders.server.ts b/app/lib/orders.server.ts index 2c8ef18..8940754 100644 --- a/app/lib/orders.server.ts +++ b/app/lib/orders.server.ts @@ -1,6 +1,6 @@ // orders/create -> analytics (F23, N02). The function's candidate message is the offer name, or "A + B" when // one line carries two offers; we attribute by name. If Shopify reports only the discount title -// ("Bundle Offers"), the usage is kept under that title. Verify the exact title on a dev store (README). +// (DISCOUNT_TITLE, "Offerlyst bundles"), the usage is kept under that title. Verify the exact title on a dev store (README). import db from "../db.server"; export interface OrderPayload { diff --git a/app/lib/publish.server.ts b/app/lib/publish.server.ts index 63685c8..5d88403 100644 --- a/app/lib/publish.server.ts +++ b/app/lib/publish.server.ts @@ -6,7 +6,7 @@ import { compile, settingsOf, type CompileResult } from "./compile"; import { gql, ensureShop, type AdminLike } from "./shop.server"; export const FUNCTION_HANDLE = "bundle-discount"; -export const DISCOUNT_TITLE = "Bundle Offers"; +export const DISCOUNT_TITLE = "Offerlyst bundles"; export const STOREFRONT_NAMESPACE = "bundle_offers"; const CREATE = `#graphql @@ -94,7 +94,7 @@ async function ensureDiscount(admin: AdminLike, shop: { id: string; discountNode }, }); const err = userErrorText(data.discountAutomaticAppCreate.userErrors); - if (err) throw new Error(`Could not create the Bundle Offers discount: ${err}`); + if (err) throw new Error(`Could not create the Offerlyst discount: ${err}`); const id = data.discountAutomaticAppCreate.automaticAppDiscount.discountId as string; await db.shop.update({ where: { id: shop.id }, data: { discountNodeId: id } }); return { id, created: true }; diff --git a/app/lib/storefront.server.ts b/app/lib/storefront.server.ts index b15e3fd..ffd9bfe 100644 --- a/app/lib/storefront.server.ts +++ b/app/lib/storefront.server.ts @@ -106,7 +106,7 @@ export function bundlesListLiquid(offers: OfferWithRelations[], shop: ShopCtx, l return `

Bundle offers

${body} - ${shop.branding ? `

Bundles by Bundle Offers

` : ""} + ${shop.branding ? `

Bundles by Offerlyst

` : ""}
`; } diff --git a/app/routes/_index/route.tsx b/app/routes/_index/route.tsx index fbcfa72..fd3284f 100644 --- a/app/routes/_index/route.tsx +++ b/app/routes/_index/route.tsx @@ -21,7 +21,7 @@ export default function App() { return (
-

Bundle Offers: run all your festival deals at once

+

Offerlyst: run all your bundle deals at once

Fixed bundles, mix & match and buy X get Y, applied automatically at checkout. Each item counts towards one offer, in the order you choose.

diff --git a/app/routes/app._index.tsx b/app/routes/app._index.tsx index 6bade3c..fdfe35b 100644 --- a/app/routes/app._index.tsx +++ b/app/routes/app._index.tsx @@ -78,7 +78,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { export default function Home() { const d = useLoaderData(); const steps = [ - { done: d.setup.embedOn, label: "Turn on the Bundle Offers app embed", help: "Loads the storefront script and styles (cart hints, add bundle to cart).", action: Open theme editor }, + { done: d.setup.embedOn, label: "Turn on the Offerlyst app embed", help: "Loads the storefront script and styles (cart hints, add bundle to cart).", action: Open theme editor }, { done: d.menuLinked, label: "Link the bundles page in your menu", help: "Your bundles page is at /apps/bundles. Add it to the main menu (Online Store › Navigation), or place the “Bundles grid” block on any page.", action: Open navigation }, { done: d.totalOffers > 0, label: "Create your first offer", help: "Start from scratch or from a festival template.", action: Create offer }, { done: d.running.length > 0, label: "Publish an offer and preview it", help: "Published offers within their dates run automatically in cart and checkout.", action: View bundles page }, @@ -86,7 +86,7 @@ export default function Home() { const doneCount = steps.filter((s) => s.done).length; return ( - + Create offer {d.shop.publishError ? ( diff --git a/app/routes/app.plans.tsx b/app/routes/app.plans.tsx index 17e79a0..ac3a165 100644 --- a/app/routes/app.plans.tsx +++ b/app/routes/app.plans.tsx @@ -10,7 +10,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { const { admin, session } = await authenticate.admin(request); const shop = await syncShop(admin, session.shop); // refresh the plan from activeSubscriptions const store = session.shop.replace(".myshopify.com", ""); - const handle = process.env.SHOPIFY_APP_HANDLE || "bundle-offers"; + const handle = process.env.SHOPIFY_APP_HANDLE || "offerlyst"; return { current: shop.planCode, pricingUrl: `https://admin.shopify.com/store/${store}/charges/${handle}/pricing_plans`, diff --git a/app/routes/privacy.tsx b/app/routes/privacy.tsx index 9229f16..59e014c 100644 --- a/app/routes/privacy.tsx +++ b/app/routes/privacy.tsx @@ -1,7 +1,7 @@ // Public privacy policy (required for the App Store listing): https:///privacy import type { MetaFunction } from "react-router"; -export const meta: MetaFunction = () => [{ title: "Privacy policy · FestiveBundle" }]; +export const meta: MetaFunction = () => [{ title: "Privacy policy · Offerlyst" }]; const CONTACT = "info@metatroncubesolutions.com"; @@ -9,7 +9,7 @@ export default function Privacy() { return (

Privacy policy

-

Last updated: October 13, 2026. This policy explains what the FestiveBundle app (by Metatroncube Software Solutions) collects when a merchant installs it on a Shopify store.

+

Last updated: October 14, 2026. This policy explains what the Offerlyst app (by Metatroncube Software Solutions) collects when a merchant installs it on a Shopify store.

What we collect

    diff --git a/app/routes/webhooks.app.uninstalled.tsx b/app/routes/webhooks.app.uninstalled.tsx index 5d241ce..ca91907 100644 --- a/app/routes/webhooks.app.uninstalled.tsx +++ b/app/routes/webhooks.app.uninstalled.tsx @@ -5,7 +5,7 @@ import db from "../db.server"; // The access token is already revoked here, so the app can't call the Admin API. Shopify removes the app's // functions with the app, so the automatic discount stops applying, and it deletes app-owned ($app and // app-data) metafields. We mark the shop and keep its data for 48 h, until shop/redact deletes it. -// The in-app "Remove all Bundle Offers data" action (Settings) deletes the discount explicitly. +// The in-app "Remove all Offerlyst data" action (Settings) deletes the discount explicitly. export const action = async ({ request }: ActionFunctionArgs) => { const { shop, session, topic } = await authenticate.webhook(request); console.log(`Received ${topic} webhook for ${shop}`); diff --git a/app/routes/webhooks.compliance.tsx b/app/routes/webhooks.compliance.tsx index 97657b6..8d5853b 100644 --- a/app/routes/webhooks.compliance.tsx +++ b/app/routes/webhooks.compliance.tsx @@ -2,7 +2,7 @@ import type { ActionFunctionArgs } from "react-router"; import { authenticate } from "../shopify.server"; import db from "../db.server"; -// Mandatory GDPR webhooks. Bundle Offers stores no customer personal data: only order ids, order names and +// Mandatory GDPR webhooks. Offerlyst stores no customer personal data: only order ids, order names and // amounts for analytics (no names, emails or addresses). customers/data_request and customers/redact have // nothing to return or erase; shop/redact (48 h after uninstall) deletes everything about the shop. export const action = async ({ request }: ActionFunctionArgs) => { diff --git a/brand/offerlyst-brand-board.png b/brand/offerlyst-brand-board.png new file mode 100644 index 0000000..814697a Binary files /dev/null and b/brand/offerlyst-brand-board.png differ diff --git a/brand/offerlyst-icon-1200.png b/brand/offerlyst-icon-1200.png new file mode 100644 index 0000000..62ddacd Binary files /dev/null and b/brand/offerlyst-icon-1200.png differ diff --git a/brand/offerlyst-icon-256.png b/brand/offerlyst-icon-256.png new file mode 100644 index 0000000..e291809 Binary files /dev/null and b/brand/offerlyst-icon-256.png differ diff --git a/brand/offerlyst-icon-64.png b/brand/offerlyst-icon-64.png new file mode 100644 index 0000000..43750f3 Binary files /dev/null and b/brand/offerlyst-icon-64.png differ diff --git a/brand/offerlyst-logo-horizontal.png b/brand/offerlyst-logo-horizontal.png new file mode 100644 index 0000000..dbb6f5b Binary files /dev/null and b/brand/offerlyst-logo-horizontal.png differ diff --git a/brand/offerlyst-symbol-transparent.png b/brand/offerlyst-symbol-transparent.png new file mode 100644 index 0000000..587d114 Binary files /dev/null and b/brand/offerlyst-symbol-transparent.png differ diff --git a/docker-compose.yml b/docker-compose.yml index 09daf8d..e59c43e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: SHOPIFY_API_KEY: ${SHOPIFY_API_KEY:?set SHOPIFY_API_KEY in .env} SHOPIFY_API_SECRET: ${SHOPIFY_API_SECRET:?set SHOPIFY_API_SECRET in .env} SHOPIFY_APP_URL: https://${APP_DOMAIN:?set APP_DOMAIN in .env} - SHOPIFY_APP_HANDLE: ${SHOPIFY_APP_HANDLE:-bundle-offers} + SHOPIFY_APP_HANDLE: ${SHOPIFY_APP_HANDLE:-offerlyst} SCOPES: read_products,write_discounts,read_discounts,read_orders,write_files,read_themes,write_app_proxy expose: - "3000" diff --git a/extensions/bundle-discount/locales/en.default.json b/extensions/bundle-discount/locales/en.default.json index a104f7f..f30eaf8 100644 --- a/extensions/bundle-discount/locales/en.default.json +++ b/extensions/bundle-discount/locales/en.default.json @@ -1,4 +1,4 @@ { - "name": "Bundle Offers", + "name": "Offerlyst bundles", "description": "Applies fixed, mix & match and buy X get Y bundle discounts. Each item counts towards one offer, in priority order." } diff --git a/extensions/bundle-discount/shopify.extension.toml b/extensions/bundle-discount/shopify.extension.toml index 3ce0bd4..9764dab 100644 --- a/extensions/bundle-discount/shopify.extension.toml +++ b/extensions/bundle-discount/shopify.extension.toml @@ -1,4 +1,4 @@ -# Bundle Offers: ONE automatic app discount per shop runs this function, which evaluates every running offer. +# Offerlyst: ONE automatic app discount per shop runs this function, which evaluates every running offer. # Rust (not JS): the JS build needed 39M instructions just to read a 200-line cart (limit 11M). See README. api_version = "2026-07" diff --git a/extensions/bundle-offers-theme/assets/bundle-offers.css b/extensions/bundle-offers-theme/assets/bundle-offers.css index 81eaa78..8930596 100644 --- a/extensions/bundle-offers-theme/assets/bundle-offers.css +++ b/extensions/bundle-offers-theme/assets/bundle-offers.css @@ -1,4 +1,4 @@ -/* Bundle Offers storefront styles. +/* Offerlyst storefront styles. Inherits the theme: fonts, text colour and background come from the page. Lines are a mix of the text colour, so they work on light and dark themes alike. The merchant can set an accent and a radius (Settings). The one decorative device is the coupon stub on the badge: festival offers as tear-off coupons. */ diff --git a/extensions/bundle-offers-theme/assets/bundle-offers.js b/extensions/bundle-offers-theme/assets/bundle-offers.js index fb49fed..6834d9a 100644 --- a/extensions/bundle-offers-theme/assets/bundle-offers.js +++ b/extensions/bundle-offers-theme/assets/bundle-offers.js @@ -1,4 +1,4 @@ -/* Bundle Offers storefront script (app embed). Vanilla JS, no dependencies. +/* Offerlyst storefront script (app embed). Vanilla JS, no dependencies. * - converts shop-currency deal amounts when the customer shops in another currency * - bundle pages (/apps/bundles/...): quantity steppers, "N of M chosen", add all items in one request * - refreshes cart hints after AJAX cart changes */ diff --git a/extensions/bundle-offers-theme/blocks/bundle-offers-embed.liquid b/extensions/bundle-offers-theme/blocks/bundle-offers-embed.liquid index d103b6b..0e03531 100644 --- a/extensions/bundle-offers-theme/blocks/bundle-offers-embed.liquid +++ b/extensions/bundle-offers-theme/blocks/bundle-offers-embed.liquid @@ -27,7 +27,7 @@ {% schema %} { - "name": "Bundle Offers", + "name": "Offerlyst", "target": "body", "javascript": "bundle-offers.js", "stylesheet": "bundle-offers.css", diff --git a/extensions/bundle-offers-theme/blocks/bundles-grid.liquid b/extensions/bundle-offers-theme/blocks/bundles-grid.liquid index e8ea790..d25c3c6 100644 --- a/extensions/bundle-offers-theme/blocks/bundles-grid.liquid +++ b/extensions/bundle-offers-theme/blocks/bundles-grid.liquid @@ -17,7 +17,7 @@ {{ 'bundle_offers.grid.continue' | t }}
{%- endif -%} - {%- if data.brand -%}

Bundles by Bundle Offers

{%- endif -%} + {%- if data.brand -%}

Bundles by Offerlyst

{%- endif -%} {% schema %} diff --git a/extensions/bundle-offers-theme/shopify.extension.toml b/extensions/bundle-offers-theme/shopify.extension.toml index 948ac4b..dea106c 100644 --- a/extensions/bundle-offers-theme/shopify.extension.toml +++ b/extensions/bundle-offers-theme/shopify.extension.toml @@ -1,2 +1,2 @@ -name = "Bundle Offers storefront" +name = "Offerlyst storefront" type = "theme" diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9c53550..d1b2511 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,4 +1,4 @@ -// Bundle Offers app database (brief §14). Postgres. +// Offerlyst app database (brief §14). Postgres. generator client { provider = "prisma-client-js" } diff --git a/shopify.app.toml b/shopify.app.toml index 92675b4..324dfc9 100644 --- a/shopify.app.toml +++ b/shopify.app.toml @@ -1,7 +1,8 @@ -# Bundle Offers — Shopify app configuration. +# Offerlyst — Shopify app configuration. # Run `npm run config:link` once to connect this folder to your app in the Partner Dashboard (fills client_id). client_id = "" -name = "Bundle Offers" +name = "Offerlyst: Bundles & BOGO" +handle = "offerlyst" application_url = "https://example.com" embedded = true