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
1e49e9ab
Commit
1e49e9ab
authored
Jul 03, 2025
by
asranov0003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add account slice
parent
18157566
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
139 additions
and
3 deletions
+139
-3
constants.ts
src/constants/constants.ts
+2
-2
Header.css
src/layouts/Header/Header.css
+14
-1
Header.tsx
src/layouts/Header/Header.tsx
+43
-0
en.json
src/locales/en/en.json
+7
-0
ru.json
src/locales/ru/ru.json
+7
-0
accountSlice.ts
src/stores/slices/accountSlice.ts
+60
-0
store.ts
src/stores/store.ts
+2
-0
global.css
src/styles/global.css
+4
-0
No files found.
src/constants/constants.ts
View file @
1e49e9ab
//
export const API_URL = "https://cabinet.thecybernanny.com/nanny/backend/rpc"; // PROD
export
const
API_URL
=
"https://cabinet.dev.thecybernanny.com/nanny/backend/rpc"
;
// DEV
export
const
API_URL
=
"https://cabinet.thecybernanny.com/nanny/backend/rpc"
;
// PROD
//
export const API_URL = "https://cabinet.dev.thecybernanny.com/nanny/backend/rpc"; // DEV
export
const
TG_USER_ID
=
window
.
Telegram
.
WebApp
.
initDataUnsafe
?.
user
?.
id
||
0
;
export
const
TOKEN
=
localStorage
.
getItem
(
`token-
${
TG_USER_ID
}
`
)
||
""
;
src/layouts/Header/Header.css
View file @
1e49e9ab
...
...
@@ -28,7 +28,7 @@
}
.sidebar
{
width
:
70
%
;
width
:
85
%
;
height
:
100%
;
display
:
flex
;
justify-content
:
space-between
;
...
...
@@ -39,6 +39,19 @@
transition
:
0.3s
;
}
.sidebar__content__info
{
padding
:
1rem
;
display
:
flex
;
align-items
:
center
;
gap
:
1rem
;
font-size
:
0.85rem
;
}
.sidebar__content__info__icon
{
font-size
:
2.5rem
;
color
:
var
(
--primary-color
);
}
.sidebar__content__navs
{
display
:
flex
;
flex-direction
:
column
;
...
...
src/layouts/Header/Header.tsx
View file @
1e49e9ab
...
...
@@ -13,6 +13,13 @@ import { MdLogout } from "react-icons/md";
import
{
RiHome3Line
}
from
"react-icons/ri"
;
import
{
BsCreditCard
}
from
"react-icons/bs"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useAppDispatch
,
useAppSelector
,
type
RootState
,
}
from
"../../stores/store"
;
import
{
accountSession
}
from
"../../stores/slices/accountSlice"
;
import
{
FaUserAlt
}
from
"react-icons/fa"
;
const
devices
=
[
{
...
...
@@ -37,6 +44,12 @@ const Header: React.FC = () => {
const
[
isSidebarOpen
,
setIsSidebarOpen
]
=
useState
(
false
);
const
sidebarRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
{
t
}
=
useTranslation
();
const
dispatch
=
useAppDispatch
();
const
{
session
}
=
useAppSelector
((
state
:
RootState
)
=>
state
.
account
);
useEffect
(()
=>
{
dispatch
(
accountSession
());
},
[
dispatch
]);
const
handleOutsideClick
=
(
e
:
MouseEvent
)
=>
{
if
(
sidebarRef
.
current
&&
!
sidebarRef
.
current
.
contains
(
e
.
target
as
Node
))
{
...
...
@@ -85,6 +98,36 @@ const Header: React.FC = () => {
<
div
className=
"sidebar__overlay"
>
<
div
className=
"sidebar"
ref=
{
sidebarRef
}
>
<
div
className=
"sidebar__content"
>
<
div
className=
"sidebar__content__info"
>
<
FaUserAlt
className=
"sidebar__content__info__icon"
/>
<
div
className=
"text-bold"
>
<
p
>
{
session
.
login
}
</
p
>
<
p
>
{
t
(
"session.activeTill"
)
}
:
{
" "
}
{
session
.
subDateEnd
?
(
<
span
className=
"text-success"
>
{
session
.
subDateEnd
}
</
span
>
)
:
(
<
span
className=
"text-danger"
>
{
t
(
"session.noSubscription"
)
}
</
span
>
)
}
</
p
>
<
p
>
{
t
(
"session.emailStatus"
)
}
:
{
" "
}
{
session
.
emailVerified
?
(
<
span
className=
"text-success"
>
{
t
(
"session.verified"
)
}
</
span
>
)
:
(
<
span
className=
"text-danger"
>
{
t
(
"session.unVerified"
)
}
</
span
>
)
}
</
p
>
</
div
>
</
div
>
<
div
className=
"sidebar__content__navs"
>
<
NavLink
to=
{
"/home"
}
...
...
src/locales/en/en.json
View file @
1e49e9ab
...
...
@@ -26,6 +26,13 @@
"settings"
:
"Settings"
,
"logout"
:
"Logout"
},
"session"
:
{
"activeTill"
:
"Active till"
,
"noSubscription"
:
"No subscription"
,
"emailStatus"
:
"Email status"
,
"verified"
:
"Verified"
,
"unVerified"
:
"Unverified"
},
"pincode"
:
{
"enterTitle"
:
"Enter your PIN Code"
,
"createTitle"
:
"Create your PIN Code"
,
...
...
src/locales/ru/ru.json
View file @
1e49e9ab
...
...
@@ -26,6 +26,13 @@
"settings"
:
"Настройки"
,
"logout"
:
"Выйти"
},
"session"
:
{
"activeTill"
:
"Активен до"
,
"noSubscription"
:
"Нет подписки"
,
"emailStatus"
:
"Статус почты"
,
"verified"
:
"Подтверждено"
,
"unVerified"
:
"Не подтверждено"
},
"pincode"
:
{
"enterTitle"
:
"Введите PIN-код"
,
"createTitle"
:
"Создайте PIN-код"
,
...
...
src/stores/slices/accountSlice.ts
0 → 100644
View file @
1e49e9ab
import
{
createAsyncThunk
,
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
sendRpcRequest
}
from
"../../services/apiClient"
;
interface
ISession
{
login
:
string
;
emailVerified
:
boolean
;
subDateEnd
:
string
;
}
interface
IAccountState
{
session
:
ISession
;
loading
:
boolean
;
}
const
initialState
:
IAccountState
=
{
session
:
{
login
:
""
,
emailVerified
:
false
,
subDateEnd
:
""
,
},
loading
:
false
,
};
export
const
accountSession
=
createAsyncThunk
(
"account/session"
,
async
(
_
,
{
rejectWithValue
})
=>
{
try
{
const
response
=
await
sendRpcRequest
<
ISession
>
(
"account.session"
);
return
response
;
}
catch
(
error
:
unknown
)
{
if
(
typeof
error
===
"object"
&&
error
!==
null
&&
"message"
in
error
)
{
return
rejectWithValue
(
error
.
message
);
}
return
rejectWithValue
(
"Unknown error occurred"
);
}
}
);
const
accountSlice
=
createSlice
({
name
:
"account"
,
initialState
,
reducers
:
{},
extraReducers
:
(
builder
)
=>
{
builder
.
addCase
(
accountSession
.
pending
,
(
state
)
=>
{
state
.
loading
=
true
;
})
.
addCase
(
accountSession
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loading
=
false
;
state
.
session
=
action
.
payload
;
})
.
addCase
(
accountSession
.
rejected
,
(
state
)
=>
{
state
.
loading
=
false
;
});
},
});
export
default
accountSlice
.
reducer
;
src/stores/store.ts
View file @
1e49e9ab
import
{
configureStore
}
from
"@reduxjs/toolkit"
;
import
authSlice
from
"./slices/authSlice"
;
import
accountSlice
from
"./slices/accountSlice"
;
import
{
useDispatch
,
useSelector
,
...
...
@@ -9,6 +10,7 @@ import {
export
const
store
=
configureStore
({
reducer
:
{
auth
:
authSlice
,
account
:
accountSlice
,
},
});
...
...
src/styles/global.css
View file @
1e49e9ab
...
...
@@ -28,6 +28,10 @@ body {
text-align
:
center
;
}
.text-bold
{
font-weight
:
bold
;
}
:root
{
--primary-color
:
#448AFF
;
--primary-muted-color
:
#BBDEFB
;
...
...
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