Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
thecybernanny-webapp
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
asranov0003
thecybernanny-webapp
Commits
819fc823
Commit
819fc823
authored
Jul 09, 2025
by
asranov0003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add select device and seperate types
parent
52fcdefa
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
43 additions
and
80 deletions
+43
-80
Header.tsx
src/layouts/Header/Header.tsx
+18
-27
deviceSlice.ts
src/stores/slices/deviceSlice.ts
+11
-53
device.types.ts
src/types/device.types.ts
+14
-0
No files found.
src/layouts/Header/Header.tsx
View file @
819fc823
...
@@ -2,11 +2,9 @@ import React, { useState, useRef, useEffect } from "react";
...
@@ -2,11 +2,9 @@ import React, { useState, useRef, useEffect } from "react";
import
"./Header.css"
;
import
"./Header.css"
;
import
{
import
{
IoNotificationsOutline
,
IoNotificationsOutline
,
IoPhonePortraitOutline
,
IoReload
,
IoReload
,
IoSettingsOutline
,
IoSettingsOutline
,
}
from
"react-icons/io5"
;
}
from
"react-icons/io5"
;
import
CSelect
from
"../../components/CSelect"
;
import
{
GrMenu
}
from
"react-icons/gr"
;
import
{
GrMenu
}
from
"react-icons/gr"
;
import
{
NavLink
}
from
"react-router-dom"
;
import
{
NavLink
}
from
"react-router-dom"
;
import
{
MdLogout
}
from
"react-icons/md"
;
import
{
MdLogout
}
from
"react-icons/md"
;
...
@@ -21,35 +19,24 @@ import {
...
@@ -21,35 +19,24 @@ import {
import
{
accountSession
}
from
"../../stores/slices/accountSlice"
;
import
{
accountSession
}
from
"../../stores/slices/accountSlice"
;
import
{
FaUserAlt
}
from
"react-icons/fa"
;
import
{
FaUserAlt
}
from
"react-icons/fa"
;
import
{
formatToDDMMYYYY
}
from
"../../utils/utils"
;
import
{
formatToDDMMYYYY
}
from
"../../utils/utils"
;
import
{
const
devices
=
[
fetchDevices
,
{
setSelectedDevice
,
label
:
"iPhone 15 Pro"
,
}
from
"../../stores/slices/deviceSlice"
;
value
:
"iphone-15-pro"
,
image
:
<
IoPhonePortraitOutline
/>,
},
{
label
:
"iPhone 14 Pro"
,
value
:
"iphone-14-pro"
,
image
:
<
IoPhonePortraitOutline
/>,
},
{
label
:
"iPhone 13 Pro"
,
value
:
"iphone-13-pro"
,
image
:
<
IoPhonePortraitOutline
/>,
},
];
const
Header
:
React
.
FC
=
()
=>
{
const
Header
:
React
.
FC
=
()
=>
{
const
[
selectedDevice
,
setSelectedDevice
]
=
useState
(
devices
[
0
].
value
);
const
[
isSidebarOpen
,
setIsSidebarOpen
]
=
useState
(
false
);
const
[
isSidebarOpen
,
setIsSidebarOpen
]
=
useState
(
false
);
const
sidebarRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
sidebarRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
dispatch
=
useAppDispatch
();
const
dispatch
=
useAppDispatch
();
const
{
session
}
=
useAppSelector
((
state
:
RootState
)
=>
state
.
account
);
const
{
session
}
=
useAppSelector
((
state
:
RootState
)
=>
state
.
account
);
const
{
devices
,
selectedDevice
}
=
useAppSelector
(
(
state
:
RootState
)
=>
state
.
device
);
useEffect
(()
=>
{
useEffect
(()
=>
{
dispatch
(
accountSession
());
dispatch
(
accountSession
());
dispatch
(
fetchDevices
());
},
[
dispatch
]);
},
[
dispatch
]);
const
handleOutsideClick
=
(
e
:
MouseEvent
)
=>
{
const
handleOutsideClick
=
(
e
:
MouseEvent
)
=>
{
...
@@ -86,12 +73,16 @@ const Header: React.FC = () => {
...
@@ -86,12 +73,16 @@ const Header: React.FC = () => {
</
div
>
</
div
>
<
div
className=
"header__devices"
>
<
div
className=
"header__devices"
>
<
CSelect
<
select
options=
{
devices
}
onChange=
{
(
e
)
=>
dispatch
(
setSelectedDevice
(
e
.
target
.
value
))
}
value=
{
selectedDevice
}
defaultValue=
{
selectedDevice
?.
id
}
onChange=
{
setSelectedDevice
}
>
style=
{
{
width
:
"250px"
}
}
{
devices
.
map
((
device
)
=>
(
/>
<
option
key=
{
device
.
id
}
value=
{
device
.
id
}
>
{
device
.
name
}
</
option
>
))
}
</
select
>
</
div
>
</
div
>
</
div
>
</
div
>
...
...
src/stores/slices/deviceSlice.ts
View file @
819fc823
import
{
createAsyncThunk
,
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
createAsyncThunk
,
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
sendRpcRequest
}
from
"../../services/apiClient"
;
import
{
sendRpcRequest
}
from
"../../services/apiClient"
;
import
type
{
IDevice
}
from
"../../types/device.types"
;
interface
IDevice
{
id
:
string
;
name
:
string
;
created
:
string
;
lastOnline
:
string
;
osOver
:
string
;
deviceInfo
:
{
date_updated
:
string
;
permissions
:
{
isGranted
:
boolean
;
permission
:
string
;
}[];
};
}
interface
IDeviceState
{
interface
IDeviceState
{
device
:
IDevice
|
null
;
devices
:
IDevice
[];
devices
:
IDevice
[];
loadingDevice
:
boolean
;
selectedDevice
:
IDevice
|
null
;
loadingDevices
:
boolean
;
loadingDevices
:
boolean
;
errorDevice
:
string
;
errorDevices
:
string
;
errorDevices
:
string
;
}
}
const
initialState
:
IDeviceState
=
{
const
initialState
:
IDeviceState
=
{
device
:
null
,
devices
:
[],
devices
:
[],
loadingDevice
:
false
,
selectedDevice
:
null
,
loadingDevices
:
false
,
loadingDevices
:
false
,
errorDevice
:
""
,
errorDevices
:
""
,
errorDevices
:
""
,
};
};
export
const
fetchDevice
=
createAsyncThunk
(
"device/fetchDevice"
,
async
(
deviceId
:
string
,
{
rejectWithValue
})
=>
{
try
{
const
response
=
await
sendRpcRequest
<
IDevice
>
(
"devices.getdevicedata"
,
{
deviceId
,
});
return
response
;
}
catch
(
error
:
unknown
)
{
if
(
typeof
error
===
"object"
&&
error
!==
null
&&
"message"
in
error
)
{
return
rejectWithValue
(
error
.
message
);
}
return
rejectWithValue
(
"Unknown error occurred"
);
}
}
);
export
const
fetchDevices
=
createAsyncThunk
(
export
const
fetchDevices
=
createAsyncThunk
(
"device/fetchDevices"
,
"device/fetchDevices"
,
async
(
_
,
{
rejectWithValue
})
=>
{
async
(
_
,
{
rejectWithValue
})
=>
{
...
@@ -75,27 +38,21 @@ export const fetchDevices = createAsyncThunk(
...
@@ -75,27 +38,21 @@ export const fetchDevices = createAsyncThunk(
const
deviceSlice
=
createSlice
({
const
deviceSlice
=
createSlice
({
name
:
"device"
,
name
:
"device"
,
initialState
,
initialState
,
reducers
:
{},
reducers
:
{
setSelectedDevice
:
(
state
,
action
)
=>
{
const
selected
=
state
.
devices
.
find
((
d
)
=>
d
.
id
===
action
.
payload
);
state
.
selectedDevice
=
selected
??
null
;
},
},
extraReducers
:
(
builder
)
=>
{
extraReducers
:
(
builder
)
=>
{
builder
builder
.
addCase
(
fetchDevice
.
pending
,
(
state
)
=>
{
state
.
loadingDevice
=
true
;
})
.
addCase
(
fetchDevice
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingDevice
=
false
;
state
.
device
=
action
.
payload
;
})
.
addCase
(
fetchDevice
.
rejected
,
(
state
,
action
)
=>
{
state
.
loadingDevice
=
false
;
state
.
errorDevice
=
action
.
payload
as
string
;
})
.
addCase
(
fetchDevices
.
pending
,
(
state
)
=>
{
.
addCase
(
fetchDevices
.
pending
,
(
state
)
=>
{
state
.
loadingDevices
=
true
;
state
.
loadingDevices
=
true
;
})
})
.
addCase
(
fetchDevices
.
fulfilled
,
(
state
,
action
)
=>
{
.
addCase
(
fetchDevices
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingDevices
=
false
;
state
.
loadingDevices
=
false
;
state
.
devices
=
action
.
payload
;
state
.
devices
=
action
.
payload
;
state
.
selectedDevice
=
action
.
payload
[
0
]
??
null
;
})
})
.
addCase
(
fetchDevices
.
rejected
,
(
state
,
action
)
=>
{
.
addCase
(
fetchDevices
.
rejected
,
(
state
,
action
)
=>
{
state
.
loadingDevices
=
false
;
state
.
loadingDevices
=
false
;
...
@@ -104,4 +61,5 @@ const deviceSlice = createSlice({
...
@@ -104,4 +61,5 @@ const deviceSlice = createSlice({
},
},
});
});
export
const
{
setSelectedDevice
}
=
deviceSlice
.
actions
;
export
default
deviceSlice
.
reducer
;
export
default
deviceSlice
.
reducer
;
src/types/device.types.ts
0 → 100644
View file @
819fc823
export
interface
IDevice
{
id
:
string
;
name
:
string
;
created
:
string
;
lastOnline
:
string
;
osOver
:
string
;
deviceInfo
:
{
date_updated
:
string
;
permissions
:
{
isGranted
:
boolean
;
permission
:
string
;
}[];
};
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment