120 lines
4.5 KiB
TypeScript
120 lines
4.5 KiB
TypeScript
import { formatValue } from '../../lib/format';
|
|
import { User as UserIcon, Clock, LogOut } from 'lucide-react';
|
|
import { Button } from '../buttons/Button';
|
|
import './card.css';
|
|
|
|
export function DailyLogCard({ row, onPunchOut }: { row: Record<string, any>; onPunchOut?: (row: any) => void }) {
|
|
const stateName = String(row.status || row.current_state_name || 'Logged');
|
|
|
|
// User details
|
|
const userObj = (row.sales_officer_name || row.user_id || row.user) as Record<string, any> | null;
|
|
const userName = formatValue(userObj?.name || row.user_name || 'Unknown User');
|
|
|
|
// Date
|
|
let dateStr = '—';
|
|
if (row.date) dateStr = String(row.date).split('T')[0];
|
|
else if (row.created_at) dateStr = String(row.created_at).split('T')[0];
|
|
|
|
try {
|
|
if (dateStr !== '—') {
|
|
const d = new Date(dateStr);
|
|
if (!isNaN(d.getTime())) {
|
|
dateStr = d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }).replace(/ /g, '-');
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
|
|
const formatTimeStr = (t: string | undefined | null) => {
|
|
if (!t) return '—';
|
|
const str = String(t);
|
|
if (str === '—') return str;
|
|
try {
|
|
let timeParts = str.split(':');
|
|
if (str.includes('T')) {
|
|
timeParts = str.split('T')[1].replace('Z', '').split('.')[0].split(':');
|
|
}
|
|
if (timeParts.length >= 2) {
|
|
let h = parseInt(timeParts[0]);
|
|
const m = timeParts[1];
|
|
const ampm = h >= 12 ? 'PM' : 'AM';
|
|
h = h % 12;
|
|
h = h ? h : 12;
|
|
const hStr = h < 10 ? '0' + h : h;
|
|
return `${hStr}:${m} ${ampm}`;
|
|
}
|
|
return str.split('.')[0];
|
|
} catch {
|
|
return str.split('.')[0];
|
|
}
|
|
};
|
|
|
|
// Checkin time
|
|
let checkInTimeRaw = row.time || row.created_at || row.punch_in_time;
|
|
let checkInTime = formatTimeStr(checkInTimeRaw);
|
|
|
|
// Check out time
|
|
let checkOutTimeRaw = row.check_out_time || row.punch_out_time;
|
|
if (!checkOutTimeRaw && stateName.toLowerCase().includes('out') && row.time) {
|
|
checkOutTimeRaw = row.time;
|
|
}
|
|
let checkOutTime = checkOutTimeRaw ? formatTimeStr(checkOutTimeRaw) : null;
|
|
|
|
const isPunchedIn = stateName.toLowerCase().includes('in') || stateName.toLowerCase().includes('active');
|
|
|
|
let statusClass = 'z-card-status--neutral';
|
|
if (isPunchedIn) statusClass = 'z-card-status--success';
|
|
else if (stateName.toLowerCase().includes('out')) statusClass = 'z-card-status--primary';
|
|
|
|
return (
|
|
<div className="z-card">
|
|
<div className="z-card-header">
|
|
<div className="z-card-header-icon">
|
|
<UserIcon size={16} className="text-strong" />
|
|
<span className="text-strong">{userName}</span>
|
|
</div>
|
|
<span className={`z-card-status ${statusClass}`}>
|
|
{stateName}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="z-card-divider"></div>
|
|
|
|
<div className="z-card-footer" style={{ flexDirection: 'column', alignItems: 'flex-start', gap: '16px' }}>
|
|
<div className="z-card-footer-item" style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: '4px' }}>
|
|
<div className="z-card-footer-label" style={{ whiteSpace: 'nowrap' }}><Clock size={14} /> PUNCH-IN TIME</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap' }}>
|
|
<span className="z-card-footer-value" style={{ display: 'flex', gap: '6px', fontSize: '13px' }}>
|
|
<span>{dateStr}</span>
|
|
<span>{checkInTime}</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{checkOutTime && (
|
|
<div className="z-card-footer-item" style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: '4px' }}>
|
|
<div className="z-card-footer-label" style={{ whiteSpace: 'nowrap' }}><Clock size={14} /> PUNCH-OUT TIME</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap' }}>
|
|
<span className="z-card-footer-value" style={{ display: 'flex', gap: '6px', fontSize: '13px' }}>
|
|
<span>{dateStr}</span>
|
|
<span>{checkOutTime}</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{isPunchedIn && onPunchOut && (
|
|
<div style={{ padding: '0 6px 0px 6px' }}>
|
|
<Button
|
|
variant="danger"
|
|
full
|
|
iconLeft={<LogOut size={16} />}
|
|
onClick={(e) => { e.stopPropagation(); onPunchOut(row); }}
|
|
>
|
|
Punch Out
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|