Files
SMATEC-FRONTEND/src/pages/Manager/Device/Camera/components/ConfigCameraV6.tsx

277 lines
8.2 KiB
TypeScript

import { apiQueryConfigAlarm } from '@/services/master/MessageController';
import { useIntl, useModel } from '@umijs/max';
import {
Button,
Card,
Col,
Row,
Select,
theme,
Tooltip,
Typography,
} from 'antd';
import { useEffect, useMemo, useState } from 'react';
const { Text } = Typography;
interface CameraV6Props {
thing: MasterModel.Thing | null;
cameraConfig?: MasterModel.CameraV6 | null;
onSubmit?: (config: MasterModel.CameraV6) => void;
isOnline?: boolean;
}
const CameraV6: React.FC<CameraV6Props> = ({
thing,
cameraConfig,
onSubmit,
isOnline = false,
}) => {
const { token } = theme.useToken();
const intl = useIntl();
const { initialState } = useModel('@@initialState');
const [selectedAlerts, setSelectedAlerts] = useState<string[]>([]);
const [recordingMode, setRecordingMode] =
useState<MasterModel.CameraRecordType>('none');
const [alarmConfig, setAlarmConfig] = useState<MasterModel.Alarm[] | null>(
null,
);
// Recording modes for V6 - using useMemo for i18n
const RECORDING_MODES = useMemo(
() => [
{
label: intl.formatMessage({
id: 'master.devices.camera.config.recordingMode.none',
defaultMessage: 'Không ghi',
}),
value: 'none',
},
{
label: intl.formatMessage({
id: 'master.devices.camera.config.recordingMode.alarm',
defaultMessage: 'Theo cảnh báo',
}),
value: 'alarm',
},
{
label: intl.formatMessage({
id: 'master.devices.camera.config.recordingMode.all',
defaultMessage: '24/24',
}),
value: 'all',
},
],
[intl],
);
// Initialize states from cameraConfig when it's available
useEffect(() => {
if (cameraConfig) {
// Set recording mode from config
if (cameraConfig.record_type) {
setRecordingMode(cameraConfig.record_type);
}
// Set selected alerts from config
if (
cameraConfig.record_alarm_list &&
Array.isArray(cameraConfig.record_alarm_list)
) {
setSelectedAlerts(cameraConfig.record_alarm_list);
}
}
}, [cameraConfig]);
// Fetch alarm config when thing data is available and recording mode is 'alarm'
useEffect(() => {
const fetchAlarmConfig = async () => {
if (
!thing ||
!initialState?.currentUserProfile?.metadata?.frontend_thing_key ||
recordingMode !== 'alarm'
) {
return;
}
try {
const resp = await apiQueryConfigAlarm(
thing.metadata?.data_channel_id || '',
initialState.currentUserProfile.metadata.frontend_thing_key,
{
offset: 0,
limit: 1,
subtopic: `config.${thing.metadata?.type}.alarms`,
},
);
if (resp.messages && resp.messages.length > 0) {
const parsed = resp.messages[0].string_value_parsed;
if (Array.isArray(parsed)) {
setAlarmConfig(parsed as MasterModel.Alarm[]);
} else {
setAlarmConfig([]);
}
}
} catch (error) {
console.error('Failed to fetch alarm config:', error);
}
};
fetchAlarmConfig();
}, [thing, initialState, recordingMode]);
const handleAlertToggle = (alertId: string) => {
if (selectedAlerts.includes(alertId)) {
setSelectedAlerts(selectedAlerts.filter((id) => id !== alertId));
} else {
setSelectedAlerts([...selectedAlerts, alertId]);
}
};
const handleClearAlerts = () => {
setSelectedAlerts([]);
};
const handleSubmitConfig = () => {
onSubmit?.({
...cameraConfig,
record_type: recordingMode,
record_alarm_list: recordingMode === 'alarm' ? selectedAlerts : [],
});
};
return (
<Card className="p-4">
{/* Recording Mode */}
<div className="mb-6">
<div className="flex flex-col sm:flex-row gap-2 sm:gap-4 items-start sm:items-end">
<div className="w-full sm:w-1/3 lg:w-1/4">
<Text strong className="block mb-2">
{intl.formatMessage({
id: 'master.devices.camera.config.recording',
defaultMessage: 'Ghi dữ liệu camera',
})}
</Text>
<Select
value={recordingMode}
onChange={setRecordingMode}
options={RECORDING_MODES}
className="w-full"
popupMatchSelectWidth={false}
/>
</div>
<Tooltip
title={
!isOnline
? intl.formatMessage({
id: 'master.devices.camera.table.offline.tooltip',
defaultMessage: 'Thiết bị đang ngoại tuyến',
})
: ''
}
>
<Button
type="primary"
onClick={handleSubmitConfig}
disabled={!isOnline}
>
{intl.formatMessage({
id: 'master.devices.camera.config.send',
defaultMessage: 'Gửi đi',
})}
</Button>
</Tooltip>
</div>
</div>
{/* Alert List - Only show when mode is 'alarm' */}
{recordingMode === 'alarm' && (
<div>
<Text strong className="block mb-2">
{intl.formatMessage({
id: 'master.devices.camera.config.alarmList',
defaultMessage: 'Danh sách cảnh báo',
})}
</Text>
<div
className="flex justify-between items-center mb-4 px-3 py-2 rounded border"
style={{
background: token.colorBgContainer,
borderColor: token.colorBorder,
}}
>
<Text type="secondary">
{intl.formatMessage(
{
id: 'master.devices.camera.config.selected',
defaultMessage: 'đã chọn {0} mục',
},
{
0: selectedAlerts.length,
},
)}
</Text>
<Button type="link" onClick={handleClearAlerts}>
{intl.formatMessage({
id: 'master.devices.camera.config.clear',
defaultMessage: 'Xóa',
})}
</Button>
</div>
{/* Alert Cards Grid */}
<Row gutter={[12, 12]}>
{alarmConfig?.map((alarm) => {
const alarmId = alarm.id ?? '';
const isSelected =
alarmId !== '' && selectedAlerts.includes(alarmId);
return (
<Col xs={12} sm={8} md={6} lg={4} xl={4} key={alarmId}>
<Card
size="small"
hoverable
onClick={() => handleAlertToggle(alarmId)}
className="cursor-pointer h-24 flex items-center justify-center"
style={{
borderColor: isSelected
? token.colorPrimary
: token.colorBorder,
borderWidth: isSelected ? 2 : 1,
background: isSelected
? token.colorPrimaryBg
: token.colorBgContainer,
}}
>
<div className="p-1 text-center w-full flex items-center justify-center h-full">
<Text
className="text-xs"
style={{
color: isSelected
? token.colorPrimary
: token.colorText,
display: '-webkit-box',
WebkitLineClamp: 3,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
wordBreak: 'break-word',
lineHeight: '1.2em',
}}
title={alarm.name}
>
{alarm.name}
</Text>
</div>
</Card>
</Col>
);
})}
</Row>
</div>
)}
</Card>
);
};
export default CameraV6;