frontend/src/pages/admin/AdminUserOverviewPage.tsx   A
last analyzed

Complexity

Total Complexity 7
Complexity/F 0

Size

Lines of Code 193
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 44.44%

Importance

Changes 0
Metric Value
wmc 7
eloc 176
mnd 7
bc 7
fnc 0
dl 0
loc 193
ccs 28
cts 63
cp 0.4444
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import React, { useState, useEffect } from 'react';
2
import { useParams } from 'react-router-dom';
3
import { API_URL, getHeader } from '../../helpers/config';
4
import { useSelector } from 'react-redux';
5
import { RootState } from '../../redux/store/store';
6
import axios, { AxiosError } from 'axios';
7
import { Link } from 'react-router-dom';
8
import { Button, ToggleSwitch, TextInput, Checkbox, Label, Card } from "flowbite-react";
9
import { toast } from 'react-toastify';
10
import AdminGate from '../../components/AdminGate';
11
12
type User = {
13
  githubId: string;
14
  username: string;
15
  email: string;
16
  roles: string[];
17
  createdAt: string;
18
  hasAcceptedTerms: boolean;
19
  avatarUrl?: string;
20
  accumulatedCost: number;
21
  balance: number;
22
}
23
24 2
const AdminUserOverviewPage: React.FC = () => {
25 1
  const { githubId } = useParams<{ githubId: string }>();
26 2
  const { token } = useSelector((state: RootState) => state.auth);
27 1
  const [user, setUser] = useState<User | null>(null);
28 1
  const [loading, setLoading] = useState(true);
29 1
  const [isKund, setIsKund] = useState(false);
30 1
  const [isAdmin, setIsAdmin] = useState(false);
31 1
  const [isDeleted, setIsDeleted] = useState(false);
32 1
  const [createdAt, setCreatedAt] = useState("");
33 1
  const [username, setUsername] = useState("");
34 1
  const [email, setEmail] = useState("");
35 1
  const [hasAcceptedTerms, setHasAcceptedTerms] = useState(false);
36 1
  const [avatarUrl, setAvatarUrl] = useState("");
37 1
  const [isMonthlyPayment, setIsMonthlyPayment] = useState(false);
38 1
  const [accumulatedCost, setAccumulatedCost] = useState(0);
39 1
  const [balance, setBalance] = useState(0);
40
41
42
43 1
  const updateUserInfo = async (e: React.FormEvent<HTMLFormElement>) => {
44
    e.preventDefault();
45
    const updatedData = {
46
      'githubId': githubId,
47
      'username': username,
48
      'email': email,
49
      'roles': [isAdmin && "admin", isKund && "user",  isDeleted && "inactive"].filter(Boolean),
50
      'hasAcceptedTerms': hasAcceptedTerms,
51
      'avatarUrl': avatarUrl,
52
      'isMonthlyPayment': isMonthlyPayment,
53
      'accumulatedCost': accumulatedCost,
54
      'balance': balance,
55
      };
56
    try {
57
      const response = await axios.patch(`${API_URL}/users/${githubId}`, updatedData, getHeader(token));
58
      console.log(response);
59
      toast.success("User was updated");
60
      } catch(error)
61
      {
62
      const axiosError = error as AxiosError;
63
      toast.error(`User was not updated ${axiosError.message}`);
64 2
      console.error("Error:", axiosError.response || axiosError.toJSON());
65
    }
66
  }
67
68
69
70 1
  useEffect(() => {
71 1
    const getUserInfo = async () => {
72
      try {
73
        const response = await axios.get(`${API_URL}/users/${githubId}`, getHeader(token));
74
        setUser(response.data);
75
        const user = response.data;
76
        setIsKund(user.roles.includes("user"));
77
        setIsAdmin(user.roles.includes("admin"));
78
        setIsDeleted(user.roles.includes("inactive"));
79
        setUsername(user.username);
80
        setEmail(user.email);
81
        setCreatedAt(user.createdAt);
82
        setHasAcceptedTerms(user.hasAcceptedTerms);
83 2
        setAvatarUrl(user.avatarUrl ?? "");
84
        setIsMonthlyPayment(user.isMonthlyPayment);
85
        setAccumulatedCost(user.accumulatedCost);
86
        setBalance(user.balance);
87
      
88
      } catch (error) {
89
        console.error('Failed to fetch user info:', error);
90
      } finally {
91
        setLoading(false);
92
      }
93
    };
94
95 2
    if (githubId) {
96
      getUserInfo();
97
    }
98
  }, [githubId, token]);
99
100 2
  if (loading) {
101 1
    return <div data-testid="admin-user-overview-page">Laddar användardata...</div>;
102
  }
103
104 2
  if (!user) {
105
    return <div data-testid="admin-user-overview-page">Ingen användare hittades.</div>;
106
  }
107
108
  return (
109
    <div className="w-full max-w-4xl mx-auto p-6 bg-white border border-gray-200 rounded-lg shadow">
110
      <AdminGate/>
111
112
      <section className="bg-white">
113
          <div className="max-w-2xl px-4 py-8 mx-auto lg:py-16">
114
              <h2 className="mb-4 text-xl font-bold text-gray-900">Användare: { username }
115
              {isDeleted &&
116
              <span className="text-red-600 text-xl">(AVAKTIVERAD)</span>}
117
              </h2>
118
              <form action="#" onSubmit={(e) => updateUserInfo(e)}>
119
                  <div className="grid gap-4 mb-4">
120
                      <div className="">
121
                          <Label htmlFor="name">Användarnamn</Label>
122
                          <TextInput color="blue" id="name" type="text" value={username} onChange={ (e)=> setUsername(e.target.value) } placeholder="användarnamn" required/>
123
                      </div>
124
125
                      <div className="">
126
                          <Label htmlFor="created" >Registrerad(readonly)</Label>
127
                          <TextInput color="blue" id="created" type="text" value={createdAt} disabled required/>
128
                      </div>
129
130
                      <div className="w-full">
131
                          <Label htmlFor="email" >E-mail</Label>
132
                          <TextInput color="blue" id="email" type="email" value={email} onChange={ (e)=> setEmail(e.target.value) } placeholder="[email protected]" required/>
133
                      </div>
134
135
                      <div className="w-full">
136
                          <Label htmlFor="githubid" >Github ID (readonly)</Label>
137
                          <TextInput color="blue" id="githubid" type="text" value= {githubId} placeholder={user.githubId} required disabled/>
138
                      </div>
139
140
                      <div className="w-full">
141
                          <Label htmlFor="avatarurl" >Avatarurl</Label>
142 2
                          <TextInput color="blue" id="avatarurl" type="url" value= {avatarUrl} placeholder={user.avatarUrl} onChange={ (e)=> setAvatarUrl(e.target.value || "") }/>
143
                          <Card
144
                              className="max-w-sm mx-auto mt-6 inline-block"
145
                              imgAlt="user image"
146
                              imgSrc={avatarUrl}/>
147
                      </div>
148
149
                      <div className="flex items-center gap-2">
150
                        <Checkbox id="kund" color="blue" checked={isKund} onChange={()=>setIsKund(!isKund)} />
151
                        <Label htmlFor="kund" className="flex">Kundbehörighet</Label>
152
                      </div>
153
                      <div className="flex items-center gap-2">
154
                        <Checkbox id="admin" color="blue" checked={isAdmin} onChange={()=>setIsAdmin(!isAdmin)} />
155
                        <Label htmlFor="admin">Adminbehörighet</Label>
156
                      </div>
157
                      <div className="flex items-center gap-2">
158
                        <Checkbox id="delete" color="red" checked={isDeleted} onChange={()=>setIsDeleted(!isDeleted)} />
159
                        <Label htmlFor="delete" className="text-red-600 font-bold">Avaktiverad</Label>
160
                      </div>
161
                      <div className="flex items-center gap-2"> 
162
                      <ToggleSwitch color="blue" checked={isMonthlyPayment} label="Månatlig betalning" onChange={() => setIsMonthlyPayment(!isMonthlyPayment)} />
163
                      <ToggleSwitch color="teal" checked={hasAcceptedTerms} label="Godkända användarvillkor" onChange={() => setHasAcceptedTerms(!hasAcceptedTerms)} />
164
                      </div>
165
                      <div className="w-full">
166
                          <Label htmlFor="ackkost" >Ackumulerad kostnad</Label>
167 2
                          <TextInput color="blue" id="ackkost" type="text" value= {accumulatedCost} onChange={(e)=> setAccumulatedCost(parseFloat(e.target.value) || 0) } placeholder="" required/>
168
                      </div>
169
170
                      <div className="w-full">
171
                        <Label htmlFor=" balans" >Balans</Label>
172 2
                        <TextInput color="blue" id="balans" type="text" value={balance} onChange={(e)=> setBalance(parseFloat(e.target.value) || 0) } placeholder="" required/>
173
174
                      </div>
175
176
                  </div>
177
                  <div className="flex items-center space-x-4">
178
                      <Button type="submit" color="blue">
179
                        Uppdatera
180
                      </Button>
181
                      <Button color="light">
182
                        <Link to="/userlistpage">Gå tillbaka</Link>
183
                      </Button>
184
                  </div>
185
              </form>
186
          </div>
187
      </section>
188
    </div>
189
  );
190
};
191
192
export default AdminUserOverviewPage;
193