2026-06-04 18:38:09 +00:00

590 lines
29 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import axios from 'axios'
import Head from 'next/head'
import React, { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
import LayoutAuthenticated from '../../layouts/Authenticated'
import SectionMain from '../../components/SectionMain'
import CardBox from '../../components/CardBox'
import BaseButton from '../../components/BaseButton'
import { getPageTitle } from '../../config'
import { useAppSelector } from '../../stores/hooks'
type Option = {
id: string
label?: string
name?: string
company_name?: string
name_ar?: string
name_en?: string
}
type Order = {
id: string
order_code?: string
customer_name?: string
customer_phone?: string
delivery_address?: string
package_description?: string
weight_size?: string
cod_amount?: string | number
current_status?: string
previous_status?: string
notes?: string
delivery_lat?: string | number
delivery_lng?: string | number
createdAt?: string
last_status_at?: string
merchant?: Option
delivery_city?: Option
delivery_region?: Option
assigned_driver?: Option
order_status_logs_order?: StatusLog[]
}
type StatusLog = {
id: string
from_status?: string
to_status?: string
comment?: string
changed_at?: string
createdAt?: string
}
type FormState = {
merchant: string
customer_name: string
customer_phone: string
delivery_city: string
delivery_region: string
delivery_address: string
package_description: string
weight_size: string
cod_amount: string
notes: string
}
const teal = '#01696f'
const statusMeta = {
pending: { label: 'قيد الانتظار', color: 'bg-slate-100 text-slate-700 ring-slate-200' },
assigned: { label: 'تم التعيين', color: 'bg-blue-100 text-blue-700 ring-blue-200' },
picked_up: { label: 'تم الاستلام', color: 'bg-yellow-100 text-yellow-800 ring-yellow-200' },
out_for_delivery: { label: 'في الطريق للتسليم', color: 'bg-orange-100 text-orange-700 ring-orange-200' },
delivered: { label: 'تم التسليم', color: 'bg-emerald-100 text-emerald-700 ring-emerald-200' },
failed_attempt: { label: 'محاولة فاشلة', color: 'bg-red-100 text-red-700 ring-red-200' },
rescheduled: { label: 'أعيدت الجدولة', color: 'bg-cyan-100 text-cyan-700 ring-cyan-200' },
cancelled: { label: 'ملغي', color: 'bg-zinc-200 text-zinc-800 ring-zinc-300' },
returned: { label: 'مرتجع', color: 'bg-purple-100 text-purple-700 ring-purple-200' },
}
const nextStatuses = {
pending: ['assigned', 'cancelled'],
assigned: ['picked_up'],
picked_up: ['out_for_delivery'],
out_for_delivery: ['delivered', 'failed_attempt'],
failed_attempt: ['rescheduled'],
rescheduled: ['out_for_delivery'],
delivered: [],
cancelled: [],
returned: [],
}
const emptyForm: FormState = {
merchant: '',
customer_name: '',
customer_phone: '',
delivery_city: '',
delivery_region: '',
delivery_address: '',
package_description: '',
weight_size: '',
cod_amount: '',
notes: '',
}
const labelFor = (status?: string) => statusMeta[status || 'pending']?.label || status || '—'
const badgeClass = (status?: string) =>
`inline-flex items-center rounded-full px-3 py-1 text-xs font-bold ring-1 ${
statusMeta[status || 'pending']?.color || statusMeta.pending.color
}`
const optionLabel = (option?: Option) =>
option?.company_name || option?.name_ar || option?.name || option?.label || option?.name_en || '—'
const todayIso = () => new Date().toISOString().slice(0, 10)
const OperationsOrdersPage = () => {
const { currentUser } = useAppSelector((state) => state.auth)
const [orders, setOrders] = useState<Order[]>([])
const [selectedOrder, setSelectedOrder] = useState<Order | null>(null)
const [merchants, setMerchants] = useState<Option[]>([])
const [drivers, setDrivers] = useState<Option[]>([])
const [cities, setCities] = useState<Option[]>([])
const [regions, setRegions] = useState<Option[]>([])
const [form, setForm] = useState<FormState>(emptyForm)
const [filters, setFilters] = useState({ query: '', status: '' })
const [nextStatus, setNextStatus] = useState('')
const [statusDriver, setStatusDriver] = useState('')
const [statusComment, setStatusComment] = useState('')
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [notice, setNotice] = useState<{ type: 'success' | 'error'; text: string } | null>(null)
const fetchOrders = useCallback(async () => {
const params: Record<string, string | number> = { page: 0, limit: 50, field: 'createdAt', sort: 'desc' }
if (filters.status) params.current_status = filters.status
if (filters.query) {
if (/^[0-9+\-\s]+$/.test(filters.query)) {
params.customer_phone = filters.query
} else {
params.order_code = filters.query
}
}
const { data } = await axios.get('orders', { params })
setOrders(Array.isArray(data.rows) ? data.rows : [])
}, [filters.query, filters.status])
const fetchLookups = useCallback(async () => {
const [merchantRes, driverRes, cityRes, regionRes] = await Promise.all([
axios.get('merchants', { params: { page: 0, limit: 100 } }),
axios.get('drivers', { params: { page: 0, limit: 100 } }),
axios.get('cities', { params: { page: 0, limit: 100 } }),
axios.get('regions', { params: { page: 0, limit: 100 } }),
])
setMerchants(merchantRes.data.rows || [])
setDrivers(driverRes.data.rows || [])
setCities(cityRes.data.rows || [])
setRegions(regionRes.data.rows || [])
}, [])
const refresh = useCallback(async () => {
setLoading(true)
try {
await Promise.all([fetchOrders(), fetchLookups()])
} catch (error) {
console.error('Failed to load parcel operations data', error)
setNotice({ type: 'error', text: 'تعذر تحميل بيانات العمليات. تحقق من الصلاحيات أو الاتصال.' })
} finally {
setLoading(false)
}
}, [fetchLookups, fetchOrders])
useEffect(() => {
if (!currentUser?.id) {
setLoading(false)
return undefined
}
refresh().catch((error) => {
console.error('Initial operations refresh failed', error)
})
const timer = window.setInterval(() => {
fetchOrders().catch((error) => {
console.error('Auto refresh failed', error)
})
}, 30000)
return () => window.clearInterval(timer)
}, [currentUser?.id, fetchOrders, refresh])
const selectedAllowedStatuses = useMemo(() => {
if (!selectedOrder) return []
return nextStatuses[selectedOrder.current_status || 'pending'] || []
}, [selectedOrder])
const stats = useMemo(() => {
const today = todayIso()
const todaysOrders = orders.filter((order) => order.createdAt?.slice(0, 10) === today)
return [
{ label: 'طلبات اليوم', value: todaysOrders.length, hint: 'تحديث تلقائي كل 30 ثانية' },
{ label: 'قيد الانتظار', value: orders.filter((order) => order.current_status === 'pending').length, hint: 'تحتاج إجراء' },
{ label: 'تم التسليم', value: orders.filter((order) => order.current_status === 'delivered').length, hint: 'طلبات مكتملة' },
{ label: 'فشل التسليم', value: orders.filter((order) => order.current_status === 'failed_attempt').length, hint: 'تحتاج متابعة' },
]
}, [orders])
const revenueToday = useMemo(
() =>
orders
.filter((order) => order.createdAt?.slice(0, 10) === todayIso())
.reduce((sum, order) => sum + Number(order.cod_amount || 0), 0),
[orders],
)
const handleFormChange = (field: keyof FormState, value: string) => {
setForm((current) => ({ ...current, [field]: value }))
}
const handleCreateOrder = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
if (!form.customer_name.trim() || !form.customer_phone.trim() || !form.delivery_address.trim()) {
setNotice({ type: 'error', text: 'اسم العميل، الهاتف، والعنوان حقول مطلوبة.' })
return
}
setSaving(true)
setNotice(null)
try {
const orderCode = `ORD-${Date.now().toString().slice(-8)}`
await axios.post('orders', {
data: {
...form,
order_code: orderCode,
current_status: 'pending',
previous_status: 'pending',
placed_at: new Date().toISOString(),
last_status_at: new Date().toISOString(),
merchant: form.merchant || null,
delivery_city: form.delivery_city || null,
delivery_region: form.delivery_region || null,
cod_amount: form.cod_amount || 0,
},
})
setForm(emptyForm)
await fetchOrders()
setNotice({ type: 'success', text: `تم إنشاء الطلب ${orderCode} وأصبح جاهزاً للتعيين.` })
} catch (error) {
console.error('Create order failed', error)
setNotice({ type: 'error', text: 'تعذر إنشاء الطلب. تأكد من البيانات والصلاحيات.' })
} finally {
setSaving(false)
}
}
const openOrder = async (order: Order) => {
setSelectedOrder(order)
setNextStatus('')
setStatusDriver(order.assigned_driver?.id || '')
setStatusComment('')
try {
const { data } = await axios.get(`orders/${order.id}`)
setSelectedOrder(data)
setStatusDriver(data?.assigned_driver?.id || '')
} catch (error) {
console.error('Load order detail failed', error)
setNotice({ type: 'error', text: 'تعذر فتح تفاصيل الطلب.' })
}
}
const handleStatusChange = async () => {
if (!selectedOrder || !nextStatus) {
setNotice({ type: 'error', text: 'اختر الحالة التالية أولاً.' })
return
}
if (nextStatus === 'assigned' && !statusDriver) {
setNotice({ type: 'error', text: 'يجب اختيار سائق قبل تحويل الطلب إلى تم التعيين.' })
return
}
setSaving(true)
setNotice(null)
try {
const { data } = await axios.put(`orders/${selectedOrder.id}/status`, {
data: {
current_status: nextStatus,
assigned_driver: statusDriver || undefined,
comment: statusComment,
},
})
setSelectedOrder(data)
setNextStatus('')
setStatusComment('')
await fetchOrders()
setNotice({ type: 'success', text: 'تم تحديث حالة الطلب وتسجيلها في السجل.' })
} catch (error) {
console.error('Status update failed', error)
setNotice({ type: 'error', text: 'رفض الخادم هذا الانتقال. تأكد من التسلسل والصلاحيات.' })
} finally {
setSaving(false)
}
}
const sortedLogs = [...(selectedOrder?.order_status_logs_order || [])].sort(
(a, b) => new Date(b.changed_at || b.createdAt || '').getTime() - new Date(a.changed_at || a.createdAt || '').getTime(),
)
return (
<>
<Head>
<title>{getPageTitle('Parcel Operations')}</title>
</Head>
<SectionMain>
<div dir="rtl" className="min-h-screen space-y-6 text-slate-900" style={{ fontFamily: 'Cairo, ui-sans-serif, system-ui' }}>
<section className="overflow-hidden rounded-[2rem] bg-gradient-to-br from-[#01696f] via-[#07575d] to-[#0f2730] p-6 text-white shadow-2xl shadow-teal-900/20 md:p-8">
<div className="grid gap-6 lg:grid-cols-[1.4fr_0.8fr]">
<div>
<div className="mb-5 inline-flex rounded-full border border-white/15 bg-white/10 px-4 py-2 text-sm font-semibold backdrop-blur">
مركز عمليات الطرود · [اسم شركتك]
</div>
<h1 className="text-3xl font-extrabold leading-tight md:text-5xl">إدارة طلبات التوصيل من الإدخال حتى تغيير الحالة</h1>
<p className="mt-4 max-w-2xl text-sm leading-7 text-teal-50 md:text-base">
شريحة تشغيلية أولى تربط إنشاء الطلب، قائمة الطلبات، تفاصيل الطلب، وتحديث الحالة وفق مسار العمل المعتمد وبصلاحيات الخادم.
</p>
<div className="mt-6 flex flex-wrap gap-3">
<BaseButton label="إنشاء طلب جديد" color="info" className="border-white/20 bg-white text-[#01696f] hover:bg-teal-50" onClick={() => document.getElementById('create-order-form')?.scrollIntoView({ behavior: 'smooth' })} />
<BaseButton label="العودة للوحة الإدارة" color="white" outline href="/dashboard" className="border-white/30 text-white hover:bg-white/10" />
</div>
</div>
<div className="rounded-3xl border border-white/10 bg-white/10 p-5 backdrop-blur">
<p className="text-sm text-teal-50">المستخدم الحالي</p>
<p className="mt-2 text-xl font-bold">{currentUser?.firstName || currentUser?.email || 'مشغل النظام'}</p>
<p className="text-sm text-teal-100">الدور: {currentUser?.app_role?.name || 'غير محدد'}</p>
<div className="mt-6 rounded-2xl bg-white p-4 text-slate-900">
<p className="text-sm text-slate-500">تحصيل اليوم COD</p>
<p className="mt-1 text-3xl font-extrabold" style={{ color: teal }}>{revenueToday.toLocaleString()} د.أ</p>
</div>
</div>
</div>
</section>
{notice && (
<div className={`rounded-2xl border px-4 py-3 text-sm font-semibold ${notice.type === 'success' ? 'border-emerald-200 bg-emerald-50 text-emerald-800' : 'border-red-200 bg-red-50 text-red-800'}`}>
{notice.text}
</div>
)}
<div className="grid gap-4 md:grid-cols-4">
{stats.map((stat) => (
<CardBox key={stat.label} className="border-0 bg-white shadow-sm ring-1 ring-slate-100">
<p className="text-sm font-semibold text-slate-500">{stat.label}</p>
<p className="mt-2 text-3xl font-extrabold" style={{ color: teal }}>{loading ? '…' : stat.value}</p>
<p className="mt-2 text-xs text-slate-400">{stat.hint}</p>
</CardBox>
))}
</div>
<div className="grid gap-6 xl:grid-cols-[0.95fr_1.35fr]">
<CardBox className="border-0 bg-white shadow-sm ring-1 ring-slate-100">
<div id="create-order-form" className="mb-5 flex items-center justify-between gap-3">
<div>
<p className="text-sm font-bold text-teal-700">طلب جديد</p>
<h2 className="text-2xl font-extrabold text-slate-900">إدخال طلب يدوي</h2>
</div>
<span className="rounded-full bg-teal-50 px-3 py-1 text-xs font-bold text-teal-700">الحالة الافتراضية: قيد الانتظار</span>
</div>
<form className="grid gap-4" onSubmit={handleCreateOrder}>
<div className="grid gap-3 md:grid-cols-2">
<FieldLabel label="التاجر">
<select className="ops-input" value={form.merchant} onChange={(event) => handleFormChange('merchant', event.target.value)}>
<option value="">بدون تاجر</option>
{merchants.map((merchant) => <option key={merchant.id} value={merchant.id}>{optionLabel(merchant)}</option>)}
</select>
</FieldLabel>
<FieldLabel label="قيمة التحصيل COD">
<input className="ops-input" inputMode="decimal" value={form.cod_amount} onChange={(event) => handleFormChange('cod_amount', event.target.value)} placeholder="0.00" />
</FieldLabel>
</div>
<div className="grid gap-3 md:grid-cols-2">
<FieldLabel label="اسم العميل *">
<input className="ops-input" value={form.customer_name} onChange={(event) => handleFormChange('customer_name', event.target.value)} placeholder="مثال: أحمد محمد" />
</FieldLabel>
<FieldLabel label="هاتف العميل *">
<input className="ops-input" value={form.customer_phone} onChange={(event) => handleFormChange('customer_phone', event.target.value)} placeholder="07xxxxxxxx" />
</FieldLabel>
</div>
<div className="grid gap-3 md:grid-cols-2">
<FieldLabel label="المدينة">
<select className="ops-input" value={form.delivery_city} onChange={(event) => handleFormChange('delivery_city', event.target.value)}>
<option value="">اختر المدينة</option>
{cities.map((city) => <option key={city.id} value={city.id}>{optionLabel(city)}</option>)}
</select>
</FieldLabel>
<FieldLabel label="المنطقة">
<select className="ops-input" value={form.delivery_region} onChange={(event) => handleFormChange('delivery_region', event.target.value)}>
<option value="">اختر المنطقة</option>
{regions.map((region) => <option key={region.id} value={region.id}>{optionLabel(region)}</option>)}
</select>
</FieldLabel>
</div>
<FieldLabel label="العنوان الكامل *">
<textarea className="ops-input min-h-20" value={form.delivery_address} onChange={(event) => handleFormChange('delivery_address', event.target.value)} placeholder="الحي، الشارع، أقرب معلم" />
</FieldLabel>
<div className="grid gap-3 md:grid-cols-2">
<FieldLabel label="وصف الطرد">
<input className="ops-input" value={form.package_description} onChange={(event) => handleFormChange('package_description', event.target.value)} placeholder="ملابس، مستندات، إلكترونيات..." />
</FieldLabel>
<FieldLabel label="الوزن/الحجم">
<input className="ops-input" value={form.weight_size} onChange={(event) => handleFormChange('weight_size', event.target.value)} placeholder="اختياري" />
</FieldLabel>
</div>
<FieldLabel label="ملاحظات داخلية">
<input className="ops-input" value={form.notes} onChange={(event) => handleFormChange('notes', event.target.value)} placeholder="أي تعليمات خاصة للتسليم" />
</FieldLabel>
<BaseButton type="submit" color="info" label={saving ? 'جارٍ الحفظ...' : 'إنشاء الطلب'} disabled={saving} className="w-full border-[#01696f] bg-[#01696f] py-3 text-white hover:bg-[#07575d]" />
</form>
</CardBox>
<CardBox className="border-0 bg-white shadow-sm ring-1 ring-slate-100" hasComponentLayout>
<div className="border-b border-slate-100 p-5">
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div>
<p className="text-sm font-bold text-teal-700">قائمة التشغيل</p>
<h2 className="text-2xl font-extrabold">آخر 50 طلب</h2>
</div>
<div className="flex gap-2">
<input className="ops-input w-full md:w-56" value={filters.query} onChange={(event) => setFilters((current) => ({ ...current, query: event.target.value }))} placeholder="بحث برقم الطلب أو الهاتف" />
<select className="ops-input w-40" value={filters.status} onChange={(event) => setFilters((current) => ({ ...current, status: event.target.value }))}>
<option value="">كل الحالات</option>
{Object.entries(statusMeta).map(([value, meta]) => <option key={value} value={value}>{meta.label}</option>)}
</select>
</div>
</div>
</div>
<div className="overflow-x-auto">
<table className="min-w-full text-right text-sm">
<thead className="bg-slate-50 text-xs font-bold text-slate-500">
<tr>
<th className="px-4 py-3">رقم الطلب</th>
<th className="px-4 py-3">العميل</th>
<th className="px-4 py-3">التاجر</th>
<th className="px-4 py-3">السائق</th>
<th className="px-4 py-3">الحالة</th>
<th className="px-4 py-3">COD</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{orders.map((order) => (
<tr key={order.id} className="cursor-pointer transition hover:bg-teal-50/60" onClick={() => openOrder(order)}>
<td className="px-4 py-4 font-extrabold text-teal-700">{order.order_code || order.id.slice(0, 8)}</td>
<td className="px-4 py-4"><p className="font-bold">{order.customer_name || '—'}</p><p className="text-xs text-slate-400">{order.customer_phone || '—'}</p></td>
<td className="px-4 py-4 text-slate-600">{optionLabel(order.merchant)}</td>
<td className="px-4 py-4 text-slate-600">{optionLabel(order.assigned_driver)}</td>
<td className="px-4 py-4"><span className={badgeClass(order.current_status)}>{labelFor(order.current_status)}</span></td>
<td className="px-4 py-4 font-bold">{Number(order.cod_amount || 0).toLocaleString()}</td>
</tr>
))}
{!orders.length && (
<tr>
<td colSpan={6} className="px-4 py-12 text-center text-slate-500">
لا توجد طلبات مطابقة حالياً. ابدأ بإنشاء أول طلب من النموذج المجاور.
</td>
</tr>
)}
</tbody>
</table>
</div>
</CardBox>
</div>
<CardBox className="border-0 bg-white shadow-sm ring-1 ring-slate-100">
{!selectedOrder ? (
<div className="rounded-3xl border border-dashed border-slate-200 bg-slate-50 p-10 text-center">
<p className="text-xl font-extrabold">اختر طلباً من القائمة</p>
<p className="mt-2 text-sm text-slate-500">ستظهر هنا تفاصيل الطلب، الإجراءات التالية المسموحة، وسجل الحالات.</p>
</div>
) : (
<div className="grid gap-6 lg:grid-cols-[1fr_0.9fr]">
<div>
<div className="flex flex-wrap items-start justify-between gap-3">
<div>
<p className="text-sm font-bold text-teal-700">تفاصيل الطلب</p>
<h2 className="text-3xl font-extrabold">{selectedOrder.order_code || selectedOrder.id.slice(0, 8)}</h2>
</div>
<span className={badgeClass(selectedOrder.current_status)}>{labelFor(selectedOrder.current_status)}</span>
</div>
<div className="mt-5 grid gap-3 md:grid-cols-2">
<Info label="العميل" value={selectedOrder.customer_name} />
<Info label="الهاتف" value={selectedOrder.customer_phone} />
<Info label="التاجر" value={optionLabel(selectedOrder.merchant)} />
<Info label="السائق" value={optionLabel(selectedOrder.assigned_driver)} />
<Info label="المدينة" value={optionLabel(selectedOrder.delivery_city)} />
<Info label="المنطقة" value={optionLabel(selectedOrder.delivery_region)} />
<Info label="العنوان" value={selectedOrder.delivery_address} wide />
<Info label="وصف الطرد" value={selectedOrder.package_description} />
<Info label="COD" value={`${Number(selectedOrder.cod_amount || 0).toLocaleString()} د.أ`} />
</div>
</div>
<div className="rounded-3xl bg-slate-50 p-5">
<h3 className="text-xl font-extrabold">تغيير الحالة</h3>
<p className="mt-1 text-sm text-slate-500">الواجهة تعرض فقط الانتقالات التالية، والخادم يرفض أي انتقال غير مسموح.</p>
<div className="mt-4 grid gap-3">
<FieldLabel label="الحالة التالية">
<select className="ops-input bg-white" value={nextStatus} onChange={(event) => setNextStatus(event.target.value)}>
<option value="">اختر الإجراء التالي</option>
{selectedAllowedStatuses.map((status) => <option key={status} value={status}>{labelFor(status)}</option>)}
</select>
</FieldLabel>
{nextStatus === 'assigned' && (
<FieldLabel label="السائق المعيّن *">
<select className="ops-input bg-white" value={statusDriver} onChange={(event) => setStatusDriver(event.target.value)}>
<option value="">اختر السائق</option>
{drivers.map((driver) => <option key={driver.id} value={driver.id}>{optionLabel(driver)}</option>)}
</select>
</FieldLabel>
)}
<FieldLabel label="تعليق على الحركة">
<textarea className="ops-input min-h-20 bg-white" value={statusComment} onChange={(event) => setStatusComment(event.target.value)} placeholder="مثال: تم التواصل مع العميل وتأكيد الموعد" />
</FieldLabel>
<BaseButton color="info" label={saving ? 'جارٍ التحديث...' : 'تطبيق تغيير الحالة'} disabled={saving || !selectedAllowedStatuses.length} onClick={handleStatusChange} className="border-[#01696f] bg-[#01696f] py-3 text-white hover:bg-[#07575d]" />
{!selectedAllowedStatuses.length && <p className="text-sm font-semibold text-slate-500">هذه حالة نهائية ولا توجد انتقالات متاحة.</p>}
</div>
</div>
<div className="lg:col-span-2">
<h3 className="mb-4 text-xl font-extrabold">سجل الحالات</h3>
<div className="space-y-3">
{sortedLogs.map((log) => (
<div key={log.id} className="rounded-2xl border border-slate-100 bg-white p-4 shadow-sm">
<div className="flex flex-wrap items-center gap-2 text-sm font-bold">
<span className={badgeClass(log.from_status)}>{labelFor(log.from_status)}</span>
<span className="text-slate-300"></span>
<span className={badgeClass(log.to_status)}>{labelFor(log.to_status)}</span>
<span className="mr-auto text-xs text-slate-400">{new Date(log.changed_at || log.createdAt || '').toLocaleString('ar')}</span>
</div>
{log.comment && <p className="mt-2 text-sm text-slate-600">{log.comment}</p>}
</div>
))}
{!sortedLogs.length && <p className="rounded-2xl bg-slate-50 p-5 text-center text-sm text-slate-500">لا توجد حركات مسجلة بعد. أول تغيير حالة سيظهر هنا.</p>}
</div>
</div>
</div>
)}
</CardBox>
</div>
<style jsx global>{`
.ops-input {
width: 100%;
border-radius: 1rem;
border: 1px solid rgb(226 232 240);
background: rgb(248 250 252);
padding: 0.75rem 1rem;
font-size: 0.875rem;
font-weight: 600;
color: rgb(15 23 42);
outline: none;
transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease;
}
.ops-input:focus {
border-color: #01696f;
background: white;
box-shadow: 0 0 0 3px rgba(1, 105, 111, 0.14);
}
`}</style>
</SectionMain>
</>
)
}
const FieldLabel = ({ label, children }: { label: string; children: React.ReactNode }) => (
<label className="block">
<span className="mb-2 block text-sm font-extrabold text-slate-700">{label}</span>
{children}
</label>
)
const Info = ({ label, value, wide }: { label: string; value?: string | number; wide?: boolean }) => (
<div className={`rounded-2xl bg-slate-50 p-4 ${wide ? 'md:col-span-2' : ''}`}>
<p className="text-xs font-bold text-slate-400">{label}</p>
<p className="mt-1 font-extrabold text-slate-800">{value || '—'}</p>
</div>
)
OperationsOrdersPage.getLayout = function getLayout(page: ReactElement) {
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
}
export default OperationsOrdersPage