diff --git a/ZONE_MIGRATION.md b/ZONE_MIGRATION.md deleted file mode 100644 index 50ffdb5..0000000 --- a/ZONE_MIGRATION.md +++ /dev/null @@ -1,185 +0,0 @@ -# ✅ Zone (Banzone) API Migration - Complete - -## Migration Banzone/Zone API vào SGW Module - -### Files Created - -1. **ZoneController.ts** - `src/services/slave/sgw/ZoneController.ts` - - ```typescript - ✅ apiGetAllBanzones(body) - Get all banzones with pagination - ✅ apiRemoveBanzone(id, groupID) - Remove a banzone - ✅ apiGetZoneById(zoneId) - Get banzone by ID - ✅ apiCreateBanzone(body) - Create new banzone - ✅ apiUpdateBanzone(id, body) - Update banzone - ``` - -2. **Type Definitions** - Added to `src/services/slave/sgw/sgw.typing.d.ts` - - ```typescript - ✅ Banzone - ✅ Condition - ✅ Geom - ✅ ZoneResponse - ✅ ZoneBodyRequest - ``` - -3. **Route Constants** - Added to `src/constants/slave/sgw/routes.ts` - ```typescript - ✅ SGW_ROUTE_BANZONES = '/api/sgw/banzones' - ✅ SGW_ROUTE_BANZONES_LIST = '/api/sgw/banzones/list' - ``` - -### Files Updated - -- ✅ `src/pages/Slave/SGW/Map/components/ShipDetail.tsx` - - Updated import: `@/services/controller/ZoneController` → `@/services/slave/sgw/ZoneController` - - Updated types: `API.Thing` → `SgwModel.SgwThing` - - Updated types: `API.UserResponse` → `MasterModel.UserResponse` - - Updated types: `API.Geom` → `SgwModel.Geom` - -### Migration Changes - -**Before:** - -```typescript -import { apiGetZoneById } from '@/services/controller/ZoneController'; - -thing: API.Thing -const zone_geom: API.Geom = ... -``` - -**After:** - -```typescript -import { apiGetZoneById } from '@/services/slave/sgw/ZoneController'; - -thing: SgwModel.SgwThing -const zone_geom: SgwModel.Geom = ... -``` - -### Type Definitions - -```typescript -declare namespace SgwModel { - interface Banzone { - id?: string; - name?: string; - province_code?: string; - type?: number; - conditions?: Condition[]; - description?: string; - geometry?: string; - enabled?: boolean; - created_at?: Date; - updated_at?: Date; - } - - interface Condition { - max?: number; - min?: number; - type?: 'length_limit' | 'month_range' | 'date_range'; - to?: number; - from?: number; - } - - interface Geom { - geom_type?: number; - geom_poly?: string; - geom_lines?: string; - geom_point?: string; - geom_radius?: number; - } - - interface ZoneResponse { - total?: number; - offset?: number; - limit?: number; - banzones?: Banzone[]; - } - - interface ZoneBodyRequest { - name?: string; - province_code?: string; - type?: number; - conditions?: Condition[]; - description?: string; - geometry?: string; - enabled?: boolean; - } -} -``` - -### API Usage Examples - -**Get Zone by ID:** - -```typescript -import { apiGetZoneById } from '@/services/slave/sgw/ZoneController'; - -const zone = await apiGetZoneById('zone-123'); -const geometry: SgwModel.Geom = JSON.parse(zone.geometry || '{}'); -``` - -**Create Banzone:** - -```typescript -import { apiCreateBanzone } from '@/services/slave/sgw/ZoneController'; - -const zoneData: SgwModel.ZoneBodyRequest = { - name: 'Vùng cấm mùa sinh sản', - province_code: 'QN', - type: 1, - enabled: true, - conditions: [ - { - type: 'month_range', - from: 4, - to: 8, - }, - ], - geometry: JSON.stringify({ - geom_type: 1, - geom_poly: 'POLYGON(...)', - }), -}; - -await apiCreateBanzone(zoneData); -``` - -**Get All Banzones:** - -```typescript -import { apiGetAllBanzones } from '@/services/slave/sgw/ZoneController'; - -const response = await apiGetAllBanzones({ - offset: 0, - limit: 20, - order: 'name', - dir: 'asc', -}); -``` - -## Status: ✅ Complete - -- ✅ 5 API functions migrated -- ✅ 5 type definitions added -- ✅ 2 route constants added -- ✅ 1 file updated (ShipDetail.tsx) -- ✅ 0 compilation errors - -## Total SGW Migration Progress - -| Module | APIs | Types | Status | -| --------- | ------ | ------- | ------ | -| Ship | 12 | 15+ | ✅ | -| Trip | 17 | 21+ | ✅ | -| Photo | 2 | 2 | ✅ | -| Zone | 5 | 5 | ✅ | -| **TOTAL** | **36** | **43+** | ✅ | - ---- - -**Migration Date:** January 23, 2026 -**Status:** ✅ Complete -**Ready for Testing:** YES diff --git a/src/app.tsx b/src/app.tsx index 94ee839..290d67d 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -25,8 +25,8 @@ import { } from './utils/storage'; const isProdBuild = process.env.NODE_ENV === 'production'; export type InitialStateResponse = { - getUserProfile?: () => Promise; - currentUserProfile?: MasterModel.ProfileResponse; + getUserProfile?: () => Promise; + currentUserProfile?: MasterModel.UserResponse; theme?: 'light' | 'dark'; }; diff --git a/src/components/Avatar/AvatarDropdown.tsx b/src/components/Avatar/AvatarDropdown.tsx index 6e96a60..bf2bdda 100644 --- a/src/components/Avatar/AvatarDropdown.tsx +++ b/src/components/Avatar/AvatarDropdown.tsx @@ -13,7 +13,7 @@ import { Dropdown } from 'antd'; export const AvatarDropdown = ({ currentUserProfile, }: { - currentUserProfile?: MasterModel.ProfileResponse; + currentUserProfile?: MasterModel.UserResponse; }) => { const intl = useIntl(); return ( diff --git a/src/components/shared/TagState.tsx b/src/components/shared/TagState.tsx index c0fe249..ad9c911 100644 --- a/src/components/shared/TagState.tsx +++ b/src/components/shared/TagState.tsx @@ -6,10 +6,9 @@ import { WarningOutlined, } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; -import { Flex, Tag, Tooltip } from 'antd'; +import { Flex, Tag, theme, Tooltip } from 'antd'; import { useState } from 'react'; import { TagStateCallbackPayload } from '../../pages/Slave/SGW/Map/type'; -import style from './index.less'; type TagStateProps = { normalCount?: number; @@ -35,7 +34,59 @@ const TagState = ({ sos: false, disconnected: false, }); + const { token } = theme.useToken(); const intl = useIntl(); + + // Style variants using antd theme tokens for dark mode support + const getTagStyle = ( + type: 'normal' | 'warning' | 'critical' | 'offline', + isActive: boolean, + ) => { + const baseStyle = { + borderRadius: token.borderRadiusSM, + borderWidth: 1, + borderStyle: 'solid' as const, + }; + + if (type === 'normal') { + return { + ...baseStyle, + color: isActive ? token.colorSuccess : token.colorSuccess, + backgroundColor: isActive + ? token.colorSuccessBg + : token.colorBgContainer, + borderColor: token.colorSuccessBorder, + }; + } + if (type === 'warning') { + return { + ...baseStyle, + color: isActive ? token.colorWarning : token.colorWarning, + backgroundColor: isActive + ? token.colorWarningBg + : token.colorBgContainer, + borderColor: token.colorWarningBorder, + }; + } + if (type === 'critical') { + return { + ...baseStyle, + color: isActive ? token.colorError : token.colorError, + backgroundColor: isActive ? token.colorErrorBg : token.colorBgContainer, + borderColor: token.colorErrorBorder, + }; + } + // offline + return { + ...baseStyle, + color: token.colorTextSecondary, + backgroundColor: isActive + ? token.colorFillSecondary + : token.colorBgContainer, + borderColor: token.colorBorder, + }; + }; + const handleTagClick = (key: keyof typeof activeStates) => { const newStates = { ...activeStates, [key]: !activeStates[key] }; setActiveStates(newStates); @@ -45,39 +96,11 @@ const TagState = ({ isWarning: newStates.warning, isCritical: newStates.critical, isSos: newStates.sos, - isDisconected: newStates.disconnected, + isDisconnected: newStates.disconnected, }); } }; - // const tagStyles = { - // sos: activeStates.sos - // ? { background: '#ff4d4f', color: '#fff', borderColor: '#ff4d4f' } - // : { background: '#fff1f0', color: '#cf1322', borderColor: '#ffa39e' }, - // normal: activeStates.normal - // ? { background: '#52c41a', color: '#fff', borderColor: '#52c41a' } - // : { background: '#f6ffed', color: '#389e0d', borderColor: '#b7eb8f' }, - // warning: activeStates.warning - // ? { - // background: '#faad14', - // color: '#fff', - // borderColor: '#faad14', - // hoverColor: '#ffe58f', - // } - // : { - // background: '#fffbe6', - // color: '#d48806', - // borderColor: '#ffe58f', - // hoverColor: '#faad14', - // }, - // critical: activeStates.critical - // ? { background: '#d4380d', color: '#fff', borderColor: '#d4380d' } - // : { background: '#fff2e8', color: '#d4380d', borderColor: '#ffd8bf' }, - // disconnected: activeStates.disconnected - // ? { background: '#8c8c8c', color: '#fff', borderColor: '#8c8c8c' } - // : { background: '#f5f5f5', color: '#8c8c8c', borderColor: '#d9d9d9' }, - // }; - return ( } checked={activeStates.sos} onChange={() => handleTagClick('sos')} @@ -113,13 +135,12 @@ const TagState = ({ {/* {normalCount > 0 && ( */} } checked={activeStates.normal} onChange={() => handleTagClick('normal')} @@ -131,14 +152,13 @@ const TagState = ({ {/* {warningCount > 0 && ( */} } - // style={tagStyles.warning} checked={activeStates.warning} onChange={() => handleTagClick('warning')} > @@ -149,16 +169,13 @@ const TagState = ({ {/* {criticalCount > 0 && ( */} } - // style={tagStyles.critical} checked={activeStates.critical} onChange={() => handleTagClick('critical')} > @@ -169,16 +186,13 @@ const TagState = ({ {/* {disconnectedCount > 0 && ( */} } - // style={tagStyles.disconnected} checked={activeStates.disconnected} onChange={() => handleTagClick('disconnected')} > diff --git a/src/components/shared/ThingShared.tsx b/src/components/shared/ThingShared.tsx index 4d62993..3f03470 100644 --- a/src/components/shared/ThingShared.tsx +++ b/src/components/shared/ThingShared.tsx @@ -5,18 +5,28 @@ import { STATUS_WARNING, } from '@/constants'; import { Badge } from 'antd'; +import IconFont from '../IconFont'; export const getBadgeStatus = (status: number) => { switch (status) { case STATUS_NORMAL: - return ; + return ; case STATUS_WARNING: - return ; + return ; case STATUS_DANGEROUS: - return ; + return ; case STATUS_SOS: - return ; + return ; default: - return ; + return ; + } +}; + +export const getBadgeConnection = (online: boolean) => { + switch (online) { + case true: + return ; + default: + return ; } }; diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 6e4ff51..265af27 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -38,6 +38,7 @@ export default { 'common.type': 'Type', 'common.type.placeholder': 'Select Type', 'common.status': 'Status', + 'common.connect': 'Connection', 'common.province': 'Province', 'common.description': 'Description', 'common.description.required': 'Description is required', @@ -48,6 +49,7 @@ export default { 'common.updated_at': 'Updated At', 'common.undefined': 'Undefined', 'common.not_empty': 'Cannot be empty!', + 'common.level.disconnected': 'Disconnected', 'common.level.normal': 'Normal', 'common.level.warning': 'Warning', 'common.level.critical': 'Critical', diff --git a/src/locales/vi-VN.ts b/src/locales/vi-VN.ts index 8b352b3..3e0062d 100644 --- a/src/locales/vi-VN.ts +++ b/src/locales/vi-VN.ts @@ -39,6 +39,7 @@ export default { 'common.type': 'Loại', 'common.type.placeholder': 'Chọn loại', 'common.status': 'Trạng thái', + 'common.connect': 'Kết nối', 'common.province': 'Tỉnh', 'common.description': 'Mô tả', 'common.description.required': 'Mô tả không được để trống', @@ -49,6 +50,7 @@ export default { 'common.updated_at': 'Ngày cập nhật', 'common.undefined': 'Chưa xác định', 'common.not_empty': 'Không được để trống!', + 'common.level.disconnected': 'Mất kết nối', 'common.level.normal': 'Bình thường', 'common.level.warning': 'Cảnh báo', 'common.level.critical': 'Nguy hiểm', diff --git a/src/pages/Manager/Device/index.tsx b/src/pages/Manager/Device/index.tsx index 4ec7c67..1120c2c 100644 --- a/src/pages/Manager/Device/index.tsx +++ b/src/pages/Manager/Device/index.tsx @@ -255,7 +255,7 @@ const ManagerDevicePage = () => { const offset = current === 1 ? 0 : (current - 1) * size; setIsLoading(true); - const metadata: Partial = {}; + const metadata: Partial = {}; if (external_id) metadata.external_id = external_id; // Add group filter if groups are selected diff --git a/src/pages/Manager/Log/index.tsx b/src/pages/Manager/Log/index.tsx index 2f6495c..9b8bee3 100644 --- a/src/pages/Manager/Log/index.tsx +++ b/src/pages/Manager/Log/index.tsx @@ -14,7 +14,7 @@ const SystemLogs = () => { const tableRef = useRef(); const { token } = theme.useToken(); - const queryUserSource = async (): Promise => { + const queryUserSource = async (): Promise => { try { const body: MasterModel.SearchUserPaginationBody = { offset: 0, diff --git a/src/pages/Manager/User/Permission/Assign.tsx b/src/pages/Manager/User/Permission/Assign.tsx index f251ec6..aa2d23e 100644 --- a/src/pages/Manager/User/Permission/Assign.tsx +++ b/src/pages/Manager/User/Permission/Assign.tsx @@ -14,7 +14,7 @@ enum AssignTabsKey { const AssignUserPage = () => { const { userId } = useParams<{ userId: string }>(); const [userProfile, setUserProfile] = - useState(null); + useState(null); const [loading, setLoading] = useState(false); const [tabSelected, setTabSelected] = useState( AssignTabsKey.group, diff --git a/src/pages/Manager/User/Permission/components/AssignGroup.tsx b/src/pages/Manager/User/Permission/components/AssignGroup.tsx index 592da1d..a8171d0 100644 --- a/src/pages/Manager/User/Permission/components/AssignGroup.tsx +++ b/src/pages/Manager/User/Permission/components/AssignGroup.tsx @@ -24,7 +24,7 @@ import { import { useRef, useState } from 'react'; const { Text } = Typography; type AssignGroupProps = { - user: MasterModel.ProfileResponse | null; + user: MasterModel.UserResponse | null; }; const AssignGroup = ({ user }: AssignGroupProps) => { const groupActionRef = useRef(); diff --git a/src/pages/Manager/User/Permission/components/ShareThing.tsx b/src/pages/Manager/User/Permission/components/ShareThing.tsx index ee23534..3d69a64 100644 --- a/src/pages/Manager/User/Permission/components/ShareThing.tsx +++ b/src/pages/Manager/User/Permission/components/ShareThing.tsx @@ -32,7 +32,7 @@ type PolicyShareDefault = { }; type ShareThingProps = { - user: MasterModel.ProfileResponse | null; + user: MasterModel.UserResponse | null; }; const ShareThing = ({ user }: ShareThingProps) => { const listActionRef = useRef(); diff --git a/src/pages/Manager/User/index.tsx b/src/pages/Manager/User/index.tsx index e69c6c6..d882ecb 100644 --- a/src/pages/Manager/User/index.tsx +++ b/src/pages/Manager/User/index.tsx @@ -34,19 +34,19 @@ const ManagerUserPage = () => { const [isLoading, setIsLoading] = useState(false); const [messageApi, contextHolder] = message.useMessage(); const [selectedRowsState, setSelectedRowsState] = useState< - MasterModel.ProfileResponse[] + MasterModel.UserResponse[] >([]); const [groupCheckedKeys, setGroupCheckedKeys] = useState< string | string[] | null >(null); - const handleClickAssign = (user: MasterModel.ProfileResponse) => { + const handleClickAssign = (user: MasterModel.UserResponse) => { const path = `${ROUTE_MANAGER_USERS}/${user.id}/${ROUTE_MANAGER_USERS_PERMISSIONS}`; history.push(path); }; - const columns: ProColumns[] = [ + const columns: ProColumns[] = [ { key: 'email', title: ( @@ -136,7 +136,7 @@ const ManagerUserPage = () => { }, ]; - const handleRemove = async (selectedRows: MasterModel.ProfileResponse[]) => { + const handleRemove = async (selectedRows: MasterModel.UserResponse[]) => { const key = 'remove_user'; if (!selectedRows) return true; @@ -151,7 +151,7 @@ const ManagerUserPage = () => { key, }); const allDelete = selectedRows.map( - async (row: MasterModel.ProfileResponse) => { + async (row: MasterModel.UserResponse) => { await apiDeleteUser(row?.id || ''); }, ); @@ -196,7 +196,7 @@ const ManagerUserPage = () => { /> - + columns={columns} tableLayout="auto" actionRef={actionRef} @@ -210,7 +210,7 @@ const ManagerUserPage = () => { selectedRowKeys: selectedRowsState.map((row) => row.id!), onChange: ( _: React.Key[], - selectedRows: MasterModel.ProfileResponse[], + selectedRows: MasterModel.UserResponse[], ) => { setSelectedRowsState(selectedRows); }, @@ -249,12 +249,12 @@ const ManagerUserPage = () => { let users = userByGroupResponses.users || []; // Apply filters if (email) { - users = users.filter((user: MasterModel.ProfileResponse) => + users = users.filter((user: MasterModel.UserResponse) => user.email?.includes(email), ); } if (phone_number) { - users = users.filter((user: MasterModel.ProfileResponse) => + users = users.filter((user: MasterModel.UserResponse) => user.metadata?.phone_number?.includes(phone_number), ); } @@ -269,7 +269,7 @@ const ManagerUserPage = () => { }; } else { // Use regular queryUsers API - const metadata: Partial = {}; + const metadata: Partial = {}; if (phone_number) metadata.phone_number = phone_number; const query: MasterModel.SearchUserPaginationBody = { diff --git a/src/pages/Profile/components/ChangeProfile.tsx b/src/pages/Profile/components/ChangeProfile.tsx index a52fbed..0d21025 100644 --- a/src/pages/Profile/components/ChangeProfile.tsx +++ b/src/pages/Profile/components/ChangeProfile.tsx @@ -32,7 +32,7 @@ const ChangeProfile = () => { }} onFinish={async (values) => { try { - const body: Partial = { + const body: Partial = { full_name: values.full_name, phone_number: values.phone_number, }; diff --git a/src/pages/Slave/SGW/Map/components/ShipDetail.tsx b/src/pages/Slave/SGW/Map/components/ShipDetail.tsx index db839df..b1493db 100644 --- a/src/pages/Slave/SGW/Map/components/ShipDetail.tsx +++ b/src/pages/Slave/SGW/Map/components/ShipDetail.tsx @@ -61,7 +61,7 @@ const ShipDetail = ({ let shipOwner: | MasterModel.UserResponse - | MasterModel.ProfileResponse + | MasterModel.UserResponse | null = null; if (resp?.owner_id) { if (initialState?.currentUserProfile?.metadata?.user_type === 'admin') { diff --git a/src/pages/Slave/SGW/Map/index.tsx b/src/pages/Slave/SGW/Map/index.tsx index 1a9f005..4e94c14 100644 --- a/src/pages/Slave/SGW/Map/index.tsx +++ b/src/pages/Slave/SGW/Map/index.tsx @@ -206,7 +206,7 @@ const MapPage = () => { ? '' : [stateCriticalQuery, stateSosQuery].filter(Boolean).join(','); let metaFormQuery: Record = {}; - if (stateQuery?.isDisconected) + if (stateQuery?.isDisconnected) metaFormQuery = { ...metaFormQuery, connected: false }; const metaStateQuery = stateQueryParams !== '' ? { state_level: stateQueryParams } : null; @@ -252,7 +252,7 @@ const MapPage = () => { }; setThings(null); - const resp = await apiSearchThings(metaQuery); + const resp = await apiSearchThings(metaQuery, 'sgw'); return resp; } catch (error) { console.error('Error when searchThings: ', error); diff --git a/src/pages/Slave/SGW/Map/type.ts b/src/pages/Slave/SGW/Map/type.ts index 2297cc2..144c993 100644 --- a/src/pages/Slave/SGW/Map/type.ts +++ b/src/pages/Slave/SGW/Map/type.ts @@ -58,7 +58,7 @@ export type TagStateCallbackPayload = { isWarning: boolean; isCritical: boolean; isSos: boolean; - isDisconected: boolean; // giữ đúng theo yêu cầu (1 'n') + isDisconnected: boolean; // giữ đúng theo yêu cầu (1 'n') }; export type PointData = { @@ -82,7 +82,7 @@ export interface GPSParseResult { export interface ShipDetailData { ship: SgwModel.ShipDetail; - owner: MasterModel.ProfileResponse | null; + owner: MasterModel.UserResponse | null; gps: GPSParseResult | null; trip_id?: string; zone_entered_alarm_list: ZoneAlarmParse[]; diff --git a/src/pages/Slave/SGW/Ship/components/EditModal.tsx b/src/pages/Slave/SGW/Ship/components/EditModal.tsx index d34dccd..b1088d2 100644 --- a/src/pages/Slave/SGW/Ship/components/EditModal.tsx +++ b/src/pages/Slave/SGW/Ship/components/EditModal.tsx @@ -13,7 +13,7 @@ interface EditModalProps { onVisibleChange: (visible: boolean) => void; onFinish: ( values: Pick & - Pick, + Pick, ) => Promise; } diff --git a/src/pages/Slave/SGW/Trip/components/CreateTrip.tsx b/src/pages/Slave/SGW/Trip/components/CreateTrip.tsx index 431a626..22da695 100644 --- a/src/pages/Slave/SGW/Trip/components/CreateTrip.tsx +++ b/src/pages/Slave/SGW/Trip/components/CreateTrip.tsx @@ -31,6 +31,18 @@ interface CreateTripProps { thing_id?: string; } +interface TripFormValues { + name: string; + departure_time: any; // dayjs object or string + departure_port_id: number; + arrival_time?: any; // dayjs object or string + arrival_port_id?: number; + fishing_ground_codes?: number[]; + fishing_gear?: SgwModel.FishingGear[]; + trip_cost?: SgwModel.TripCost[]; + ship_id?: string; +} + const CreateTrip: React.FC = ({ onSuccess, thing_id }) => { const [visible, setVisible] = useState(false); const [selectedThing, setSelectedThing] = useState(); @@ -41,9 +53,9 @@ const CreateTrip: React.FC = ({ onSuccess, thing_id }) => { const [loading, setLoading] = useState(false); const [lastTrip, setLastTrip] = useState(null); const [initialFormValues, setInitialFormValues] = useState< - Partial + Partial >({}); - const formRef = useRef>(null); + const formRef = useRef>(null); const { homeports, getHomeportsByProvinceCode } = useModel( 'slave.sgw.useHomePorts', ); @@ -125,7 +137,7 @@ const CreateTrip: React.FC = ({ onSuccess, thing_id }) => { } }; - const handleSubmit = async (values: SgwModel.TripFormValues) => { + const handleSubmit = async (values: TripFormValues) => { try { if (!selectedShip) { message.error('Vui lòng chọn tàu!'); @@ -155,7 +167,7 @@ const CreateTrip: React.FC = ({ onSuccess, thing_id }) => { .filter((n: number) => !isNaN(n)) : [], fishing_gears: values.fishing_gear, - trip_cost: values.trip_cost, + trip_cost: values.trip_cost || [], }; const thingIdToUse = selectedShip.thing_id || selectedThing; @@ -211,7 +223,7 @@ const CreateTrip: React.FC = ({ onSuccess, thing_id }) => { > stepsProps={{ size: 'small' }} - onFinish={handleSubmit} + onFinish={(values) => handleSubmit(values as TripFormValues)} submitter={{ render: (_: SubmitterProps, dom) => dom, }} diff --git a/src/pages/Slave/Spole/Monitor/index.tsx b/src/pages/Slave/Spole/Monitor/index.tsx index c258c09..99f19de 100644 --- a/src/pages/Slave/Spole/Monitor/index.tsx +++ b/src/pages/Slave/Spole/Monitor/index.tsx @@ -1,9 +1,284 @@ -import React from 'react'; - +import TagState from '@/components/shared/TagState'; +import { + getBadgeConnection, + getBadgeStatus, +} from '@/components/shared/ThingShared'; +import TreeGroup from '@/components/shared/TreeGroup'; +import { DEFAULT_PAGE_SIZE } from '@/constants'; +import { apiSearchThings } from '@/services/master/ThingController'; +import { + ActionType, + ProCard, + ProColumns, + ProTable, +} from '@ant-design/pro-components'; +import { FormattedMessage, useIntl } from '@umijs/max'; +import { Flex, Grid, theme, Typography } from 'antd'; +import moment from 'moment'; +import React, { useRef, useState } from 'react'; +import { TagStateCallbackPayload } from '../../SGW/Map/type'; +const { Text } = Typography; const SpoleHome: React.FC = () => { + const { useBreakpoint } = Grid; + const intl = useIntl(); + const screens = useBreakpoint(); + const { token } = theme.useToken(); + const actionRef = useRef(null); + const [isLoading, setIsLoading] = useState(false); + const [groupCheckedKeys, setGroupCheckedKeys] = useState< + string | string[] | null + >(null); + const [thing, setThing] = useState< + SpoleModel.SpoleThingsResponse | undefined + >(undefined); + const [stateQuery, setStateQuery] = useState< + TagStateCallbackPayload | undefined + >(undefined); + + const columns: ProColumns[] = [ + { + key: 'name', + ellipsis: true, + title: ( + + ), + tip: intl.formatMessage({ + id: 'master.devices.name.tip', + defaultMessage: 'The device name', + }), + dataIndex: 'name', + hideInSearch: true, + copyable: true, + }, + { + key: 'connected', + hideInSearch: true, + ellipsis: true, + title: ( + + ), + dataIndex: ['metadata', 'connected'], + filters: [ + { text: 'Connected', value: true }, + { text: 'Disconnected', value: false }, + ], + onFilter: (value: any, row) => row?.metadata?.connected === value, + render: (_, row) => { + const connectionDuration = row.metadata!.connected + ? row.metadata!.uptime! * 1000 + : (Math.round(new Date().getTime() / 1000) - + row.metadata!.updated_time!) * + 1000; + + return ( + + {getBadgeConnection(row.metadata!.connected || false)} + + {connectionDuration > 0 + ? moment.duration(connectionDuration).humanize() + : ''} + + + ); + }, + }, + { + title: intl.formatMessage({ + id: 'common.status', + defaultMessage: 'Status', + }), + dataIndex: ['metadata', 'state_level'], + key: 'state_level', + hideInSearch: true, + filters: true, + onFilter: true, + valueType: 'select', + valueEnum: { + 0: { + text: intl.formatMessage({ + id: 'common.level.normal', + defaultMessage: 'Normal', + }), + status: 'Normal', + }, + 1: { + text: intl.formatMessage({ + id: 'common.level.warning', + defaultMessage: 'Warning', + }), + status: 'Warning', + }, + 2: { + text: intl.formatMessage({ + id: 'common.level.critical', + defaultMessage: 'Critical', + }), + status: 'Critical', + }, + }, + render: (_, row) => { + const alarm = JSON.parse(row?.metadata?.alarm_list || '{}'); + const text = alarm?.map((a: any) => a?.name).join(', '); + return ( + + {getBadgeStatus(Number(row?.metadata?.state_level ?? -1))} + + {text} + + + ); + }, + }, + ]; + const handleTagStateChange = (payload: TagStateCallbackPayload) => { + setStateQuery(payload); + actionRef.current?.reload(); + }; return (
-

Trang chủ (Spole)

+ + + { + setGroupCheckedKeys(value); + if (actionRef.current) { + actionRef.current.reload(); + } + }} + /> + + + + bordered={true} + columns={columns} + tableLayout="auto" + actionRef={actionRef} + rowKey="id" + search={{ + layout: 'vertical', // Hoặc 'vertical' để xếp dọc + defaultCollapsed: false, // Mặc định mở rộng + span: 12, // Chiếm 12 cột trên lưới (tổng 24 cột) + filterType: 'light', // Loại filter + labelWidth: 'auto', // Độ rộng nhãn + }} + size="large" + dateFormatter="string" + pagination={{ + defaultPageSize: DEFAULT_PAGE_SIZE * 2, + showSizeChanger: true, + pageSizeOptions: ['10', '15', '20'], + showTotal: (total, range) => + `${range[0]}-${range[1]} + ${intl.formatMessage({ + id: 'common.paginations.of', + defaultMessage: 'of', + })} + ${total} ${intl.formatMessage({ + id: 'master.devices.table.pagination', + defaultMessage: 'devices', + })}`, + }} + request={async (params = {}) => { + const { + current = 1, + pageSize, + name, + external_id, + keyword, + } = params; + const size = pageSize || DEFAULT_PAGE_SIZE * 2; + const offset = current === 1 ? 0 : (current - 1) * size; + setIsLoading(true); + const stateNormalQuery = stateQuery?.isNormal ? 'normal' : ''; + const stateSosQuery = stateQuery?.isSos ? 'sos' : ''; + const stateWarningQuery = stateQuery?.isWarning + ? stateNormalQuery + ',warning' + : stateNormalQuery; + const stateCriticalQuery = stateQuery?.isCritical + ? stateWarningQuery + ',critical' + : stateWarningQuery; + const stateQueryParams = + stateQuery?.isNormal && + stateQuery?.isWarning && + stateQuery?.isCritical && + stateQuery?.isSos + ? '' + : [stateCriticalQuery, stateSosQuery] + .filter(Boolean) + .join(','); + let metaFormQuery: Record = {}; + if (stateQuery?.isDisconnected) + metaFormQuery = { ...metaFormQuery, connected: false }; + const metaStateQuery = + stateQueryParams !== '' + ? { state_level: stateQueryParams } + : null; + let metadata: Partial = {}; + if (external_id) metadata.external_id = external_id; + + if (metaStateQuery) metadata = { ...metadata, ...metaStateQuery }; + // Add group filter if groups are selected + if (groupCheckedKeys && groupCheckedKeys.length > 0) { + const groupId = Array.isArray(groupCheckedKeys) + ? groupCheckedKeys.join(',') + : groupCheckedKeys; + metadata.group_id = groupId; + } + + const query: MasterModel.SearchThingPaginationBody = { + offset: offset, + limit: size, + order: 'name', + dir: 'asc', + }; + if (keyword) query.name = keyword; + if (Object.keys(metadata).length > 0) query.metadata = metadata; + try { + const response = await apiSearchThings(query, 'spole'); + setIsLoading(false); + setThing(response); + return { + data: response.things || [], + success: true, + total: response.total || 0, + }; + } catch (error) { + setIsLoading(false); + return { + data: [], + success: false, + total: 0, + }; + } + }} + options={{ + search: true, + setting: false, + density: false, + reload: true, + }} + toolbar={{ + actions: [ + , + ], + }} + /> + +
); }; diff --git a/src/services/master/AuthController.ts b/src/services/master/AuthController.ts index ce737b2..0f4561f 100644 --- a/src/services/master/AuthController.ts +++ b/src/services/master/AuthController.ts @@ -16,13 +16,13 @@ export async function apiLogin(body: MasterModel.LoginRequestBody) { } export async function apiQueryProfile() { - return request(API_PATH_GET_PROFILE); + return request(API_PATH_GET_PROFILE); } export async function apiUpdateProfile( - body: Partial, + body: Partial, ) { - return request(API_USERS, { + return request(API_USERS, { method: 'PUT', data: { metadata: body, diff --git a/src/services/master/ThingController.ts b/src/services/master/ThingController.ts index 545aa5d..c9c78ad 100644 --- a/src/services/master/ThingController.ts +++ b/src/services/master/ThingController.ts @@ -5,6 +5,19 @@ import { } from '@/constants/api'; import { request } from '@umijs/max'; +export async function apiSearchThings( + body: MasterModel.SearchPaginationBody, + domain: 'spole', +): Promise; +export async function apiSearchThings( + body: MasterModel.SearchPaginationBody, + domain: 'sgw', +): Promise; +export async function apiSearchThings( + body: MasterModel.SearchPaginationBody, + domain?: 'gms', +): Promise; + export async function apiSearchThings( body: MasterModel.SearchPaginationBody, domain: string = process.env.DOMAIN_ENV || 'gms', diff --git a/src/services/master/UserController.ts b/src/services/master/UserController.ts index ff10be3..313692b 100644 --- a/src/services/master/UserController.ts +++ b/src/services/master/UserController.ts @@ -4,19 +4,21 @@ import { request } from '@umijs/max'; export async function apiQueryUsers( params: MasterModel.SearchUserPaginationBody, ) { - return request(API_USERS, { + return request(API_USERS, { params: params, }); } export async function apiQueryUserById(userId: string) { - return request(`${API_USERS}/${userId}`); + return request(`${API_USERS}/${userId}`); } export async function apiQueryUsersByGroup( group_id: string, -): Promise { - return request(`${API_USERS_BY_GROUP}/${group_id}`); +): Promise { + return request( + `${API_USERS_BY_GROUP}/${group_id}`, + ); } export async function apiCreateUsers(body: MasterModel.CreateUserBodyRequest) { diff --git a/src/services/master/typings.d.ts b/src/services/master/typings.d.ts index f55cd7e..004803e 100644 --- a/src/services/master/typings.d.ts +++ b/src/services/master/typings.d.ts @@ -8,254 +8,12 @@ declare namespace MasterModel { limit?: number; dir?: 'asc' | 'desc'; } - interface SearchThingPaginationBody extends SearchPaginationBody { - order?: string; - metadata?: ThingMetadata; - } - interface ThingMetadata { - group_id?: string; - external_id?: string; - } - interface SearchAlarmPaginationBody extends SearchPaginationBody { - order?: 'name' | undefined; - thing_name?: string; - thing_id?: string; - level?: number; - } - - interface SearchUserPaginationBody extends SearchPaginationBody { - order?: 'email' | 'name' | undefined; - email?: string; - metadata?: Partial; - } - interface SearchLogPaginationBody extends SearchPaginationBody { - from?: number; - to?: number; - publisher?: string; - subtopic?: string; - } - - // Auth - interface LoginRequestBody { - guid: string; - email: string; - password: string; - } - - interface LoginResponse { - token?: string; - } - - interface ChangePasswordRequestBody { - old_password: string; - password: string; - } - - interface ProfileResponse { - id?: string; - email?: string; - metadata?: ProfileMetadata; - } - - interface ProfileMetadata { - frontend_thing_id?: string; - frontend_thing_key?: string; - full_name?: string; - phone_number?: string; - telegram?: string; - user_type?: 'admin' | 'enduser' | 'sysadmin' | 'users'; - } - - // User - interface CreateUserMetadata extends ProfileMetadata { - group_id?: string; - } - - interface CreateUserBodyRequest extends Partial { - password: string; - full_name?: string; - metadata?: CreateUserMetadata; - } - - interface UserResponse { - total?: number; - offset?: number; - limit?: number; - users: ProfileResponse[]; - } - interface AlarmsResponse { - total?: number; - limit?: number; - order?: string; - dir?: string; - alarms?: Alarm[]; - } - - interface ConfirmAlarmRequest { - id?: string; - description?: string; - thing_id?: string; - time?: number; - } - // Alarm - interface Alarm { - name?: string; - time?: number; - level?: number; - id?: string; - confirmed?: boolean; - confirmed_email?: string; - confirmed_time?: number; - confirmed_desc?: string; - thing_id?: string; - thing_name?: string; - thing_type?: string; - } - - // Thing - interface ThingMetadata { - address?: string; - alarm_list?: string; - cfg_channel_id?: string; - connected?: boolean; - ctrl_channel_id?: string; - data_channel_id?: string; - enduser?: string; - external_id?: string; - group_id?: string; - req_channel_id?: string; - state?: string; - state_level?: number; - state_updated_time?: number; - type?: string; - updated_time?: number; - } - - interface ThingsResponse { + interface PaginationReponse { total?: number; offset?: number; limit?: number; order?: string; direction?: string; - metadata?: ThingsResponseMetadata; - things?: Thing[]; - } - - interface ThingsResponseMetadata { - total_connected?: number; - total_filter?: number; - total_sos?: number; - total_state_level_0?: number; - total_state_level_1?: number; - total_state_level_2?: number; - total_thing?: number; - } - - interface Thing { - id?: string; - name?: string; - key?: string; - metadata?: T; - } - - // Thing Policy - interface ThingPolicyResponse { - total?: number; - offset?: number; - limit?: number; - order?: string; - direction?: string; - metadata?: null; - things?: ThingPolicy[]; - } - - interface ThingPolicy { - policies?: Policy[]; - thing_id?: string; - thing_name?: string; - external_id?: string; - } - - type Policy = 'read' | 'delete' | 'write'; - - // Group - - interface GroupBodyRequest { - id?: string; - name: string; - parent_id?: string; - description?: string; - metadata?: GroupMetadata; - } - interface AddGroupBodyResponse extends Partial { - id: string; - } - - interface GroupMetadata { - code?: string; - short_name?: string; - has_thing?: boolean; - [key: string]: unknown; - } - - interface GroupResponse { - total?: number; - level?: number; - name?: string; - groups?: GroupNode[]; - } - interface GroupNode { - id: string; - name: string; - owner_id: string; - description: string; - metadata: GroupMetadata; - level: number; - path: string; - parent_id?: string; - created_at?: string; - updated_at?: string; - children?: GroupNode[]; // Đệ quy: mỗi node có thể có children là mảng GroupNode - [key: string]: any; // Nếu có thêm trường động - } - - interface GroupQueryParams { - level?: number; - tree?: boolean; - [key: string]: any; - } - - // Log - - type LogTypeRequest = 'user_logs' | undefined; - - interface LogResponse { - offset?: number; - limit?: number; - publisher?: string; - from?: number; - to?: number; - format?: string; - total?: number; - messages?: Message[]; - } - - interface Message { - channel?: string; - subtopic?: string; - publisher?: string; - protocol?: string; - name?: string; - time?: number; - string_value?: string; - } - - // User - - interface AssignMemberRequest { - group_id: string; - type: 'users' | 'admin' | 'things' | undefined; - members: string[]; } } diff --git a/src/services/master/typings/alarm.d.ts b/src/services/master/typings/alarm.d.ts new file mode 100644 index 0000000..31ec180 --- /dev/null +++ b/src/services/master/typings/alarm.d.ts @@ -0,0 +1,36 @@ +declare namespace MasterModel { + interface SearchAlarmPaginationBody extends SearchPaginationBody { + order?: 'name' | undefined; + thing_name?: string; + thing_id?: string; + level?: number; + } + interface AlarmsResponse { + total?: number; + limit?: number; + order?: string; + dir?: string; + alarms?: Alarm[]; + } + + interface ConfirmAlarmRequest { + id?: string; + description?: string; + thing_id?: string; + time?: number; + } + // Alarm + interface Alarm { + name?: string; + time?: number; + level?: number; + id?: string; + confirmed?: boolean; + confirmed_email?: string; + confirmed_time?: number; + confirmed_desc?: string; + thing_id?: string; + thing_name?: string; + thing_type?: string; + } +} diff --git a/src/services/master/typings/auth.d.ts b/src/services/master/typings/auth.d.ts new file mode 100644 index 0000000..edb3fa7 --- /dev/null +++ b/src/services/master/typings/auth.d.ts @@ -0,0 +1,11 @@ +declare namespace MasterModel { + interface LoginRequestBody { + guid: string; + email: string; + password: string; + } + + interface LoginResponse { + token?: string; + } +} diff --git a/src/services/master/typings/group.d.ts b/src/services/master/typings/group.d.ts new file mode 100644 index 0000000..3d9f1ca --- /dev/null +++ b/src/services/master/typings/group.d.ts @@ -0,0 +1,52 @@ +declare namespace MasterModel { + interface GroupBodyRequest { + id?: string; + name: string; + parent_id?: string; + description?: string; + metadata?: GroupMetadata; + } + interface AddGroupBodyResponse extends Partial { + id: string; + } + + interface GroupMetadata { + code?: string; + short_name?: string; + has_thing?: boolean; + [key: string]: unknown; + } + + interface GroupResponse { + total?: number; + level?: number; + name?: string; + groups?: GroupNode[]; + } + interface GroupNode { + id: string; + name: string; + owner_id: string; + description: string; + metadata: GroupMetadata; + level: number; + path: string; + parent_id?: string; + created_at?: string; + updated_at?: string; + children?: GroupNode[]; // Đệ quy: mỗi node có thể có children là mảng GroupNode + [key: string]: any; // Nếu có thêm trường động + } + + interface GroupQueryParams { + level?: number; + tree?: boolean; + [key: string]: any; + } + + interface AssignMemberRequest { + group_id: string; + type: 'users' | 'admin' | 'things' | undefined; + members: string[]; + } +} diff --git a/src/services/master/typings/log.d.ts b/src/services/master/typings/log.d.ts new file mode 100644 index 0000000..641d4f2 --- /dev/null +++ b/src/services/master/typings/log.d.ts @@ -0,0 +1,31 @@ +declare namespace MasterModel { + interface SearchLogPaginationBody extends SearchPaginationBody { + from?: number; + to?: number; + publisher?: string; + subtopic?: string; + } + + type LogTypeRequest = 'user_logs' | undefined; + + interface LogResponse { + offset?: number; + limit?: number; + publisher?: string; + from?: number; + to?: number; + format?: string; + total?: number; + messages?: Message[]; + } + + interface Message { + channel?: string; + subtopic?: string; + publisher?: string; + protocol?: string; + name?: string; + time?: number; + string_value?: string; + } +} diff --git a/src/services/master/typings/thing.d.ts b/src/services/master/typings/thing.d.ts new file mode 100644 index 0000000..6c9bb01 --- /dev/null +++ b/src/services/master/typings/thing.d.ts @@ -0,0 +1,73 @@ +declare namespace MasterModel { + interface SearchThingPaginationBody + extends SearchPaginationBody { + order?: string; + metadata?: T; + } + + interface SearchThingMetadata { + external_id?: string; + state_level?: string; // vd: "normal,warning,critical,sos" + /** kết nối */ + connected?: boolean; + group_id?: string; // "id1,id2,id3" + } + + // Thing + interface ThingReponseMetadata { + address?: string; + alarm_list?: string; + cfg_channel_id?: string; + connected?: boolean; + ctrl_channel_id?: string; + data_channel_id?: string; + enduser?: string; + external_id?: string; + group_id?: string; + req_channel_id?: string; + state?: string; + state_level?: number; + state_updated_time?: number; + type?: string; + updated_time?: number; + } + + interface ThingsResponse< + T extends ThingReponseMetadata = ThingReponseMetadata, + > extends PaginationReponse { + metadata?: ThingsResponseMetadata; + things?: Thing[]; + } + + interface ThingsResponseMetadata { + total_connected?: number; + total_filter?: number; + total_sos?: number; + total_state_level_0?: number; + total_state_level_1?: number; + total_state_level_2?: number; + total_thing?: number; + } + + interface Thing { + id?: string; + name?: string; + key?: string; + metadata?: T; + } + + // Thing Policy + interface ThingPolicyResponse extends PaginationReponse { + metadata?: null; + things?: ThingPolicy[]; + } + + interface ThingPolicy { + policies?: Policy[]; + thing_id?: string; + thing_name?: string; + external_id?: string; + } + + type Policy = 'read' | 'delete' | 'write'; +} diff --git a/src/services/master/typings/user.d.ts b/src/services/master/typings/user.d.ts new file mode 100644 index 0000000..a23edb6 --- /dev/null +++ b/src/services/master/typings/user.d.ts @@ -0,0 +1,44 @@ +declare namespace MasterModel { + interface SearchUserPaginationBody extends SearchPaginationBody { + order?: 'email' | 'name' | undefined; + email?: string; + metadata?: Partial; + } + interface ChangePasswordRequestBody { + old_password: string; + password: string; + } + + interface UserResponse { + id?: string; + email?: string; + metadata?: UserMetadata; + } + + interface UserMetadata { + frontend_thing_id?: string; + frontend_thing_key?: string; + full_name?: string; + phone_number?: string; + telegram?: string; + user_type?: 'admin' | 'enduser' | 'sysadmin' | 'users'; + } + + // User + interface CreateUserMetadata extends UserMetadata { + group_id?: string; + } + + interface CreateUserBodyRequest extends Partial { + password: string; + full_name?: string; + metadata?: CreateUserMetadata; + } + + interface UserListResponse { + total?: number; + offset?: number; + limit?: number; + users: UserResponse[]; + } +} diff --git a/src/services/slave/sgw/ZoneController.ts b/src/services/slave/sgw/ZoneController.ts index 28bf538..7463230 100644 --- a/src/services/slave/sgw/ZoneController.ts +++ b/src/services/slave/sgw/ZoneController.ts @@ -42,7 +42,7 @@ export async function apiGetZoneById( * Create a new banzone * @param body Banzone data */ -export async function apiCreateBanzone(body: SgwModel.ZoneBodyRequest) { +export async function apiCreateBanzone(body: SgwModel.ZoneBasicInfo) { return request(SGW_ROUTE_BANZONES, { method: 'POST', data: body, @@ -56,7 +56,7 @@ export async function apiCreateBanzone(body: SgwModel.ZoneBodyRequest) { */ export async function apiUpdateBanzone( id: string, - body: SgwModel.ZoneBodyRequest, + body: SgwModel.ZoneBasicInfo, ) { return request(`${SGW_ROUTE_BANZONES}/${id}`, { method: 'PUT', diff --git a/src/services/slave/sgw/sgw.typing.d.ts b/src/services/slave/sgw/sgw.typing.d.ts index e775e68..c78a891 100644 --- a/src/services/slave/sgw/sgw.typing.d.ts +++ b/src/services/slave/sgw/sgw.typing.d.ts @@ -3,7 +3,7 @@ declare namespace SgwModel { // Thing - interface ThingMedata extends MasterModel.ThingMetadata { + interface ThingMedata extends MasterModel.ThingReponseMetadata { gps?: string; gps_time?: string; ship_group_id?: string; @@ -23,442 +23,4 @@ declare namespace SgwModel { type SgwThingsResponse = MasterModel.ThingsResponse; type SgwThing = MasterModel.Thing; - - // Ship - interface ShipMetadata { - crew_count?: number; - home_port?: string; - home_port_point?: string; - ship_type?: string; - trip_arrival_port?: string; - trip_arrival_port_point?: string; - trip_arrival_time?: Date; - trip_depart_port?: string; - trip_depart_port_point?: string; - trip_departure_time?: Date; - trip_id?: string; - trip_name?: string; - trip_state?: number; - } - - interface ShipType { - id?: number; - name?: string; - description?: string; - } - - interface ShipDetail { - id?: string; - thing_id?: string; - owner_id?: string; - name?: string; - ship_type?: number; - home_port?: number; - ship_length?: number; - ship_power?: number; - reg_number?: string; - imo_number?: string; - mmsi_number?: string; - fishing_license_number?: string; - fishing_license_expiry_date?: Date | string; - province_code?: string; - ship_group_id?: string | null; - created_at?: Date | string; - updated_at?: Date | string; - metadata?: ShipMetadata; - } - - interface ShipCreateParams { - thing_id?: string; - name?: string; - reg_number?: string; - ship_type?: number; - ship_length?: number; - ship_power?: number; - ship_group_id?: string; - home_port?: number; - fishing_license_number?: string; - fishing_license_expiry_date?: string; - } - - interface ShipUpdateParams { - name?: string; - reg_number?: string; - ship_type?: number; - ship_group_id?: string | null; - ship_length?: number; - ship_power?: number; - home_port?: number; - fishing_license_number?: string; - fishing_license_expiry_date?: string; - metadata?: Record; - } - - interface ShipQueryParams { - offset?: number; - limit?: number; - order?: string; - dir?: 'asc' | 'desc'; - name?: string; - registration_number?: string; - ship_type?: number; - ship_group_id?: string; - thing_id?: string; - } - - interface ShipQueryResponse { - ships: ShipDetail[]; - total: number; - offset: number; - limit: number; - } - - interface ShipsResponse { - total?: number; - offset?: number; - limit?: number; - order?: string; - direction?: string; - Ship?: Ship[]; - } - - // Ship Group - interface ShipGroup { - id: string; - name: string; - owner_id?: string; - description?: string; - created_at?: Date | string; - updated_at?: Date | string; - metadata?: Record; - } - - interface GroupShipResponse { - id?: string; - name?: string; - owner_id?: string; - description?: string; - } - - interface ShipGroupCreateParams { - name: string; - description?: string; - metadata?: Record; - } - - interface ShipGroupUpdateParams { - name?: string; - description?: string; - metadata?: Record; - } - - // Port - interface Port { - id: number; - name: string; - type: string; - classification: string; - position_point: string; - has_origin_confirm: boolean; - province_code: string; - updated_at: string; - is_deleted: boolean; - } - - interface PortQueryParams { - name?: string; - order?: string; - dir?: 'asc' | 'desc'; - limit?: number; - offset?: number; - metadata?: { - province_code?: string; - }; - } - - interface PortQueryResponse { - total: number; - offset: number; - limit: number; - ports: Port[]; - } - - // Trip Management - interface FishingGear { - name: string; - number: string; - } - - interface TripCost { - type: string; - unit: string; - amount: string; - total_cost: string; - cost_per_unit: string; - } - - interface TripCrewPerson { - personal_id: string; - name: string; - phone: string; - email: string; - birth_date: Date; - note: string; - address: string; - created_at: Date; - updated_at: Date; - } - - interface TripCrews { - role: string; - joined_at: Date; - left_at: Date | null; - note: string | null; - Person: TripCrewPerson; - } - - interface CrewCreateParams { - personal_id: string; - name: string; - phone?: string; - email?: string; - birth_date?: string; - address?: string; - note?: string; - } - - interface CrewUpdateParams { - name?: string; - phone?: string; - email?: string; - birth_date?: string; - address?: string; - note?: string; - } - - interface TripCrewCreateParams { - trip_id: string; - personal_id: string; - role: string; - note?: string; - } - - interface TripCrewUpdateParams { - trip_id: string; - personal_id: string; - role?: string; - note?: string; - } - - interface TripCrewQueryResponse { - trip_crews: TripCrews[]; - total?: number; - } - - interface FishingLogInfo { - fish_species_id?: number; - fish_name?: string; - catch_number?: number; - catch_unit?: string; - fish_size?: number; - fish_rarity?: number; - fish_condition?: string; - gear_usage?: string; - } - - interface FishingLog { - fishing_log_id?: string; - trip_id: string; - start_at: Date; - end_at: Date; - start_lat: number; - start_lon: number; - haul_lat: number; - haul_lon: number; - status: number; - weather_description: string; - info?: FishingLogInfo[]; - sync: boolean; - } - - interface NewFishingLogRequest { - trip_id: string; - start_at: Date; - start_lat: number; - start_lon: number; - weather_description: string; - } - - interface FishSpecies { - id: number; - name: string; - scientific_name?: string; - description?: string; - } - - interface FishSpeciesResponse extends FishSpecies {} - - interface Trip { - id: string; - ship_id: string; - ship_length: number; - vms_id: string; - name: string; - fishing_gears: FishingGear[]; - crews?: TripCrews[]; - departure_time: string; - departure_port_id: number; - arrival_time: string; - arrival_port_id: number; - fishing_ground_codes: number[]; - total_catch_weight: number | null; - total_species_caught: number | null; - trip_cost: TripCost[]; - trip_status: number; - approved_by: string; - notes: string | null; - fishing_logs: FishingLog[] | null; - sync: boolean; - } - - interface TripUpdateStateRequest { - status: number; - note?: string; - } - - interface TripCreateParams { - name: string; - departure_time: string; - departure_port_id: number; - arrival_time?: string; - arrival_port_id?: number; - fishing_ground_codes?: number[]; - fishing_gears?: FishingGear[]; - crews?: TripCrews[]; - trip_cost?: TripCost[]; - notes?: string; - } - - interface TripFormValues { - name: string; - departure_time: any; // dayjs object or string - departure_port_id: number; - arrival_time?: any; // dayjs object or string - arrival_port_id?: number; - fishing_ground_codes?: number[]; - fishing_gear?: FishingGear[]; - trip_cost?: TripCost[]; - ship_id?: string; - } - - interface TripQueryParams { - name?: string; - order?: string; - dir?: 'asc' | 'desc'; - offset?: number; - limit?: number; - metadata?: { - from?: string; - to?: string; - ship_name?: string; - reg_number?: string; - province_code?: string; - owner_id?: string; - ship_id?: string; - status?: string; - }; - } - - interface TripQueryResponse { - trips: Trip[]; - total: number; - offset: number; - limit: number; - } - - interface TripUpdateParams { - name?: string; - departure_time?: string; - departure_port_id?: number; - arrival_time?: string; - arrival_port_id?: number; - fishing_ground_codes?: number[]; - fishing_gears?: FishingGear[]; - crews?: TripCrews[]; - trip_cost?: TripCost[]; - trip_status?: number; - notes?: string; - } - - interface TripDeleteParams { - trip_ids: string[]; - } - - // Photo Management - interface PhotoGetParams { - type: 'ship' | 'people'; - id: string; - } - - interface PhotoUploadParams { - type: 'ship' | 'people'; - id: string; - file: File; - } - - // Banzone Management - interface Banzone { - id?: string; - name?: string; - province_code?: string; - type?: number; - conditions?: Condition[]; - description?: string; - geometry?: string; - enabled?: boolean; - created_at?: Date; - updated_at?: Date; - } - - interface Condition { - max?: number; - min?: number; - type?: 'length_limit' | 'month_range' | 'date_range'; - to?: number; - from?: number; - } - - interface Geom { - geom_type?: number; - geom_poly?: string; - geom_lines?: string; - geom_point?: string; - geom_radius?: number; - } - - interface ZoneResponse { - total?: number; - offset?: number; - limit?: number; - banzones?: Banzone[]; - } - - interface ZoneBodyRequest { - name?: string; - province_code?: string; - type?: number; - conditions?: Condition[]; - description?: string; - geometry?: string; - enabled?: boolean; - } -} - -declare namespace WsTypes { - interface WsThingResponse { - thing_id?: string; - key?: string; - data?: string; - time?: number; - } } diff --git a/src/services/slave/sgw/typings/crew.d.ts b/src/services/slave/sgw/typings/crew.d.ts new file mode 100644 index 0000000..57ce375 --- /dev/null +++ b/src/services/slave/sgw/typings/crew.d.ts @@ -0,0 +1,17 @@ +declare namespace SgwModel { + interface CrewBaseInfo { + name: string; + phone?: string; + email?: string; + birth_date?: string; + address?: string; + note?: string; + } + interface CrewCreateParams extends CrewBaseInfo { + personal_id?: string; + } + + interface CrewUpdateParams extends Partial { + personal_id?: string; + } +} diff --git a/src/services/slave/sgw/typings/fish.d.ts b/src/services/slave/sgw/typings/fish.d.ts new file mode 100644 index 0000000..054fa27 --- /dev/null +++ b/src/services/slave/sgw/typings/fish.d.ts @@ -0,0 +1,8 @@ +declare namespace SgwModel { + interface FishSpeciesResponse { + id: number; + name: string; + scientific_name?: string; + description?: string; + } +} diff --git a/src/services/slave/sgw/typings/fishing_log.d.ts b/src/services/slave/sgw/typings/fishing_log.d.ts new file mode 100644 index 0000000..895c5b8 --- /dev/null +++ b/src/services/slave/sgw/typings/fishing_log.d.ts @@ -0,0 +1,35 @@ +declare namespace SgwModel { + interface FishingLogInfo { + fish_species_id?: number; + fish_name?: string; + catch_number?: number; + catch_unit?: string; + fish_size?: number; + fish_rarity?: number; + fish_condition?: string; + gear_usage?: string; + } + + interface FishingLog { + fishing_log_id?: string; + trip_id: string; + start_at: Date; + end_at: Date; + start_lat: number; + start_lon: number; + haul_lat: number; + haul_lon: number; + status: number; + weather_description: string; + info?: FishingLogInfo[]; + sync: boolean; + } + + interface NewFishingLogRequest { + trip_id: string; + start_at: Date; + start_lat: number; + start_lon: number; + weather_description: string; + } +} diff --git a/src/services/slave/sgw/typings/gear.d.ts b/src/services/slave/sgw/typings/gear.d.ts new file mode 100644 index 0000000..9766f73 --- /dev/null +++ b/src/services/slave/sgw/typings/gear.d.ts @@ -0,0 +1,6 @@ +declare namespace SgwModel { + interface FishingGear { + name: string; + number: string; + } +} diff --git a/src/services/slave/sgw/typings/photo.d.ts b/src/services/slave/sgw/typings/photo.d.ts new file mode 100644 index 0000000..1653f42 --- /dev/null +++ b/src/services/slave/sgw/typings/photo.d.ts @@ -0,0 +1,17 @@ +declare namespace SgwModel { + // interface PhotoBasicInfo { + // type: 'ship' | 'people'; + // id: string; + // } + + interface PhotoGetParams { + type: 'ship' | 'people'; + id: string; + } + + interface PhotoUploadParams { + type: 'ship' | 'people'; + id: string; + file: File; + } +} diff --git a/src/services/slave/sgw/typings/port.d.ts b/src/services/slave/sgw/typings/port.d.ts new file mode 100644 index 0000000..c79a98c --- /dev/null +++ b/src/services/slave/sgw/typings/port.d.ts @@ -0,0 +1,24 @@ +declare namespace SgwModel { + interface Port { + id: number; + name: string; + type: string; + classification: string; + position_point: string; + has_origin_confirm: boolean; + province_code: string; + updated_at: string; + is_deleted: boolean; + } + + interface PortQueryParams extends MasterModel.SearchPaginationBody { + order?: string; + metadata?: { + province_code?: string; + }; + } + + interface PortQueryResponse extends MasterModel.PaginationReponse { + ports: Port[]; + } +} diff --git a/src/services/slave/sgw/typings/ship.d.ts b/src/services/slave/sgw/typings/ship.d.ts new file mode 100644 index 0000000..aa25051 --- /dev/null +++ b/src/services/slave/sgw/typings/ship.d.ts @@ -0,0 +1,89 @@ +declare namespace SgwModel { + interface ShipBaseInfo { + name?: string; + reg_number?: string; + ship_type?: number; + ship_length?: number; + ship_power?: number; + home_port?: number; + fishing_license_number?: string; + fishing_license_expiry_date?: string; + ship_group_id?: string | null; // Lưu ý: Update có thể cần null để gỡ nhóm + } + + interface ShipType { + id?: number; + name?: string; + description?: string; + } + interface ShipMetadata { + crew_count?: number; + home_port?: string; + home_port_point?: string; + ship_type?: string; + trip_arrival_port?: string; + trip_arrival_port_point?: string; + trip_arrival_time?: Date; + trip_depart_port?: string; + trip_depart_port_point?: string; + trip_departure_time?: Date; + trip_id?: string; + trip_name?: string; + trip_state?: number; + } + + interface ShipDetail extends ShipBaseInfo { + id?: string; + thing_id?: string; + owner_id?: string; + imo_number?: string; + mmsi_number?: string; + province_code?: string; + created_at?: Date | string; + updated_at?: Date | string; + metadata?: ShipMetadata; + } + interface ShipCreateParams extends ShipBaseInfo { + thing_id?: string; + } + interface ShipUpdateParams { + metadata?: Record; + } + interface ShipQueryParams extends MasterModel.SearchPaginationBody { + order?: string; + registration_number?: string; + ship_type?: number; + ship_group_id?: string; + thing_id?: string; + } + interface ShipQueryResponse extends MasterModel.PaginationReponse { + ships: ShipDetail[]; + } + + // Ship Group + + interface ShipGroupBaseInfo { + name: string; + owner_id?: string; + description?: string; + } + + interface ShipGroup extends ShipGroupBaseInfo { + id: string; + created_at?: Date | string; + updated_at?: Date | string; + metadata?: Record; + } + + interface GroupShipResponse extends ShipGroupBaseInfo { + id?: string; + } + + interface ShipGroupCreateParams extends ShipGroupBaseInfo { + metadata?: Record; + } + + interface ShipGroupUpdateParams extends ShipGroupBaseInfo { + metadata?: Record; + } +} diff --git a/src/services/slave/sgw/typings/trip.d.ts b/src/services/slave/sgw/typings/trip.d.ts new file mode 100644 index 0000000..31fa836 --- /dev/null +++ b/src/services/slave/sgw/typings/trip.d.ts @@ -0,0 +1,111 @@ +declare namespace SgwModel { + interface TripBasicInfo { + name: string; + departure_time: string; + arrival_time: string; + trip_status: number; + ship_name: string; + departure_port_id: number; + arrival_port_id: number; + fishing_ground_codes: number[]; + trip_cost: TripCost[]; + notes: string | null; + fishing_gears?: FishingGear[]; + } + + interface Trip extends TripBasicInfo { + id: string; + ship_id: string; + ship_length: number; + vms_id: string; + crews?: TripCrews[]; + total_catch_weight: number | null; + total_species_caught: number | null; + trip_status: number; + approved_by: string; + fishing_logs: FishingLog[] | null; + sync: boolean; + } + interface TripQueryParams extends MasterModel.SearchPaginationBody { + order?: string; + metadata?: { + from?: string; + to?: string; + ship_name?: string; + reg_number?: string; + province_code?: string; + owner_id?: string; + ship_id?: string; + status?: string; + }; + } + + interface TripQueryResponse extends MasterModel.PaginationReponse { + trips: Trip[]; + } + + interface TripUpdateParams extends Partial { + crews?: TripCrews[]; + } + + interface TripDeleteParams { + trip_ids: string[]; + } + + interface TripUpdateStateRequest { + status: number; + note?: string; + } + + interface TripCreateParams extends Partial { + crews?: TripCrews[]; + } + + interface TripCost { + type: string; + unit: string; + amount: string; + total_cost: string; + cost_per_unit: string; + } + + // Trip Crews + + interface TripCrewPerson { + personal_id: string; + name: string; + phone: string; + email: string; + birth_date: Date; + note: string; + address: string; + created_at: Date; + updated_at: Date; + } + + interface TripCrews { + role: string; + joined_at: Date; + left_at: Date | null; + note: string | null; + Person: TripCrewPerson; + } + + interface TripCrewBasicInfo { + personal_id: string; + role: string; + note?: string; + } + + interface TripCrewCreateParams extends Partial { + trip_id: string; + } + + interface TripCrewUpdateParams extends Partial { + trip_id: string; + } + interface TripCrewQueryResponse { + trip_crews: TripCrews[]; + total?: number; + } +} diff --git a/src/services/slave/sgw/typings/ws.d.ts b/src/services/slave/sgw/typings/ws.d.ts new file mode 100644 index 0000000..faf1947 --- /dev/null +++ b/src/services/slave/sgw/typings/ws.d.ts @@ -0,0 +1,8 @@ +declare namespace WsTypes { + interface WsThingResponse { + thing_id?: string; + key?: string; + data?: string; + time?: number; + } +} diff --git a/src/services/slave/sgw/typings/zone.d.ts b/src/services/slave/sgw/typings/zone.d.ts new file mode 100644 index 0000000..c2dadd0 --- /dev/null +++ b/src/services/slave/sgw/typings/zone.d.ts @@ -0,0 +1,38 @@ +declare namespace SgwModel { + // Banzone Management + interface ZoneBasicInfo { + name?: string; + province_code?: string; + type?: number; + conditions?: Condition[]; + description?: string; + geometry?: string; + enabled?: boolean; + } + + interface Banzone extends ZoneBasicInfo { + id?: string; + created_at?: Date; + updated_at?: Date; + } + + interface Condition { + max?: number; + min?: number; + type?: 'length_limit' | 'month_range' | 'date_range'; + to?: number; + from?: number; + } + + interface Geom { + geom_type?: number; + geom_poly?: string; + geom_lines?: string; + geom_point?: string; + geom_radius?: number; + } + + interface ZoneResponse extends Partial { + banzones?: Banzone[]; + } +} diff --git a/src/services/slave/spole/spole.typings.d.ts b/src/services/slave/spole/spole.typings.d.ts index 62d89da..2dfd69f 100644 --- a/src/services/slave/spole/spole.typings.d.ts +++ b/src/services/slave/spole/spole.typings.d.ts @@ -2,8 +2,11 @@ // 该文件由 OneAPI 自动生成,请勿手动修改! declare namespace SpoleModel { - interface ThingMedata extends MasterModel.ThingMetadata {} + interface ThingMetdadata extends MasterModel.ThingReponseMetadata { + uptime?: number; + } - type SpoleThingsResponse = MasterModel.ThingsResponse; - type SpoleThing = MasterModel.Thing; + type SpoleThingsResponse = + MasterModel.ThingsResponse; + type SpoleThing = MasterModel.Thing; }