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
8fe43471
Commit
8fe43471
authored
Jul 24, 2025
by
asranov0003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: render usage limit days and times
parent
043d50ba
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
148 additions
and
2 deletions
+148
-2
en.json
src/locales/en/en.json
+9
-0
ru.json
src/locales/ru/ru.json
+9
-0
UsageLimits.css
src/pages/UsageLimits/UsageLimits.css
+17
-0
UsageLimits.tsx
src/pages/UsageLimits/UsageLimits.tsx
+52
-2
usageLimitSlice.ts
src/stores/slices/usageLimitSlice.ts
+59
-0
store.ts
src/stores/store.ts
+2
-0
No files found.
src/locales/en/en.json
View file @
8fe43471
...
...
@@ -164,5 +164,14 @@
"error"
:
{
"requiredField"
:
"This field is required"
,
"requiredCheckbox"
:
"You must accept this"
},
"weekdays"
:
{
"monday"
:
"Monday"
,
"tuesday"
:
"Tuesday"
,
"wednesday"
:
"Wednesday"
,
"thursday"
:
"Thursday"
,
"friday"
:
"Friday"
,
"saturday"
:
"Saturday"
,
"sunday"
:
"Sunday"
}
}
src/locales/ru/ru.json
View file @
8fe43471
...
...
@@ -154,5 +154,14 @@
"error"
:
{
"requiredField"
:
"Это поле обязательное"
,
"requiredCheckbox"
:
"Вы должны принять это"
},
"weekdays"
:
{
"monday"
:
"Понедельник"
,
"tuesday"
:
"Вторник"
,
"wednesday"
:
"Среда"
,
"thursday"
:
"Четверг"
,
"friday"
:
"Пятница"
,
"saturday"
:
"Суббота"
,
"sunday"
:
"Воскресенье"
}
}
src/pages/UsageLimits/UsageLimits.css
View file @
8fe43471
...
...
@@ -7,4 +7,20 @@
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
}
.usagelimits__days
{
margin-top
:
1rem
;
display
:
flex
;
flex-direction
:
column
;
gap
:
0.5rem
}
.usagelimits__day
{
display
:
flex
;
justify-content
:
space-between
;
padding
:
1rem
;
border-radius
:
10px
;
background
:
var
(
--content-bg-color
);
cursor
:
pointer
;
}
\ No newline at end of file
src/pages/UsageLimits/UsageLimits.tsx
View file @
8fe43471
import
React
from
"react"
;
import
React
,
{
useEffect
}
from
"react"
;
import
"./UsageLimits.css"
;
import
SectionHeader
from
"../../layouts/SectionHeader"
;
import
CButton
from
"../../components/CButton"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useAppDispatch
,
type
RootState
}
from
"../../stores/store"
;
import
{
useSelector
}
from
"react-redux"
;
import
{
fetchTimeLimits
}
from
"../../stores/slices/usageLimitSlice"
;
import
CLoading
from
"../../components/CLoading"
;
const
UsageLimits
:
React
.
FC
=
()
=>
{
const
{
selectedDevice
}
=
useSelector
((
state
:
RootState
)
=>
state
.
device
);
const
{
days
,
isLoadingDays
}
=
useSelector
(
(
state
:
RootState
)
=>
state
.
usageLimit
);
const
dispatch
=
useAppDispatch
();
const
{
t
}
=
useTranslation
();
useEffect
(()
=>
{
if
(
!
selectedDevice
?.
id
)
return
;
dispatch
(
fetchTimeLimits
(
selectedDevice
?.
id
));
},
[
dispatch
,
selectedDevice
]);
const
weekDays
=
[
t
(
"weekdays.monday"
),
t
(
"weekdays.tuesday"
),
t
(
"weekdays.wednesday"
),
t
(
"weekdays.thursday"
),
t
(
"weekdays.friday"
),
t
(
"weekdays.saturday"
),
t
(
"weekdays.sunday"
),
];
const
formatTime
=
(
minutes
:
number
)
=>
{
const
hrs
=
String
(
Math
.
floor
(
minutes
/
60
)).
padStart
(
2
,
"0"
);
const
mins
=
String
(
minutes
%
60
).
padStart
(
2
,
"0"
);
return
`
${
hrs
}
:
${
mins
}
`
;
};
return
(
<
div
className=
"usagelimits wrapper"
>
<
SectionHeader
to=
"/home/manage-apps"
/>
...
...
@@ -16,9 +47,28 @@ const UsageLimits: React.FC = () => {
<
h3
>
{
t
(
"usageLimits.title"
)
}
</
h3
>
<
p
>
{
t
(
"usageLimits.desc"
)
}
</
p
>
<
p
>
{
t
(
"usageLimits.allowedApps"
)
}
</
p
>
{
isLoadingDays
&&
(
<
div
className=
"cloading__center"
>
<
CLoading
/>
</
div
>
)
}
{
!
isLoadingDays
&&
(
<
div
className=
"usagelimits__days"
>
{
days
.
map
((
day
,
index
)
=>
{
return
(
<
div
className=
"usagelimits__day"
key=
{
index
}
>
<
p
>
{
weekDays
[
index
]
}
</
p
>
<
p
>
{
formatTime
(
day
)
}
</
p
>
</
div
>
);
})
}
</
div
>
)
}
</
div
>
<
CButton
title=
{
t
(
"button.save"
)
}
/>
<
CButton
title=
{
t
(
"button.save"
)
}
isLoading=
{
isLoadingDays
}
/>
</
div
>
</
div
>
);
...
...
src/stores/slices/usageLimitSlice.ts
0 → 100644
View file @
8fe43471
import
{
createAsyncThunk
,
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
sendRpcRequest
}
from
"../../services/apiClient"
;
interface
IUsageLimitState
{
days
:
number
[];
isLoadingDays
:
boolean
;
errorDays
:
string
|
null
;
}
const
initialState
:
IUsageLimitState
=
{
days
:
[],
isLoadingDays
:
false
,
errorDays
:
null
,
};
export
const
fetchTimeLimits
=
createAsyncThunk
(
"timeLimits/fetchTimeLimits"
,
async
(
deviceId
:
string
,
{
rejectWithValue
})
=>
{
try
{
const
response
=
await
sendRpcRequest
<
{
days
:
number
[]
}
>
(
"apps.timelimitget"
,
{
deviceId
,
}
);
return
response
.
days
;
}
catch
(
error
:
unknown
)
{
if
(
typeof
error
===
"object"
&&
error
!==
null
&&
"message"
in
error
)
{
return
rejectWithValue
(
error
.
message
);
}
return
rejectWithValue
(
"Unknown error occurred"
);
}
}
);
const
usageLimitSlice
=
createSlice
({
name
:
"usageLimit"
,
initialState
,
reducers
:
{},
extraReducers
:
(
builder
)
=>
{
builder
.
addCase
(
fetchTimeLimits
.
pending
,
(
state
)
=>
{
state
.
isLoadingDays
=
true
;
state
.
errorDays
=
null
;
})
.
addCase
(
fetchTimeLimits
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
isLoadingDays
=
false
;
state
.
days
=
action
.
payload
;
})
.
addCase
(
fetchTimeLimits
.
rejected
,
(
state
,
action
)
=>
{
state
.
isLoadingDays
=
false
;
state
.
errorDays
=
action
.
payload
as
string
;
});
},
});
export
default
usageLimitSlice
.
reducer
;
src/stores/store.ts
View file @
8fe43471
...
...
@@ -8,6 +8,7 @@ import appsSlice from "./slices/appsSlice";
import
browserHistorySlice
from
"./slices/browserHistorySlice"
;
import
callHistorySlice
from
"./slices/callHistorySlice"
;
import
messengerHistorySlice
from
"./slices/messengerHistorySlice"
;
import
usageLimitSlice
from
"./slices/usageLimitSlice"
;
import
{
useDispatch
,
useSelector
,
...
...
@@ -25,6 +26,7 @@ export const store = configureStore({
browserHistory
:
browserHistorySlice
,
callHistory
:
callHistorySlice
,
messengerHistory
:
messengerHistorySlice
,
usageLimit
:
usageLimitSlice
,
},
});
...
...
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