GameStar365 Player Web V2
Next-generation casino & sports gaming platform — blazing-fast Next.js 15 frontend with glass-morphism casino theme, 7-language i18n, real-time socket updates, and modular player account suite.
⚡ Quick Install
● npmcp .env.example .env.local
npm run dev
→ http://localhost:3000/bn
Introduction
What & why — objective and scope
GameStar365 Player Web V2 is the customer-facing frontend for the GameStar365 iGaming ecosystem. It delivers a premium casino experience — slots, live-casino, sports, lottery, fish-shooting, crash & arcade — through a single responsive web app optimized for mobile-first emerging markets (Bangladesh, Thailand, Indonesia, Malaysia).
Replace legacy playerUi with a modern, maintainable, SEO-friendly platform that loads instantly, supports 7 locales (bn/en/th/id/ms/it/ar with RTL), and handles real-money flows (deposit/withdraw, KYC, VIP, spin-bonus, promotions) securely.
All player journeys: discovery (hero slider, hot/top/exclusive games, providers, categories), authentication (login/register, phone OTP), wallet (deposit/withdraw + gateway allocation), account (KYC, balance, turnover, bet history, referrals, gift/spin bonuses), promotions, VIP, support chat, notifications & search.
Key Objectives
- Performance: div-scroll architecture, AppImage lazy-load 200px before viewport, AVIF/WebP, 30-day image cache, bundle-optimized imports.
- Consistency: single source of truth — colors in globals.css :root, endpoints in config/api.ts, icons in components/icons.
- Reliability: Axios refresh queue, socket.io real-time, React Query staleTime tiers (30s realtime → 1h static).
- Compliance: CSP, X-Frame nosniff, protected routes via middleware cookie, SEO JSON-LD + sitemap.
Problem Statement
The pain points this rebuild solves
Legacy Fragmentation
Previous playerUi scattered tokens across files, duplicated API URLs, no typed contracts — hard to onboard, easy to break.
Poor Perceived Performance
Images blocked render, layout shifted, no lazy strategy — users saw white screens before banners.
Unscalable i18n & Routing
Locale hard-coded, RTL unsupported, 404s not localized — adding a language meant touching dozens of files.
Fragile Finance Flows
Deposit/withdraw lacked session init, gateway allocation & withdrawal-capability checks — causing failed transactions and support load.
Features
Player-facing capabilities at a glance
Game Discovery
Hot / Top / Exclusive sliders, category-wise games, provider modals, favorites, search with debounce.
Auth & Security
Login/Register, phone OTP, refresh-token queue, protected routes, XSS/CSP headers, cookie sync.
Wallet
Deposit/withdraw per method, automated gateway allocation, session init, history, verify-deposit.
Account Suite
KYC submit/status, password change, turnover, bet results, sports wagers, referral commissions.
VIP & Bonuses
VIP overview/levels/rules/history, spin-bonus wheel (@mertercelik), gift bonus, promotions claim.
Engagement
Banners, promotions, announcements, popup, advertisements, sponsors, social, support chat & notifications.
Design System
/bn/design-system — live demos of buttons, modals, icons, tokens, hooks, Redux slices.
Global i18n
7 locales (bn/en/th/id/ms/it/ar) with RTL, next-intl, per-page metadata, flag + native name.
Real-time
Socket.io client, notification socket handler, announcement marquee, balance polling (30s).
Tech Stack
Frameworks, libraries & tooling
| Dependency | Version | Purpose |
|---|---|---|
| @mertercelik/react-prize-wheel | ^1.1.0 | Spin wheel bonus |
| class-variance-authority | ^0.7.1 | Button variant CVA |
| clsx + tailwind-merge | ^2.1.1 / ^3.3.0 | cn() utility |
| lucide-react / react-icons | ^1.14 / ^5.5.0 | Icons (fa6 + lucide) |
| moment | ^2.30.1 | Date formatting (replace with date-fns recommended) |
| react-intersection-observer | ^9.16.0 | AppImage lazy trigger |
| react-phone-number-input | ^3.4.17 | Phone OTP field (dark pill styling) |
| react-qr-code | ^2.2.0 | QR for app-download / deposit |
| use-sound | ^5.0.0 | Spin tada/fanfare sounds |
| sharp | ^0.34.1 | Next.js image optimization |
| Layer | Choices | Why |
|---|---|---|
| Rendering | App Router, RSC + Client components | SEO + interactivity per route |
| Styling | Tailwind + CSS variables (globals.css) | Single token file drives theme |
| State | Redux (auth/ui/game/notification) + Query | Persisted auth + cached API |
| Auth | Bearer token + refresh queue + auth cookie | Protected routes via middleware |
| Media | AppImage + sharp, AVIF/WebP, remotePatterns | Instant perceived load |
| Tooling | ESLint 9, TS strict, autoprefixer, postcss | DX + build safety |
System Architecture
Folder map, data flow & design principles
🌐 Middleware
Locale detect → redirect /bn • cookie gs365_locale • protected route guard
src/middleware.ts📄 App Router
[locale]/layout + page per route • SEO metadata • sitemap.ts
src/app/[locale]🧩 Providers
Redux Provider + QueryClient + next-intl + next-themes
components/layout/Providers🎣 Hooks + Store
useAppDispatch, useModal, useNotification • slices: auth/ui/game/notification
🔌 API Layer
apiClient (Axios + refresh queue) • ENDPOINTS • React Query keys + CACHE_TIME
🎨 UI Kit
Button, Modal, AppImage, GameCard, HeroSlider — glass + dark navy tokens
Folder Structure — Full (84 files, 19 folders)
Redux Store — 4 Slices (detailed)
| Slice | State Shape | Key Reducers / Selectors |
|---|---|---|
| auth 56L | {user: User|null, isAuthenticated, isLoading} | setUser, setTokens→tokenStorage, logout→clearTokens, updateBalance; selectUser/isAuthenticated/isAuthLoading |
| ui 88L | {theme:'dark', locale, sidebarOpen:false, activeModal:null, scrollPosition:0, gameSiteImageSize:'horizontal', settingsLoaded, settingsVersion} | setTheme/Locale, toggleSidebar, open/closeModal, setScrollPosition, setGameSiteImageSize; settingsVersion busts game images |
| game 78L | {activeCategory:'all', activeProvider:null, searchQuery:'', sortBy:'popular', favorites: string[]} | setActiveCategory/Provider, setSearchQuery/SortBy, toggleFavorite (splice), setFavorites, resetFilters; selectIsFavorite(factory) |
| notification 116L | {items: NotificationItem[]} + MAX_PER_POSITION=3 FIFO | addNotification auto-id `notification_${counter}_${Date.now()}`, remove, clearAll; helpers notify.success(4000ms)/error(6000)/warning(5000)/info(4000) |
Hooks Catalog — 30+ in `src/hooks/index.ts` (915L)
| Hook | Purpose | Cache / Notes |
|---|---|---|
| useModal | isOpen/open/close/toggle | local state |
| useAuth | selectUser + loginMutation POST /api/users/login → setTokens + dispatch | React Query disabled ME (404 workaround) |
| useBalance | GET /api/balance/player/:id polling | every 5s, stale 3s, enabled if auth |
| useVipOverview | GET /api/vip/overview | polling 5s, nested data.data shape |
| useGamePlay / useFavorites / usePopup | play(gameId)+playSports, optimistic favorites, popup frequency engine | popup: every_visit / once_per_session (sessionStorage) / once_per_day (localStorage) / once_per_user; page targeting + priority sort |
| useSiteSettings | GET /api/settings?_=${Date.now()} cache-busted | dispatches setGameSiteImageSize + document.title |
| useMenuProviders / useCategories / useBanners / useAnnouncement / useNotifications … | All public listings | CACHE_TIME STATIC or NORMAL; notifications polling 20s stale 0 |
| useScrollLock / useDebounce / useLocalStorage / useWindowSize / useCopyToClipboard | UI utilities | windowSize: mobile <768, tablet 768-1024 |
lib & SEO
queryClient.ts — 23L
stale 5m NORMAL, gc 30m, retry 2 exp backoff min(2^attempt*1000,30000), refetchOnWindowFocus dev only, mutations retry 0.
seo.ts — 78L
getOrganizationSchema, getWebsiteSchema (SearchAction /games?q={term}), getBreadcrumbSchema, getCanonicalUrl + getAlternateLinks hreflang. Used in [locale]/layout generateMetadata.
utils.ts — 122L
cn = twMerge(clsx), formatCurrency/formatNumber/formatCompactNumber, delay, generateId, debounce, truncate, getCdnUrl, getOrCreateGuestId → localStorage `chat_guest_id` g_${time36}${rand36}.
webVitals.ts — 20L + socket 61L
sendBeacon('/api/vitals'). Socket: io(SOCKET_URL) websocket+polling, emitEvent/joinChat/leaveChat.
Contexts — 5 providers
| Context | Lines | What it owns |
|---|---|---|
| ChatContext | 347L | Messages/Chats, useSocket newMessage/chatUpdated, createChat + sendMessage + uploadAttachment → IMAGE_UPLOAD_URL, guestId fallback |
| ProviderContext | 52L | Map<number,string> from game+sports providers, useProviderName(id) |
| auth-context | 41L | Wraps Redux selectUser → {id,username,role,vipLevel}, logout POST + clearTokens |
| SearchContext | 32L | isOpen/openSearch/closeSearch/toggleSearch global |
| SupportPanelContext | 42L | activeTab home/messages/help/social + scroll lock |
Installation & Setup
From clone to running locally
Prerequisites
- Node.js ≥ 18.18 (Next 15 requires ≥18) • npm 9+
- Access to API base URL (staging or prod) for NEXT_PUBLIC_API_BASE_URL
- Fonts: public/fonts/display.woff2 + body.woff2 • Logo: public/images/logo.webp
git clone <repo-url> gs365-player-webV2 cd gs365-player-webV2 npm install # or use npm ci for clean install npm ci
cp .env.example .env.local # Edit .env.local — minimum required: NEXT_PUBLIC_API_BASE_URL=https://api-staging1615.trueeit.com NEXT_PUBLIC_CDN_URL=https://cdn.gamestar365.com NEXT_PUBLIC_APP_URL=http://localhost:3000 NEXT_PUBLIC_APP_NAME="Game Star 365" NEXT_PUBLIC_DEFAULT_LOCALE=bn NEXT_PUBLIC_AUTH_TOKEN_KEY=gs365_token NEXT_PUBLIC_ENABLE_DEVTOOLS=true
npm run dev # → http://localhost:3000 (auto-redirects to /bn) # Production npm run build && npm start # Type check npm run type-check # Lint npm run lint
| Script | Command | Env file |
|---|---|---|
| dev | npm run dev | .env.local |
| build | npm run build | .env.production |
| start | npm start | .env.production |
| staging | DOTENV_CONFIG_PATH=.env.staging npm run build | .env.staging |
Usage Guide
How to work with the codebase day-to-day
Routing — Full 29 routes (middleware gated)
| Route | page.tsx | Protected | Notes |
|---|---|---|---|
| / | redirect | — | → /bn (reads gs365_locale cookie) |
| /:locale | ✅ | — | Home — hero + categories + events + game sections |
| /:locale/login & /register | ✅ | — | Also via AuthModal (xl with login-left-2.png) |
| /:locale/favorites | ✅ | — | FavoritesContent + optimistic toggle |
| /:locale/hot-games, /top-games, /exclusive-games, /category-wise-games | ✅ | — | Each has loading.tsx skeleton |
| /:locale/category/[id] | ✅ | — | CategoryGamesContent dynamic |
| /:locale/provider/[id] & /providers/[id] | ✅ | — | ?type=games&providerName= |
| /:locale/promotions & /promotions/[id] | ✅ | — | PromotionDetailsContent |
| /:locale/deposit & /deposit/[methodId] | ✅ | 🔒 | DepositContent + SingleDeposit (gateway allocation 600s countdown) |
| /:locale/withdraw & /withdraw/[methodId] | ✅ | 🔒 | WithdrawContent + capability check |
| /:locale/account-information & /account-information/[section] | ✅ | 🔒 | 16 sub-components: KYC, turnover, BettingHistory, SportsWagers, TransactionHistory, Referral, GiftBonus… |
| /:locale/vip, /vip-v2, /vip-details | ✅ | — | Classic + V2 redesign |
| /:locale/affiliate-program | ✅ | 🔒 | ReferralInfo + commissions |
| /:locale/app-download, /about-us, /privacy-policy, /terms-and-conditions | ✅ | — | Static content (114 terms + 48 privacy keys) |
| /:locale/design-system | ✅ | — | Guard with NEXT_PUBLIC_SHOW_DESIGN_SYSTEM=false in prod |
| /:locale/[...notFound] | ✅ | — | Locale-aware 404 |
Common Tasks
// Server component
import { getTranslations } from "next-intl/server";
const t = await getTranslations("games");
return <h1>{t("popularGames")}</h1>
// Client component
"use client";
import { useTranslations } from "next-intl";
const t = useTranslations("auth");
return <button>{t("login")}</button>import { useQuery } from "@tanstack/react-query";
import { api } from "@/lib/apiClient";
import { ENDPOINTS, QUERY_KEYS, CACHE_TIME } from "@/config/api";
const { data, isLoading } = useQuery({
queryKey: QUERY_KEYS.PROMOTIONS.ALL,
queryFn: () => api.get(ENDPOINTS.PROMOTIONS.LIST).then(r => r.data),
staleTime: CACHE_TIME.NORMAL, // 5m
});import { useAppDispatch, useAppSelector } from "@/store/hooks";
import { selectUser } from "@/store/slices/authSlice";
import { notify } from "@/store/slices/notificationSlice";
const user = useAppSelector(selectUser);
dispatch(notify.success("Deposit Successful","৳500 added"));import AppImage, { preloadImages } from "@/components/ui/AppImage";
<AppImage src="/games/slots.webp" alt="Slots" width={200} height={300} />
preloadImages(["/banner1.webp","/banner2.webp"]);
import Modal from "@/components/modals/Modal";
import { useModal } from "@/hooks";
const { isOpen, open, close } = useModal();
<Modal isOpen={isOpen} onClose={close} size="md" position="center"><div className="p-6">content</div></Modal>Modules / Features Breakdown
Major files & what they own
| Module | Path / Count | Responsibility |
|---|---|---|
| Layout | components/layout/*10 | AppShell (div-scroll root), TopNavbar(70px) + BottomNavbar(64px), DesktopSidebar 260px collapsible + pink active, FooterV2, Providers (Redux+Query+next-intl), SiteSettingsLoader |
| Buttons | components/buttons/Button.tsx — 177L CVA | 8 variants: primary (hot pink), secondary (outline→primary), outline, ghost, danger, success, accent (gold), glass; 8 sizes: xs h-7 … xl h-14 + icon/icon-sm/icon-lg; props: isLoading + Loader2, leftIcon/rightIcon, fullWidth |
| Modals | components/modals/*10 | Modal (240L, sm/md/lg/xl/2xl/fullscreen, center/bottom/top, backdrop blur 8px, portal, scroll lock) • StatusModal (278L, success/error/warning/info + ConfirmModal hook useStatusModal) • Login(218L) • Register(339L, 4 fields+OTP+terms) • AuthWrapper • Filter(218L sort/provider) • ProviderModal(133L 2xl) • WalletModal ~1500L (deposit→methods→amount→transfer→submit + withdraw, 600s countdown, receipt upload) • Claimable/Info NotificationModals |
| Game | components/game/*13 | GameCard (3:4 aspect), GameSlider, Hot/Top/Exclusive/CategoryWise sections, EventCard, PromotionCard, FilterBar/Pills/Actions, Provider modal |
| Deposit | components/deposit/*7 | DepositAmountProvider, DepositOptionsInfo, DepositPromotionSelect, DepositProvider, DepositSubmit, SessionAlertModal, WithdrawOptionsInfo — orchestrated by WalletModal |
| UI Kit | components/ui/*10 | AppImage (IO lazy 200px, skeleton/blur, preloadImages), Logo (LOGO_CONFIG), LanguageSelector, MenuItem (glass), GameGridSkeleton, Pagination, CustomSelect, NavigationProgress (#nprogress 3px hot pink) |
| Hero / Sponsors / Sports | hero/*2, sponsors/*2, sports/*3, Advertisement/*1 | HeroSliderWrapper (Swiper), SponsorsClient (active-utils sponsors/ambassadors/licenses), SportsSection, AdvertisementSection |
| Spin / Support / Search | spin/*3, SupportUs/*7, search/*1, popup/*1, notifications/*4 | SpinWheel (react-prize-wheel) + SpinLauncher/Wheel, SupportPanel (4 tabs), SearchPopup (debounced), PopupContent (frequency engine), NotificationContainer + SocketHandler |
| i18n | i18n/* + messages 10 files | locales.ts 7 locales (bn🇧🇩 en🇬🇧 th🇹🇭 id🇮🇩 ms🇲🇾 it🇮🇹 ar🇸🇦 RTL), flags, defaultLocale from env; en.json 36 namespaces, ~1430 keys — see i18n details below |
i18n — 36 namespaces, ~1430 keys (en.json source)
| Locale | Flag | Native Name | Status |
|---|---|---|---|
| bn | 🇧🇩 | বাংলা | ✅ default, complete |
| en | 🇬🇧 | English | ✅ source 1430 keys |
| th | 🇹🇭 | ภาษาไทย | ✅ complete |
| id | 🇮🇩 | Bahasa Indonesia | 🔲 from en |
| ms | 🇲🇾 | Bahasa Melayu | 🔲 |
| it | 🇮🇹 | Italiano | 🔲 |
| ar | 🇸🇦 | العربية | RTL true |
| Namespace | Keys | Example |
|---|---|---|
| deposit | 87 | deposit.yourWallet |
| terms | 146 | terms.heading |
| aboutUs | 114 | aboutUs.hero |
| accountInfo | 101 | accountInfo.title |
| auth | 56 | auth.welcomeTo |
| common/footer/kyc … | … | common.loading / footer.gameCategory |
One-Place Customization
- Colors/theme: src/styles/globals.css :root
- Fonts: globals.css @font-face + --font-display/body
- Logo: components/ui/Logo.tsx → LOGO_CONFIG
- Endpoints: config/api.ts → ENDPOINTS
- Buttons: components/buttons/Button.tsx → buttonVariants
- Icons: components/icons/index.ts
- Copy: i18n/messages/[lang].json
- Default locale: middleware.ts → defaultLocale
API Reference
Centralized in src/config/api.ts — ENDPOINTS + QUERY_KEYS + CACHE_TIME
Base URL from NEXT_PUBLIC_API_BASE_URL (default http://localhost:8000). Timeout 30s. Auth via Authorization: Bearer <gs365_token>. All in src/config/api.ts 429L — 30 groups, typed QUERY_KEYS, 5 CACHE_TIME tiers. Axios request interceptor excludes PUBLIC_ENDPOINTS; refresh queue with failedQueue + isRefreshing flag.
| Group | Key ENDPOINTS | Method |
|---|---|---|
| AUTH | LOGIN, REGISTER, LOGOUT, REFRESH, ME, FORGOT_PASSWORD, VERIFY_EMAIL/PHONE | POST /api/users/login |
| USER | PROFILE, BALANCE/:userId, FAVORITES, ADD/REMOVE_FAVORITE, CHANGE_PASSWORD | GET /api/balance/player/:id polling |
| GAMES | PLAY, SPORTS_PLAY, BY_PROVIDER/:id, BY_CATEGORY/:cat | POST /api/games/play {userId,gameId,betAmount:0} |
| PUBLIC | providers, categories/:id, hot/top/exclusive/category-wise-games, banners/home, promotions? id= | GET /api/public/banners/home |
| CATEGORY_* | CATEGORY_GAMES, CATEGORY_WISE_PROVIDER/:id, CATEGORY_PROVIDER (type+providerId+categoryId) | GET /api/public/category-provider |
| PAYMENT | PAYMENT_METHOD, DEPOSIT_TRANSACTION, WITHDRAW_CAPABILITY, ALLOCATE_GATEWAY, SESSION_INIT/ACTIVE, VERIFY_DEPOSIT_AMOUNT, TRANSACTIONS/player | POST /api/payment-session/init |
| FINANCE | DEPOSIT_METHODS, WITHDRAW_METHODS, HISTORY | GET /finance/deposit/methods |
| VIP / BONUS | vip/overview|levels|rules|history|redeem, spin, gift-bonus, referral-stats/commissions | GET /api/vip/overview |
| USER DATA | turnover, bet-results, sports-wagers/stats, transactions, user-phones (OTP) | GET /api/turnover? userId & status |
| CONTENT | banners, promotions, announcement, popup, advertisement, active-utils, social-platforms, events, faq | GET /api/public/popup |
| CHAT | chats, chats/count-unread, messages/send-message, messages/user-admin | POST /api/messages/send-message |
| SEARCH / COUNTRIES | PUBLIC SEARCH, COUNTRIES | GET /api/public/search?q=& /api/countries |
POST /api/users/login
Content-Type: application/json
{
"username": "player01",
"password": "••••••••"
}{
"success": true,
"status": 200,
"message": "Login successful",
"data": {
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"user": { "id": 123, "username": "player01" }
},
"traceId": "abc-xyz",
"timestamp": "2026-09-01T00:00:00Z"
}import { api, apiClient } from "@/lib/apiClient";
import { ENDPOINTS, QUERY_KEYS } from "@/config/api";
// typed helpers
const res = await api.get<Promotion[]>(ENDPOINTS.PROMOTIONS.LIST);
// raw axios
const raw = await apiClient.post(ENDPOINTS.AUTH.LOGIN, { username, password });| Cache Tier | Value | Use |
|---|---|---|
| STATIC | 1h | Site config, categories |
| NORMAL | 5m | Banners, promotions |
| SHORT | 1m | Lists |
| REALTIME | 30s | Balance, jackpots |
| NEVER | ∞ | No refetch |
Configuration
Env vars, Tailwind & Next config
| Variable | Default / Example | Purpose |
|---|---|---|
| NEXT_PUBLIC_APP_NAME | Game Star 365 | Document title + SEO |
| NEXT_PUBLIC_APP_URL | https://gamestar365.com (dev) / staging1615v2 / production1615v2 | Canonical, sitemap BASE_URL, OG |
| NEXT_PUBLIC_APP_ENV | development | staging | production | Env badge |
| NEXT_PUBLIC_API_BASE_URL | https://api-staging1615.trueeit.com → api.trueeit.com prod | apiClient baseURL + socket fallback |
| NEXT_PUBLIC_SOCKET_URL | = API_BASE_URL if empty | Socket.IO URL |
| NEXT_PUBLIC_API_TIMEOUT | 30000 | Axios timeout ms |
| NEXT_PUBLIC_CDN_URL | https://cdn.gamestar365.com → production1615v2 in prod | getCdnUrl() base |
| NEXT_PUBLIC_IMAGE_CACHE_TTL | 300 | Image cache |
| NEXT_PUBLIC_AUTH_TOKEN_KEY | gs365_token | localStorage token |
| NEXT_PUBLIC_AUTH_REFRESH_TOKEN_KEY | gs365_refresh | Refresh token |
| NEXT_PUBLIC_DEFAULT_LOCALE | bn | Middleware fallback |
| NEXT_PUBLIC_DEFAULT_COUNTRY_CODE | BD | Phone input default |
| NEXT_PUBLIC_ENABLE_DEVTOOLS | true dev & staging, false prod | React Query devtools + redux devTools |
| NEXT_PUBLIC_ENABLE_MOCK_API | false | Mock toggle (unused) |
| NEXT_PUBLIC_AFFILIATE_URL | https://affiliate.gs365bd.com | Affiliate link |
| NEXT_PUBLIC_IMAGE_BASE_URL | https://glorypos.com/image-upload | IMAGE_UPLOAD_URL /upload (chat attachment) |
| .env file | API | APP_URL |
|---|---|---|
| .env (dev) | api-staging1615.trueeit.com | gamestar365.com |
| .env.staging | api-staging1615.trueeit.com | staging1615v2.trueeit.com |
| .env.production | api.trueeit.com | production1615v2.trueeit.com |
Colors: brand.primary/hover/active, secondary, accent, surface(5), text(5), border(3), status(8) all → CSS vars. Fonts display/body/mono, radius 4xl-5xl, zIndex 60-1200 (modal 1000, notification 1100), keyframes slideInUp/Down/Right/Left, fadeIn, shimmer, pulse, notificationSlide. DarkMode ['class'].
Images: AVIF/WebP, deviceSizes 320-1920, imageSizes 16-384, cache 30d, allow SVG. CSP + 6 security headers (see below). remotePatterns: *.trueeit.com, *.netlify.app, bshots.egcvi.com, *.egcvi.com. compiler.removeConsole in prod, optimizePackageImports: @reduxjs/toolkit.
Security Headers (on `/(.*)`)
| Header | Value |
|---|---|
| X-DNS-Prefetch-Control | on |
| X-Frame-Options | SAMEORIGIN |
| X-Content-Type-Options | nosniff |
| Referrer-Policy | strict-origin-when-cross-origin |
| Permissions-Policy | camera=() microphone=() geolocation=(self) |
| Content-Security-Policy | default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self' https://*.trueeit.com ws: wss:; frame-src youtube(+nocookie) |
| Cache-Control | /_next/static & /fonts → public, max-age=31536000, immutable |
SEO, PWA & Public Assets
SEO — sitemap.ts (50L) + layouts
BASE_URL × 7 locales × 6 static routes = 42 URLs, changeFreq daily/weekly, priority 1/0.8, hreflang alternates. Root layout metadata template `%s | Game Star 365`, viewport maxScale 1, themeColor #0d1b4b. [locale]/layout generateMetadata from Dictionary meta → OG/Twitter large_image, icons, manifest.
PWA — manifest.json + robots.txt
manifest: name GS365, start_url /, display standalone, icons 512 maskable, categories games/entertainment. robots: Allow /, Disallow /api /admin /_next /404, Sitemap gamestar365.com/sitemap.xml.
public/ — 26.8 MB APK
assets/bank-logo+bank-banner (brac/dbl), images/logo.png+login.webp+payment-method 24 SVGs (bKash etc.), profile/lady-avatar.jpg, referral/flow+leaderboard+avatar, spin .mp3+.gif, gs365bd.apk, icons/icon.svg, og-image.jpg 1200×630 needed.
Scripts & CI
scripts/ empty (0 files). No Dockerfile / .github workflows — deployment is `next build && next start` to trueeit hosts. No tests (*.test.* none). Add scripts/check-env, seed-i18n.
NEXT_PUBLIC_APP_NAME="Game Star 365" NEXT_PUBLIC_APP_ENV="development" NEXT_PUBLIC_API_BASE_URL="https://api-staging1615.trueeit.com" NEXT_PUBLIC_CDN_URL="https://cdn.gamestar365.com" NEXT_PUBLIC_AUTH_TOKEN_KEY="gs365_token" NEXT_PUBLIC_AUTH_REFRESH_TOKEN_KEY="gs365_refresh" NEXT_PUBLIC_ENABLE_MOCK_API=false
Testing
Current state & how to add tests
Glob `**/*.test.*` + `**/*.spec.*` → 0 files. No jest/vitest/playwright/cypress, no __tests__/ folder. package.json has only lint + type-check. Use these verification steps today:
npm run type-check # tsc --noEmit — strict types npm run lint # eslint 9 + eslint-config-next npm run build # exposes missing env, broken imports, route errors # Manual smoke: visit /bn/design-system — every component has live demo
- Unit: store/slices/*, lib/utils.ts, apiClient normalizeError
- Integration: auth flow + refresh queue, React Query keys
- E2E: locale redirect, protected routes, game launch, payment session
Known Limitations / Challenges
Honest trade-offs
🔒 No Test Coverage + No CI
0 test files, no GitHub workflows, no Dockerfile — regression relies on manual QA + /design-system page.
🌐 i18n Incomplete
vi/id/km+zh placeholders not in Locale union — need translate from en.json; Arabic RTL partially tested; 36 namespaces cross-check needed.
🖼️ Missing Assets
public/fonts/display.woff2+body.woff2, logo.webp, og-image.jpg 1200×630, icons 192/512 missing → fallback placeholder.svg; gs365bd.apk 26.8MB ships.
🔑 Token in localStorage
Bearer + refresh in LS; CSP allows unsafe-eval/inline — tighten. httpOnly cookie stronger; refresh queue handles 401 but no rate-limit.
📱 Div-scroll Quirks + Monolith
Body 100dvh locked — embeds expecting window scroll break. WalletModal 1500L monolith should split; moment 2.30 heavy vs date-fns.
📦 Dual Icons + CSP
Both react-icons & lucide-react used — consolidate. CSP script-src unsafe-* needed for Next inline — narrow in prod. seo TODO: dynamic game slugs commented out in sitemap.
Future Improvements / Roadmap
What to build next (from README)
New UI components — components/ui/BannerCarousel.tsx + GameCategoryTabs.tsx
Auth modal parity with LoginModal, sidebar collapse states
Games, Promotions, Profile pages under [locale]/ with loading.tsx skeletons
manifest.json already present — add service worker, icon-192/512, install prompt
Replace moment, optimizePackageImports for Swiper, bundle analyzer CI gate
Vitest + Playwright + GitHub Actions (type-check → lint → build → preview)
httpOnly refresh cookie, strict CSP, rate-limit login, device fingerprint
Contributing Guidelines
How to contribute safely
Workflow
git checkout -b feat/your-feature # Make changes — respect ONE-PLACE rules in README npm run lint && npm run type-check npm run build # Push & open PR — include screenshots for UI changes
Conventions
- Components: PascalCase files, ⭐ marks global primitives (Button, Modal, AppImage)
- Tokens: never hard-code colors — use CSS vars
- API: add endpoint to ENDPOINTS + QUERY_KEYS (no inline URLs)
- Copy: update all messages/*.json (at least en) or add TODO flag
- Commits: feat: fix: docs: chore:
License
Proprietary — all rights reserved
package.json marks private: true. No public license file is shipped. All code, assets, and brand (GameStar365, logos, game thumbnails) are proprietary to the operator. Do not distribute without written permission.
Built for TrueIT Projects • Source project: gs365-player-webV2 • Docs standalone at GameStar365-Docs/docs • Press ? to focus search • Smooth scroll + copy buttons enabled