Skip to content

Commit fba2710

Browse files
committed
feature: replaced DOm scripts, improved loading, improved user handling, improve JWT token processing
1 parent 499cfb4 commit fba2710

File tree

12 files changed

+100
-46
lines changed

12 files changed

+100
-46
lines changed

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<link rel="icon" href="./src/assets/images/favicon.png" type="image/x-icon"/>
77
<!-- <link rel="stylesheet" href="./src/assets/css/main.css">-->
8-
<script type="module" src="./src/assets/js/main.js" defer></script>
8+
<!-- <script type="module" src="./src/assets/js/main.js" defer></script>-->
99
<script type="text/javascript" src="%VITE_API_ADDRESS%dstyles/marble/js/" defer></script>
1010
<title>Orthodox Calendar App</title>
1111
</head>

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
},
1212
"dependencies": {
1313
"axios": "^1.8.4",
14+
"js-cookie": "^3.0.5",
15+
"jwt-decode": "^4.0.0",
1416
"punycode": "^2.3.1",
1517
"react": "^19.0.0",
1618
"react-dom": "^19.0.0",

src/assets/js/utils/main_section_preview.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ function toggleIfContent(element){
1111
}
1212

1313
export default function mainSectionToggle(){
14-
1514
const section = document.querySelector('#component')
1615
let toggle = false;
1716
for (const child of Object.values(section.children)){

src/components/articles/Article.jsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ import useAPI from "../../hooks/useAPI.js";
22
import useOrderedStyles from "../../hooks/useOrderedStyles.js";
33
import {useContext, useEffect, useState} from "react";
44
import AuthContext from "../../contexts/AuthContext.js";
5-
import {useNavigate, useParams} from "react-router";
5+
import {Link, useNavigate, useParams} from "react-router";
66
import ArticleTile from "./ArticleTile.jsx";
77
import saintImage from "../../assets/images/articles/saint.webp";
8+
import routes from "../../routes/routes.js";
89

910
export default function Article(props) {
1011
const {date, feast, saint, holiday, author, image, content, title} = props
1112
const {id} = useParams()
1213
const {loadArticles} = useAPI();
1314
const {addStyle} = useOrderedStyles()
14-
const {is_authenticated} = useContext(AuthContext)
15+
const {is_authenticated, is_owner, is_admin, is_superuser} = useContext(AuthContext)
1516
const [article, setArticle] = useState({});
1617
const navigate = useNavigate();
1718

@@ -41,16 +42,13 @@ export default function Article(props) {
4142
<p>{article.content}</p>
4243
<nav>
4344
<ul>
44-
{/*{% if article.is_own %}*/}
45-
{/*<li><a href="{% url "article-delete" article.pk %}">*/}
46-
{/* */}
47-
{/*</a></li>*/}
48-
{/*{% endif %}*/}
45+
{(is_owner() || is_superuser()) && (<li><Link to={routes["article-delete"].replace(":id", article.id)} onClick={e=>e.stopPropagation()}>
46+
47+
</Link></li>)}
4948
{/*{% if article.can_change or article.is_own %}*/}
50-
{/*<li><a href="{% url "article-edit" article.pk %}">*/}
51-
{/* */}
52-
{/*</a></li>*/}
53-
{/*{% endif %}*/}
49+
{(is_owner() || is_superuser() || is_admin()) && (<li><Link onClick={e=>e.stopPropagation()} to={routes["article-edit"].replace(":id", article.id)}>
50+
51+
</Link></li>)}
5452
</ul>
5553
</nav>
5654
</main>

src/components/articles/ArticleTile.jsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import routes from "../../routes/routes.js";
2+
import {Link} from "react-router";
23

34
export default function ArticleTile(props) {
45

5-
const {id, date, feast, saint, holiday, author, image, content, title, navigate} = props
6+
const {id, date, feast, saint, holiday, author, image, content, title, navigate, is_owner, is_superuser, is_admin} = props
67

78
return (
89
<>
@@ -17,16 +18,13 @@ export default function ArticleTile(props) {
1718
<p>{content.slice(0,75) + "..."}</p>
1819
<nav>
1920
<ul>
20-
{/*{% if article.is_own %}*/}
21-
{/*<li><a href="{% url " article-delete" article.pk %}">*/}
22-
{/* */}
23-
{/*</a></li>*/}
24-
{/*{% endif %}*/}
21+
{(is_owner() || is_superuser()) && (<li><Link onClick={e=>e.stopPropagation()} to={routes["article-delete"].replace(":id", id)}>
22+
23+
</Link></li>)}
2524
{/*{% if article.can_change or article.is_own %}*/}
26-
{/*<li><a href="{% url " article-edit" article.pk %}">*/}
27-
{/* */}
28-
{/*</a></li>*/}
29-
{/*{% endif %}*/}
25+
{(is_owner() || is_superuser() || is_admin()) && (<li><Link onClick={e=>e.stopPropagation()} to={routes["article-edit"].replace(":id", id)}>
26+
27+
</Link></li>)}
3028
</ul>
3129
</nav>
3230

src/components/articles/Articles.jsx

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import AuthContext from "../../contexts/AuthContext.js";
77
import routes from "../../routes/routes.js";
88
import {useNavigate} from "react-router";
99
import saintImage from "../../assets/images/articles/saint.webp";
10+
import React from "react";
1011

1112

1213
export default function Articles(props) {
@@ -16,7 +17,7 @@ export default function Articles(props) {
1617

1718
const {loadArticles, apiMethods, apiLoaded, loadApiFiles} = useAPI();
1819
const {addStyle} = useOrderedStyles()
19-
const {is_authenticated} = useContext(AuthContext)
20+
const {is_authenticated, is_owner, is_superuser, is_admin, user} = useContext(AuthContext)
2021
const [articles, setArticles] = useState([]);
2122
const [dayData, setDayData] = useState({});
2223
const navigate = useNavigate();
@@ -29,18 +30,18 @@ export default function Articles(props) {
2930
useEffect(() => {
3031
loadArticles(date, feast, saint, holiday, author).then(response =>{
3132
setArticles(response.data.map(article => {
32-
return <ArticleTile key={article.id} {...article} navigate={navigate}/>
33+
return <ArticleTile {...article} key={article.id} is_owner={is_owner} is_superuser={is_superuser} is_admin={is_admin} navigate={navigate}/>
3334
}))
3435
}
3536
)
3637

3738

3839

3940

40-
},[date, feast, saint, holiday, author]);
41+
},[date, feast, saint, holiday, author, user]);
4142

4243
useEffect(() => {
43-
if(apiLoaded) {
44+
if(apiLoaded && date) {
4445
const {get} = apiMethods
4546
console.log("apiLoaded")
4647
get("holidays", {by_date: date, related: true}).then(res => {
@@ -50,31 +51,31 @@ export default function Articles(props) {
5051
} else {
5152
(async () => await loadApiFiles())();
5253
}
53-
}, [apiLoaded])
54+
}, [apiLoaded, articles])
5455

5556
const final = []
5657
let add;
5758

5859
final.push(
59-
(<div id="calendar-main">
60+
date && (<React.Fragment key={"calendar-main"}><div id="calendar-main">
6061
<div className="calendar">
6162
Използван календар:
62-
<p>{dayData.holidays?.calendar}</p>
63+
<p>{dayData?.calendar}</p>
6364
</div>
6465
<div className="saint">
6566
<p className="desc">Православни светци, чествани днес:</p>
66-
{dayData?.saint?.map((item, index) => (<p>{item.name}</p>))}
67+
{dayData?.saint?.map((item, index) => (<p key={item.id}>{item.name}</p>))}
6768
</div>
6869
<div className="feast">
6970
<p className="desc">Православни празници днес:</p>
70-
{dayData?.feast?.map((item, index) => (<p>{item.name}</p>))}
71+
{dayData?.feast?.map((item, index) => (<p key={item.id}>{item.name}</p>))}
7172
</div>
72-
</div>)
73+
</div></React.Fragment>)
7374
)
7475

7576
if (is_authenticated) {
7677
add = (
77-
<>
78+
<React.Fragment key={"add-article"}>
7879
<article className="new-article-tile" onClick={() => navigate(routes["article-create"])}>
7980
<figure>
8081
<img src={saintImage} alt={"няма намерени картички"}/>
@@ -84,20 +85,20 @@ export default function Articles(props) {
8485
<p className="new-article-sign"> + </p>
8586
</main>
8687
</article>
87-
</>
88+
</React.Fragment>
8889
)
8990
}
9091

9192
final.push( articles.length ? (
92-
<>
93+
<React.Fragment key={"articles"}>
9394
<p style={{"textTransform": "capitalize"}}>Преглед на картички от статиите</p>
9495
<section className="article-list">
9596
{articles}
9697
{add}
9798
</section>
98-
</>
99+
</React.Fragment>
99100
) : (
100-
<>
101+
<React.Fragment key={"no-articles"}>
101102
<section className="article-list">
102103
<article>
103104
<figure>
@@ -109,7 +110,7 @@ export default function Articles(props) {
109110
</main>
110111
</article>
111112
</section>
112-
</>
113+
</React.Fragment>
113114
))
114115

115116
return final

src/components/common/Base.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,20 @@ export default function Base(){
2020
const {addStyle,addExternalStyle} = useOrderedStyles();
2121
addStyle(`/main.css`,'main')
2222
addExternalStyle(`${import.meta.env.VITE_API_ADDRESS}dstyles/marble/css/`)
23-
const {context} = useAuth()
23+
const {context, login, user} = useAuth()
2424
const reloadCallback = ()=> setReload(!reload)
2525

2626

2727

2828
useEffect(() => {
29+
(async () =>{
30+
await login()
31+
})();
2932
setReloadPage("home", reloadCallback)
3033
}, []);
3134

35+
36+
3237
return (
3338
<>
3439
{/*<ExternalStyle url={`${import.meta.env.VITE_API_ADDRESS}dstyles/marble/css/`}/>*/}
@@ -175,8 +180,8 @@ export default function Base(){
175180
{% endblock %}-->*/}
176181
</div>
177182

178-
<div id="component" className="no-component-background disable-preview">
179-
<div className="message-container disable-preview">
183+
<div id="component" className="no-component-background" >
184+
<div className="message-container" >
180185
{/*<!--{% block message_content %}
181186
{% for message in messages %}
182187
<div>

src/hooks/useAuth.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import {useContext} from "react";
1+
import {useContext, useEffect} from "react";
22
import AuthContext from "../contexts/AuthContext.js";
33
import axios, {AxiosHeaders} from "axios";
4+
import {jwtDecode} from "jwt-decode";
45

56
export default function useAuth() {
67
const context = useContext(AuthContext);
7-
const {token, setToken, api} = context;
8+
const {token, setToken, api, setUser, user} = context;
89

910
const login = (username, password)=>{
1011
let response;
11-
if(!token){
12+
13+
console.log(token)
14+
15+
if(username && password){
1216
// fetch(`${import.meta.env.VITE_API_URL}/api/token/`, {})
1317
// response = fetch(`${import.meta.env.VITE_API_ADDRESS}api/token/`,{
1418
// method: 'POST',
@@ -33,6 +37,8 @@ export default function useAuth() {
3337
response = api.post(`/api/token/`,{username,password}).
3438
then(response=>{
3539
setToken(response.data.access);
40+
console.log(jwtDecode(response.data.access))
41+
setUser(jwtDecode(response.data.access))
3642
return {status: 200, data: response};
3743
}).catch((error) => {
3844
console.error("Error logging in:", error);
@@ -62,6 +68,8 @@ export default function useAuth() {
6268
response = api.post(`/api/token/refresh/`).
6369
then(response=>{
6470
setToken(response.data.access);
71+
console.log(jwtDecode(response.data.access))
72+
setUser(jwtDecode(response.data.access))
6573
return {status: 200, data: response};
6674
}).catch((error) => {
6775
console.error("Error logging in:", error);
@@ -73,8 +81,11 @@ export default function useAuth() {
7381
}
7482

7583
const logout = async () => {
84+
await api.post(`/api/token/logout/`, {});
7685
await setToken(null);
86+
await setUser({});
7787
}
7888

89+
7990
return {context, login, logout};
8091
}

src/providers/authProvider.jsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import AuthContext from "../contexts/AuthContext.js";
55
const AuthProvider = ({ children }) => {
66
const [token, setToken] = useState(null);
77
const [apiLoaded, setApiLoaded] = useState(false);
8+
const [user, setUser] = useState({});
89

910

1011
const api = useMemo(() => {
@@ -27,7 +28,19 @@ const AuthProvider = ({ children }) => {
2728
}
2829

2930
setApiLoaded(prev => !prev);
30-
}, [token, api]);
31+
}, [token, api])
32+
33+
const is_owner = (author_id) => {
34+
return user?.user_id && (user?.user_id === author_id);
35+
}
36+
37+
const is_admin = () => {
38+
return !!user?.is_admin;
39+
}
40+
41+
const is_superuser = () => {
42+
return !!user?.is_superuser;
43+
}
3144

3245
const contextValue = useMemo(
3346
() => ({
@@ -36,6 +49,11 @@ const AuthProvider = ({ children }) => {
3649
api,
3750
apiLoaded,
3851
is_authenticated: !!token,
52+
user,
53+
setUser,
54+
is_owner,
55+
is_admin,
56+
is_superuser,
3957
}),
4058
[token, api, apiLoaded]
4159
);

0 commit comments

Comments
 (0)