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
b4eebb44
Commit
b4eebb44
authored
Sep 24, 2025
by
asranov0003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: make delete edit and refresh device
parent
7ad3afd9
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
238 additions
and
5 deletions
+238
-5
en.json
src/locales/en/en.json
+4
-1
ru.json
src/locales/ru/ru.json
+4
-1
uz.json
src/locales/uz/uz.json
+4
-1
Settings.tsx
src/pages/Settings/Settings.tsx
+166
-2
deviceSlice.ts
src/stores/slices/deviceSlice.ts
+60
-0
No files found.
src/locales/en/en.json
View file @
b4eebb44
...
...
@@ -20,7 +20,10 @@
"pageInDevelopment"
:
"Page in Development"
,
"pageInDevelopmentDesc"
:
"This page is currently under development. Use the application to view recordings."
,
"hours"
:
"Hours"
,
"minutes"
:
"Minutes"
"minutes"
:
"Minutes"
,
"rename"
:
"Rename"
,
"deviceName"
:
"Device name"
,
"deletion"
:
"Deletion"
},
"auth"
:
{
"entrance"
:
"Sign In"
,
...
...
src/locales/ru/ru.json
View file @
b4eebb44
...
...
@@ -20,7 +20,10 @@
"pageInDevelopment"
:
"Страница в разработке"
,
"pageInDevelopmentDesc"
:
"Эта страница в настоящее время разрабатывается. Используйте приложение для просмотра записей."
,
"hours"
:
"Часы"
,
"minutes"
:
"Минуты"
"minutes"
:
"Минуты"
,
"rename"
:
"Переименовать"
,
"deviceName"
:
"Имя устройства"
,
"deletion"
:
"Удаление"
},
"auth"
:
{
"entrance"
:
"Вход"
,
...
...
src/locales/uz/uz.json
View file @
b4eebb44
...
...
@@ -20,7 +20,10 @@
"pageInDevelopment"
:
"Sahifa ishlab chiqilmoqda"
,
"pageInDevelopmentDesc"
:
"Ushbu sahifa hozirda ishlab chiqilmoqda. Yozuvlarni ko'rish uchun ilovadan foydalaning."
,
"hours"
:
"Soatlar"
,
"minutes"
:
"Daqiqalar"
"minutes"
:
"Daqiqalar"
,
"rename"
:
"O'zgartirish"
,
"deviceName"
:
"Qurilma nomi"
,
"deletion"
:
"O'chirish"
},
"auth"
:
{
"entrance"
:
"Kirish"
,
...
...
src/pages/Settings/Settings.tsx
View file @
b4eebb44
...
...
@@ -6,8 +6,8 @@ import en from "../../assets/images/flag-en.png";
import
ru
from
"../../assets/images/flag-ru.png"
;
import
uz
from
"../../assets/images/flag-uz.png"
;
import
{
FiUserMinus
}
from
"react-icons/fi"
;
import
{
IoKeyOutline
}
from
"react-icons/io5"
;
import
{
MdOutlineLogout
}
from
"react-icons/md"
;
import
{
IoKeyOutline
,
IoPhonePortraitOutline
}
from
"react-icons/io5"
;
import
{
MdOutline
Edit
,
MdOutline
Logout
}
from
"react-icons/md"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useAppDispatch
,
...
...
@@ -31,6 +31,11 @@ import { FaChevronDown, FaChevronUp, FaImages } from "react-icons/fa";
import
{
FaRegTrashCan
}
from
"react-icons/fa6"
;
import
{
RiHeadphoneFill
}
from
"react-icons/ri"
;
import
{
GrMultimedia
}
from
"react-icons/gr"
;
import
{
deleteDevice
,
editDevice
,
refreshDevices
,
}
from
"../../stores/slices/deviceSlice"
;
const
deleteDatas
=
[
{
...
...
@@ -63,6 +68,13 @@ const Settings: React.FC = () => {
const
[
selectedDeleteData
,
setSelectedDeleteData
]
=
useState
<
null
|
number
>
(
null
);
const
[
selectedDevice
,
setSelectedDevice
]
=
useState
<
{
id
:
string
;
name
:
string
;
}
|
null
>
(
null
);
const
[
isOpenDevices
,
setIsOpenDevices
]
=
useState
(
false
);
const
[
isOpenEditDevice
,
setIsOpenEditDevice
]
=
useState
(
false
);
const
[
isOpenDeleteDevice
,
setIsOpenDeleteDevice
]
=
useState
(
false
);
const
[
isOpenDeleteDataModal
,
setIsOpenDeleteDataModal
]
=
useState
(
false
);
const
{
language
,
changeLanguage
}
=
useLanguage
();
const
{
t
}
=
useTranslation
();
...
...
@@ -72,6 +84,9 @@ const Settings: React.FC = () => {
const
{
session
,
deletingData
}
=
useAppSelector
(
(
state
:
RootState
)
=>
state
.
account
);
const
{
devices
,
isDeletingDevice
,
isEditingDevice
}
=
useAppSelector
(
(
state
:
RootState
)
=>
state
.
device
);
const
dispatch
=
useAppDispatch
();
const
toggleLogoutModal
=
()
=>
{
...
...
@@ -90,6 +105,48 @@ const Settings: React.FC = () => {
setIsOpenDeleteDataModal
((
prev
)
=>
!
prev
);
};
const
toggleIsOpenDevices
=
()
=>
{
setIsOpenDevices
((
prev
)
=>
!
prev
);
};
const
toggleIsOpenEditDevice
=
()
=>
{
setIsOpenEditDevice
((
prev
)
=>
!
prev
);
};
const
toggleIsOpenDeleteDevice
=
()
=>
{
setIsOpenDeleteDevice
((
prev
)
=>
!
prev
);
};
const
handleEditDevice
=
async
()
=>
{
try
{
if
(
selectedDevice
)
{
await
dispatch
(
editDevice
({
deviceId
:
selectedDevice
.
id
,
name
:
selectedDevice
.
name
})
);
await
dispatch
(
refreshDevices
());
}
}
catch
(
error
)
{
console
.
error
(
"Error editing device: "
,
error
);
}
finally
{
setIsOpenEditDevice
(
false
);
}
};
const
handleDeleteDevice
=
async
()
=>
{
try
{
if
(
selectedDevice
)
{
await
dispatch
(
deleteDevice
(
selectedDevice
.
id
));
}
await
dispatch
(
refreshDevices
());
}
catch
(
error
)
{
console
.
error
(
"Error deleting device: "
,
error
);
}
finally
{
setIsOpenDeleteDevice
(
false
);
}
};
const
handleDeleteData
=
async
()
=>
{
try
{
if
(
selectedDeleteData
)
{
...
...
@@ -181,6 +238,51 @@ const Settings: React.FC = () => {
/>
<
div
>
<
div
onClick=
{
toggleIsOpenDevices
}
className=
"settings__data__header"
>
<
p
>
{
t
(
"settings.myDevices"
)
}
</
p
>
{
isOpenDevices
?
<
FaChevronUp
/>
:
<
FaChevronDown
/>
}
</
div
>
{
isOpenDevices
&&
(
<
div
className=
"settings__data__list"
>
{
devices
.
length
>
0
?
(
<>
{
devices
.
map
((
device
)
=>
{
return
(
<
div
key=
{
device
.
id
}
className=
"settings__data__item"
>
<
div
className=
"settings__data__item__info"
>
<
IoPhonePortraitOutline
className=
"settings__data__item__info__icon"
/>
<
p
>
{
device
.
name
}
</
p
>
</
div
>
<
div
className=
"settings__data__item__actions"
>
<
MdOutlineEdit
className=
"settings__data__item__actions__icon"
onClick=
{
()
=>
{
setSelectedDevice
(
device
);
setIsOpenEditDevice
(
true
);
}
}
/>
<
FaRegTrashCan
className=
"settings__data__item__actions__icon text-danger"
onClick=
{
()
=>
{
setSelectedDevice
(
device
);
setIsOpenDeleteDevice
(
true
);
}
}
/>
</
div
>
</
div
>
);
})
}
</>
)
:
(
<
p
className=
"settings__data__no"
>
{
t
(
"settings.noDevices"
)
}
</
p
>
)
}
</
div
>
)
}
</
div
>
<
div
>
<
div
onClick=
{
toggleIsOpenDeleteData
}
className=
"settings__data__header"
...
...
@@ -259,6 +361,68 @@ const Settings: React.FC = () => {
/>
<
CModal
isOpen=
{
isOpenEditDevice
}
onToggle=
{
toggleIsOpenEditDevice
}
content=
{
<
div
className=
"modal__box"
>
<
h3
className=
"modal__box__title"
>
{
t
(
"common.rename"
)
}
</
h3
>
<
CInput
placeholder=
{
t
(
"common.deviceName"
)
}
value=
{
selectedDevice
?.
name
||
""
}
onChange=
{
(
e
)
=>
{
if
(
selectedDevice
)
{
setSelectedDevice
({
...
selectedDevice
,
name
:
e
.
target
.
value
,
});
}
}
}
/>
<
div
className=
"modal__box__actions"
>
<
CButton
title=
{
t
(
"button.cancel"
)
}
onClick=
{
toggleIsOpenEditDevice
}
/>
<
CButton
title=
{
t
(
"button.ok"
)
}
onClick=
{
handleEditDevice
}
isLoading=
{
isEditingDevice
}
/>
</
div
>
</
div
>
}
/>
<
CModal
isOpen=
{
isOpenDeleteDevice
}
onToggle=
{
toggleIsOpenDeleteDevice
}
content=
{
<
div
className=
"modal__box"
>
<
h3
className=
"modal__box__title"
>
{
t
(
"device.deleteDevice"
)
}
<
br
/>
{
selectedDevice
?.
name
}
</
h3
>
<
p
style=
{
{
textAlign
:
"center"
}
}
>
{
t
(
"device.deleteDeviceDesc"
)
}
</
p
>
<
div
className=
"modal__box__actions"
>
<
CButton
title=
{
t
(
"button.cancel"
)
}
onClick=
{
toggleIsOpenDeleteDevice
}
/>
<
CButton
title=
{
t
(
"button.delete"
)
}
variant=
"danger"
onClick=
{
handleDeleteDevice
}
isLoading=
{
isDeletingDevice
}
/>
</
div
>
</
div
>
}
/>
<
CModal
isOpen=
{
isOpenDeleteAccountModal
}
onToggle=
{
toggleDeleteAccountModal
}
content=
{
...
...
src/stores/slices/deviceSlice.ts
View file @
b4eebb44
...
...
@@ -14,6 +14,7 @@ interface IDeviceState {
isAudioListLoading
:
boolean
;
loadingDevices
:
boolean
;
errorDevices
:
string
;
isEditingDevice
?:
boolean
;
isDeletingDevice
:
boolean
;
}
...
...
@@ -25,6 +26,7 @@ const initialState: IDeviceState = {
isAudioListLoading
:
false
,
loadingDevices
:
false
,
errorDevices
:
""
,
isEditingDevice
:
false
,
isDeletingDevice
:
false
,
};
...
...
@@ -116,6 +118,48 @@ export const fetchAudioFiles = createAsyncThunk(
}
);
export
const
editDevice
=
createAsyncThunk
(
"device/editDevice"
,
async
(
{
deviceId
,
name
}:
{
deviceId
:
string
;
name
:
string
},
{
rejectWithValue
}
)
=>
{
try
{
const
response
=
await
sendRpcRequest
(
"devices.setDeviceName"
,
{
deviceId
,
name
,
});
return
response
;
}
catch
(
error
:
unknown
)
{
if
(
typeof
error
===
"object"
&&
error
!==
null
&&
"message"
in
error
)
{
return
rejectWithValue
(
error
.
message
);
}
return
rejectWithValue
(
"Unknown error occurred"
);
}
}
);
// force refresh (no condition)
export
const
refreshDevices
=
createAsyncThunk
(
"device/refreshDevices"
,
async
(
_
,
{
rejectWithValue
})
=>
{
try
{
const
response
=
await
sendRpcRequest
<
{
list
:
IDevice
[]
}
>
(
"devices.getlist"
);
return
response
.
list
;
}
catch
(
error
:
unknown
)
{
if
(
typeof
error
===
"object"
&&
error
!==
null
&&
"message"
in
error
)
{
return
rejectWithValue
(
error
.
message
);
}
return
rejectWithValue
(
"Unknown error occurred"
);
}
}
);
export
const
deleteDevice
=
createAsyncThunk
(
"device/deleteDevice"
,
async
(
deviceId
:
string
,
{
rejectWithValue
})
=>
{
...
...
@@ -167,6 +211,17 @@ const deviceSlice = createSlice({
state
.
deviceData
=
action
.
payload
;
})
.
addCase
(
editDevice
.
pending
,
(
state
)
=>
{
state
.
isEditingDevice
=
true
;
})
.
addCase
(
editDevice
.
fulfilled
,
(
state
)
=>
{
state
.
isEditingDevice
=
false
;
})
.
addCase
(
editDevice
.
rejected
,
(
state
,
action
)
=>
{
state
.
isEditingDevice
=
false
;
console
.
error
(
"Error editing device:"
,
action
.
payload
);
})
.
addCase
(
deleteDevice
.
pending
,
(
state
)
=>
{
state
.
isDeletingDevice
=
true
;
})
...
...
@@ -188,6 +243,11 @@ const deviceSlice = createSlice({
.
addCase
(
fetchAudioFiles
.
rejected
,
(
state
,
action
)
=>
{
state
.
isAudioListLoading
=
false
;
console
.
error
(
"Error fetching audio files:"
,
action
.
payload
);
})
.
addCase
(
refreshDevices
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
devices
=
action
.
payload
;
state
.
selectedDevice
=
action
.
payload
[
0
]
??
null
;
});
},
});
...
...
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