-
Notifications
You must be signed in to change notification settings - Fork 448
Description
<thml lang"km">
| school_id | authority | unit | user | school | name_building | log_num | log_total | building_num | building_total | usage_bur | usage_tec | usage_log | usage_mix | province | district | commune | address | width | length | area | type_stone | type_wood | type_mix | year | has_water | has_elec | cadastral | floors | total_room | total_value | status | created_at |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1030401017 | ក្រសួងអប់រំ យុវជន និងកីឡា | មន្ទីរអប់រំ យុវជន និងកីឡាខេត្តបន្ទាយមានជ័យ | ការិយាយល័យអប់រំ យុវជន និងកីឡាស្រុកភ្នំស្រុក | សាលាបឋមសិក្សា រោគ | A | 9 | 51 | 1 | 5 | FALSE | FALSE | FALSE | TRUE | Banteay Meanchey | Phnum srok | Spean sreng | Rauk | 8 | 20 | 160 | FALSE | TRUE | FALSE | 1891 | TRUE | TRUE | E0 | 3 | 3050000 | បាត់ពីដី(រុះរើ) | ""2025-12-23T08:35:32.832Z"" | |
| 1030401017 | ក្រសួងអប់រំ យុវជន និងកីឡា | មន្ទីរអប់រំ យុវជន និងកីឡាខេត្តបន្ទាយមានជ័យ | ការិយាយល័យអប់រំ យុវជន និងកីឡាស្រុកភ្នំស្រុក | សាលាបឋមសិក្សា រោគ | B | 9 | 51 | 2 | 5 | FALSE | FALSE | FALSE | TRUE | Banteay Meanchey | Phnum srok | Spean sreng | Rauk | 7 | 24 | 168 | TRUE | FALSE | FALSE | 2012 | TRUE | TRUE | E0 | 3 | 13010000 | មធ្យម | ""2025-12-23T08:42:55.379Z"" | |
| 1030401017 | ក្រសួងអប់រំ យុវជន និងកីឡា | មន្ទីរអប់រំ យុវជន និងកីឡាខេត្តបន្ទាយមានជ័យ | ការិយាយល័យអប់រំ យុវជន និងកីឡាស្រុកភ្នំស្រុក | សាលាបឋមសិក្សា រោគ | C | 9 | 51 | 3 | 5 | FALSE | FALSE | FALSE | TRUE | Banteay Meanchey | Phnum srok | Spean sreng | Rauk | 9 | 32 | 288 | TRUE | FALSE | FALSE | 2014 | TRUE | TRUE | E0 | 4 | 190170000 | មធ្យម | ""2025-12-28T17:36:57.646Z"" | |
| 1030401017 | ក្រសួងអប់រំ យុវជន និងកីឡា | មន្ទីរអប់រំ យុវជន និងកីឡាខេត្តបន្ទាយមានជ័យ | ការិយាយល័យអប់រំ យុវជន និងកីឡាស្រុកភ្នំស្រុក | សាលាបឋមសិក្សា រោគ | D | 9 | 51 | 4 | 5 | FALSE | FALSE | FALSE | TRUE | Banteay Meanchey | Phnum srok | Spean sreng | Rauk | 9 | 42 | 342 | TRUE | FALSE | FALSE | 2018 | TRUE | TRUE | E0 | 6 | 288000000 | ល្អ | ""2025-12-28T17:46:12.865Z"" | |
| 1030401017 | ក្រសួងអប់រំ យុវជន និងកីឡា | មន្ទីរអប់រំ យុវជន និងកីឡាខេត្តបន្ទាយមានជ័យ | ការិយាយល័យអប់រំ យុវជន និងកីឡាស្រុកភ្នំស្រុក | សាលាបឋមសិក្សា រោគ | E | 9 | 51 | 5 | 5 | TRUE | FALSE | FALSE | FALSE | Banteay Meanchey | Phnum srok | Spean sreng | Rauk | 7 | 8 | 56 | TRUE | FALSE | FALSE | 2025 | TRUE | TRUE | E0 | 2 | 3500000 | ល្អ | ""2025-12-28T17:46:12.865Z"" |
– Building Permit System Database Schema
– Based on Cambodian Government Building Permit Form
CREATE TABLE IF NOT EXISTS permits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
– Basic Info (Header)
authority TEXT NOT NULL, – អាជ្ញាធរកាន់កាប់ទ្រព្យសម្បត្តិរដ្ឋ
unit TEXT NOT NULL, – អង្គភាពប្រើប្រាស់
user TEXT NOT NULL, – អ្នកប្រើប្រាស់
school TEXT, – ឈ្មោះសាលា (or project name)
– Permit Numbers
log_num INTEGER NOT NULL, – សលាកបត្រលម្អិតអាគារសង់លើដី លេខ X
log_total INTEGER NOT NULL DEFAULT 51, – លើ Y
building_num INTEGER NOT NULL, – អគារលេខ X
building_total INTEGER NOT NULL DEFAULT 4, – លើ Y
– Usage Type (ការប្រើប្រាស់)
usage_bur BOOLEAN DEFAULT FALSE, – ការិយាល័យ (BUR)
usage_tec BOOLEAN DEFAULT FALSE, – អគារបច្ចេកទេស (TEC)
usage_log BOOLEAN DEFAULT FALSE, – លំនៅដ្ឋាន (LOG)
usage_mix BOOLEAN DEFAULT FALSE, – ចម្រុះ (MIX)
– Location (ទីតាំង)
province TEXT NOT NULL, – ក្រុង/ខេត្ត
district TEXT NOT NULL, – ស្រុក
commune TEXT NOT NULL, – ឃុំ
village TEXT, – ភូមិ
address TEXT, – អាស័យដ្ឋានពិតប្រាកដ
– Dimensions (ទំហំ)
width REAL NOT NULL, – ទទឹង (ម៉ែត្រ)
length REAL NOT NULL, – បណ្ដោយ (ម៉ែត្រ)
area REAL NOT NULL, – ផ្ទៃក្រឡា (ម៉ែត្រក្រឡា)
– Construction Details
type_stone BOOLEAN DEFAULT FALSE, – ធ្វើអំពីថ្ម
type_wood BOOLEAN DEFAULT FALSE, – ធ្វើអំពីឈើ
type_mix BOOLEAN DEFAULT FALSE, – ធ្វើចម្រុះ
cadastral TEXT, – លេខចុះបញ្ជីសុរិយោដី
year INTEGER NOT NULL, – ឆ្នាំសាងសង់
floors TEXT NOT NULL DEFAULT ‘E0’, – ចំនួនជាន់
– Utilities (ប្រើប្រាស់)
has_water BOOLEAN DEFAULT FALSE, – មានទឹក
has_phone BOOLEAN DEFAULT FALSE, – ទូរស័ព្ទ
has_elec BOOLEAN DEFAULT FALSE, – អគ្គិសនី
has_spare BOOLEAN DEFAULT FALSE, – ទំនេរ
– Financial
total_value REAL NOT NULL, – តម្លៃអគារសរុប (រៀល)
– Structure Condition (លំនឹងពីទូទៅ)
cond_foundation TEXT DEFAULT ‘មធ្យម’, – មូលដ្ឋាន
cond_wall TEXT DEFAULT ‘មធ្យម’, – ជញ្ជាំង
cond_roof TEXT DEFAULT ‘មធ្យម’, – ដំបូល
cond_interior TEXT DEFAULT ‘មធ្យម’, – រៀបចំខាងក្នុង
– Additional Notes
notes TEXT, – ព័ត៌មានផ្សេងៗ
– Timestamps (កាលបរិច្ឆេទ)
date_created TEXT NOT NULL, – កាលបរិច្ឆេទបង្កើត (YYYY-MM-DD)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
– Indexes for common queries
CREATE INDEX IF NOT EXISTS idx_permits_province ON permits(province);
CREATE INDEX IF NOT EXISTS idx_permits_user ON permits(user);
CREATE INDEX IF NOT EXISTS idx_permits_building_num ON permits(building_num);
CREATE INDEX IF NOT EXISTS idx_permits_date_created ON permits(date_created);
import { usePermit } from “@/hooks/use-permits”;
import { useRoute, Link } from “wouter”;
import { Button } from “@/components/ui/button”;
import { Printer, ArrowLeft, PenLine } from “lucide-react”;
import { useRef } from “react”;
import { formatKhmerDate, formatWesternDate, formatKhmerNumber } from “@/lib/khmer-date-utils”;
export default function ViewPermit() {
const [, params] = useRoute(”/permit/:id”);
const id = parseInt(params?.id || “0”);
const { data: permit, isLoading } = usePermit(id);
const printRef = useRef(null);
const handlePrint = () => {
window.print();
};
if (isLoading) return
if (!permit) return
const dateCreated = permit.dateCreated ? new Date(permit.dateCreated) : new Date();
const khmerDate = formatKhmerDate(dateCreated);
const westernDate = formatWesternDate(dateCreated);
return (
{/* Controls (Hidden on Print) */}
<div className="max-w-[297mm] mx-auto mb-3 flex justify-center gap-3 no-print">
<Link href="/">
<Button variant="outline" size="sm" className="gap-2">
<ArrowLeft className="w-4 h-4" /> ត្រឡប់ក្រោយ
</Button>
</Link>
<Link href={`/edit/${id}`}>
<Button variant="outline" size="sm" className="gap-2">
<PenLine className="w-4 h-4" /> កែសម្រួល
</Button>
</Link>
<Button onClick={handlePrint} size="sm" className="bg-green-600 hover:bg-green-700 gap-2 text-white">
<Printer className="w-4 h-4" /> បោះពុម្ព
</Button>
</div>
{/* A4 Landscape Paper (297mm x 210mm) */}
<div
ref={printRef}
className="max-w-[297mm] h-[210mm] mx-auto bg-white shadow-xl print:shadow-none p-[10mm] text-[11px] leading-tight text-black font-khmer relative"
>
{/* Header */}
<div className="text-center mb-3">
<div className="text-[12px] font-bold leading-tight">
<p>ព្រះរាជាណាចក្រកម្ពុជា</p>
<p>ជាតិ សាសនា ព្រះមហាក្សត្រ</p>
</div>
<div className="text-[11px] mt-1">
════ ◉◉◉❂❂◉◉◉ ════
</div>
</div>
{/* Authority Lines */}
<div className="text-[10px] leading-tight mb-2">
<p>អាជ្ញាធរកាន់កាប់ទ្រព្យសម្បត្តិរដ្ឋ : <span className="font-semibold">{permit.authority}</span></p>
<p>អង្គភាពប្រើប្រាស់ : <span className="font-semibold">{permit.unit}</span></p>
</div>
{/* Title */}
<div className="text-center font-bold mb-2">
<p className="text-[13px]">សលាកបត្រលម្អិតនៃអាគារសង់លើដី លេខ {permit.logNum} លើ {permit.logTotal}</p>
</div>
{/* Building Number Row */}
<div className="flex gap-4 mb-1 text-[10px]">
<div className="flex-1">
អគារលេខ <span className="font-bold">{permit.buildingNum}</span> លើ <span className="font-bold">{permit.buildingTotal}</span>
</div>
</div>
{/* Usage Type Row */}
<div className="flex gap-2 items-center mb-2 text-[10px]">
<span className="font-semibold">ការប្រើប្រាស់ សូមគូស :</span>
<div className="flex gap-3">
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.usageBur ? 'font-bold' : ''}`}>
{permit.usageBur && '✓'}
</div>
<span>ការិយាល័យ(BUR)</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.usageTec ? 'font-bold' : ''}`}>
{permit.usageTec && '✓'}
</div>
<span>អគារបច្ចេកទេស(TEC)</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.usageLog ? 'font-bold' : ''}`}>
{permit.usageLog && '✓'}
</div>
<span>លំនៅដ្ឋាន(LOG)</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.usageMix ? 'font-bold' : ''}`}>
{permit.usageMix && '✓'}
</div>
<span>ចម្រុះ(MIX)</span>
</label>
</div>
</div>
{/* Two Column Layout */}
<div className="grid grid-cols-2 gap-6 mb-2">
{/* Left Column: Location */}
<div className="text-[10px] space-y-0.5">
<div className="flex gap-2">
<span className="font-semibold w-20">ទីតាំង-ខេត្ត :</span>
<span className="flex-1">{permit.province}</span>
<span className="font-semibold w-16">ទំហំ</span>
</div>
<div className="flex gap-2">
<span className="font-semibold w-20">ស្រុក :</span>
<span className="flex-1">{permit.district}</span>
<span className="font-semibold w-16">ទទឹង (ម៉ែត្រ) :</span>
<span>{permit.width}</span>
</div>
<div className="flex gap-2">
<span className="font-semibold w-20"></span>
<span className="flex-1"></span>
<span className="font-semibold w-16">បណ្ដោយ (ម៉ែត្រ) :</span>
<span>{permit.length}</span>
</div>
<div className="flex gap-2">
<span className="font-semibold">អាស័យដ្ឋានពិតប្រាកដ :</span>
<span className="flex-1">{permit.address || `ភូមិ${permit.village || ''} ឃុំ${permit.commune} ស្រុក${permit.district} ខេត្ត${permit.province}`}</span>
</div>
<div className="flex gap-2">
<span className="font-semibold w-20"></span>
<span className="flex-1"></span>
<span className="font-semibold w-16">ផ្ទៃក្រឡា (ម²) :</span>
<span>{permit.area}</span>
</div>
</div>
{/* Right Column: Construction Details */}
<div className="text-[10px] space-y-0.5">
<div className="flex gap-2 items-center">
<span className="font-semibold">ប្រភេទសំណង់ :</span>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.typeStone ? 'font-bold' : ''}`}>
{permit.typeStone && '✓'}
</div>
<span>ធ្វើអំពីថ្ម</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.typeWood ? 'font-bold' : ''}`}>
{permit.typeWood && '✓'}
</div>
<span>ធ្វើអំពីឈើ</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.typeMix ? 'font-bold' : ''}`}>
{permit.typeMix && '✓'}
</div>
<span>ធ្វើចម្រុះ</span>
</label>
</div>
<div className="flex gap-2">
<span className="font-semibold w-32">លេខចុះបញ្ជីសុរិយោដី :</span>
<span className="flex-1">{permit.cadastral || ''}</span>
</div>
<div className="flex gap-2">
<span className="font-semibold w-20">ឆ្នាំសាងសង់ :</span>
<span>{permit.year}</span>
<span className="font-semibold ml-4">ចំនួនជាន់ :</span>
<span>{permit.floors}</span>
</div>
<div className="flex gap-2">
<span className="font-semibold">តម្លៃអគារសរុប(រៀល) :</span>
<span className="font-bold">{formatKhmerNumber(permit.totalValue)}</span>
</div>
<div className="flex gap-4">
<div className="flex gap-2 items-center">
<span className="font-semibold">មានទឹក :</span>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.hasWater ? 'font-bold' : ''}`}>
{permit.hasWater && '✓'}
</div>
<span>មាន</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${!permit.hasWater ? 'font-bold' : ''}`}>
{!permit.hasWater && '✓'}
</div>
<span>គ្មាន</span>
</label>
</div>
<div className="flex gap-2 items-center">
<span className="font-semibold">ទូរស័ព្ទ :</span>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.hasPhone ? 'font-bold' : ''}`}>
{permit.hasPhone && '✓'}
</div>
<span>មាន</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${!permit.hasPhone ? 'font-bold' : ''}`}>
{!permit.hasPhone && '✓'}
</div>
<span>គ្មាន</span>
</label>
</div>
</div>
<div className="flex gap-4">
<div className="flex gap-2 items-center">
<span className="font-semibold">អគ្គិសនី :</span>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.hasElec ? 'font-bold' : ''}`}>
{permit.hasElec && '✓'}
</div>
<span>មាន</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${!permit.hasElec ? 'font-bold' : ''}`}>
{!permit.hasElec && '✓'}
</div>
<span>គ្មាន</span>
</label>
</div>
<div className="flex gap-2 items-center">
<span className="font-semibold">ទំនេរ :</span>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${permit.hasSpare ? 'font-bold' : ''}`}>
{permit.hasSpare && '✓'}
</div>
<span>មាន</span>
</label>
<label className="flex items-center gap-1">
<div className={`w-3 h-3 border border-black flex items-center justify-center ${!permit.hasSpare ? 'font-bold' : ''}`}>
{!permit.hasSpare && '✓'}
</div>
<span>គ្មាន</span>
</label>
</div>
</div>
</div>
</div>
{/* Structure Condition Table */}
<div className="mb-2 text-[10px]">
<div className="font-semibold mb-1">លំនឹងពីទូទៅ:</div>
<table className="w-full border border-black">
<thead>
<tr className="bg-gray-100">
<th className="border border-black p-1 text-center">គូស ធិក</th>
<th className="border border-black p-1 text-center">ល្អ</th>
<th className="border border-black p-1 text-center">មធ្យម</th>
<th className="border border-black p-1 text-center">អន់</th>
</tr>
</thead>
<tbody>
<tr>
<td className="border border-black p-1">មូលដ្ឋាន</td>
<td className="border border-black p-1 text-center">{permit.condFoundation === 'ល្អ' && '✓'}</td>
<td className="border border-black p-1 text-center">{permit.condFoundation === 'មធ្យម' && '✓'}</td>
<td className="border border-black p-1 text-center">{permit.condFoundation === 'អន់' && '✓'}</td>
</tr>
<tr>
<td className="border border-black p-1">ជញ្ជាំង</td>
<td className="border border-black p-1 text-center">{permit.condWall === 'ល្អ' && '✓'}</td>
<td className="border border-black p-1 text-center">{permit.condWall === 'មធ្យម' && '✓'}</td>
<td className="border border-black p-1 text-center">{permit.condWall === 'អន់' && '✓'}</td>
</tr>
<tr>
<td className="border border-black p-1">ដំបូល</td>
<td className="border border-black p-1 text-center">{permit.condRoof === 'ល្អ' && '✓'}</td>
<td className="border border-black p-1 text-center">{permit.condRoof === 'មធ្យម' && '✓'}</td>
<td className="border border-black p-1 text-center">{permit.condRoof === 'អន់' && '✓'}</td>
</tr>
<tr>
<td className="border border-black p-1">រៀបចំខាងក្នុង</td>
<td className="border border-black p-1 text-center">{permit.condInterior === 'ល្អ' && '✓'}</td>
<td className="border border-black p-1 text-center">{permit.condInterior === 'មធ្យម' && '✓'}</td>
<td className="border border-black p-1 text-center">{permit.condInterior === 'អន់' && '✓'}</td>
</tr>
</tbody>
</table>
</div>
{/* Additional Notes / Comments */}
<div className="text-[9px] mb-2">
<div className="font-semibold mb-0.5">
ព័ត៌មានផ្សេងទៀត (បញ្ជាក់ៈ បើមានវិធីកម្មលើចលនា ឬចលនប់កម្មសិទ្ធិ ការកាន់កាប់ខុសចលនា ឬអគារ បើដី ឬអគាររបស់សហគ្រាសជួលរួចហើយឬសេចក្តីផ្សេងៗដែលចាំបាច់):
</div>
<div className="border border-gray-400 p-1.5 min-h-[30px] bg-gray-50">
{permit.notes || ''}
</div>
</div>
{/* Signature Section - Bottom Right */}
<div className="absolute bottom-[10mm] right-[10mm] text-[10px] text-center">
<div className="mb-1">{khmerDate}</div>
<div className="mb-1">{permit.school} {westernDate}</div>
<div className="h-12"></div>
<div className="font-bold">ប្រធានអង្គភាព</div>
</div>
</div>
</div>
);
}
// Khmer Date Utilities - Matching Budget Request System
// ឧបករណ៍ប្រើប្រាស់កាលបរិច្ឆេទខ្មែរ
export function toKhmerNumeral(num: number | string): string {
const khmerDigits = [‘០’, ‘១’, ‘២’, ‘៣’, ‘៤’, ‘៥’, ‘៦’, ‘៧’, ‘៨’, ‘៩’];
return String(num).split(’’).map(d => khmerDigits[parseInt(d)] || d).join(’’);
}
export function getKhmerLunarDate(date: Date): {
month: string;
day: number;
phase: string;
} {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const a = Math.floor((14 - month) / 12);
const y = year + 4800 - a;
const m = month + 12 * a - 3;
const jd = day + Math.floor((153 * m + 2) / 5) + 365 * y +
Math.floor(y / 4) - Math.floor(y / 100) +
Math.floor(y / 400) - 32045;
const lunarMonths = [
‘មិគសិរ’, ‘បុស្ស’, ‘មាឃ’, ‘ផល្គុន’, ‘ចេត្រ’, ‘ពិសាខ’,
‘ជេស្ឋ’, ‘អាសាឍ’, ‘ស្រាពណ៍’, ‘ភទ្របទ’, ‘អស្សុជ’, ‘កក្ដិក’
];
const daysSinceEpoch = jd - 2451550.1;
const lunarCycle = 29.530588853;
const totalLunarDays = daysSinceEpoch % lunarCycle;
const lunarDay = Math.floor(totalLunarDays) + 1;
const totalLunarMonths = Math.floor(daysSinceEpoch / lunarCycle);
const monthIndex = totalLunarMonths % 12;
const lunarPhase = lunarDay <= 15 ? ‘កើត’ : ‘រោច’;
const displayDay = lunarDay <= 15 ? lunarDay : lunarDay - 15;
return {
month: lunarMonths[(monthIndex + 1) % 12],
day: Math.min(displayDay, 15),
phase: lunarPhase
};
}
export function formatKhmerDate(date: Date): string {
const year = date.getFullYear() + 543; // Buddhist Era
const khmerDays = [
‘អាទិត្យ’, ‘ច័ន្ទ’, ‘អង្គារ’, ‘ពុធ’, ‘ព្រហស្បតិ៍’, ‘សុក្រ’, ‘សៅរ៍’
];
const dayOfWeek = khmerDays[date.getDay()];
const lunar = getKhmerLunarDate(date);
const animalYears = [
‘ជូត’, ‘ឆ្លូវ’, ‘ខាល’, ‘ថោះ’, ‘រោង’, ‘ម្សាញ់’,
‘មមី’, ‘ម្ត’, ‘វក’, ‘រកា’, ‘ច’, ‘កុរ’
];
const animalIndex = (date.getFullYear() + 4) % 12;
const eraCycles = [
‘ឯកស័ក’, ‘ទោស័ក’, ‘ត្រីស័ក’, ‘ចត្វាស័ក’, ‘បញ្ចស័ក’,
‘ឆស័ក’, ‘សប្តស័ក’, ‘អដ្ឋស័ក’, ‘នព្វស័ក’, ‘សំរឹទ្ធិស័ក’
];
const eraIndex = (date.getFullYear() + 5) % 10;
const yearCycle = ${animalYears[animalIndex]} ${eraCycles[eraIndex]};
return ថ្ងៃ${dayOfWeek} ${toKhmerNumeral(lunar.day)}${lunar.phase} ខែ${lunar.month} ឆ្នាំ${yearCycle} ព.ស ${toKhmerNumeral(year)};
}
export function formatWesternDate(date: Date): string {
const day = date.getDate();
const month = date.getMonth() + 1;
const year = date.getFullYear();
const khmerMonths = [
‘មករា’, ‘កុម្ភៈ’, ‘មីនា’, ‘មេសា’, ‘ឧសភា’, ‘មិថុនា’,
‘កក្កដា’, ‘សីហា’, ‘កញ្ញា’, ‘តុលា’, ‘វិច្ឆិកា’, ‘ធ្នូ’
];
return ថ្ងៃទី${toKhmerNumeral(day)} ខែ${khmerMonths[month - 1]} ឆ្នាំ${toKhmerNumeral(year)};
}
// Format number to Khmer with commas
export function formatKhmerNumber(num: number): string {
const formatted = num.toLocaleString(‘en-US’);
return toKhmerNumeral(formatted);
}
// Convert number to Khmer words
export function numberToKhmerWords(num: number): string {
if (num === 0) return ‘សូន្យរៀលគត់’;
const ones = [’’, ‘មួយ’, ‘ពីរ’, ‘បី’, ‘បួន’, ‘ប្រាំ’, ‘ប្រាំមួយ’, ‘ប្រាំពីរ’, ‘ប្រាំបី’, ‘ប្រាំបួន’];
const tens = [’’, ‘ដប់’, ‘ម្ភៃ’, ‘សាមសិប’, ‘សែសិប’, ‘ហាសិប’, ‘ហុកសិប’, ‘ចិតសិប’, ‘ប៉ែតសិប’, ‘កៅសិប’];
function convertThreeDigits(n: number): string {
let result = ‘’;
const hundreds = Math.floor(n / 100);
if (hundreds > 0) {
result += ones[hundreds] + ‘រយ’;
n %= 100;
}
const tensDigit = Math.floor(n / 10);
if (tensDigit > 0) {
result += tens[tensDigit];
n %= 10;
}
if (n > 0) {
result += ones[n];
}
return result;
}
let result = ‘’;
let n = Math.floor(num);
const millions = Math.floor(n / 1000000);
if (millions > 0) {
result += convertThreeDigits(millions) + ‘លាន’;
n %= 1000000;
}
const thousands = Math.floor(n / 1000);
if (thousands > 0) {
result += convertThreeDigits(thousands) + ‘ពាន់’;
n %= 1000;
}
const hundreds = Math.floor(n / 100);
if (hundreds > 0) {
result += ones[hundreds] + ‘រយ’;
n %= 100;
}
const tensDigit = Math.floor(n / 10);
if (tensDigit > 0) {
result += tens[tensDigit];
n %= 10;
}
if (n > 0) {
result += ones[n];
}
return result + ‘រៀលគត់’;
}