diff --git a/.umirc.gms.ts b/.umirc.gms.ts index 23225da..a226861 100644 --- a/.umirc.gms.ts +++ b/.umirc.gms.ts @@ -3,6 +3,7 @@ import { alarmsRoute, commonManagerRoutes, loginRoute, + managerCameraRoute, managerRouteBase, notFoundRoute, profileRoute, @@ -25,7 +26,7 @@ export default defineConfig({ }, { ...managerRouteBase, - routes: [...commonManagerRoutes], + routes: [...commonManagerRoutes, managerCameraRoute], }, { path: '/', diff --git a/config/routes.ts b/config/routes.ts index 48fa513..5a7c959 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -80,6 +80,11 @@ export const commonManagerRoutes = [ }, ]; +export const managerCameraRoute = { + path: '/manager/devices/:thingId/camera', + component: './Manager/Device/Camera', +}; + export const managerRouteBase = { name: 'manager', icon: 'icon-setting', diff --git a/src/locales/en-US/master/master-thing-en.ts b/src/locales/en-US/master/master-thing-en.ts index d4e4306..886df79 100644 --- a/src/locales/en-US/master/master-thing-en.ts +++ b/src/locales/en-US/master/master-thing-en.ts @@ -21,6 +21,9 @@ export default { 'master.devices.create.error': 'Device creation failed', 'master.devices.groups': 'Groups', 'master.devices.groups.required': 'Please select groups', + // Update info device + 'master.devices.update.success': 'Updated successfully', + 'master.devices.update.error': 'Update failed', // Edit device modal 'master.devices.update.title': 'Update device', 'master.devices.ok': 'OK', @@ -32,4 +35,13 @@ export default { 'master.devices.address': 'Address', 'master.devices.address.placeholder': 'Enter address', 'master.devices.address.required': 'Please enter address', + // Location modal + 'master.devices.location.title': 'Update location', + 'master.devices.location.latitude': 'Latitude', + 'master.devices.location.latitude.required': 'Please enter latitude', + 'master.devices.location.longitude': 'Longitude', + 'master.devices.location.longitude.required': 'Please enter longitude', + 'master.devices.location.placeholder': 'Enter data', + 'master.devices.location.update.success': 'Location updated successfully', + 'master.devices.location.update.error': 'Location update failed', }; diff --git a/src/locales/vi-VN/master/master-thing-vi.ts b/src/locales/vi-VN/master/master-thing-vi.ts index 8361fc3..13acb7d 100644 --- a/src/locales/vi-VN/master/master-thing-vi.ts +++ b/src/locales/vi-VN/master/master-thing-vi.ts @@ -3,6 +3,7 @@ export default { 'master.thing.external_id': 'External ID', 'master.thing.group': 'Nhóm', 'master.thing.address': 'Địa chỉ', + // Device translations 'master.devices.title': 'Quản lý thiết bị', 'master.devices.name': 'Tên', @@ -20,6 +21,9 @@ export default { 'master.devices.create.error': 'Tạo thiết bị lỗi', 'master.devices.groups': 'Đơn vị', 'master.devices.groups.required': 'Vui lòng chọn đơn vị', + // Update info device + 'master.devices.update.success': 'Cập nhật thành công', + 'master.devices.update.error': 'Cập nhật thất bại', // Edit device modal 'master.devices.update.title': 'Cập nhật thiết bị', 'master.devices.ok': 'Đồng ý', @@ -31,4 +35,13 @@ export default { 'master.devices.address': 'Địa chỉ', 'master.devices.address.placeholder': 'Nhập địa chỉ', 'master.devices.address.required': 'Vui lòng nhập địa chỉ', + // Location modal + 'master.devices.location.title': 'Cập nhật vị trí', + 'master.devices.location.latitude': 'Vị độ', + 'master.devices.location.latitude.required': 'Vui lòng nhập vị độ', + 'master.devices.location.longitude': 'Kinh độ', + 'master.devices.location.longitude.required': 'Vui lòng nhập kinh độ', + 'master.devices.location.placeholder': 'Nhập dữ liệu', + 'master.devices.location.update.success': 'Cập nhật vị trí thành công', + 'master.devices.location.update.error': 'Cập nhật vị trí thất bại', }; diff --git a/src/pages/Manager/Device/Camera/index.tsx b/src/pages/Manager/Device/Camera/index.tsx new file mode 100644 index 0000000..4d21199 --- /dev/null +++ b/src/pages/Manager/Device/Camera/index.tsx @@ -0,0 +1,483 @@ +import { apiSearchThings } from '@/services/master/ThingController'; +import { + ArrowLeftOutlined, + DeleteOutlined, + EditOutlined, + PlusOutlined, + ReloadOutlined, + SettingOutlined, +} from '@ant-design/icons'; +import { PageContainer } from '@ant-design/pro-components'; +import { history, useParams } from '@umijs/max'; +import { + Button, + Card, + Checkbox, + Col, + Form, + Input, + InputNumber, + Modal, + Row, + Select, + Space, + Spin, + Table, + Typography, +} from 'antd'; +import { useEffect, useState } from 'react'; + +const { Text } = Typography; + +// Camera types +const CAMERA_TYPES = [ + { label: 'HIKVISION', value: 'HIKVISION' }, + { label: 'DAHUA', value: 'DAHUA' }, + { label: 'GENERIC', value: 'GENERIC' }, +]; + +// Recording modes +const RECORDING_MODES = [ + { label: 'Theo cảnh báo', value: 'alarm' }, + { label: 'Liên tục', value: 'continuous' }, + { label: 'Thủ công', value: 'manual' }, +]; + +// Alert types for configuration +const ALERT_TYPES = [ + { id: 'motion', name: 'Chuyển Động có cảnh báo' }, + { id: 'smoke', name: 'Khói có cảnh báo' }, + { id: 'door', name: 'Cửa có cảnh báo' }, + { id: 'ac1_high', name: 'Điện AC 1 cao' }, + { id: 'ac1_low', name: 'Điện AC 1 thấp' }, + { id: 'ac1_lost', name: 'Điện AC 1 mất' }, + { id: 'load_high', name: 'Điện tải cao' }, + { id: 'load_low', name: 'Điện tải thấp' }, + { id: 'load_lost', name: 'Điện tải mất' }, + { id: 'grid_high', name: 'Điện lưới cao' }, + { id: 'grid_low', name: 'Điện lưới thấp' }, + { id: 'grid_lost', name: 'Điện lưới mất' }, + { id: 'ac1_on_error', name: 'Điều hòa 1 bật lỗi' }, + { id: 'ac1_off_error', name: 'Điều hòa 1 tắt lỗi' }, + { id: 'ac1_has_error', name: 'Điều hòa 1 có thể lỗi' }, + { id: 'ac2_on_error', name: 'Điều hòa 2 bật lỗi' }, + { id: 'ac2_off_error', name: 'Điều hòa 2 tắt lỗi' }, + { id: 'ac2_has_error', name: 'Điều hòa 2 điều hòa có thể lỗi' }, + { id: 'room_temp_high', name: 'Nhiệt độ phòng máy nhiệt độ phòng máy cao' }, + { id: 'rectifier_error', name: 'Rectifier bật lỗi' }, + { id: 'meter_volt_high', name: 'Công tơ điện điện áp cao' }, + { id: 'meter_volt_low', name: 'Công tơ điện điện áp thấp' }, + { id: 'meter_lost', name: 'Công tơ điện mất điện áp' }, + { id: 'lithium_volt_low', name: 'Pin lithium điện áp thấp' }, + { id: 'lithium_temp_high', name: 'Pin lithium nhiệt độ cao' }, + { id: 'lithium_capacity_low', name: 'Pin lithium dung lượng thấp' }, +]; + +// Camera interface +interface Camera { + id: string; + name: string; + type: string; + ipAddress: string; +} + +interface CameraFormValues { + name: string; + type: string; + account: string; + password: string; + ipAddress: string; + rtspPort: number; + httpPort: number; + stream: number; + channel: number; +} + +const CameraConfigPage = () => { + const { thingId } = useParams<{ thingId: string }>(); + const [form] = Form.useForm(); + const [isModalVisible, setIsModalVisible] = useState(false); + const [cameras, setCameras] = useState([]); + const [selectedAlerts, setSelectedAlerts] = useState([ + 'motion', + 'smoke', + 'door', + ]); + const [recordingMode, setRecordingMode] = useState('alarm'); + const [thingName, setThingName] = useState(''); + const [loading, setLoading] = useState(true); + + // Fetch thing info on mount + useEffect(() => { + const fetchThingInfo = async () => { + if (!thingId) return; + try { + setLoading(true); + const response = await apiSearchThings({ + offset: 0, + limit: 1, + id: thingId, + }); + if (response?.things && response.things.length > 0) { + setThingName(response.things[0].name || thingId); + } else { + setThingName(thingId); + } + } catch (error) { + console.error('Failed to fetch thing info:', error); + setThingName(thingId); + } finally { + setLoading(false); + } + }; + fetchThingInfo(); + }, [thingId]); + + const handleBack = () => { + history.push('/manager/devices'); + }; + + const handleOpenModal = () => { + form.resetFields(); + form.setFieldsValue({ + type: 'HIKVISION', + rtspPort: 554, + httpPort: 80, + stream: 0, + channel: 0, + }); + setIsModalVisible(true); + }; + + const handleCloseModal = () => { + setIsModalVisible(false); + form.resetFields(); + }; + + const handleSubmitCamera = async () => { + try { + const values = await form.validateFields(); + console.log('Camera values:', values); + // TODO: Call API to create camera + setCameras([ + ...cameras, + { + id: String(cameras.length + 1), + name: values.name, + type: values.type, + ipAddress: values.ipAddress, + }, + ]); + handleCloseModal(); + } catch (error) { + console.error('Validation failed:', error); + } + }; + + const handleAlertToggle = (alertId: string) => { + if (selectedAlerts.includes(alertId)) { + setSelectedAlerts(selectedAlerts.filter((id) => id !== alertId)); + } else { + setSelectedAlerts([...selectedAlerts, alertId]); + } + }; + + const handleClearAlerts = () => { + setSelectedAlerts([]); + }; + + const handleSubmitAlerts = () => { + console.log('Submit alerts:', selectedAlerts); + // TODO: Call API to save alert configuration + }; + + const columns = [ + { + title: '', + dataIndex: 'checkbox', + width: 50, + render: () => , + }, + { + title: 'Tên', + dataIndex: 'name', + key: 'name', + render: (text: string) => {text}, + }, + { + title: 'Loại', + dataIndex: 'type', + key: 'type', + }, + { + title: 'Địa chỉ IP', + dataIndex: 'ipAddress', + key: 'ipAddress', + }, + { + title: 'Thao tác', + key: 'action', + render: () => +