diff --git a/src/lib/statusHelpers.ts b/src/lib/statusHelpers.ts
new file mode 100644
index 0000000..1b98912
--- /dev/null
+++ b/src/lib/statusHelpers.ts
@@ -0,0 +1,124 @@
+/**
+ * Centralized status management for consistent labels, colors, and badges
+ * Single source of truth for all status-related UI
+ */
+
+export type PaymentStatus = 'paid' | 'pending' | 'failed' | 'cancelled' | 'refunded' | 'partially_refunded';
+export type ConsultingSlotStatus = 'pending_payment' | 'confirmed' | 'completed' | 'cancelled';
+
+/**
+ * Get Indonesian label for payment status
+ */
+export const getPaymentStatusLabel = (status: PaymentStatus | string | null): string => {
+ switch (status) {
+ case 'paid':
+ return 'Lunas';
+ case 'pending':
+ return 'Pending';
+ case 'failed':
+ return 'Gagal';
+ case 'cancelled':
+ return 'Dibatalkan';
+ case 'refunded':
+ return 'Refund';
+ case 'partially_refunded':
+ return 'Refund Sebagian';
+ default:
+ return status || 'Pending';
+ }
+};
+
+/**
+ * Get CSS class for payment status badge
+ */
+export const getPaymentStatusColor = (status: PaymentStatus | string | null): string => {
+ switch (status) {
+ case 'paid':
+ return 'bg-brand-accent text-white';
+ case 'pending':
+ return 'bg-amber-500 text-white';
+ case 'failed':
+ return 'bg-red-500 text-white';
+ case 'cancelled':
+ return 'bg-destructive text-white';
+ case 'refunded':
+ return 'bg-purple-500 text-white';
+ case 'partially_refunded':
+ return 'bg-purple-500/80 text-white';
+ default:
+ return 'bg-secondary text-primary';
+ }
+};
+
+/**
+ * Get label for consulting slot status
+ */
+export const getConsultingSlotStatusLabel = (status: ConsultingSlotStatus | string): string => {
+ switch (status) {
+ case 'pending_payment':
+ return 'Menunggu Pembayaran';
+ case 'confirmed':
+ return 'Terkonfirmasi';
+ case 'completed':
+ return 'Selesai';
+ case 'cancelled':
+ return 'Dibatalkan';
+ default:
+ return status;
+ }
+};
+
+/**
+ * Get CSS class for consulting slot status badge
+ */
+export const getConsultingSlotStatusColor = (status: ConsultingSlotStatus | string): string => {
+ switch (status) {
+ case 'pending_payment':
+ return 'bg-amber-500 text-white';
+ case 'confirmed':
+ return 'bg-green-500 text-white';
+ case 'completed':
+ return 'bg-blue-500 text-white';
+ case 'cancelled':
+ return 'bg-destructive text-white';
+ default:
+ return 'bg-secondary text-primary';
+ }
+};
+
+/**
+ * Get label for product type
+ */
+export const getProductTypeLabel = (type: string): string => {
+ switch (type) {
+ case 'consulting':
+ return 'Konsultasi';
+ case 'webinar':
+ return 'Webinar';
+ case 'bootcamp':
+ return 'Bootcamp';
+ default:
+ return type;
+ }
+};
+
+/**
+ * Check if order can be refunded
+ */
+export const canRefundOrder = (paymentStatus: PaymentStatus | string | null, refundedAt: string | null = null): boolean => {
+ return paymentStatus === 'paid' && !refundedAt;
+};
+
+/**
+ * Check if order can be cancelled
+ */
+export const canCancelOrder = (paymentStatus: PaymentStatus | string | null, refundedAt: string | null = null): boolean => {
+ return !refundedAt && paymentStatus !== 'cancelled' && paymentStatus !== 'refunded' && paymentStatus !== 'partially_refunded';
+};
+
+/**
+ * Check if order can be marked as paid
+ */
+export const canMarkAsPaid = (paymentStatus: PaymentStatus | string | null, refundedAt: string | null = null): boolean => {
+ return !refundedAt && paymentStatus !== 'paid' && paymentStatus !== 'refunded' && paymentStatus !== 'partially_refunded';
+};
diff --git a/src/pages/admin/AdminOrders.tsx b/src/pages/admin/AdminOrders.tsx
index 8d2c64c..5eabdeb 100644
--- a/src/pages/admin/AdminOrders.tsx
+++ b/src/pages/admin/AdminOrders.tsx
@@ -15,6 +15,7 @@ import { Textarea } from "@/components/ui/textarea";
import { formatIDR, formatDateTime } from "@/lib/format";
import { Eye, CheckCircle, XCircle, Video, ExternalLink, Trash2, AlertTriangle, RefreshCw, Link as LinkIcon } from "lucide-react";
import { toast } from "@/hooks/use-toast";
+import { getPaymentStatusLabel, getPaymentStatusColor, canRefundOrder, canCancelOrder, canMarkAsPaid } from "@/lib/statusHelpers";
interface Order {
id: string;
@@ -272,20 +273,11 @@ export default function AdminOrders() {
};
const getStatusBadge = (status: string | null) => {
- switch (status) {
- case "paid":
- return