feat: Refactor theme management and localization for camera and terminal components
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// 运行时配置
|
||||
|
||||
import { getTheme } from '@/utils/storage';
|
||||
import { getLocale, history, Link, RunTimeLayoutConfig } from '@umijs/max';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -10,7 +11,6 @@ import IconFont from './components/IconFont';
|
||||
import LanguageSwitcher from './components/Lang/LanguageSwitcher';
|
||||
import ThemeProvider from './components/Theme/ThemeProvider';
|
||||
import ThemeSwitcher from './components/Theme/ThemeSwitcher';
|
||||
import { THEME_KEY } from './constants';
|
||||
import { ROUTE_FORGOT_PASSWORD, ROUTE_LOGIN } from './constants/routes';
|
||||
import NotFoundPage from './pages/Exception/NotFound';
|
||||
import UnAccessPage from './pages/Exception/UnAccess';
|
||||
@@ -52,8 +52,7 @@ export async function getInitialState(): Promise<InitialStateResponse> {
|
||||
|
||||
// Public routes that don't require authentication
|
||||
if (publicRoutes.includes(pathname)) {
|
||||
const currentTheme =
|
||||
(localStorage.getItem(THEME_KEY) as 'light' | 'dark') || 'light';
|
||||
const currentTheme = (getTheme() as 'light' | 'dark') || 'light';
|
||||
return {
|
||||
theme: currentTheme,
|
||||
};
|
||||
@@ -101,8 +100,7 @@ export async function getInitialState(): Promise<InitialStateResponse> {
|
||||
}
|
||||
};
|
||||
const resp = await getUserProfile();
|
||||
const currentTheme =
|
||||
(localStorage.getItem(THEME_KEY) as 'light' | 'dark') || 'light';
|
||||
const currentTheme = (getTheme() as 'light' | 'dark') || 'light';
|
||||
return {
|
||||
getUserProfile: getUserProfile!,
|
||||
currentUserProfile: resp,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { THEME_KEY } from '@/constants';
|
||||
import { setTheme } from '@/utils/storage';
|
||||
import { MoonOutlined, SunOutlined } from '@ant-design/icons';
|
||||
import { useModel } from '@umijs/max';
|
||||
import { Segmented } from 'antd';
|
||||
@@ -34,7 +34,7 @@ const ThemeSwitcher: React.FC<ThemeSwitcherProps> = ({ className }) => {
|
||||
|
||||
if (!supportsViewTransition) {
|
||||
// Fallback: just change theme without animation
|
||||
localStorage.setItem(THEME_KEY, newTheme);
|
||||
setTheme(newTheme);
|
||||
setIsDark(newTheme === 'dark');
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('theme-change', { detail: { theme: newTheme } }),
|
||||
@@ -58,7 +58,7 @@ const ThemeSwitcher: React.FC<ThemeSwitcherProps> = ({ className }) => {
|
||||
|
||||
// Start the view transition
|
||||
const transition = (document as any).startViewTransition(() => {
|
||||
localStorage.setItem(THEME_KEY, newTheme);
|
||||
setTheme(newTheme);
|
||||
setIsDark(newTheme === 'dark');
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('theme-change', { detail: { theme: newTheme } }),
|
||||
@@ -107,8 +107,3 @@ const ThemeSwitcher: React.FC<ThemeSwitcherProps> = ({ className }) => {
|
||||
};
|
||||
|
||||
export default ThemeSwitcher;
|
||||
|
||||
// Helper function để get theme từ localStorage
|
||||
export const getTheme = (): 'light' | 'dark' => {
|
||||
return (localStorage.getItem(THEME_KEY) as 'light' | 'dark') || 'light';
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { THEME_KEY } from '@/constants';
|
||||
import { getTheme, setTheme } from '@/utils/storage';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { getTheme } from './ThemeSwitcher';
|
||||
import './style.less';
|
||||
|
||||
const ThemeSwitcherAuth = () => {
|
||||
@@ -21,7 +20,7 @@ const ThemeSwitcherAuth = () => {
|
||||
|
||||
const handleSwitch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newTheme = e.target.checked ? 'dark' : 'light';
|
||||
localStorage.setItem(THEME_KEY, newTheme);
|
||||
setTheme(newTheme);
|
||||
setIsDark(newTheme === 'dark');
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('theme-change', { detail: { theme: newTheme } }),
|
||||
|
||||
@@ -11,6 +11,7 @@ export const DURATION_POLLING_PRESENTATIONS = 120000; //milliseconds
|
||||
export const STATUS_NORMAL = 0;
|
||||
export const STATUS_WARNING = 1;
|
||||
export const STATUS_DANGEROUS = 2;
|
||||
|
||||
export const STATUS_SOS = 3;
|
||||
|
||||
export const COLOR_DISCONNECT = '#d9d9d9';
|
||||
@@ -22,6 +23,7 @@ export const COLOR_SOS = '#ff0000';
|
||||
export const ACCESS_TOKEN = 'access_token';
|
||||
export const REFRESH_TOKEN = 'refresh_token';
|
||||
export const THEME_KEY = 'theme';
|
||||
export const TERMINAL_THEME_KEY = 'terminal_theme_key';
|
||||
// Global Constants
|
||||
export const LIMIT_TREE_LEVEL = 5;
|
||||
export const DEFAULT_PAGE_SIZE = 5;
|
||||
|
||||
@@ -46,56 +46,79 @@ export default {
|
||||
'master.devices.location.update.error': 'Location update failed',
|
||||
|
||||
// Camera translations
|
||||
'master.camera.loading': 'Loading...',
|
||||
'master.camera.config.success': 'Configuration sent successfully',
|
||||
'master.camera.config.error.deviceOffline':
|
||||
'master.devices.camera.loading': 'Loading...',
|
||||
'master.devices.camera.config.success': 'Configuration sent successfully',
|
||||
'master.devices.camera.config.error.deviceOffline':
|
||||
'Device is offline, cannot send configuration',
|
||||
'master.camera.config.error.missingConfig':
|
||||
'master.devices.camera.config.error.missingConfig':
|
||||
'Missing device configuration information',
|
||||
'master.camera.config.error.mqttNotConnected': 'MQTT not connected',
|
||||
'master.devices.camera.config.error.mqttNotConnected': 'MQTT not connected',
|
||||
// Camera Form Modal
|
||||
'master.camera.form.title.add': 'Add New Camera',
|
||||
'master.camera.form.title.edit': 'Edit Camera',
|
||||
'master.camera.form.name': 'Name',
|
||||
'master.camera.form.name.placeholder': 'Enter name',
|
||||
'master.camera.form.name.required': 'Please enter name',
|
||||
'master.camera.form.type': 'Type',
|
||||
'master.camera.form.type.required': 'Please select type',
|
||||
'master.camera.form.username': 'Username',
|
||||
'master.camera.form.username.placeholder': 'Enter username',
|
||||
'master.camera.form.username.required': 'Please enter username',
|
||||
'master.camera.form.password': 'Password',
|
||||
'master.camera.form.password.placeholder': 'Enter password',
|
||||
'master.camera.form.password.required': 'Please enter password',
|
||||
'master.camera.form.ip': 'IP Address',
|
||||
'master.camera.form.ip.placeholder': '192.168.1.10',
|
||||
'master.camera.form.ip.required': 'Please enter IP address',
|
||||
'master.camera.form.rtspPort': 'RTSP Port',
|
||||
'master.camera.form.rtspPort.required': 'Please enter RTSP port',
|
||||
'master.camera.form.httpPort': 'HTTP Port',
|
||||
'master.camera.form.httpPort.required': 'Please enter HTTP port',
|
||||
'master.camera.form.stream': 'Stream',
|
||||
'master.camera.form.stream.required': 'Please enter stream',
|
||||
'master.camera.form.channel': 'Channel',
|
||||
'master.camera.form.channel.required': 'Please enter channel',
|
||||
'master.camera.form.cancel': 'Cancel',
|
||||
'master.camera.form.submit': 'OK',
|
||||
'master.camera.form.update': 'Update',
|
||||
'master.devices.camera.form.title.add': 'Add New Camera',
|
||||
'master.devices.camera.form.title.edit': 'Edit Camera',
|
||||
'master.devices.camera.form.name': 'Name',
|
||||
'master.devices.camera.form.name.placeholder': 'Enter name',
|
||||
'master.devices.camera.form.name.required': 'Please enter name',
|
||||
'master.devices.camera.form.type': 'Type',
|
||||
'master.devices.camera.form.type.required': 'Please select type',
|
||||
'master.devices.camera.form.username': 'Username',
|
||||
'master.devices.camera.form.username.placeholder': 'Enter username',
|
||||
'master.devices.camera.form.username.required': 'Please enter username',
|
||||
'master.devices.camera.form.password': 'Password',
|
||||
'master.devices.camera.form.password.placeholder': 'Enter password',
|
||||
'master.devices.camera.form.password.required': 'Please enter password',
|
||||
'master.devices.camera.form.ip': 'IP Address',
|
||||
'master.devices.camera.form.ip.placeholder': '192.168.1.10',
|
||||
'master.devices.camera.form.ip.required': 'Please enter IP address',
|
||||
'master.devices.camera.form.rtspPort': 'RTSP Port',
|
||||
'master.devices.camera.form.rtspPort.required': 'Please enter RTSP port',
|
||||
'master.devices.camera.form.httpPort': 'HTTP Port',
|
||||
'master.devices.camera.form.httpPort.required': 'Please enter HTTP port',
|
||||
'master.devices.camera.form.stream': 'Stream',
|
||||
'master.devices.camera.form.stream.required': 'Please enter stream',
|
||||
'master.devices.camera.form.channel': 'Channel',
|
||||
'master.devices.camera.form.channel.required': 'Please enter channel',
|
||||
'master.devices.camera.form.cancel': 'Cancel',
|
||||
'master.devices.camera.form.submit': 'OK',
|
||||
'master.devices.camera.form.update': 'Update',
|
||||
// Camera Table
|
||||
'master.camera.table.add': 'Add New Camera',
|
||||
'master.camera.table.column.name': 'Name',
|
||||
'master.camera.table.column.type': 'Type',
|
||||
'master.camera.table.column.ip': 'IP Address',
|
||||
'master.camera.table.column.action': 'Actions',
|
||||
'master.camera.table.offline.tooltip': 'Device is offline',
|
||||
'master.camera.table.pagination': 'Showing {0}-{1} of {2} cameras',
|
||||
'master.devices.camera.table.add': 'Add New Camera',
|
||||
'master.devices.camera.table.column.name': 'Name',
|
||||
'master.devices.camera.table.column.type': 'Type',
|
||||
'master.devices.camera.table.column.ip': 'IP Address',
|
||||
'master.devices.camera.table.column.action': 'Actions',
|
||||
'master.devices.camera.table.offline.tooltip': 'Device is offline',
|
||||
'master.devices.camera.table.pagination': 'Showing {0}-{1} of {2} cameras',
|
||||
// Camera Config V6
|
||||
'master.camera.config.recording': 'Camera Recording',
|
||||
'master.camera.config.send': 'Send',
|
||||
'master.camera.config.alarmList': 'Alarm List',
|
||||
'master.camera.config.selected': '{0} items selected',
|
||||
'master.camera.config.clear': 'Clear',
|
||||
'master.camera.config.recordingMode.none': 'No Recording',
|
||||
'master.camera.config.recordingMode.alarm': 'On Alarm',
|
||||
'master.camera.config.recordingMode.all': '24/7',
|
||||
'master.devices.camera.config.recording': 'Camera Recording',
|
||||
'master.devices.camera.config.send': 'Send',
|
||||
'master.devices.camera.config.alarmList': 'Alarm List',
|
||||
'master.devices.camera.config.selected': '{0} items selected',
|
||||
'master.devices.camera.config.clear': 'Clear',
|
||||
'master.devices.camera.config.recordingMode.none': 'No Recording',
|
||||
'master.devices.camera.config.recordingMode.alarm': 'On Alarm',
|
||||
'master.devices.camera.config.recordingMode.all': '24/7',
|
||||
|
||||
// Terminal translations
|
||||
'master.devices.terminal.pageTitle': 'Terminal',
|
||||
'master.devices.terminal.loadDeviceError': 'Cannot load device information.',
|
||||
'master.devices.terminal.mqttError': 'Cannot connect to MQTT.',
|
||||
'master.devices.terminal.genericError': 'An error occurred',
|
||||
'master.devices.terminal.unsupported.title':
|
||||
'Device does not support terminal',
|
||||
'master.devices.terminal.unsupported.desc':
|
||||
'GMSv5 devices are not supported. Please use a different device.',
|
||||
'master.devices.terminal.missingChannel.title':
|
||||
'Missing control channel information',
|
||||
'master.devices.terminal.missingChannel.desc':
|
||||
'Device has not been configured with ctrl_channel_id, cannot open terminal.',
|
||||
'master.devices.terminal.missingCredential.title':
|
||||
'Missing authentication information',
|
||||
'master.devices.terminal.missingCredential.desc':
|
||||
'Current account has not been granted frontend_thing_id/frontend_thing_key.',
|
||||
'master.devices.terminal.offline':
|
||||
'Device is offline. Terminal is in view-only mode.',
|
||||
'master.devices.terminal.connecting': 'Preparing terminal session...',
|
||||
'master.devices.terminal.action.clear': 'Clear screen',
|
||||
'master.devices.terminal.action.theme': 'Theme',
|
||||
};
|
||||
|
||||
@@ -46,56 +46,78 @@ export default {
|
||||
'master.devices.location.update.error': 'Cập nhật vị trí thất bại',
|
||||
|
||||
// Camera translations
|
||||
'master.camera.loading': 'Đang tải...',
|
||||
'master.camera.config.success': 'Đã gửi cấu hình thành công',
|
||||
'master.camera.config.error.deviceOffline':
|
||||
'master.devices.camera.loading': 'Đang tải...',
|
||||
'master.devices.camera.config.success': 'Đã gửi cấu hình thành công',
|
||||
'master.devices.camera.config.error.deviceOffline':
|
||||
'Thiết bị đang ngoại tuyến, không thể gửi cấu hình',
|
||||
'master.camera.config.error.missingConfig':
|
||||
'master.devices.camera.config.error.missingConfig':
|
||||
'Thiếu thông tin cấu hình thiết bị',
|
||||
'master.camera.config.error.mqttNotConnected': 'MQTT chưa kết nối',
|
||||
'master.devices.camera.config.error.mqttNotConnected': 'MQTT chưa kết nối',
|
||||
// Camera Form Modal
|
||||
'master.camera.form.title.add': 'Tạo mới camera',
|
||||
'master.camera.form.title.edit': 'Chỉnh sửa camera',
|
||||
'master.camera.form.name': 'Tên',
|
||||
'master.camera.form.name.placeholder': 'Nhập tên',
|
||||
'master.camera.form.name.required': 'Vui lòng nhập tên',
|
||||
'master.camera.form.type': 'Loại',
|
||||
'master.camera.form.type.required': 'Vui lòng chọn loại',
|
||||
'master.camera.form.username': 'Tài khoản',
|
||||
'master.camera.form.username.placeholder': 'Nhập tài khoản',
|
||||
'master.camera.form.username.required': 'Vui lòng nhập tài khoản',
|
||||
'master.camera.form.password': 'Mật khẩu',
|
||||
'master.camera.form.password.placeholder': 'Nhập mật khẩu',
|
||||
'master.camera.form.password.required': 'Vui lòng nhập mật khẩu',
|
||||
'master.camera.form.ip': 'Địa chỉ IP',
|
||||
'master.camera.form.ip.placeholder': '192.168.1.10',
|
||||
'master.camera.form.ip.required': 'Vui lòng nhập địa chỉ IP',
|
||||
'master.camera.form.rtspPort': 'Cổng RTSP',
|
||||
'master.camera.form.rtspPort.required': 'Vui lòng nhập cổng RTSP',
|
||||
'master.camera.form.httpPort': 'Cổng HTTP',
|
||||
'master.camera.form.httpPort.required': 'Vui lòng nhập cổng HTTP',
|
||||
'master.camera.form.stream': 'Luồng',
|
||||
'master.camera.form.stream.required': 'Vui lòng nhập luồng',
|
||||
'master.camera.form.channel': 'Kênh',
|
||||
'master.camera.form.channel.required': 'Vui lòng nhập kênh',
|
||||
'master.camera.form.cancel': 'Hủy',
|
||||
'master.camera.form.submit': 'Đồng ý',
|
||||
'master.camera.form.update': 'Cập nhật',
|
||||
'master.devices.camera.form.title.add': 'Tạo mới camera',
|
||||
'master.devices.camera.form.title.edit': 'Chỉnh sửa camera',
|
||||
'master.devices.camera.form.name': 'Tên',
|
||||
'master.devices.camera.form.name.placeholder': 'Nhập tên',
|
||||
'master.devices.camera.form.name.required': 'Vui lòng nhập tên',
|
||||
'master.devices.camera.form.type': 'Loại',
|
||||
'master.devices.camera.form.type.required': 'Vui lòng chọn loại',
|
||||
'master.devices.camera.form.username': 'Tài khoản',
|
||||
'master.devices.camera.form.username.placeholder': 'Nhập tài khoản',
|
||||
'master.devices.camera.form.username.required': 'Vui lòng nhập tài khoản',
|
||||
'master.devices.camera.form.password': 'Mật khẩu',
|
||||
'master.devices.camera.form.password.placeholder': 'Nhập mật khẩu',
|
||||
'master.devices.camera.form.password.required': 'Vui lòng nhập mật khẩu',
|
||||
'master.devices.camera.form.ip': 'Địa chỉ IP',
|
||||
'master.devices.camera.form.ip.placeholder': '192.168.1.10',
|
||||
'master.devices.camera.form.ip.required': 'Vui lòng nhập địa chỉ IP',
|
||||
'master.devices.camera.form.rtspPort': 'Cổng RTSP',
|
||||
'master.devices.camera.form.rtspPort.required': 'Vui lòng nhập cổng RTSP',
|
||||
'master.devices.camera.form.httpPort': 'Cổng HTTP',
|
||||
'master.devices.camera.form.httpPort.required': 'Vui lòng nhập cổng HTTP',
|
||||
'master.devices.camera.form.stream': 'Luồng',
|
||||
'master.devices.camera.form.stream.required': 'Vui lòng nhập luồng',
|
||||
'master.devices.camera.form.channel': 'Kênh',
|
||||
'master.devices.camera.form.channel.required': 'Vui lòng nhập kênh',
|
||||
'master.devices.camera.form.cancel': 'Hủy',
|
||||
'master.devices.camera.form.submit': 'Đồng ý',
|
||||
'master.devices.camera.form.update': 'Cập nhật',
|
||||
// Camera Table
|
||||
'master.camera.table.add': 'Tạo mới camera',
|
||||
'master.camera.table.column.name': 'Tên',
|
||||
'master.camera.table.column.type': 'Loại',
|
||||
'master.camera.table.column.ip': 'Địa chỉ IP',
|
||||
'master.camera.table.column.action': 'Thao tác',
|
||||
'master.camera.table.offline.tooltip': 'Thiết bị đang ngoại tuyến',
|
||||
'master.camera.table.pagination': 'Hiển thị {0}-{1} của {2} camera',
|
||||
'master.devices.camera.table.add': 'Tạo mới camera',
|
||||
'master.devices.camera.table.column.name': 'Tên',
|
||||
'master.devices.camera.table.column.type': 'Loại',
|
||||
'master.devices.camera.table.column.ip': 'Địa chỉ IP',
|
||||
'master.devices.camera.table.column.action': 'Thao tác',
|
||||
'master.devices.camera.table.offline.tooltip': 'Thiết bị đang ngoại tuyến',
|
||||
'master.devices.camera.table.pagination': 'Hiển thị {0}-{1} của {2} camera',
|
||||
// Camera Config V6
|
||||
'master.camera.config.recording': 'Ghi dữ liệu camera',
|
||||
'master.camera.config.send': 'Gửi đi',
|
||||
'master.camera.config.alarmList': 'Danh sách cảnh báo',
|
||||
'master.camera.config.selected': 'đã chọn {0} mục',
|
||||
'master.camera.config.clear': 'Xóa',
|
||||
'master.camera.config.recordingMode.none': 'Không ghi',
|
||||
'master.camera.config.recordingMode.alarm': 'Theo cảnh báo',
|
||||
'master.camera.config.recordingMode.all': '24/24',
|
||||
'master.devices.camera.config.recording': 'Ghi dữ liệu camera',
|
||||
'master.devices.camera.config.send': 'Gửi đi',
|
||||
'master.devices.camera.config.alarmList': 'Danh sách cảnh báo',
|
||||
'master.devices.camera.config.selected': 'đã chọn {0} mục',
|
||||
'master.devices.camera.config.clear': 'Xóa',
|
||||
'master.devices.camera.config.recordingMode.none': 'Không ghi',
|
||||
'master.devices.camera.config.recordingMode.alarm': 'Theo cảnh báo',
|
||||
'master.devices.camera.config.recordingMode.all': '24/24',
|
||||
|
||||
// Terminal translations
|
||||
'master.devices.terminal.pageTitle': 'Terminal',
|
||||
'master.devices.terminal.loadDeviceError':
|
||||
'Không thể tải thông tin thiết bị.',
|
||||
'master.devices.terminal.mqttError': 'Không thể kết nối MQTT.',
|
||||
'master.devices.terminal.genericError': 'Đã có lỗi xảy ra',
|
||||
'master.devices.terminal.unsupported.title': 'Thiết bị không hỗ trợ terminal',
|
||||
'master.devices.terminal.unsupported.desc':
|
||||
'Thiết bị GMSv5 không được hỗ trợ. Vui lòng sử dụng thiết bị khác.',
|
||||
'master.devices.terminal.missingChannel.title':
|
||||
'Thiếu thông tin kênh điều khiển',
|
||||
'master.devices.terminal.missingChannel.desc':
|
||||
'Thiết bị chưa được cấu hình ctrl_channel_id nên không thể mở terminal.',
|
||||
'master.devices.terminal.missingCredential.title': 'Thiếu thông tin xác thực',
|
||||
'master.devices.terminal.missingCredential.desc':
|
||||
'Tài khoản hiện tại chưa được cấp frontend_thing_id/frontend_thing_key.',
|
||||
'master.devices.terminal.offline':
|
||||
'Thiết bị đang ngoại tuyến. Terminal chuyển sang chế độ chỉ xem.',
|
||||
'master.devices.terminal.connecting': 'Đang chuẩn bị phiên terminal...',
|
||||
'master.devices.terminal.action.clear': 'Xóa màn hình',
|
||||
'master.devices.terminal.action.theme': 'Giao diện',
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import Footer from '@/components/Footer';
|
||||
import LangSwitches from '@/components/Lang/LanguageSwitcherAuth';
|
||||
import ThemeSwitcherAuth from '@/components/Theme/ThemeSwitcherAuth';
|
||||
import { THEME_KEY } from '@/constants';
|
||||
import { ROUTE_LOGIN } from '@/constants/routes';
|
||||
import { apiUserResetPassword } from '@/services/master/UserController';
|
||||
import { parseAccessToken } from '@/utils/jwt';
|
||||
import { getDomainTitle, getLogoImage } from '@/utils/logo';
|
||||
import { getTheme } from '@/utils/storage';
|
||||
import { ProForm, ProFormText } from '@ant-design/pro-components';
|
||||
import {
|
||||
FormattedMessage,
|
||||
@@ -28,9 +28,7 @@ const ResetPassword = () => {
|
||||
const [tokenValid, setTokenValid] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [isDark, setIsDark] = useState(
|
||||
(localStorage.getItem(THEME_KEY) as 'light' | 'dark') === 'dark',
|
||||
);
|
||||
const [isDark, setIsDark] = useState(getTheme() === 'dark');
|
||||
const { token } = theme.useToken();
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import Footer from '@/components/Footer';
|
||||
import LangSwitches from '@/components/Lang/LanguageSwitcherAuth';
|
||||
import ThemeSwitcherAuth from '@/components/Theme/ThemeSwitcherAuth';
|
||||
import { THEME_KEY } from '@/constants';
|
||||
import { ROUTER_HOME } from '@/constants/routes';
|
||||
import {
|
||||
apiForgotPassword,
|
||||
@@ -14,6 +13,7 @@ import { getDomainTitle, getLogoImage } from '@/utils/logo';
|
||||
import {
|
||||
getBrowserId,
|
||||
getRefreshToken,
|
||||
getTheme,
|
||||
removeAccessToken,
|
||||
removeRefreshToken,
|
||||
setAccessToken,
|
||||
@@ -63,9 +63,7 @@ const FormWrapper = ({
|
||||
};
|
||||
|
||||
const LoginPage = () => {
|
||||
const [isDark, setIsDark] = useState(
|
||||
(localStorage.getItem(THEME_KEY) as 'light' | 'dark') === 'dark',
|
||||
);
|
||||
const [isDark, setIsDark] = useState(getTheme() === 'dark');
|
||||
const { token } = theme.useToken();
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const urlParams = new URL(window.location.href).searchParams;
|
||||
@@ -108,8 +106,7 @@ const LoginPage = () => {
|
||||
setInitialState((s: any) => ({
|
||||
...s,
|
||||
currentUserProfile: userInfo,
|
||||
theme:
|
||||
(localStorage.getItem(THEME_KEY) as 'light' | 'dark') || 'light',
|
||||
theme: getTheme() as 'light' | 'dark',
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -150,9 +147,7 @@ const LoginPage = () => {
|
||||
setInitialState((s: any) => ({
|
||||
...s,
|
||||
currentUserProfile: userInfo,
|
||||
theme:
|
||||
(localStorage.getItem(THEME_KEY) as 'light' | 'dark') ||
|
||||
'light',
|
||||
theme: getTheme() as 'light' | 'dark',
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -180,9 +175,7 @@ const LoginPage = () => {
|
||||
setInitialState((s: any) => ({
|
||||
...s,
|
||||
currentUserProfile: userInfo,
|
||||
theme:
|
||||
(localStorage.getItem(THEME_KEY) as 'light' | 'dark') ||
|
||||
'light',
|
||||
theme: getTheme() as 'light' | 'dark',
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
<Modal
|
||||
title={intl.formatMessage({
|
||||
id: isEditMode
|
||||
? 'master.camera.form.title.edit'
|
||||
: 'master.camera.form.title.add',
|
||||
? 'master.devices.camera.form.title.edit'
|
||||
: 'master.devices.camera.form.title.add',
|
||||
defaultMessage: isEditMode ? 'Chỉnh sửa camera' : 'Tạo mới camera',
|
||||
})}
|
||||
open={open}
|
||||
@@ -86,15 +86,15 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
footer={[
|
||||
<Button key="cancel" onClick={handleCancel}>
|
||||
{intl.formatMessage({
|
||||
id: 'master.camera.form.cancel',
|
||||
id: 'master.devices.camera.form.cancel',
|
||||
defaultMessage: 'Hủy',
|
||||
})}
|
||||
</Button>,
|
||||
<Button key="submit" type="primary" onClick={handleSubmit}>
|
||||
{intl.formatMessage({
|
||||
id: isEditMode
|
||||
? 'master.camera.form.update'
|
||||
: 'master.camera.form.submit',
|
||||
? 'master.devices.camera.form.update'
|
||||
: 'master.devices.camera.form.submit',
|
||||
defaultMessage: isEditMode ? 'Cập nhật' : 'Đồng ý',
|
||||
})}
|
||||
</Button>,
|
||||
@@ -115,7 +115,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
>
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.name',
|
||||
id: 'master.devices.camera.form.name',
|
||||
defaultMessage: 'Tên',
|
||||
})}
|
||||
name="name"
|
||||
@@ -123,7 +123,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.name.required',
|
||||
id: 'master.devices.camera.form.name.required',
|
||||
defaultMessage: 'Vui lòng nhập tên',
|
||||
}),
|
||||
},
|
||||
@@ -131,7 +131,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'master.camera.form.name.placeholder',
|
||||
id: 'master.devices.camera.form.name.placeholder',
|
||||
defaultMessage: 'nhập dữ liệu',
|
||||
})}
|
||||
/>
|
||||
@@ -139,7 +139,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.type',
|
||||
id: 'master.devices.camera.form.type',
|
||||
defaultMessage: 'Loại',
|
||||
})}
|
||||
name="cate_id"
|
||||
@@ -147,7 +147,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.type.required',
|
||||
id: 'master.devices.camera.form.type.required',
|
||||
defaultMessage: 'Vui lòng chọn loại',
|
||||
}),
|
||||
},
|
||||
@@ -158,7 +158,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.username',
|
||||
id: 'master.devices.camera.form.username',
|
||||
defaultMessage: 'Tài khoản',
|
||||
})}
|
||||
name="username"
|
||||
@@ -166,7 +166,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.username.required',
|
||||
id: 'master.devices.camera.form.username.required',
|
||||
defaultMessage: 'Vui lòng nhập tài khoản',
|
||||
}),
|
||||
},
|
||||
@@ -174,7 +174,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'master.camera.form.username.placeholder',
|
||||
id: 'master.devices.camera.form.username.placeholder',
|
||||
defaultMessage: 'nhập tài khoản',
|
||||
})}
|
||||
autoComplete="off"
|
||||
@@ -183,7 +183,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.password',
|
||||
id: 'master.devices.camera.form.password',
|
||||
defaultMessage: 'Mật khẩu',
|
||||
})}
|
||||
name="password"
|
||||
@@ -191,7 +191,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.password.required',
|
||||
id: 'master.devices.camera.form.password.required',
|
||||
defaultMessage: 'Vui lòng nhập mật khẩu',
|
||||
}),
|
||||
},
|
||||
@@ -199,7 +199,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
>
|
||||
<Input.Password
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'master.camera.form.password.placeholder',
|
||||
id: 'master.devices.camera.form.password.placeholder',
|
||||
defaultMessage: 'nhập mật khẩu',
|
||||
})}
|
||||
autoComplete="new-password"
|
||||
@@ -208,7 +208,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.ip',
|
||||
id: 'master.devices.camera.form.ip',
|
||||
defaultMessage: 'Địa chỉ IP',
|
||||
})}
|
||||
name="ip"
|
||||
@@ -216,7 +216,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.ip.required',
|
||||
id: 'master.devices.camera.form.ip.required',
|
||||
defaultMessage: 'Vui lòng nhập địa chỉ IP',
|
||||
}),
|
||||
},
|
||||
@@ -224,7 +224,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'master.camera.form.ip.placeholder',
|
||||
id: 'master.devices.camera.form.ip.placeholder',
|
||||
defaultMessage: '192.168.1.10',
|
||||
})}
|
||||
/>
|
||||
@@ -234,7 +234,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.rtspPort',
|
||||
id: 'master.devices.camera.form.rtspPort',
|
||||
defaultMessage: 'Cổng RTSP',
|
||||
})}
|
||||
name="rtsp_port"
|
||||
@@ -242,7 +242,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.rtspPort.required',
|
||||
id: 'master.devices.camera.form.rtspPort.required',
|
||||
defaultMessage: 'Vui lòng nhập cổng RTSP',
|
||||
}),
|
||||
},
|
||||
@@ -254,7 +254,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.httpPort',
|
||||
id: 'master.devices.camera.form.httpPort',
|
||||
defaultMessage: 'Cổng HTTP',
|
||||
})}
|
||||
name="http_port"
|
||||
@@ -262,7 +262,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.httpPort.required',
|
||||
id: 'master.devices.camera.form.httpPort.required',
|
||||
defaultMessage: 'Vui lòng nhập cổng HTTP',
|
||||
}),
|
||||
},
|
||||
@@ -277,7 +277,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.stream',
|
||||
id: 'master.devices.camera.form.stream',
|
||||
defaultMessage: 'Luồng',
|
||||
})}
|
||||
name="stream"
|
||||
@@ -285,7 +285,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.stream.required',
|
||||
id: 'master.devices.camera.form.stream.required',
|
||||
defaultMessage: 'Vui lòng nhập luồng',
|
||||
}),
|
||||
},
|
||||
@@ -297,7 +297,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label={intl.formatMessage({
|
||||
id: 'master.camera.form.channel',
|
||||
id: 'master.devices.camera.form.channel',
|
||||
defaultMessage: 'Kênh',
|
||||
})}
|
||||
name="channel"
|
||||
@@ -305,7 +305,7 @@ const CameraFormModal: React.FC<CameraFormModalProps> = ({
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'master.camera.form.channel.required',
|
||||
id: 'master.devices.camera.form.channel.required',
|
||||
defaultMessage: 'Vui lòng nhập kênh',
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -51,7 +51,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
const columns = [
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'master.camera.table.column.name',
|
||||
id: 'master.devices.camera.table.column.name',
|
||||
defaultMessage: 'Tên',
|
||||
}),
|
||||
dataIndex: 'name',
|
||||
@@ -62,7 +62,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'master.camera.table.column.type',
|
||||
id: 'master.devices.camera.table.column.type',
|
||||
defaultMessage: 'Loại',
|
||||
}),
|
||||
dataIndex: 'cate_id',
|
||||
@@ -71,7 +71,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'master.camera.table.column.ip',
|
||||
id: 'master.devices.camera.table.column.ip',
|
||||
defaultMessage: 'Địa chỉ IP',
|
||||
}),
|
||||
dataIndex: 'ip',
|
||||
@@ -80,7 +80,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'master.camera.table.column.action',
|
||||
id: 'master.devices.camera.table.column.action',
|
||||
defaultMessage: 'Thao tác',
|
||||
}),
|
||||
key: 'action',
|
||||
@@ -89,7 +89,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
title={
|
||||
!isOnline
|
||||
? intl.formatMessage({
|
||||
id: 'master.camera.table.offline.tooltip',
|
||||
id: 'master.devices.camera.table.offline.tooltip',
|
||||
defaultMessage: 'Thiết bị đang ngoại tuyến',
|
||||
})
|
||||
: ''
|
||||
@@ -113,7 +113,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
title={
|
||||
!isOnline
|
||||
? intl.formatMessage({
|
||||
id: 'master.camera.table.offline.tooltip',
|
||||
id: 'master.devices.camera.table.offline.tooltip',
|
||||
defaultMessage: 'Thiết bị đang ngoại tuyến',
|
||||
})
|
||||
: ''
|
||||
@@ -126,7 +126,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
disabled={!isOnline}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'master.camera.table.add',
|
||||
id: 'master.devices.camera.table.add',
|
||||
defaultMessage: 'Tạo mới camera',
|
||||
})}
|
||||
</Button>
|
||||
@@ -140,7 +140,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
title={
|
||||
!isOnline
|
||||
? intl.formatMessage({
|
||||
id: 'master.camera.table.offline.tooltip',
|
||||
id: 'master.devices.camera.table.offline.tooltip',
|
||||
defaultMessage: 'Thiết bị đang ngoại tuyến',
|
||||
})
|
||||
: ''
|
||||
@@ -172,7 +172,7 @@ const CameraTable: React.FC<CameraTableProps> = ({
|
||||
showTotal: (total: number, range: [number, number]) =>
|
||||
intl.formatMessage(
|
||||
{
|
||||
id: 'master.camera.table.pagination',
|
||||
id: 'master.devices.camera.table.pagination',
|
||||
defaultMessage: 'Hiển thị {0}-{1} của {2} camera',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,14 +21,14 @@ const CameraV5: React.FC<CameraV5Props> = ({
|
||||
() => [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'master.camera.config.recordingMode.none',
|
||||
id: 'master.devices.camera.config.recordingMode.none',
|
||||
defaultMessage: 'Không ghi',
|
||||
}),
|
||||
value: 'none',
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'master.camera.config.recordingMode.all',
|
||||
id: 'master.devices.camera.config.recordingMode.all',
|
||||
defaultMessage: '24/24',
|
||||
}),
|
||||
value: 'all',
|
||||
@@ -52,7 +52,7 @@ const CameraV5: React.FC<CameraV5Props> = ({
|
||||
<div className="w-full sm:w-1/3 lg:w-1/4">
|
||||
<Text strong className="block mb-2">
|
||||
{intl.formatMessage({
|
||||
id: 'master.camera.config.recording',
|
||||
id: 'master.devices.camera.config.recording',
|
||||
defaultMessage: 'Ghi dữ liệu camera',
|
||||
})}
|
||||
</Text>
|
||||
@@ -66,7 +66,7 @@ const CameraV5: React.FC<CameraV5Props> = ({
|
||||
</div>
|
||||
<Button type="primary" onClick={handleSubmit}>
|
||||
{intl.formatMessage({
|
||||
id: 'master.camera.config.send',
|
||||
id: 'master.devices.camera.config.send',
|
||||
defaultMessage: 'Gửi đi',
|
||||
})}
|
||||
</Button>
|
||||
|
||||
@@ -42,21 +42,21 @@ const CameraV6: React.FC<CameraV6Props> = ({
|
||||
() => [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'master.camera.config.recordingMode.none',
|
||||
id: 'master.devices.camera.config.recordingMode.none',
|
||||
defaultMessage: 'Không ghi',
|
||||
}),
|
||||
value: 'none',
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'master.camera.config.recordingMode.alarm',
|
||||
id: 'master.devices.camera.config.recordingMode.alarm',
|
||||
defaultMessage: 'Theo cảnh báo',
|
||||
}),
|
||||
value: 'alarm',
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'master.camera.config.recordingMode.all',
|
||||
id: 'master.devices.camera.config.recordingMode.all',
|
||||
defaultMessage: '24/24',
|
||||
}),
|
||||
value: 'all',
|
||||
@@ -148,7 +148,7 @@ const CameraV6: React.FC<CameraV6Props> = ({
|
||||
<div className="w-full sm:w-1/3 lg:w-1/4">
|
||||
<Text strong className="block mb-2">
|
||||
{intl.formatMessage({
|
||||
id: 'master.camera.config.recording',
|
||||
id: 'master.devices.camera.config.recording',
|
||||
defaultMessage: 'Ghi dữ liệu camera',
|
||||
})}
|
||||
</Text>
|
||||
@@ -164,7 +164,7 @@ const CameraV6: React.FC<CameraV6Props> = ({
|
||||
title={
|
||||
!isOnline
|
||||
? intl.formatMessage({
|
||||
id: 'master.camera.table.offline.tooltip',
|
||||
id: 'master.devices.camera.table.offline.tooltip',
|
||||
defaultMessage: 'Thiết bị đang ngoại tuyến',
|
||||
})
|
||||
: ''
|
||||
@@ -176,7 +176,7 @@ const CameraV6: React.FC<CameraV6Props> = ({
|
||||
disabled={!isOnline}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'master.camera.config.send',
|
||||
id: 'master.devices.camera.config.send',
|
||||
defaultMessage: 'Gửi đi',
|
||||
})}
|
||||
</Button>
|
||||
@@ -189,7 +189,7 @@ const CameraV6: React.FC<CameraV6Props> = ({
|
||||
<div>
|
||||
<Text strong className="block mb-2">
|
||||
{intl.formatMessage({
|
||||
id: 'master.camera.config.alarmList',
|
||||
id: 'master.devices.camera.config.alarmList',
|
||||
defaultMessage: 'Danh sách cảnh báo',
|
||||
})}
|
||||
</Text>
|
||||
@@ -204,7 +204,7 @@ const CameraV6: React.FC<CameraV6Props> = ({
|
||||
<Text type="secondary">
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: 'master.camera.config.selected',
|
||||
id: 'master.devices.camera.config.selected',
|
||||
defaultMessage: 'đã chọn {0} mục',
|
||||
},
|
||||
{
|
||||
@@ -214,7 +214,7 @@ const CameraV6: React.FC<CameraV6Props> = ({
|
||||
</Text>
|
||||
<Button type="link" onClick={handleClearAlerts}>
|
||||
{intl.formatMessage({
|
||||
id: 'master.camera.config.clear',
|
||||
id: 'master.devices.camera.config.clear',
|
||||
defaultMessage: 'Xóa',
|
||||
})}
|
||||
</Button>
|
||||
|
||||
@@ -146,7 +146,7 @@ const CameraConfigPage = () => {
|
||||
if (!isOnline) {
|
||||
message.error(
|
||||
intl.formatMessage({
|
||||
id: 'master.camera.config.error.deviceOffline',
|
||||
id: 'master.devices.camera.config.error.deviceOffline',
|
||||
defaultMessage: 'Thiết bị đang ngoại tuyến, không thể gửi cấu hình',
|
||||
}),
|
||||
);
|
||||
@@ -156,7 +156,7 @@ const CameraConfigPage = () => {
|
||||
if (!thing?.metadata?.cfg_channel_id || !thing?.metadata?.external_id) {
|
||||
message.error(
|
||||
intl.formatMessage({
|
||||
id: 'master.camera.config.error.missingConfig',
|
||||
id: 'master.devices.camera.config.error.missingConfig',
|
||||
defaultMessage: 'Thiếu thông tin cấu hình thiết bị',
|
||||
}),
|
||||
);
|
||||
@@ -188,7 +188,7 @@ const CameraConfigPage = () => {
|
||||
mqttClient.publish(pubTopic, payload);
|
||||
message.success(
|
||||
intl.formatMessage({
|
||||
id: 'master.camera.config.success',
|
||||
id: 'master.devices.camera.config.success',
|
||||
defaultMessage: 'Đã gửi cấu hình thành công',
|
||||
}),
|
||||
);
|
||||
@@ -196,7 +196,7 @@ const CameraConfigPage = () => {
|
||||
} else {
|
||||
message.error(
|
||||
intl.formatMessage({
|
||||
id: 'master.camera.config.error.mqttNotConnected',
|
||||
id: 'master.devices.camera.config.error.mqttNotConnected',
|
||||
defaultMessage: 'MQTT chưa kết nối',
|
||||
}),
|
||||
);
|
||||
@@ -290,7 +290,7 @@ const CameraConfigPage = () => {
|
||||
<span>
|
||||
{thing?.name ||
|
||||
intl.formatMessage({
|
||||
id: 'master.camera.loading',
|
||||
id: 'master.devices.camera.loading',
|
||||
defaultMessage: 'Loading...',
|
||||
})}
|
||||
</span>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getBadgeConnection } from '@/components/shared/ThingShared';
|
||||
import { ROUTE_MANAGER_DEVICES } from '@/constants/routes';
|
||||
import { apiGetThingDetail } from '@/services/master/ThingController';
|
||||
import { mqttClient } from '@/utils/mqttClient';
|
||||
import { getTerminalTheme, setTerminalTheme } from '@/utils/storage';
|
||||
import { BgColorsOutlined, ClearOutlined } from '@ant-design/icons';
|
||||
import { PageContainer, ProCard } from '@ant-design/pro-components';
|
||||
import { history, useIntl, useModel, useParams } from '@umijs/max';
|
||||
@@ -210,7 +211,7 @@ const DeviceTerminalPage = () => {
|
||||
console.error('Failed to load device details', error);
|
||||
setTerminalError(
|
||||
intl.formatMessage({
|
||||
id: 'terminal.loadDeviceError',
|
||||
id: 'master.devices.terminal.loadDeviceError',
|
||||
defaultMessage: 'Không thể tải thông tin thiết bị.',
|
||||
}),
|
||||
);
|
||||
@@ -231,7 +232,7 @@ const DeviceTerminalPage = () => {
|
||||
|
||||
// Khôi phục theme từ localStorage khi component mount
|
||||
useEffect(() => {
|
||||
const savedTheme = localStorage.getItem('terminal_theme_key');
|
||||
const savedTheme = getTerminalTheme();
|
||||
if (savedTheme && TERMINAL_THEMES[savedTheme]) {
|
||||
setSelectedThemeKey(savedTheme);
|
||||
}
|
||||
@@ -326,7 +327,7 @@ const DeviceTerminalPage = () => {
|
||||
mqttClient.disconnect();
|
||||
setTerminalError(
|
||||
intl.formatMessage({
|
||||
id: 'terminal.mqttError',
|
||||
id: 'master.devices.terminal.mqttError',
|
||||
defaultMessage: 'Không thể kết nối MQTT.',
|
||||
}),
|
||||
);
|
||||
@@ -428,7 +429,7 @@ const DeviceTerminalPage = () => {
|
||||
const pageTitle =
|
||||
thing?.name ||
|
||||
intl.formatMessage({
|
||||
id: 'terminal.pageTitle',
|
||||
id: 'master.devices.terminal.pageTitle',
|
||||
defaultMessage: 'Terminal',
|
||||
});
|
||||
|
||||
@@ -456,12 +457,12 @@ const DeviceTerminalPage = () => {
|
||||
/**
|
||||
* Xử lý thay đổi theme
|
||||
* - Cập nhật state
|
||||
* - Lưu vào localStorage
|
||||
* - Lưu vào localStorage thông qua storage utility
|
||||
*/
|
||||
const handleThemeChange: MenuProps['onClick'] = (e) => {
|
||||
const themeKey = e.key;
|
||||
setSelectedThemeKey(themeKey);
|
||||
localStorage.setItem('terminal_theme_key', themeKey);
|
||||
setTerminalTheme(themeKey);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -507,7 +508,7 @@ const DeviceTerminalPage = () => {
|
||||
<Result
|
||||
status="error"
|
||||
title={intl.formatMessage({
|
||||
id: 'terminal.genericError',
|
||||
id: 'master.devices.terminal.genericError',
|
||||
defaultMessage: 'Đã có lỗi xảy ra',
|
||||
})}
|
||||
subTitle={terminalError}
|
||||
@@ -530,11 +531,11 @@ const DeviceTerminalPage = () => {
|
||||
renderBlockingResult(
|
||||
'info',
|
||||
intl.formatMessage({
|
||||
id: 'terminal.unsupported.title',
|
||||
id: 'master.devices.terminal.unsupported.title',
|
||||
defaultMessage: 'Thiết bị không hỗ trợ terminal',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'terminal.unsupported.desc',
|
||||
id: 'master.devices.terminal.unsupported.desc',
|
||||
defaultMessage:
|
||||
'Thiết bị GMSv5 không được hỗ trợ. Vui lòng sử dụng thiết bị khác.',
|
||||
}),
|
||||
@@ -549,11 +550,11 @@ const DeviceTerminalPage = () => {
|
||||
renderBlockingResult(
|
||||
'warning',
|
||||
intl.formatMessage({
|
||||
id: 'terminal.missingChannel.title',
|
||||
id: 'master.devices.terminal.missingChannel.title',
|
||||
defaultMessage: 'Thiếu thông tin kênh điều khiển',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'terminal.missingChannel.desc',
|
||||
id: 'master.devices.terminal.missingChannel.desc',
|
||||
defaultMessage:
|
||||
'Thiết bị chưa được cấu hình ctrl_channel_id nên không thể mở terminal.',
|
||||
}),
|
||||
@@ -569,11 +570,11 @@ const DeviceTerminalPage = () => {
|
||||
renderBlockingResult(
|
||||
'warning',
|
||||
intl.formatMessage({
|
||||
id: 'terminal.missingCredential.title',
|
||||
id: 'master.devices.terminal.missingCredential.title',
|
||||
defaultMessage: 'Thiếu thông tin xác thực',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'terminal.missingCredential.desc',
|
||||
id: 'master.devices.terminal.missingCredential.desc',
|
||||
defaultMessage:
|
||||
'Tài khoản hiện tại chưa được cấp frontend_thing_id/frontend_thing_key.',
|
||||
}),
|
||||
@@ -588,7 +589,7 @@ const DeviceTerminalPage = () => {
|
||||
type="warning"
|
||||
showIcon
|
||||
message={intl.formatMessage({
|
||||
id: 'terminal.offline',
|
||||
id: 'master.devices.terminal.offline',
|
||||
defaultMessage:
|
||||
'Thiết bị đang ngoại tuyến. Terminal chuyển sang chế độ chỉ xem.',
|
||||
})}
|
||||
@@ -600,8 +601,9 @@ const DeviceTerminalPage = () => {
|
||||
type="info"
|
||||
showIcon
|
||||
message={intl.formatMessage({
|
||||
id: 'terminal.connecting',
|
||||
defaultMessage: 'Đang chuẩn bị phiên terminal...',
|
||||
id: 'master.devices.terminal.connecting',
|
||||
defaultMessage:
|
||||
'Đang chuẩn bị phiên master.devices.terminal...',
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
@@ -631,7 +633,7 @@ const DeviceTerminalPage = () => {
|
||||
disabled={!isSessionReady}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'terminal.action.clear',
|
||||
id: 'master.devices.terminal.action.clear',
|
||||
defaultMessage: 'Xóa màn hình',
|
||||
})}
|
||||
</Button>
|
||||
@@ -645,7 +647,7 @@ const DeviceTerminalPage = () => {
|
||||
>
|
||||
<Button icon={<BgColorsOutlined />}>
|
||||
{intl.formatMessage({
|
||||
id: 'terminal.action.theme',
|
||||
id: 'master.devices.terminal.action.theme',
|
||||
defaultMessage: 'Theme',
|
||||
})}
|
||||
: {TERMINAL_THEMES[selectedThemeKey]?.name || 'Dark'}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { ACCESS_TOKEN, REFRESH_TOKEN } from '@/constants';
|
||||
import {
|
||||
ACCESS_TOKEN,
|
||||
REFRESH_TOKEN,
|
||||
TERMINAL_THEME_KEY,
|
||||
THEME_KEY,
|
||||
} from '@/constants';
|
||||
|
||||
export function getAccessToken(): string {
|
||||
return localStorage.getItem(ACCESS_TOKEN) || '';
|
||||
@@ -24,6 +29,30 @@ export function removeRefreshToken() {
|
||||
localStorage.removeItem(REFRESH_TOKEN);
|
||||
}
|
||||
|
||||
export function getTerminalTheme(): string {
|
||||
return localStorage.getItem(TERMINAL_THEME_KEY) || 'dark';
|
||||
}
|
||||
|
||||
export function setTerminalTheme(themeKey: string) {
|
||||
localStorage.setItem(TERMINAL_THEME_KEY, themeKey);
|
||||
}
|
||||
|
||||
export function removeTerminalTheme() {
|
||||
localStorage.removeItem(TERMINAL_THEME_KEY);
|
||||
}
|
||||
|
||||
export function getTheme(): string {
|
||||
return localStorage.getItem(THEME_KEY) || 'light';
|
||||
}
|
||||
|
||||
export function setTheme(themeKey: string) {
|
||||
localStorage.setItem(THEME_KEY, themeKey);
|
||||
}
|
||||
|
||||
export function removeTheme() {
|
||||
localStorage.removeItem(THEME_KEY);
|
||||
}
|
||||
|
||||
export function getBrowserId() {
|
||||
const id = localStorage.getItem('sip-browserid');
|
||||
if (!id) {
|
||||
@@ -42,14 +71,18 @@ export function getBrowserId() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all localStorage data except browserId
|
||||
* Clear all localStorage data except browserId, theme and terminal theme
|
||||
*/
|
||||
export function clearAllData() {
|
||||
const browserId = localStorage.getItem('sip-browserid');
|
||||
const theme = getTheme();
|
||||
const terminalTheme = getTerminalTheme();
|
||||
localStorage.clear();
|
||||
if (browserId) {
|
||||
localStorage.setItem('sip-browserid', browserId);
|
||||
}
|
||||
localStorage.setItem(THEME_KEY, theme);
|
||||
localStorage.setItem(TERMINAL_THEME_KEY, terminalTheme);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user