尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 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.