x
This commit is contained in:
1 parent
a6661f946f
commit
85a0f984eb
4 files changed
+546
-447
No files matched your search
@@ -45,6 +45,7 @@ import MediaLibraryPage from './pages/MediaLibraryPage'
|
|||||||
import ContactMessagesPage from './pages/ContactMessagesPage'
|
import ContactMessagesPage from './pages/ContactMessagesPage'
|
||||||
import ProductAttributesPage from './pages/ProductAttributesPage'
|
import ProductAttributesPage from './pages/ProductAttributesPage'
|
||||||
import TiktokPriceSimulatorPage from './pages/TiktokPriceSimulatorPage'
|
import TiktokPriceSimulatorPage from './pages/TiktokPriceSimulatorPage'
|
||||||
|
import TiktokShopCasePage from './pages/TiktokShopCasePage'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@@ -90,6 +91,7 @@ function App() {
|
|||||||
<Route path="/tiktok" element={<TiktokPage />} />
|
<Route path="/tiktok" element={<TiktokPage />} />
|
||||||
<Route path="/tiktok-analysis" element={<TiktokAnalysisPage />} />
|
<Route path="/tiktok-analysis" element={<TiktokAnalysisPage />} />
|
||||||
<Route path="/tiktok-price-simulator" element={<TiktokPriceSimulatorPage />} />
|
<Route path="/tiktok-price-simulator" element={<TiktokPriceSimulatorPage />} />
|
||||||
|
<Route path="/tiktok-shop-case" element={<TiktokShopCasePage />} />
|
||||||
<Route path="/shopee" element={<ShopeePage />} />
|
<Route path="/shopee" element={<ShopeePage />} />
|
||||||
<Route path="/shopee/:orderId" element={<ShopeeOrderDetailPage />} />
|
<Route path="/shopee/:orderId" element={<ShopeeOrderDetailPage />} />
|
||||||
<Route path="/shopee-analytics" element={<ShopeeAnalyticsPage />} />
|
<Route path="/shopee-analytics" element={<ShopeeAnalyticsPage />} />
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
ChevronDown,
|
ChevronDown,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Calculator,
|
Calculator,
|
||||||
|
Info,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
const MAIN_MENU = [
|
const MAIN_MENU = [
|
||||||
@@ -53,6 +54,7 @@ const MAIN_MENU = [
|
|||||||
{ icon: BarChart3, label: 'TikTok Analytics', to: '/tiktok-analytics' },
|
{ icon: BarChart3, label: 'TikTok Analytics', to: '/tiktok-analytics' },
|
||||||
{ icon: Layers, label: 'TikTok ทุกร้าน', to: '/tiktok-overview' },
|
{ icon: Layers, label: 'TikTok ทุกร้าน', to: '/tiktok-overview' },
|
||||||
{ icon: Calculator, label: 'TikTok Price Simulator', to: '/tiktok-price-simulator' },
|
{ icon: Calculator, label: 'TikTok Price Simulator', to: '/tiktok-price-simulator' },
|
||||||
|
{ icon: Info, label: 'TikTok Shop Case', to: '/tiktok-shop-case' },
|
||||||
{ icon: ShoppingCart, label: 'Lazada Orders', to: '/lazada-order' },
|
{ icon: ShoppingCart, label: 'Lazada Orders', to: '/lazada-order' },
|
||||||
{ icon: BarChart3, label: 'Lazada Analytics', to: '/lazada-analytics' },
|
{ icon: BarChart3, label: 'Lazada Analytics', to: '/lazada-analytics' },
|
||||||
{ icon: Layers, label: 'Lazada ทุกร้าน', to: '/lazada-overview' },
|
{ icon: Layers, label: 'Lazada ทุกร้าน', to: '/lazada-overview' },
|
||||||
|
|||||||
@@ -837,97 +837,6 @@ function BreakEvenAlert() {
|
|||||||
|
|
||||||
export default function TiktokPriceSimulatorPage() {
|
export default function TiktokPriceSimulatorPage() {
|
||||||
const [resetKey, setResetKey] = useState(0)
|
const [resetKey, setResetKey] = useState(0)
|
||||||
const [selectedCase, setSelectedCase] = useState<'case1' | 'case2' | 'case3'>('case1')
|
|
||||||
|
|
||||||
const [simBeforeDiscount, setSimBeforeDiscount] = useState(79.00)
|
|
||||||
const [simDiscountType, setSimDiscountType] = useState<'pct' | 'amount'>('amount')
|
|
||||||
const [simDiscountVal, setSimDiscountVal] = useState(17.90)
|
|
||||||
const [simCost, setSimCost] = useState(30.00)
|
|
||||||
|
|
||||||
const [simTransPct, setSimTransPct] = useState(3.21)
|
|
||||||
const [simCommPct, setSimCommPct] = useState(10.70)
|
|
||||||
const [simGrowthPct, setSimGrowthPct] = useState(7.50)
|
|
||||||
const [simAffiliatePct, setSimAffiliatePct] = useState(0)
|
|
||||||
|
|
||||||
const caseData = {
|
|
||||||
case1: {
|
|
||||||
beforeDiscount: 79.00,
|
|
||||||
shopDiscount: -17.90,
|
|
||||||
baseAmt: 61.10,
|
|
||||||
origTrans: -1.96,
|
|
||||||
origComm: -6.54,
|
|
||||||
origGrowth: -4.58,
|
|
||||||
origAffiliate: 0,
|
|
||||||
origTotalFees: -13.08,
|
|
||||||
originalPayout: 48.02,
|
|
||||||
defaultCost: 30.00,
|
|
||||||
label: 'เคสที่ 1: บิลสินค้า ฿79 (สุทธิ ฿48.02)',
|
|
||||||
},
|
|
||||||
case2: {
|
|
||||||
beforeDiscount: 190.00,
|
|
||||||
shopDiscount: -20.90,
|
|
||||||
baseAmt: 169.10,
|
|
||||||
origTrans: -5.43,
|
|
||||||
origComm: -18.09,
|
|
||||||
origGrowth: -12.67,
|
|
||||||
origAffiliate: 0,
|
|
||||||
origTotalFees: -36.19,
|
|
||||||
originalPayout: 132.91,
|
|
||||||
defaultCost: 80.00,
|
|
||||||
label: 'เคสที่ 2: บิลสินค้า ฿190 (สุทธิ ฿132.91)',
|
|
||||||
},
|
|
||||||
case3: {
|
|
||||||
beforeDiscount: 190.00,
|
|
||||||
shopDiscount: -37.81,
|
|
||||||
baseAmt: 152.19,
|
|
||||||
origTrans: -4.89,
|
|
||||||
origComm: -16.28,
|
|
||||||
origGrowth: -11.40,
|
|
||||||
origAffiliate: -7.61,
|
|
||||||
origTotalFees: -40.18,
|
|
||||||
originalPayout: 112.01,
|
|
||||||
defaultCost: 70.00,
|
|
||||||
label: 'เคสที่ 3: บิลสินค้า ฿190 + Affiliate (สุทธิ ฿112.01)',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeCase = caseData[selectedCase]
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setSimBeforeDiscount(activeCase.beforeDiscount)
|
|
||||||
setSimDiscountType('amount')
|
|
||||||
setSimDiscountVal(Math.abs(activeCase.shopDiscount))
|
|
||||||
setSimCost(activeCase.defaultCost)
|
|
||||||
setSimTransPct(3.21)
|
|
||||||
setSimCommPct(10.70)
|
|
||||||
setSimGrowthPct(7.50)
|
|
||||||
setSimAffiliatePct(selectedCase === 'case3' ? 5.00 : 0)
|
|
||||||
}, [selectedCase])
|
|
||||||
|
|
||||||
// Simulated calculations
|
|
||||||
const simDiscountAmount = simDiscountType === 'pct' ? simBeforeDiscount * (simDiscountVal / 100) : simDiscountVal
|
|
||||||
const baseAmt = Math.max(0, simBeforeDiscount - simDiscountAmount)
|
|
||||||
|
|
||||||
const simTransAmt = baseAmt * (simTransPct / 100)
|
|
||||||
const simCommAmt = baseAmt * (simCommPct / 100)
|
|
||||||
const simGrowthAmt = baseAmt * (simGrowthPct / 100)
|
|
||||||
const simAffiliateAmt = baseAmt * (simAffiliatePct / 100)
|
|
||||||
const simTotalAmt = simTransAmt + simCommAmt + simGrowthAmt + simAffiliateAmt
|
|
||||||
const simTotalPct = simTransPct + simCommPct + simGrowthPct + simAffiliatePct
|
|
||||||
const simPayout = baseAmt - simTotalAmt
|
|
||||||
|
|
||||||
const originalPayout = activeCase.originalPayout
|
|
||||||
const diffVal = simPayout - originalPayout
|
|
||||||
const diffPct = originalPayout > 0 ? (diffVal / originalPayout) * 100 : 0
|
|
||||||
|
|
||||||
// Profit/Margin/ROI Calculations
|
|
||||||
const origProfit = originalPayout - simCost
|
|
||||||
const origProfitMargin = activeCase.baseAmt > 0 ? (origProfit / activeCase.baseAmt) * 100 : 0
|
|
||||||
const origROI = simCost > 0 ? (origProfit / simCost) * 100 : 0
|
|
||||||
|
|
||||||
const simProfit = simPayout - simCost
|
|
||||||
const simProfitMargin = baseAmt > 0 ? (simProfit / baseAmt) * 100 : 0
|
|
||||||
const simROI = simCost > 0 ? (simProfit / simCost) * 100 : 0
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen p-6 md:p-8 max-w-5xl mx-auto animate-fade-in" key={resetKey}>
|
<div className="min-h-screen p-6 md:p-8 max-w-5xl mx-auto animate-fade-in" key={resetKey}>
|
||||||
@@ -948,10 +857,6 @@ export default function TiktokPriceSimulatorPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setResetKey(k => k + 1)
|
setResetKey(k => k + 1)
|
||||||
setSimTransPct(3.21)
|
|
||||||
setSimCommPct(10.70)
|
|
||||||
setSimGrowthPct(7.50)
|
|
||||||
setSimAffiliatePct(selectedCase === 'case3' ? 5.00 : 0)
|
|
||||||
}}
|
}}
|
||||||
className="btn-ghost flex items-center gap-2 text-xs shrink-0"
|
className="btn-ghost flex items-center gap-2 text-xs shrink-0"
|
||||||
title="รีเซ็ตทุกค่า"
|
title="รีเซ็ตทุกค่า"
|
||||||
@@ -1030,358 +935,6 @@ export default function TiktokPriceSimulatorPage() {
|
|||||||
ถ้าแคมเปญไหนบังคับให้ขาดทุน → อย่าเข้า หรือส่งเฉพาะ SKU ที่มี Buffer สูงพอเท่านั้น
|
ถ้าแคมเปญไหนบังคับให้ขาดทุน → อย่าเข้า หรือส่งเฉพาะ SKU ที่มี Buffer สูงพอเท่านั้น
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Real Payout Case Study Example */}
|
|
||||||
<div className="admin-card border-white/10 mt-6 animate-fade-in">
|
|
||||||
<div className="flex items-center justify-between mb-4 flex-wrap gap-4 border-b border-white/5 pb-4">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Info className="w-5 h-5 text-cyan-glow" />
|
|
||||||
<h2 className="text-lg font-bold text-white">กรณีศึกษา & เครื่องมือจำลองค่าธรรมเนียมจริง (TikTok Shop)</h2>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="flex p-1 bg-night-900 border border-white/10 rounded-lg">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-all ${selectedCase === 'case1' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
|
||||||
onClick={() => setSelectedCase('case1')}
|
|
||||||
>
|
|
||||||
ตัวอย่างที่ 1 (ยอด ฿79)
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-all ${selectedCase === 'case2' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
|
||||||
onClick={() => setSelectedCase('case2')}
|
|
||||||
>
|
|
||||||
ตัวอย่างที่ 2 (ยอด ฿190)
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-all ${selectedCase === 'case3' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
|
||||||
onClick={() => setSelectedCase('case3')}
|
|
||||||
>
|
|
||||||
ตัวอย่างที่ 3 (ยอด ฿190 + Affiliate)
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setSimTransPct(3.21)
|
|
||||||
setSimCommPct(10.70)
|
|
||||||
setSimGrowthPct(7.50)
|
|
||||||
setSimAffiliatePct(selectedCase === 'case3' ? 5.00 : 0)
|
|
||||||
}}
|
|
||||||
className="btn-ghost text-xs py-1.5 px-3 rounded border border-white/10"
|
|
||||||
>
|
|
||||||
ใช้ค่าตั้งต้นตัวอย่าง
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-xs text-slate-400 mb-2 font-medium">
|
|
||||||
รายละเอียดบิลจริงดั้งเดิมของ {selectedCase === 'case1' ? 'ตัวอย่างที่ 1' : selectedCase === 'case2' ? 'ตัวอย่างที่ 2' : 'ตัวอย่างที่ 3'}:
|
|
||||||
</p>
|
|
||||||
<div className="grid grid-cols-3 gap-4 p-3 rounded-lg bg-night-800/40 border border-white/5 text-xs mb-6 opacity-80">
|
|
||||||
<div>
|
|
||||||
<span className="text-slate-500 block">ยอดรวมสินค้าก่อนลด</span>
|
|
||||||
<span className="font-bold text-white font-mono">{fmt(activeCase.beforeDiscount)}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="text-slate-500 block">ส่วนลดจากร้านค้า (ลดไป %)</span>
|
|
||||||
<span className="font-bold text-rose-400 font-mono">
|
|
||||||
{fmt(activeCase.shopDiscount)} (-{((Math.abs(activeCase.shopDiscount) / activeCase.beforeDiscount) * 100).toFixed(1)}%)
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="text-slate-500 block">ยอดรวมหลังหักส่วนลด (ฐานคิดค่าคอม)</span>
|
|
||||||
<span className="font-bold text-cyan-glow font-mono">{fmt(activeCase.baseAmt)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Simulator controls - Price & Discounts */}
|
|
||||||
<p className="text-xs text-slate-400 mb-2 font-medium">1. จำลองการตั้งราคา ส่วนลด และต้นทุนสินค้า (ปรับแต่งเองได้):</p>
|
|
||||||
<div className="mb-4 grid grid-cols-1 sm:grid-cols-3 gap-4 p-4 rounded-xl bg-night-800/40 border border-white/5">
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs text-slate-400 font-medium mb-1.5">ราคาขายก่อนลด (List Price)</label>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step="1"
|
|
||||||
min="0"
|
|
||||||
value={simBeforeDiscount}
|
|
||||||
onChange={e => setSimBeforeDiscount(parseFloat(e.target.value) || 0)}
|
|
||||||
className="admin-input w-full text-xs"
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-slate-500">฿</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs text-slate-400 font-medium mb-1.5">ส่วนลดจากร้านค้า (คูปองร้าน)</label>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="flex p-1 bg-night-900 border border-white/10 rounded-lg shrink-0">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`px-2.5 py-1 text-[10px] font-medium rounded transition-all ${simDiscountType === 'pct' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
|
||||||
onClick={() => setSimDiscountType('pct')}
|
|
||||||
>
|
|
||||||
%
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`px-2.5 py-1 text-[10px] font-medium rounded transition-all ${simDiscountType === 'amount' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
|
||||||
onClick={() => setSimDiscountType('amount')}
|
|
||||||
>
|
|
||||||
฿
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step="0.1"
|
|
||||||
min="0"
|
|
||||||
value={simDiscountVal}
|
|
||||||
onChange={e => setSimDiscountVal(parseFloat(e.target.value) || 0)}
|
|
||||||
className="admin-input w-full text-xs"
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-slate-500">{simDiscountType === 'pct' ? '%' : '฿'}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs text-slate-400 font-medium mb-1.5">ต้นทุนสินค้า (Cost)</label>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step="1"
|
|
||||||
min="0"
|
|
||||||
value={simCost}
|
|
||||||
onChange={e => setSimCost(parseFloat(e.target.value) || 0)}
|
|
||||||
className="admin-input w-full text-xs"
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-slate-500">฿</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Simulator controls - Fees */}
|
|
||||||
<p className="text-xs text-slate-400 mb-2 font-medium">2. จำลองการปรับเปลี่ยนอัตราค่าธรรมเนียม (%):</p>
|
|
||||||
<div className="mb-6 grid grid-cols-2 sm:grid-cols-4 gap-4 p-4 rounded-xl bg-night-800/40 border border-white/5">
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs text-slate-400 font-medium mb-1.5">Transaction (%)</label>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step="0.01"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
value={simTransPct}
|
|
||||||
onChange={e => setSimTransPct(parseFloat(e.target.value) || 0)}
|
|
||||||
className="admin-input w-full text-xs"
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-slate-500">%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs text-slate-400 font-medium mb-1.5">ค่าคอม TikTok (%)</label>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step="0.01"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
value={simCommPct}
|
|
||||||
onChange={e => setSimCommPct(parseFloat(e.target.value) || 0)}
|
|
||||||
className="admin-input w-full text-xs"
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-slate-500">%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs text-slate-400 font-medium mb-1.5">สนับสนุนเติบโต (%)</label>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step="0.01"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
value={simGrowthPct}
|
|
||||||
onChange={e => setSimGrowthPct(parseFloat(e.target.value) || 0)}
|
|
||||||
className="admin-input w-full text-xs"
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-slate-500">%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs text-slate-400 font-medium mb-1.5">ค่าคอมแอฟฟิลิเอต (%)</label>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step="0.01"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
value={simAffiliatePct}
|
|
||||||
onChange={e => setSimAffiliatePct(parseFloat(e.target.value) || 0)}
|
|
||||||
className="admin-input w-full text-xs"
|
|
||||||
/>
|
|
||||||
<span className="text-xs text-slate-500">%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Side-by-side comparison tables */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
{/* ก่อนปรับ (ค่าธรรมเนียมจากบิลตัวอย่างจริง) */}
|
|
||||||
<div className="rounded-xl bg-night-800/30 border border-white/5 p-4 space-y-4">
|
|
||||||
<h3 className="text-xs font-bold text-slate-400 uppercase tracking-wider border-b border-white/5 pb-2 flex justify-between">
|
|
||||||
<span>ก่อนปรับ (บิลดั้งเดิม)</span>
|
|
||||||
<span className="text-slate-500">รวม {((Math.abs(activeCase.origTotalFees) / activeCase.baseAmt) * 100).toFixed(2)}%</span>
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2 text-xs">
|
|
||||||
<div className="flex justify-between text-slate-400">
|
|
||||||
<span>ยอดรวมสินค้าก่อนลด</span>
|
|
||||||
<span className="font-mono">{fmt(activeCase.beforeDiscount)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-slate-400">
|
|
||||||
<span>ส่วนลดจากร้านค้า</span>
|
|
||||||
<span className="font-mono text-rose-400">{fmt(activeCase.shopDiscount)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-t border-white/5 pt-1.5 font-medium text-slate-300">
|
|
||||||
<span>ยอดสินค้าสุทธิ (ฐานคิดคอม)</span>
|
|
||||||
<span>{fmt(activeCase.baseAmt)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between pl-3 text-slate-500">
|
|
||||||
<span>- ค่าคำสั่งซื้อ (Transaction 3.21%)</span>
|
|
||||||
<span className="font-mono text-rose-400">{fmt(activeCase.origTrans)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between pl-3 text-slate-500">
|
|
||||||
<span>- ค่าคอม TikTok Shop (10.70%)</span>
|
|
||||||
<span className="font-mono text-rose-400">{fmt(activeCase.origComm)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between pl-3 text-slate-500">
|
|
||||||
<span>- ค่าสนับสนุนการเติบโต (7.50%)</span>
|
|
||||||
<span className="font-mono text-rose-400">{fmt(activeCase.origGrowth)}</span>
|
|
||||||
</div>
|
|
||||||
{activeCase.origAffiliate !== 0 && (
|
|
||||||
<div className="flex justify-between pl-3 text-slate-500">
|
|
||||||
<span>- ค่าคอมแอฟฟิลิเอต (5.00%)</span>
|
|
||||||
<span className="font-mono text-rose-400">{fmt(activeCase.origAffiliate)}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex justify-between border-t border-white/5 pt-1.5 font-medium text-slate-300">
|
|
||||||
<span>รวมค่าธรรมเนียมโดนหัก</span>
|
|
||||||
<span className="font-mono text-rose-400">{fmt(activeCase.origTotalFees)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-t border-white/5 pt-2 font-bold text-white text-sm">
|
|
||||||
<span>ยอดเงินโอนเข้าบัญชีสุทธิ</span>
|
|
||||||
<span className="font-mono text-cyan-glow">{fmt(activeCase.originalPayout)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-t border-white/5 pt-1.5 text-slate-400">
|
|
||||||
<span>- ต้นทุนสินค้า (Cost)</span>
|
|
||||||
<span className="font-mono text-rose-400">฿-{simCost.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-t border-white/5 pt-2 font-bold text-white text-sm">
|
|
||||||
<span>กำไรสุทธิ (Net Profit)</span>
|
|
||||||
<span className={`font-mono ${origProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
|
||||||
{origProfit >= 0 ? '' : '-'}฿{Math.abs(origProfit).toFixed(2)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-slate-400 pl-3">
|
|
||||||
<span>กำไรต่อยอดขาย (Margin %)</span>
|
|
||||||
<span className={`font-mono ${origProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
|
||||||
{origProfitMargin.toFixed(1)}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-slate-400 pl-3">
|
|
||||||
<span>กำไรต่อทุน (ROI %)</span>
|
|
||||||
<span className={`font-mono ${origProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
|
||||||
{origROI.toFixed(1)}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* หลังปรับ (จำลองการปรับค่าธรรมเนียม) */}
|
|
||||||
<div className="rounded-xl bg-cyan-glow/5 border border-cyan-glow/20 p-4 space-y-4">
|
|
||||||
<h3 className="text-xs font-bold text-cyan-glow uppercase tracking-wider border-b border-cyan-glow/10 pb-2 flex justify-between">
|
|
||||||
<span>หลังปรับ (ผลการจำลองใหม่)</span>
|
|
||||||
<span>รวม {simTotalPct.toFixed(2)}%</span>
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2 text-xs">
|
|
||||||
<div className="flex justify-between text-slate-400">
|
|
||||||
<span>ยอดรวมสินค้าก่อนลด</span>
|
|
||||||
<span className="font-mono">{fmt(simBeforeDiscount)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-slate-400">
|
|
||||||
<span>ส่วนลดจากร้านค้า</span>
|
|
||||||
<span className="font-mono text-rose-400">
|
|
||||||
-{fmt(simDiscountAmount)} {simDiscountType === 'pct' && `(${simDiscountVal}%)`}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-t border-cyan-glow/10 pt-1.5 font-medium text-slate-300">
|
|
||||||
<span>ยอดสินค้าสุทธิ (ฐานคิดคอม)</span>
|
|
||||||
<span className="font-mono text-cyan-glow">{fmt(baseAmt)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between pl-3 text-slate-500">
|
|
||||||
<span>- ค่าคำสั่งซื้อ ({simTransPct.toFixed(2)}%)</span>
|
|
||||||
<span className="font-mono text-rose-400">฿-{simTransAmt.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between pl-3 text-slate-500">
|
|
||||||
<span>- ค่าคอม TikTok Shop ({simCommPct.toFixed(2)}%)</span>
|
|
||||||
<span className="font-mono text-rose-400">฿-{simCommAmt.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between pl-3 text-slate-500">
|
|
||||||
<span>- ค่าสนับสนุนการเติบโต ({simGrowthPct.toFixed(2)}%)</span>
|
|
||||||
<span className="font-mono text-rose-400">฿-{simGrowthAmt.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
{simAffiliatePct > 0 && (
|
|
||||||
<div className="flex justify-between pl-3 text-slate-500">
|
|
||||||
<span>- ค่าคอมคอมแอฟฟิลิเอต ({simAffiliatePct.toFixed(2)}%)</span>
|
|
||||||
<span className="font-mono text-rose-400">฿-{simAffiliateAmt.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex justify-between border-t border-cyan-glow/10 pt-1.5 font-medium text-slate-300">
|
|
||||||
<span>รวมค่าธรรมเนียมโดนหัก</span>
|
|
||||||
<span className="font-mono text-rose-400">฿-{simTotalAmt.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-t border-cyan-glow/10 pt-2 font-bold text-white text-sm">
|
|
||||||
<span>ยอดเงินโอนเข้าบัญชีสุทธิ</span>
|
|
||||||
<span className="font-mono text-cyan-glow">฿{simPayout.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-t border-cyan-glow/10 pt-1.5 text-slate-400">
|
|
||||||
<span>- ต้นทุนสินค้า (Cost)</span>
|
|
||||||
<span className="font-mono text-rose-400">฿-{simCost.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-t border-cyan-glow/10 pt-2 font-bold text-white text-sm">
|
|
||||||
<span>กำไรสุทธิ (Net Profit)</span>
|
|
||||||
<span className={`font-mono ${simProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
|
||||||
{simProfit >= 0 ? '' : '-'}฿{Math.abs(simProfit).toFixed(2)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-slate-400 pl-3">
|
|
||||||
<span>กำไรต่อยอดขาย (Margin %)</span>
|
|
||||||
<span className={`font-mono ${simProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
|
||||||
{simProfitMargin.toFixed(1)}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-slate-400 pl-3">
|
|
||||||
<span>กำไรต่อทุน (ROI %)</span>
|
|
||||||
<span className={`font-mono ${simProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
|
||||||
{simROI.toFixed(1)}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Difference comparison status card */}
|
|
||||||
<div className={`mt-6 rounded-xl p-4 flex items-center justify-between text-xs border ${diffVal >= 0 ? 'bg-green-500/10 border-green-500/30 text-green-400' : 'bg-rose-500/10 border-rose-500/30 text-rose-400'}`}>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Info className="w-4 h-4" />
|
|
||||||
<span className="font-medium">
|
|
||||||
{diffVal >= 0
|
|
||||||
? `ยอดรับเงินเพิ่มขึ้น ฿${diffVal.toFixed(2)} จากโครงสร้างธรรมเนียมใหม่`
|
|
||||||
: `ยอดเงินโอนเข้าบัญชีสุทธิจะต่างไป ฿${diffVal.toFixed(2)} (${diffVal >= 0 ? '+' : ''}${diffPct.toFixed(1)}%) เมื่อเทียบกับบิลจริงดั้งเดิม`}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span className="font-bold font-mono text-sm">{diffVal >= 0 ? '+' : ''}฿{diffVal.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,542 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import {
|
||||||
|
Calculator,
|
||||||
|
Info,
|
||||||
|
RefreshCw
|
||||||
|
} from 'lucide-react'
|
||||||
|
|
||||||
|
function fmt(val: number) {
|
||||||
|
return '฿' + val.toLocaleString('th-TH', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TiktokShopCasePage() {
|
||||||
|
const [resetKey, setResetKey] = useState(0)
|
||||||
|
const [selectedCase, setSelectedCase] = useState<'case1' | 'case2' | 'case3'>('case1')
|
||||||
|
|
||||||
|
const [simBeforeDiscount, setSimBeforeDiscount] = useState(79.00)
|
||||||
|
const [discounts, setDiscounts] = useState<Array<{ type: 'pct' | 'amount'; value: number }>>([
|
||||||
|
{ type: 'amount', value: 17.90 },
|
||||||
|
{ type: 'amount', value: 0 },
|
||||||
|
{ type: 'amount', value: 0 },
|
||||||
|
])
|
||||||
|
const [simCost, setSimCost] = useState(30.00)
|
||||||
|
|
||||||
|
const [simTransPct, setSimTransPct] = useState(3.21)
|
||||||
|
const [simCommPct, setSimCommPct] = useState(10.70)
|
||||||
|
const [simGrowthPct, setSimGrowthPct] = useState(7.50)
|
||||||
|
const [simAffiliatePct, setSimAffiliatePct] = useState(0)
|
||||||
|
|
||||||
|
const caseData = {
|
||||||
|
case1: {
|
||||||
|
beforeDiscount: 79.00,
|
||||||
|
shopDiscount: -17.90,
|
||||||
|
baseAmt: 61.10,
|
||||||
|
origTrans: -1.96,
|
||||||
|
origComm: -6.54,
|
||||||
|
origGrowth: -4.58,
|
||||||
|
origAffiliate: 0,
|
||||||
|
origTotalFees: -13.08,
|
||||||
|
originalPayout: 48.02,
|
||||||
|
defaultCost: 30.00,
|
||||||
|
label: 'เคสที่ 1: บิลสินค้า ฿79 (สุทธิ ฿48.02)',
|
||||||
|
},
|
||||||
|
case2: {
|
||||||
|
beforeDiscount: 190.00,
|
||||||
|
shopDiscount: -20.90,
|
||||||
|
baseAmt: 169.10,
|
||||||
|
origTrans: -5.43,
|
||||||
|
origComm: -18.09,
|
||||||
|
origGrowth: -12.67,
|
||||||
|
origAffiliate: 0,
|
||||||
|
origTotalFees: -36.19,
|
||||||
|
originalPayout: 132.91,
|
||||||
|
defaultCost: 80.00,
|
||||||
|
label: 'เคสที่ 2: บิลสินค้า ฿190 (สุทธิ ฿132.91)',
|
||||||
|
},
|
||||||
|
case3: {
|
||||||
|
beforeDiscount: 190.00,
|
||||||
|
shopDiscount: -37.81,
|
||||||
|
baseAmt: 152.19,
|
||||||
|
origTrans: -4.89,
|
||||||
|
origComm: -16.28,
|
||||||
|
origGrowth: -11.40,
|
||||||
|
origAffiliate: -7.61,
|
||||||
|
origTotalFees: -40.18,
|
||||||
|
originalPayout: 112.01,
|
||||||
|
defaultCost: 70.00,
|
||||||
|
label: 'เคสที่ 3: บิลสินค้า ฿190 + Affiliate (สุทธิ ฿112.01)',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeCase = caseData[selectedCase]
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSimBeforeDiscount(activeCase.beforeDiscount)
|
||||||
|
setDiscounts([
|
||||||
|
{ type: 'amount', value: Math.abs(activeCase.shopDiscount) },
|
||||||
|
{ type: 'amount', value: 0 },
|
||||||
|
{ type: 'amount', value: 0 },
|
||||||
|
])
|
||||||
|
setSimCost(activeCase.defaultCost)
|
||||||
|
setSimTransPct(3.21)
|
||||||
|
setSimCommPct(10.70)
|
||||||
|
setSimGrowthPct(7.50)
|
||||||
|
setSimAffiliatePct(selectedCase === 'case3' ? 5.00 : 0)
|
||||||
|
}, [selectedCase])
|
||||||
|
|
||||||
|
// Simulated calculations
|
||||||
|
// Deduct discounts. Let's make it cumulative based on the original list price:
|
||||||
|
const simDiscountAmount = discounts.reduce((sum, d) => {
|
||||||
|
const amt = d.type === 'pct' ? simBeforeDiscount * (d.value / 100) : d.value
|
||||||
|
return sum + amt
|
||||||
|
}, 0)
|
||||||
|
const baseAmt = Math.max(0, simBeforeDiscount - simDiscountAmount)
|
||||||
|
|
||||||
|
const simTransAmt = baseAmt * (simTransPct / 100)
|
||||||
|
const simCommAmt = baseAmt * (simCommPct / 100)
|
||||||
|
const simGrowthAmt = baseAmt * (simGrowthPct / 100)
|
||||||
|
const simAffiliateAmt = baseAmt * (simAffiliatePct / 100)
|
||||||
|
const simTotalAmt = simTransAmt + simCommAmt + simGrowthAmt + simAffiliateAmt
|
||||||
|
const simTotalPct = simTransPct + simCommPct + simGrowthPct + simAffiliatePct
|
||||||
|
const simPayout = baseAmt - simTotalAmt
|
||||||
|
|
||||||
|
const originalPayout = activeCase.originalPayout
|
||||||
|
const diffVal = simPayout - originalPayout
|
||||||
|
const diffPct = originalPayout > 0 ? (diffVal / originalPayout) * 100 : 0
|
||||||
|
|
||||||
|
// Profit/Margin/ROI Calculations
|
||||||
|
const origProfit = originalPayout - simCost
|
||||||
|
const origProfitMargin = activeCase.baseAmt > 0 ? (origProfit / activeCase.baseAmt) * 100 : 0
|
||||||
|
const origROI = simCost > 0 ? (origProfit / simCost) * 100 : 0
|
||||||
|
|
||||||
|
const simProfit = simPayout - simCost
|
||||||
|
const simProfitMargin = baseAmt > 0 ? (simProfit / baseAmt) * 100 : 0
|
||||||
|
const simROI = simCost > 0 ? (simProfit / simCost) * 100 : 0
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen p-6 md:p-8 max-w-5xl mx-auto animate-fade-in" key={resetKey}>
|
||||||
|
{/* Page header */}
|
||||||
|
<div className="flex items-start justify-between mb-8 gap-4">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-cyan-glow/10 border border-cyan-glow/20 flex items-center justify-center">
|
||||||
|
<Calculator className="w-5 h-5 text-cyan-glow" />
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold text-white tracking-tight">TikTok Shop Case</h1>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-slate-500 max-w-2xl">
|
||||||
|
ศึกษาโครงสร้างบิลรับเงินจริงจากระบบ TikTok Shop พร้อมทั้งจำลองคำนวณราคาขาย, คูปองส่วนลดร้านค้า, ต้นทุน และประเมินสถิติกำไร & ROI ก่อนขายจริง
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setResetKey(k => k + 1)
|
||||||
|
setSimTransPct(3.21)
|
||||||
|
setSimCommPct(10.70)
|
||||||
|
setSimGrowthPct(7.50)
|
||||||
|
setSimAffiliatePct(selectedCase === 'case3' ? 5.00 : 0)
|
||||||
|
}}
|
||||||
|
className="btn-ghost flex items-center gap-2 text-xs shrink-0"
|
||||||
|
title="รีเซ็ตทุกค่า"
|
||||||
|
>
|
||||||
|
<RefreshCw className="w-4 h-4" />
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Real Payout Case Study Example */}
|
||||||
|
<div className="admin-card border-white/10 animate-fade-in">
|
||||||
|
<div className="flex items-center justify-between mb-4 flex-wrap gap-4 border-b border-white/5 pb-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Info className="w-5 h-5 text-cyan-glow" />
|
||||||
|
<h2 className="text-lg font-bold text-white">กรณีศึกษา & เครื่องมือจำลองค่าธรรมเนียมจริง</h2>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex p-1 bg-night-900 border border-white/10 rounded-lg">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-all ${selectedCase === 'case1' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
||||||
|
onClick={() => setSelectedCase('case1')}
|
||||||
|
>
|
||||||
|
ตัวอย่างที่ 1 (ยอด ฿79)
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-all ${selectedCase === 'case2' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
||||||
|
onClick={() => setSelectedCase('case2')}
|
||||||
|
>
|
||||||
|
ตัวอย่างที่ 2 (ยอด ฿190)
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-all ${selectedCase === 'case3' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
||||||
|
onClick={() => setSelectedCase('case3')}
|
||||||
|
>
|
||||||
|
ตัวอย่างที่ 3 (ยอด ฿190 + Affiliate)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setSimTransPct(3.21)
|
||||||
|
setSimCommPct(10.70)
|
||||||
|
setSimGrowthPct(7.50)
|
||||||
|
setSimAffiliatePct(selectedCase === 'case3' ? 5.00 : 0)
|
||||||
|
}}
|
||||||
|
className="btn-ghost text-xs py-1.5 px-3 rounded border border-white/10"
|
||||||
|
>
|
||||||
|
ใช้ค่าตั้งต้นตัวอย่าง
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xs text-slate-400 mb-2 font-medium">
|
||||||
|
รายละเอียดบิลจริงดั้งเดิมของ {selectedCase === 'case1' ? 'ตัวอย่างที่ 1' : selectedCase === 'case2' ? 'ตัวอย่างที่ 2' : 'ตัวอย่างที่ 3'}:
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-3 gap-4 p-3 rounded-lg bg-night-800/40 border border-white/5 text-xs mb-6 opacity-80">
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-500 block">ยอดรวมสินค้าก่อนลด</span>
|
||||||
|
<span className="font-bold text-white font-mono">{fmt(activeCase.beforeDiscount)}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-500 block">ส่วนลดจากร้านค้า (ลดไป %)</span>
|
||||||
|
<span className="font-bold text-rose-400 font-mono">
|
||||||
|
{fmt(activeCase.shopDiscount)} (-{((Math.abs(activeCase.shopDiscount) / activeCase.beforeDiscount) * 100).toFixed(1)}%)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-slate-500 block">ยอดรวมหลังหักส่วนลด (ฐานคิดค่าคอม)</span>
|
||||||
|
<span className="font-bold text-cyan-glow font-mono">{fmt(activeCase.baseAmt)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Simulator controls - Price & Discounts */}
|
||||||
|
<p className="text-xs text-slate-400 mb-2 font-medium">1. จำลองการตั้งราคา ส่วนลด และต้นทุนสินค้า (ปรับแต่งเองได้):</p>
|
||||||
|
<div className="mb-4 grid grid-cols-1 sm:grid-cols-3 gap-4 p-4 rounded-xl bg-night-800/40 border border-white/5">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs text-slate-400 font-medium mb-1.5">ราคาขายก่อนลด (List Price)</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="1"
|
||||||
|
min="0"
|
||||||
|
value={simBeforeDiscount}
|
||||||
|
onChange={e => setSimBeforeDiscount(parseFloat(e.target.value) || 0)}
|
||||||
|
className="admin-input w-full text-xs"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-slate-500">฿</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs text-slate-400 font-medium mb-1.5">ต้นทุนสินค้า (Cost)</label>
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="1"
|
||||||
|
min="0"
|
||||||
|
value={simCost}
|
||||||
|
onChange={e => setSimCost(parseFloat(e.target.value) || 0)}
|
||||||
|
className="admin-input w-full text-xs"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-slate-500">฿</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{[20, 45, 50].map(val => (
|
||||||
|
<button
|
||||||
|
key={val}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setSimCost(val)}
|
||||||
|
className={`px-2.5 py-1 text-[10px] font-medium rounded border transition-all ${simCost === val ? 'bg-cyan-glow/20 text-cyan-glow border-cyan-glow/30' : 'bg-night-900 text-slate-400 border-white/10 hover:text-white hover:border-white/20'}`}
|
||||||
|
>
|
||||||
|
฿{val}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3 sm:col-span-2">
|
||||||
|
<label className="block text-xs text-slate-400 font-medium">ส่วนลดจากร้านค้า (คูปองร้านค้า 1-3)</label>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||||
|
{discounts.map((discount, index) => (
|
||||||
|
<div key={index} className="flex flex-col gap-2 p-2.5 rounded-lg bg-night-900/60 border border-white/5">
|
||||||
|
<span className="text-[10px] text-slate-500 font-bold">คูปองที่ #{index + 1}</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex p-0.5 bg-night-950 border border-white/10 rounded-md shrink-0">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`px-1.5 py-0.5 text-[9px] font-medium rounded transition-all ${discount.type === 'pct' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
||||||
|
onClick={() => {
|
||||||
|
const updated = [...discounts]
|
||||||
|
updated[index] = { ...updated[index], type: 'pct' }
|
||||||
|
setDiscounts(updated)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
%
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`px-1.5 py-0.5 text-[9px] font-medium rounded transition-all ${discount.type === 'amount' ? 'bg-cyan-glow/20 text-cyan-glow' : 'text-slate-400 hover:text-white'}`}
|
||||||
|
onClick={() => {
|
||||||
|
const updated = [...discounts]
|
||||||
|
updated[index] = { ...updated[index], type: 'amount' }
|
||||||
|
setDiscounts(updated)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
฿
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.1"
|
||||||
|
min="0"
|
||||||
|
value={discount.value || ''}
|
||||||
|
onChange={e => {
|
||||||
|
const updated = [...discounts]
|
||||||
|
updated[index] = { ...updated[index], value: parseFloat(e.target.value) || 0 }
|
||||||
|
setDiscounts(updated)
|
||||||
|
}}
|
||||||
|
placeholder="0"
|
||||||
|
className="admin-input w-full text-xs p-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Simulator controls - Fees */}
|
||||||
|
<p className="text-xs text-slate-400 mb-2 font-medium">2. จำลองการปรับเปลี่ยนอัตราค่าธรรมเนียม (%):</p>
|
||||||
|
<div className="mb-6 grid grid-cols-2 sm:grid-cols-4 gap-4 p-4 rounded-xl bg-night-800/40 border border-white/5">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs text-slate-400 font-medium mb-1.5">Transaction (%)</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
value={simTransPct}
|
||||||
|
onChange={e => setSimTransPct(parseFloat(e.target.value) || 0)}
|
||||||
|
className="admin-input w-full text-xs"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-slate-500">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs text-slate-400 font-medium mb-1.5">ค่าคอม TikTok (%)</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
value={simCommPct}
|
||||||
|
onChange={e => setSimCommPct(parseFloat(e.target.value) || 0)}
|
||||||
|
className="admin-input w-full text-xs"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-slate-500">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs text-slate-400 font-medium mb-1.5">สนับสนุนเติบโต (%)</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
value={simGrowthPct}
|
||||||
|
onChange={e => setSimGrowthPct(parseFloat(e.target.value) || 0)}
|
||||||
|
className="admin-input w-full text-xs"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-slate-500">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs text-slate-400 font-medium mb-1.5">ค่าคอมแอฟฟิลิเอต (%)</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
value={simAffiliatePct}
|
||||||
|
onChange={e => setSimAffiliatePct(parseFloat(e.target.value) || 0)}
|
||||||
|
className="admin-input w-full text-xs"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-slate-500">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Side-by-side comparison tables */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
{/* ก่อนปรับ (ค่าธรรมเนียมจากบิลตัวอย่างจริง) */}
|
||||||
|
<div className="rounded-xl bg-night-800/30 border border-white/5 p-4 space-y-4">
|
||||||
|
<h3 className="text-xs font-bold text-slate-400 uppercase tracking-wider border-b border-white/5 pb-2 flex justify-between">
|
||||||
|
<span>ก่อนปรับ (บิลดั้งเดิม)</span>
|
||||||
|
<span className="text-slate-500">รวม {((Math.abs(activeCase.origTotalFees) / activeCase.baseAmt) * 100).toFixed(2)}%</span>
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-2 text-xs">
|
||||||
|
<div className="flex justify-between text-slate-400">
|
||||||
|
<span>ยอดรวมสินค้าก่อนลด</span>
|
||||||
|
<span className="font-mono">{fmt(activeCase.beforeDiscount)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-slate-400">
|
||||||
|
<span>ส่วนลดจากร้านค้า</span>
|
||||||
|
<span className="font-mono text-rose-400">{fmt(activeCase.shopDiscount)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between border-t border-white/5 pt-1.5 font-medium text-slate-300">
|
||||||
|
<span>ยอดสินค้าสุทธิ (ฐานคิดคอม)</span>
|
||||||
|
<span>{fmt(activeCase.baseAmt)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between pl-3 text-slate-500">
|
||||||
|
<span>- ค่าคำสั่งซื้อ (Transaction 3.21%)</span>
|
||||||
|
<span className="font-mono text-rose-400">{fmt(activeCase.origTrans)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between pl-3 text-slate-500">
|
||||||
|
<span>- ค่าคอม TikTok Shop (10.70%)</span>
|
||||||
|
<span className="font-mono text-rose-400">{fmt(activeCase.origComm)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between pl-3 text-slate-500">
|
||||||
|
<span>- ค่าสนับสนุนการเติบโต (7.50%)</span>
|
||||||
|
<span className="font-mono text-rose-400">{fmt(activeCase.origGrowth)}</span>
|
||||||
|
</div>
|
||||||
|
{activeCase.origAffiliate !== 0 && (
|
||||||
|
<div className="flex justify-between pl-3 text-slate-500">
|
||||||
|
<span>- ค่าคอมแอฟฟิลิเอต (5.00%)</span>
|
||||||
|
<span className="font-mono text-rose-400">{fmt(activeCase.origAffiliate)}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex justify-between border-t border-white/5 pt-1.5 font-medium text-slate-300">
|
||||||
|
<span>รวมค่าธรรมเนียมโดนหัก</span>
|
||||||
|
<span className="font-mono text-rose-400">{fmt(activeCase.origTotalFees)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between border-t border-white/5 pt-2 font-bold text-white text-sm">
|
||||||
|
<span>ยอดเงินโอนเข้าบัญชีสุทธิ</span>
|
||||||
|
<span className="font-mono text-cyan-glow">{fmt(activeCase.originalPayout)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between border-t border-white/5 pt-1.5 text-slate-400">
|
||||||
|
<span>- ต้นทุนสินค้า (Cost)</span>
|
||||||
|
<span className="font-mono text-rose-400">฿-{simCost.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between border-t border-white/5 pt-2 font-bold text-white text-sm">
|
||||||
|
<span>กำไรสุทธิ (Net Profit)</span>
|
||||||
|
<span className={`font-mono ${origProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
||||||
|
{origProfit >= 0 ? '' : '-'}฿{Math.abs(origProfit).toFixed(2)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-slate-400 pl-3">
|
||||||
|
<span>กำไรต่อยอดขาย (Margin %)</span>
|
||||||
|
<span className={`font-mono ${origProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
||||||
|
{origProfitMargin.toFixed(1)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-slate-400 pl-3">
|
||||||
|
<span>กำไรต่อทุน (ROI %)</span>
|
||||||
|
<span className={`font-mono ${origProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
||||||
|
{origROI.toFixed(1)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* หลังปรับ (จำลองการปรับค่าธรรมเนียม) */}
|
||||||
|
<div className="rounded-xl bg-cyan-glow/5 border border-cyan-glow/20 p-4 space-y-4">
|
||||||
|
<h3 className="text-xs font-bold text-cyan-glow uppercase tracking-wider border-b border-cyan-glow/10 pb-2 flex justify-between">
|
||||||
|
<span>หลังปรับ (ผลการจำลองใหม่)</span>
|
||||||
|
<span>รวม {simTotalPct.toFixed(2)}%</span>
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-2 text-xs">
|
||||||
|
<div className="flex justify-between text-slate-400">
|
||||||
|
<span>ยอดรวมสินค้าก่อนลด</span>
|
||||||
|
<span className="font-mono">{fmt(simBeforeDiscount)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-slate-400">
|
||||||
|
<span>ส่วนลดจากร้านค้า (รวม)</span>
|
||||||
|
<span className="font-mono text-rose-400">
|
||||||
|
-{fmt(simDiscountAmount)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{discounts.map((d, i) => d.value > 0 ? (
|
||||||
|
<div key={i} className="flex justify-between text-[10px] text-slate-500 pl-3">
|
||||||
|
<span>- คูปอง #{i + 1} ({d.type === 'pct' ? `${d.value}%` : `฿${d.value}`})</span>
|
||||||
|
<span className="font-mono text-rose-400">
|
||||||
|
-{fmt(d.type === 'pct' ? simBeforeDiscount * (d.value / 100) : d.value)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : null)}
|
||||||
|
<div className="flex justify-between border-t border-cyan-glow/10 pt-1.5 font-medium text-slate-300">
|
||||||
|
<span>ยอดสินค้าสุทธิ (ฐานคิดคอม)</span>
|
||||||
|
<span className="font-mono text-cyan-glow">{fmt(baseAmt)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between pl-3 text-slate-500">
|
||||||
|
<span>- ค่าคำสั่งซื้อ ({simTransPct.toFixed(2)}%)</span>
|
||||||
|
<span className="font-mono text-rose-400">฿-{simTransAmt.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between pl-3 text-slate-500">
|
||||||
|
<span>- ค่าคอม TikTok Shop ({simCommPct.toFixed(2)}%)</span>
|
||||||
|
<span className="font-mono text-rose-400">฿-{simCommAmt.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between pl-3 text-slate-500">
|
||||||
|
<span>- ค่าสนับสนุนการเติบโต ({simGrowthPct.toFixed(2)}%)</span>
|
||||||
|
<span className="font-mono text-rose-400">฿-{simGrowthAmt.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
{simAffiliatePct > 0 && (
|
||||||
|
<div className="flex justify-between pl-3 text-slate-500">
|
||||||
|
<span>- ค่าคอมคอมแอฟฟิลิเอต ({simAffiliatePct.toFixed(2)}%)</span>
|
||||||
|
<span className="font-mono text-rose-400">฿-{simAffiliateAmt.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex justify-between border-t border-cyan-glow/10 pt-1.5 font-medium text-slate-300">
|
||||||
|
<span>รวมค่าธรรมเนียมโดนหัก</span>
|
||||||
|
<span className="font-mono text-rose-400">฿-{simTotalAmt.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between border-t border-cyan-glow/10 pt-2 font-bold text-white text-sm">
|
||||||
|
<span>ยอดเงินโอนเข้าบัญชีสุทธิ</span>
|
||||||
|
<span className="font-mono text-cyan-glow">฿{simPayout.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between border-t border-cyan-glow/10 pt-1.5 text-slate-400">
|
||||||
|
<span>- ต้นทุนสินค้า (Cost)</span>
|
||||||
|
<span className="font-mono text-rose-400">฿-{simCost.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between border-t border-cyan-glow/10 pt-2 font-bold text-white text-sm">
|
||||||
|
<span>กำไรสุทธิ (Net Profit)</span>
|
||||||
|
<span className={`font-mono ${simProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
||||||
|
{simProfit >= 0 ? '' : '-'}฿{Math.abs(simProfit).toFixed(2)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-slate-400 pl-3">
|
||||||
|
<span>กำไรต่อยอดขาย (Margin %)</span>
|
||||||
|
<span className={`font-mono ${simProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
||||||
|
{simProfitMargin.toFixed(1)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-slate-400 pl-3">
|
||||||
|
<span>กำไรต่อทุน (ROI %)</span>
|
||||||
|
<span className={`font-mono ${simProfit >= 0 ? 'text-green-400' : 'text-rose-400'}`}>
|
||||||
|
{simROI.toFixed(1)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Difference comparison status card */}
|
||||||
|
<div className={`mt-6 rounded-xl p-4 flex items-center justify-between text-xs border ${diffVal >= 0 ? 'bg-green-500/10 border-green-500/30 text-green-400' : 'bg-rose-500/10 border-rose-500/30 text-rose-400'}`}>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Info className="w-4 h-4" />
|
||||||
|
<span className="font-medium">
|
||||||
|
{diffVal >= 0
|
||||||
|
? `ยอดรับเงินเพิ่มขึ้น ฿${diffVal.toFixed(2)} จากโครงสร้างธรรมเนียมใหม่`
|
||||||
|
: `ยอดเงินโอนเข้าบัญชีสุทธิจะต่างไป ฿${diffVal.toFixed(2)} (${diffVal >= 0 ? '+' : ''}${diffPct.toFixed(1)}%) เมื่อเทียบกับบิลจริงดั้งเดิม`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="font-bold font-mono text-sm">{diffVal >= 0 ? '+' : ''}฿{diffVal.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user