BolaChat is live in Zambia and Zimbabwe, with Malawi coming soon. Choose your country to get started.
SocialTypeScript · CSSGitHub @BolabetOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A rebuild of the BolaChat marketing site (https://bolachat.africa) - the WhatsApp-native AI sports betting product from Bola Group / Bolabet, live in Zambia with Zimbabwe and Malawi planned. This repo replaces the original one-off page with a maintainable, multi-market Next.js app.
This is not a redesign. It reproduces the live product's structure and copy, on a clean architecture that can add new countries without touching presentation code.
Visual design follows the Bolabet Corporate Identity Manual v1.0 (the parent brand's CI, applied here to the BolaChat sub-brand):
#231F20 background, Action Yellow
#F5E500 as the single accent, Chalk White #FFFFFF text. This is the
CI's "Home Kit" (3.3) - the black/yellow combination specified for core
sports betting product UI.--whatsapp) and Telegram CTA buttons use Telegram's blue
(--telegram), both in globals.css, so they still read as "this opens
WhatsApp / Telegram" at a glance. Every other accent on the site - links,
badges, focus states, the wordmark split - is Action Yellow. See
Button.tsx: the whatsapp and telegram variants are reserved for
wa.me / t.me links only..font-heading utility class; Inter for
body/UI text, with tabular lining numerals turned on site-wide
(font-feature-settings: "tnum") so odds and amounts align in columns -
apply the tabular-nums class directly to any new numeric display.
Both fonts are self-hosted via @fontsource-variable/* (no runtime call
to Google's font CDN).-9.45deg, stored as --bola-angle.
Applied to graphic elements only (the BolaSlash component, the
Responsible Gaming badge's plates, the half-tone texture) - never to
running text, which gets its slant from the italic headline typeface
instead..bola-halftone CSS utility (see SportArt.tsx for usage).ResponsibleGamingBadge.tsx, used in the Footer.BolaWordmark.tsx reproduces the
BOLA/BET colour-split pattern for BOLA/CHAT.New brand-system components live in src/components/ui/:
BolaWordmark.tsx, BolaSlash.tsx, ResponsibleGamingBadge.tsx.
eslint-config-next)@fontsource-variable/*) for
brand typography - see "Brand system" aboveNo Google Fonts CDN dependency: fonts are bundled as static files through
npm (@fontsource-variable/archivo, @fontsource-variable/inter) rather
than loaded via next/font/google, so the build never depends on reaching
fonts.googleapis.com at request time - one less thing that can fail in a
locked-down CI/build environment, and one fewer third-party network request
for visitors.
src/
app/
layout.tsx Root HTML shell, global metadata defaults
page.tsx "/" - choose-your-country fallback (see below)
robots.ts /robots.txt
sitemap.ts /sitemap.xml, generated from the market registry
zm/page.tsx Zambia route - metadata + <MarketPage market={zm} />
mw/page.tsx Malawi route
zw/page.tsx Zimbabwe route
components/
MarketPage.tsx The one page template every market route renders
layout/ Header, Footer
sections/ Hero, PowerPlays, HowItWorks, WhyMessaging, SportsGrid, FinalCta
content/
powerplays/ <code>.json - weekly PowerPlays, edited via /admin
ui/ Button, ChannelButtons, Container, Badge, SectionHeading, SportArt, ChatMockup, WhatsAppIcon, TelegramIcon
config/
markets/ zm.ts / mw.ts / zw.ts / index.ts - see "Market configuration" below
lib/
whatsapp.ts wa.me URL builder (single source of truth for WhatsApp links)
telegram.ts t.me URL builder (single source of truth for Telegram links)
channels.ts channelNames(): "WhatsApp" vs "WhatsApp and Telegram" copy per market
utils.ts cx() class-name helper
types/
market.ts MarketConfig contract every market file must satisfy
styles/
globals.css Design tokens (CSS variables) + Tailwind import
public/
images/sports/ Drop real photography here (see below)
icons/
Nothing in components/ imports a market-specific value directly - every
component that needs one receives a market: MarketConfig prop and reads
from it. That's what makes /zm, /mw, and /zw three routes over one
codebase instead of three separate sites.
Each market is one file in src/config/markets/ implementing the
MarketConfig type (src/types/market.ts): currency, WhatsApp number, optional Telegram bot username (omit telegram for markets without Telegram), optional comingSoon flag,
registration/login/deposit URLs, legal links, support details, SEO
metadata, and analytics IDs (left empty until you're ready to enable them).
Known placeholders you must replace before a market goes live - each is
marked // TODO in its config file:
| Market | What's a placeholder |
|---|---|
Zambia (zm.ts) | Helpline contact (WhatsApp number set; no Telegram in this market) |
Malawi (mw.ts) | Currently comingSoon (renders the coming-soon page). To launch: remove comingSoon, add the real WhatsApp number (and a telegram block if applicable), then confirm domain, all legal URLs, helpline |
Zimbabwe (zw.ts) | WhatsApp number and Telegram bot set. Still to confirm: domain (bolabet.co.zw), currency (USD vs ZiG), all legal URLs, helpline |
Zambia's domain (bolabet.co.zm) and currency (Kwacha, symbol K) are
carried over from the live site's own copy, so those aren't placeholders.
src/config/markets/zm.ts to src/config/markets/<code>.ts (use
the two-letter country code, lowercase).src/config/markets/index.ts:
import { <code> } from "./<code>";
export const markets: Record<MarketCode, MarketConfig> = {
zm, mw, zw, <code>,
};
"<code>" to the MarketCode union in src/types/market.ts.src/app/<code>/page.tsx - copy src/app/zm/page.tsx and swap
the import.That's it. sitemap.ts and the root "choose your country" page both read
from the registry automatically, so they pick up the new market without
any further changes.
The site lives on bolachat.africa (set in src/config/site.ts). Each
market is a path on that one domain:
| Address | Shows |
|---|---|
bolachat.africa | "Choose your country" page |
bolachat.africa/zm | Zambia |
bolachat.africa/zw | Zimbabwe |
bolachat.africa/mw | Malawi (coming soon page) |
Canonical URLs and the sitemap use these addresses. Connecting the domain
(Vercel): add bolachat.africa and www.bolachat.africa under Project ->
Settings -> Domains, then create the DNS records Vercel shows at the DNS
provider. No per-market domain setup is needed.
Just edit the relevant field in that market's file under
src/config/markets/. There's no build step or code generation - the
config file is the config.
Photography lives in public/images/sports/ and is rendered with
next/image in Hero (epl.jpg) and SportsGrid
(soccer.jpg, cricket.jpg, rugby.jpg). To swap an image, replace the
file (keep the name) or change the src in the component. Each image sits
in a parent with a fixed aspect ratio (aspect-[4/3]) and uses object-cover, so layout doesn't
shift. <SportArt /> is kept as a fallback placeholder but is no longer
used on the page.
/admin)The weekly PowerPlays section on each market page is edited through a
password-protected admin panel at bolachat.africa/admin - no code changes
needed.
copy.powerPlayExample in the market config), labelled
"Example", exactly as before.src/content/powerplays/<code>.json and uploaded images in
public/images/powerplays/. "Save & publish" commits both to GitHub in a
single commit, which triggers the normal deploy - the live site updates
once that deploy finishes (a couple of minutes). Market pages stay fully
static; only the /api/admin/* routes run server-side. If two people
edit at once, the second save is refused with a "reload" message rather
than overwriting the first.| Variable | Value |
|---|---|
ADMIN_PASSWORD | The admin sign-in password. Use a long one. Unset = admin disabled. |
ADMIN_SESSION_SECRET | Optional random string for signing the session cookie (defaults to ADMIN_PASSWORD). |
ADMIN_GITHUB_TOKEN | A GitHub fine-grained personal access token limited to this repo with Contents: Read and write. |
ADMIN_GITHUB_REPO | Optional, defaults to Bolabet/BolaChat. |
ADMIN_GITHUB_BRANCH | Optional, the branch the site deploys from - defaults to main. |
See .env.example. Locally, npm run dev without ADMIN_GITHUB_TOKEN
saves straight to the files in your working copy instead of GitHub.
npm install
npm run dev
Open http://localhost:3000, then visit /zm, /mw, or /zw directly
(there's no automatic redirect yet - see "Geo-routing" below).
npm run build
npm run start
npm run build must complete with zero errors before anything is pushed.
It prerenders every public route as static HTML (/, /zm, /mw,
/zw, /robots.txt, /sitemap.xml). The only server-side code is the
/api/admin/* routes behind the PowerPlays admin (see above).
If you're editing through the GitHub web UI rather than cloning locally:
This app deliberately avoids anything Vercel-only:
@vercel/* packages.fetch and node:crypto.robots.ts, sitemap.ts) uses the standard Next.js Metadata
API, which works the same regardless of host.Geo-routing is intentionally not implemented here. Per the brief, this
codebase only prepares the ground for it: /zm, /mw, and /zw exist as
fully independent static routes, so a Cloudflare Worker sitting in front of
this deployment can inspect the visitor's country and redirect/rewrite to
the right path - the same pattern already used on the ChatBet geo-redirect
project - without any changes to this frontend. When that Worker is ready,
/ (the current "choose your country" fallback) is the safe default for
traffic the Worker can't classify.
<title>, meta description, canonical
URL, and Open Graph/Twitter metadata (see each src/app/<code>/page.tsx).robots.ts and sitemap.ts are generated, not static files, so the
sitemap always matches the registered markets.<a>/<button> elements (no
clickable <div>s), focus states are visible, and images/icons carry
alt/aria-label text.AnalyticsConfig on each market (in src/types/market.ts) has empty slots
for a GA4 ID, GTM container ID, Meta Pixel ID, and TikTok Pixel ID. Nothing
is loaded until you fill these in and wire up the corresponding script -
intentionally, so no third-party script ships before it's actually needed.