Passed
Push — development ( 2f9cbb...cdc56b )
by Peter
09:24 queued 15s
created

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

Complexity

Total Complexity 7
Complexity/F 0

Size

Lines of Code 213
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 45%

Importance

Changes 0
Metric Value
eloc 171
dl 0
loc 213
ccs 27
cts 60
cp 0.45
rs 10
c 0
b 0
f 0
wmc 7
mnd 7
bc 7
fnc 0
bpm 0
cpm 0
noi 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
13
14
type User = {
15
  githubId: string;
16
  username: string;
17
  email: string;
18
  roles: string[];
19
  createdAt: string;
20
  hasAcceptedTerms: boolean;
21
  avatarUrl?: string;
22
  accumulatedCost: number;
23
  balance: number;
24
}
25
26 2
const AdminUserOverviewPage: React.FC = () => {
27 1
  const { githubId } = useParams<{ githubId: string }>();
28 2
  const { token } = useSelector((state: RootState) => state.auth);
29 1
  const [user, setUser] = useState<User | null>(null);
30 1
  const [loading, setLoading] = useState(true);
31 1
  const [isKund, setIsKund] = useState(false);
32 1
  const [isAdmin, setIsAdmin] = useState(false);
33 1
  const [createdAt, setCreatedAt] = useState("");
34 1
  const [username, setUsername] = useState("");
35 1
  const [email, setEmail] = useState("");
36 1
  const [hasAcceptedTerms, setHasAcceptedTerms] = useState(false);
37 1
  const [avatarUrl, setAvatarUrl] = useState("");
38 1
  const [isMonthlyPayment, setIsMonthlyPayment] = useState(false);
39 1
  const [accumulatedCost, setAccumulatedCost] = useState(0);
40 1
  const [balance, setBalance] = useState(0);
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"].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
        setUsername(user.username);
79
        setEmail(user.email);
80
        setCreatedAt(user.createdAt);
81
        setHasAcceptedTerms(user.hasAcceptedTerms);
82 2
        setAvatarUrl(user.avatarUrl ?? "");
83
        setIsMonthlyPayment(user.isMonthlyPayment);
84
        setAccumulatedCost(user.accumulatedCost);
85
        setBalance(user.balance);
86
      
87
      } catch (error) {
88
        console.error('Failed to fetch user info:', error);
89
      } finally {
90
        setLoading(false);
91
      }
92
    };
93
94 2
    if (githubId) {
95
      getUserInfo();
96
    }
97
  }, [githubId]);
98
99 2
  if (loading) {
100 1
    return <div data-testid="admin-user-overview-page">Laddar användardata...</div>;
101
  }
102
103 2
  if (!user) {
104
    return <div data-testid="admin-user-overview-page">Ingen användare hittades.</div>;
105
  }
106
107
  return (
108
    <div className="w-full max-w-4xl mx-auto p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700">
109
      <AdminGate/>
110
      {/* <h1 className="text-2xl font-bold text-center mb-6 text-gray-900 dark:text-white">{user.username}</h1>
111
      <div className="space-y-4">
112
        <p className="text-gray-600 dark:text-gray-400">Email: <b>{user.email}</b></p>
113
        <p className="font-normal text-gray-700 dark:text-gray-400 p-2">Skapat: <b>{user.createdAt || "No User"}</b></p>
114
        <p className="font-normal text-gray-700 dark:text-gray-400 p-2">Github Id: <b>{user.githubId || ":("}</b></p>
115
        <p className="text-gray-600 dark:text-gray-400">Roller: <b>{user.roles.join(', ')}</b></p>
116
        <p className="text-gray-600 dark:text-gray-400">
117
          <b>{user.hasAcceptedTerms ? 'Godkänt användarvillkor' : 'Ej godkänt användarvillkor'}</b>
118
        </p>
119
        {user.avatarUrl && (
120
          <img
121
            src={user.avatarUrl}
122
            alt={`${user.username}'s avatar`}
123
            className="w-24 h-24 rounded-full mt-4"
124
          />
125
        )}
126
127
128
        <p>
129
            <Link to="/userlistpage" className="py-2 m-16 px-4 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
130
                Gå tillbaka
131
            </Link>
132
        </p>
133
      </div> */}
134
135
136
      <section className="bg-white dark:bg-gray-900">
137
          <div className="max-w-2xl px-4 py-8 mx-auto lg:py-16">
138
              <h2 className="mb-4 text-xl font-bold text-gray-900 dark:text-white">Användare: { username } </h2>
139
              <form action="#" onSubmit={(e) => updateUserInfo(e)}>
140
                  <div className="grid gap-4 mb-4 sm:grid-cols-2 sm:gap-6 sm:mb-5">
141
                      <div className="sm:col-span-2">
142
                          <Label htmlFor="name">Användarnamn</Label>
143
                          <TextInput color="blue" id="name" type="text" value={username} onChange={ (e)=> setUsername(e.target.value) } placeholder="användarnamn" required/>
144
                      </div>
145
146
                      <div className="sm:col-span-2">
147
                          <Label htmlFor="created" >Registrerad(readonly)</Label>
148
                          <TextInput color="blue" id="created" type="text" value={createdAt} disabled required/>
149
                      </div>
150
151
                      <div className="w-full">
152
                          <Label htmlFor="email" >E-mail</Label>
153
                          <TextInput color="blue" id="email" type="email" value={email} onChange={ (e)=> setEmail(e.target.value) } placeholder="[email protected]" required/>
154
                      </div>
155
156
                      <div className="w-full">
157
                          <Label htmlFor="githubid" >Github ID (readonly)</Label>
158
                          <TextInput color="blue" id="githubid" type="text" value= {githubId} placeholder={user.githubId} required disabled/>
159
                      </div>
160
161
                      <div className="w-full">
162
                          <Label htmlFor="avatarurl" >Avatarurl</Label>
163 2
                          <TextInput color="blue" id="avatarurl" type="url" value= {avatarUrl} placeholder={user.avatarUrl} onChange={ (e)=> setAvatarUrl(e.target.value || "") }/>
164
                          <Card
165
                              className="max-w-sm mx-auto mt-6 inline-block"
166
                              imgAlt="user image"
167
                              imgSrc={avatarUrl}/>
168
                      </div>
169
170
                      <div className="flex items-center gap-2">
171
                        <Checkbox id="kund" color="blue" checked={isKund} onChange={()=>setIsKund(!isKund)} />
172
                        <Label htmlFor="kund" className="flex">Kundbehörighet</Label>
173
                      </div>
174
                      <div className="flex items-center gap-2">
175
                        <Checkbox id="admin" color="blue" checked={isAdmin} onChange={()=>setIsAdmin(!isAdmin)} />
176
                        <Label htmlFor="admin">Adminbehörighet</Label>
177
                      </div>
178
                      <div className="flex items-center gap-2"> 
179
                      <ToggleSwitch color="blue" checked={isMonthlyPayment} label="Månatlig betalning" onChange={() => setIsMonthlyPayment(!isMonthlyPayment)} />
180
                      <ToggleSwitch color="teal" checked={hasAcceptedTerms} label="Godkända användarvillkor" onChange={() => setHasAcceptedTerms(!hasAcceptedTerms)} />
181
                      </div>
182
                      <div className="w-full">
183
                          <Label htmlFor="ackkost" >Ackumulerad kostnad</Label>
184 2
                          <TextInput color="blue" id="ackkost" type="text" value= {accumulatedCost} onChange={(e)=> setAccumulatedCost(parseFloat(e.target.value) || 0) } placeholder="" required/>
185
                      </div>
186
187
                      <div className="w-full">
188
                        <Label htmlFor=" balans" >Balans</Label>
189 2
                        <TextInput color="blue" id="balans" type="text" value={balance} onChange={(e)=> setBalance(parseFloat(e.target.value) || 0) } placeholder="" required/>
190
191
                      </div>
192
193
                  </div>
194
                  <div className="flex items-center space-x-4">
195
                      <Button type="submit" color="blue">
196
                        Uppdatera
197
                      </Button>
198
                      <Button color="failure">
199
                        Radera (inte implementerad än)
200
                      </Button>
201
                      <Button color="light">
202
                        <Link to="/userlistpage">Gå tillbaka</Link>
203
                      </Button>
204
                  </div>
205
              </form>
206
          </div>
207
      </section>
208
    </div>
209
  );
210
};
211
212
export default AdminUserOverviewPage;
213