diff --git a/actions-web.php b/actions-web.php
index 93a5328d..5a7a8ccd 100644
--- a/actions-web.php
+++ b/actions-web.php
@@ -301,16 +301,6 @@ function trips($userId, $bike = 0)
echo json_encode($jsoncontent); // TODO change to response function
}
-function getuserlist()
-{
- global $db;
- $result = $db->query('SELECT users.userId,username,mail,number,privileges,credit,userLimit FROM users LEFT JOIN credit ON users.userId=credit.userId LEFT JOIN limits ON users.userId=limits.userId ORDER BY username');
- while ($row = $result->fetch_assoc()) {
- $jsoncontent[] = array('userid' => $row['userId'], 'username' => $row['username'], 'mail' => $row['mail'], 'number' => $row['number'], 'privileges' => $row['privileges'], 'credit' => $row['credit'], 'limit' => $row['userLimit']);
- }
- echo json_encode($jsoncontent); // TODO change to response function
-}
-
function getuserstats()
{
global $db;
@@ -335,40 +325,6 @@ function getusagestats()
echo json_encode($jsoncontent); // TODO change to response function
}
-function edituser($userid)
-{
- global $db;
- $result = $db->query('SELECT users.userId,userName,mail,number,privileges,userLimit,credit FROM users LEFT JOIN limits ON users.userId=limits.userId LEFT JOIN credit ON users.userId=credit.userId WHERE users.userId=' . $userid);
- $row = $result->fetch_assoc();
- $jsoncontent = array('userid' => $row['userId'], 'username' => $row['userName'], 'email' => $row['mail'], 'phone' => $row['number'], 'privileges' => $row['privileges'], 'limit' => $row['userLimit'], 'credit' => $row['credit']);
- echo json_encode($jsoncontent); // TODO change to response function
-}
-
-function saveuser($userid, $username, $email, $phone, $privileges, $limit)
-{
- global $db;
- $result = $db->query("UPDATE users SET username='$username',mail='$email',privileges='$privileges' WHERE userId=" . $userid);
- if ($phone) {
- $result = $db->query("UPDATE users SET number='$phone' WHERE userId=" . $userid);
- }
-
- $result = $db->query("UPDATE limits SET userLimit='$limit' WHERE userId=" . $userid);
- response(_('Details of user') . ' ' . $username . ' ' . _('updated') . '.');
-}
-
-function addcredit($userid, $creditmultiplier)
-{
- global $db, $user, $creditSystem;
-
- $minRequiredCredit = $creditSystem->getMinRequiredCredit();
- $addcreditamount = $minRequiredCredit * $creditmultiplier;
- $result = $db->query('UPDATE credit SET credit=credit+' . $addcreditamount . ' WHERE userId=' . $userid);
- $result = $db->query("INSERT INTO history SET userId=$userid,bikeNum=0,action='CREDITCHANGE',parameter='" . $addcreditamount . '|add+' . $addcreditamount . "'");
- $userName = $user->findUserName($userid);
-
- response(_('Added') . ' ' . $addcreditamount . $creditSystem->getCreditCurrency() . ' ' . _('credit for') . ' ' . $userName . '.');
-}
-
function validatecoupon($userid, $coupon)
{
global $db, $creditSystem;
diff --git a/command.php b/command.php
index b3e34787..6a23acb4 100644
--- a/command.php
+++ b/command.php
@@ -106,12 +106,6 @@
checkprivileges($userid);
liststands();
break;
- case "userlist":
- logrequest($userid,$action);
- $auth->refreshSession();
- checkprivileges($userid);
- getuserlist();
- break;
case "userstats":
logrequest($userid,$action);
$auth->refreshSession();
@@ -124,24 +118,6 @@
checkprivileges($userid);
getusagestats();
break;
- case "edituser":
- logrequest($userid,$action);
- $auth->refreshSession();
- checkprivileges($userid);
- edituser($_GET["edituserid"]);
- break;
- case "saveuser":
- logrequest($userid,$action);
- $auth->refreshSession();
- checkprivileges($userid);
- saveuser($_GET["edituserid"],$_GET["username"],$_GET["email"],$_GET["phone"],$_GET["privileges"],$_GET["limit"]);
- break;
- case "addcredit":
- logrequest($userid,$action);
- $auth->refreshSession();
- checkprivileges($userid);
- addcredit($_GET["edituserid"],$_GET["creditmultiplier"]);
- break;
case "trips":
logrequest($userid,$action);
$auth->refreshSession();
diff --git a/config/routes.php b/config/routes.php
index 46cbeca4..4e6545d0 100644
--- a/config/routes.php
+++ b/config/routes.php
@@ -46,6 +46,18 @@
$routes->add('api_coupon_generate', '/api/coupon/generate')
->methods(['POST'])
->controller([\BikeShare\Controller\Api\CouponController::class, 'generate']);
+ $routes->add('api_user_index', '/api/user')
+ ->methods(['GET'])
+ ->controller([\BikeShare\Controller\Api\UserController::class, 'index']);
+ $routes->add('api_user_item', '/api/user/{userId}')
+ ->methods(['GET'])
+ ->controller([\BikeShare\Controller\Api\UserController::class, 'item']);
+ $routes->add('api_user_item_update', '/api/user/{userId}')
+ ->methods(['PUT'])
+ ->controller([\BikeShare\Controller\Api\UserController::class, 'update']);
+ $routes->add('api_credit_add', '/api/credit')
+ ->methods(['PUT'])
+ ->controller([\BikeShare\Controller\Api\CreditController::class, 'add']);
$routes->add('personal_stats_year', '/personalStats/year/{year}')
->methods(['GET'])
diff --git a/public/js/admin.js b/public/js/admin.js
index 80f2d571..94216506 100644
--- a/public/js/admin.js
+++ b/public/js/admin.js
@@ -1,7 +1,4 @@
-var oTable;
-
$(document).ready(function () {
- $("#edituser").hide();
$("#where").click(function () {
if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-where');
bikeInfo($('#bikeNumber').val());
@@ -16,14 +13,6 @@ $(document).ready(function () {
last($(this).data('bike-number'));
event.preventDefault();
});
- $("#stands").click(function () {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-stands');
- stands();
- });
- $("#userlist").click(function () {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-userlist');
- userlist();
- });
$("#userstats").click(function () {
if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-userstats');
userstats();
@@ -36,55 +25,54 @@ $(document).ready(function () {
sellcoupon($(this).data('coupon'));
event.preventDefault();
});
- $("#generatecoupons1").click(function () {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-generatecoupons');
- generatecoupons(1);
- });
- $("#generatecoupons2").click(function () {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-generatecoupons');
- generatecoupons(5);
+ $('#userconsole').on('click', '.edituser', function (event) {
+ if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-edituser', $(this).attr('data-userid'));
+ edituser($(this).attr('data-userid'));
+ event.preventDefault();
+ })
+ $('#edituser').on('click', '.cancel', function (event) {
+ $('#edituser')
+ .addClass('d-none')
+ .find('input').val('');
+ event.preventDefault();
});
- $("#generatecoupons3").click(function () {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-generatecoupons');
- generatecoupons(10);
+ $(".generatecoupons").click(function (event) {
+ generatecoupons($(this).data('multiplier'));
+ event.preventDefault();
});
$("#trips").click(function () {
if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-trips');
trips();
});
- $("#saveuser").click(function () {
- saveuser();
- return false;
- });
- $("#addcredit").click(function () {
- addcredit(1);
- return false;
- });
- $("#addcredit2").click(function () {
- addcredit(5);
- return false;
+ $("#saveuser").click(function (event) {
+ saveuser($('#userid').val());
+ event.preventDefault();
});
- $("#addcredit3").click(function () {
- addcredit(10);
- return false;
+ $(".addcredit").click(function (event) {
+ addcredit($('#userid').val(), $(this).data('multiplier'));
+ event.preventDefault();
});
$('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
const target = $(e.target).attr("href");
switch (target) {
case "#fleet":
+ if (window.ga) ga('send', 'event', 'bikes', 'admin-fleet');
bikeInfo();
break;
case "#users":
+ if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-userlist');
userlist();
break;
case "#stands":
+ if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-stands');
stands();
break;
case "#reports":
userstats();
break;
case "#credit":
+ if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-couponlist');
couponlist();
break;
}
@@ -151,8 +139,6 @@ function generateBikeCards(data) {
}
function bikeInfo(bikeNumber) {
- if (window.ga) ga('send', 'event', 'bikes', 'where', bikeNumber);
-
$.ajax({
url: "/api/bike" + (bikeNumber ? "/" + bikeNumber : ""),
method: "GET",
@@ -167,8 +153,6 @@ function bikeInfo(bikeNumber) {
}
function last(bikeNumber) {
- if (window.ga) ga('send', 'event', 'bikes', 'last', bikeNumber);
-
$.ajax({
url: "/api/bikeLastUsage/" + bikeNumber,
method: "GET",
@@ -203,7 +187,6 @@ function last(bikeNumber) {
console.error("Error fetching bike data:", error);
}
});
-
}
function stands() {
@@ -216,41 +199,63 @@ function stands() {
}
function userlist() {
- var code = "";
- $.ajax({
- url: "command.php?action=userlist"
- }).done(function (jsonresponse) {
- jsonobject = $.parseJSON(jsonresponse);
- if (jsonobject.length > 0) code = '
' + _user + ' ' + _privileges + ' ' + _limit + ' ';
- if (creditenabled == 1) code = code + '' + _credit + ' ';
- code = code + ' ';
- for (var i = 0, len = jsonobject.length; i < len; i++) {
- code = code + '' + jsonobject[i]["username"] + ' ' + jsonobject[i]["number"] + ' ' + jsonobject[i]["mail"] + '' + jsonobject[i]["privileges"] + ' ' + jsonobject[i]["limit"] + ' ';
- if (creditenabled == 1) {
- code = code + '' + jsonobject[i]["credit"] + creditcurrency + ' ';
+ let table = $('#user-table').DataTable({
+ destroy: true,
+ searching: false,
+ ajax: {
+ url: '/api/user',
+ dataSrc: '',
+ cache: true
+ },
+ dom: 'lrtip',
+ columns: [
+ {
+ data: 'username',
+ name: 'username',
+ render: function(data, type, row) {
+ return `${data}
+ ${row.number}
+ ${row.mail}`;
+ }
+ },
+ {
+ data: 'privileges',
+ name: 'privileges'
+ },
+ {
+ data: 'userLimit',
+ name: 'userLimit'
+ },
+ {
+ data: 'credit',
+ name: 'credit',
+ visible: creditenabled === 1,
+ render: function(data, type, row) {
+ return `${data} ${creditcurrency}`;
+ }
}
+ ],
+ error: function(xhr, error, code) {
+ console.error('Error loading data:', error);
}
- if (jsonobject.length > 0) code = code + '
';
- $('#userconsole').html(code);
- createeditlinks();
- oTable = $('#usertable').dataTable({
- "dom": 'f<"filtertoolbar">prti',
- "paging": false,
- "ordering": false,
- "info": false
- });
- $('div.filtertoolbar').html(' ');
- $('#usertable th').each(function () {
- $('#columnfilter').append($(" ").attr('value', $(this).text()).text($(this).text()));
- });
- $('#usertable_filter input').keyup(function () {
- x = $('#columnfilter').prop("selectedIndex") - 1;
- if (x == -1) fnResetAllFilters(); else oTable.fnFilter($(this).val(), x);
- });
- $('#columnfilter').change(function () {
- x = $('#columnfilter').prop("selectedIndex") - 1;
- if (x == -1) fnResetAllFilters(); else oTable.fnFilter($('#usertable_filter input').val(), x);
- });
+ });
+
+ // Variable to track the column index for custom search
+ let searchColumnIndex = 0;
+
+ // Update dropdown and search column index
+ $('.search-option').click(function () {
+ const columnName = $(this).data('column');
+ searchColumnIndex = table.column(`${columnName}:name`).index(); // Get column index
+ $('#searchTypeDropdown').text($(this).text()); // Update dropdown button text
+ });
+
+ // Custom search logic
+ $('#customSearchInput').on('keyup', function () {
+ const searchTerm = this.value;
+ table.columns(searchColumnIndex)
+ .search(searchTerm)
+ .draw();
});
}
@@ -266,7 +271,6 @@ function userstats() {
}
if (jsonobject.length > 0) code = code + '';
$('#reportsconsole').html(code);
- createeditlinks();
$('#userstatstable').dataTable({
"paging": false,
"ordering": false,
@@ -290,61 +294,81 @@ function usagestats() {
});
}
-function createeditlinks() {
- $('.edituser').each(function () {
- $(this).click(function () {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-edituser', $(this).attr('data-userid'));
- edituser($(this).attr('data-userid'));
- });
- });
-}
-
function edituser(userid) {
$.ajax({
- url: "command.php?action=edituser&edituserid=" + userid
- }).done(function (jsonresponse) {
- jsonobject = $.parseJSON(jsonresponse);
- if (jsonobject) {
- $('#userid').val(jsonobject["userid"]);
- $('#username').val(jsonobject["username"]);
- $('#email').val(jsonobject["email"]);
- if ($('#phone')) $('#phone').val(jsonobject["phone"]);
- $('#privileges').val(jsonobject["privileges"]);
- $('#limit').val(jsonobject["limit"]);
- $('#edituser').show();
- $('a[href="#users"]').trigger('click');
+ url: "/api/user/" + userid,
+ method: "GET",
+ dataType: "json",
+ success: function(data) {
+ console.log(data);
+ $container = $("#edituser");
+ $container.find('input').val('');
+ $container.find('#userid').val(data.userId);
+ $container.find('#username').val(data.username);
+ $container.find('#email').val(data.mail);
+ if ($container.find('#phone').length) {
+ $container.find('#phone').val(data.number);
+ }
+ $container.find('#privileges').val(data.privileges);
+ $container.find('#limit').val(data.userLimit);
+ $container.removeClass('d-none');
+ $('html, body').animate({
+ scrollTop: $container.offset().top
+ }, 500);
+ },
+ error: function(xhr, status, error) {
+ console.error("Error fetching user data:", error);
}
});
}
-function saveuser() {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-saveuser', $('#userid').val());
- var phone = "";
- if ($('#phone')) phone = "&phone=" + $('#phone').val();
+function saveuser(userId) {
+ if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-saveuser', userId);
+
$.ajax({
- url: "command.php?action=saveuser&edituserid=" + $('#userid').val() + "&username=" + $('#username').val() + "&email=" + $('#email').val() + "&privileges=" + $('#privileges').val() + "&limit=" + $('#limit').val() + phone
- }).done(function (jsonresponse) {
- jsonobject = $.parseJSON(jsonresponse);
- $("#edituser").hide();
- handleresponse("userconsole", jsonobject);
- setTimeout(userlist, 2000);
+ url: "/api/user/" + userId,
+ method: "PUT",
+ dataType: "json",
+ data: {
+ 'username': $('#username').val(),
+ 'email': $('#email').val(),
+ 'number': $('#phone').length ? $('#phone').val() : '',
+ 'privileges': $('#privileges').val(),
+ 'userLimit': $('#limit').val()
+
+ },
+ success: function(data) {
+ $("#edituser").addClass('d-none');
+ userlist();
+ },
+ error: function(xhr, status, error) {
+ console.error("Error update user data:", error);
+ }
});
}
-function addcredit(creditmultiplier) {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-addcredit', $('#userid').val());
+function addcredit(userId, multiplier) {
+ if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-addcredit', userId, multiplier);
+
$.ajax({
- url: "command.php?action=addcredit&edituserid=" + $('#userid').val() + "&creditmultiplier=" + creditmultiplier
- }).done(function (jsonresponse) {
- jsonobject = $.parseJSON(jsonresponse);
- $("#edituser").hide();
- handleresponse("userconsole", jsonobject);
- setTimeout(userlist, 2000);
+ url: "/api/credit",
+ method: "PUT",
+ dataType: "json",
+ data: {
+ 'userId': userId,
+ 'multiplier': multiplier
+ },
+ success: function(data) {
+ $("#edituser").addClass('d-none');
+ userlist();
+ },
+ error: function(xhr, status, error) {
+ console.error("Error update user data:", error);
+ }
});
}
function couponlist() {
- if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-couponlist');
$.ajax({
url: "/api/coupon",
method: "GET",
@@ -375,6 +399,7 @@ function couponlist() {
}
function generatecoupons(multiplier) {
+ if (window.ga) ga('send', 'event', 'buttons', 'click', 'admin-generatecoupons', multiplier);
$.ajax({
url: "/api/coupon/generate",
method: "POST",
@@ -447,11 +472,3 @@ function revert(bikeNumber) {
handleresponse("fleetconsole", jsonobject);
});
}
-
-function fnResetAllFilters() {
- var oSettings = oTable.fnSettings();
- for (iCol = 0; iCol < oSettings.aoPreSearchCols.length; iCol++) {
- oSettings.aoPreSearchCols[iCol].sSearch = '';
- }
- oTable.fnDraw();
-}
\ No newline at end of file
diff --git a/src/Controller/Api/BikeController.php b/src/Controller/Api/BikeController.php
index 1d79f044..04c38e4b 100644
--- a/src/Controller/Api/BikeController.php
+++ b/src/Controller/Api/BikeController.php
@@ -36,7 +36,7 @@ public function index(
}
/**
- * @Route("/bike/{bikeNumber}", name="api_bike_item", methods={"GET"})
+ * @Route("/api/bike/{bikeNumber}", name="api_bike_item", methods={"GET"})
*/
public function item(
$bikeNumber,
diff --git a/src/Controller/Api/CreditController.php b/src/Controller/Api/CreditController.php
new file mode 100644
index 00000000..e2dfacd2
--- /dev/null
+++ b/src/Controller/Api/CreditController.php
@@ -0,0 +1,73 @@
+isGranted('ROLE_ADMIN')) {
+ $logger->info(
+ 'User tried to access admin page without permission',
+ [
+ 'user' => $this->getUser()->getUserIdentifier(),
+ ]
+ );
+
+ return $this->json([], Response::HTTP_FORBIDDEN);
+ }
+
+ $userId = $request->request->getInt('userId');
+ $multiplier = $request->request->getInt('multiplier');
+
+ if (
+ empty($userId)
+ || empty($multiplier)
+ || !is_numeric($userId)
+ || !is_numeric($multiplier)
+ || $multiplier < 1
+ || $multiplier > 10
+ ) {
+ return $this->json([], Response::HTTP_BAD_REQUEST);
+ }
+
+ $minRequiredCredit = $creditSystem->getMinRequiredCredit();
+ $creditAmount = $minRequiredCredit * $multiplier;
+
+ $creditRepository->addItem($userId, (float)$creditAmount);
+ $historyRepository->addItem(
+ $userId,
+ 0, //BikeNum
+ 'CREDITCHANGE', //action
+ $creditAmount . '|add+' . $creditAmount //parameter
+ );
+
+ $user = $userRepository->findItem($userId);
+
+ return new Response(
+ 'Added ' . $creditAmount . $creditSystem->getCreditCurrency() . ' credit for '
+ . $user['username'] . '.'
+ );
+ }
+}
diff --git a/src/Controller/Api/UserController.php b/src/Controller/Api/UserController.php
new file mode 100644
index 00000000..b7183259
--- /dev/null
+++ b/src/Controller/Api/UserController.php
@@ -0,0 +1,119 @@
+isGranted('ROLE_ADMIN')) {
+ $logger->info(
+ 'User tried to access admin page without permission',
+ [
+ 'user' => $this->getUser()->getUserIdentifier(),
+ ]
+ );
+
+ return $this->json([], Response::HTTP_FORBIDDEN);
+ }
+
+ $bikes = $userRepository->findAll();
+
+ return $this->json($bikes);
+ }
+
+ /**
+ * @Route("/api/user/{userId}", name="api_user_item", methods={"GET"})
+ */
+ public function item(
+ $userId,
+ UserRepository $userRepository,
+ LoggerInterface $logger
+ ): Response {
+ if (!$this->isGranted('ROLE_ADMIN')) {
+ $logger->info(
+ 'User tried to access admin page without permission',
+ [
+ 'user' => $this->getUser()->getUserIdentifier(),
+ ]
+ );
+
+ return $this->json([], Response::HTTP_FORBIDDEN);
+ }
+
+ if (empty($userId) || !is_numeric($userId)) {
+ return $this->json([], Response::HTTP_BAD_REQUEST);
+ }
+
+ $user = $userRepository->findItem((int)$userId);
+
+ return $this->json($user);
+ }
+
+ /**
+ * @Route("/api/user/{userId}", name="api_user_item_update", methods={"PUT"})
+ */
+ public function update(
+ $userId,
+ Request $request,
+ UserRepository $userRepository,
+ Configuration $configuration,
+ LoggerInterface $logger
+ ): Response {
+ if (!$this->isGranted('ROLE_ADMIN')) {
+ $logger->info(
+ 'User tried to access admin page without permission',
+ [
+ 'user' => $this->getUser()->getUserIdentifier(),
+ ]
+ );
+
+ return $this->json([], Response::HTTP_FORBIDDEN);
+ }
+
+ if (empty($userId) || !is_numeric($userId)) {
+ return $this->json([], Response::HTTP_BAD_REQUEST);
+ }
+
+ $userName = $request->request->get('username');
+ $email = $request->request->get('email');
+ $number = $request->request->get('number');
+ $privileges = $request->request->getInt('privileges');
+ $userLimit = $request->request->getInt('userLimit');
+
+ if (
+ empty($userName)
+ || empty($email)
+ || !filter_var($email, FILTER_VALIDATE_EMAIL)
+ || ($configuration->get('connectors')['sms'] !== '' && empty($number))
+ ) {
+ return $this->json([], Response::HTTP_BAD_REQUEST);
+ }
+
+ $userRepository->updateItem(
+ (int)$userId,
+ $userName,
+ $email,
+ $number,
+ $privileges,
+ $userLimit
+ );
+
+ return new Response('Details of user ' . $userName . ' updated.');
+ }
+}
diff --git a/src/EventListener/ControllerEventListener.php b/src/EventListener/ControllerEventListener.php
index 9535167d..88326461 100644
--- a/src/EventListener/ControllerEventListener.php
+++ b/src/EventListener/ControllerEventListener.php
@@ -17,6 +17,10 @@ class ControllerEventListener
'api_coupon_index',
'api_coupon_sell',
'api_coupon_generate',
+ 'api_user_index',
+ 'api_user_item',
+ 'api_user_item_update',
+ 'api_credit_add',
];
private DbInterface $db;
diff --git a/src/EventListener/ResponseEventListener.php b/src/EventListener/ResponseEventListener.php
index 3cb86f84..464c0a43 100644
--- a/src/EventListener/ResponseEventListener.php
+++ b/src/EventListener/ResponseEventListener.php
@@ -13,6 +13,9 @@ class ResponseEventListener
private const LOGGED_ROUTES = [
'api_coupon_sell',
'api_coupon_generate',
+ 'api_user_item',
+ 'api_user_item_update',
+ 'api_credit_add',
];
private DbInterface $db;
diff --git a/src/Repository/CreditRepository.php b/src/Repository/CreditRepository.php
new file mode 100644
index 00000000..c6b809b4
--- /dev/null
+++ b/src/Repository/CreditRepository.php
@@ -0,0 +1,25 @@
+db = $db;
+ }
+
+ public function addItem(int $userId, float $creditAmount): void
+ {
+ $userId = $this->db->escape($userId);
+ $creditAmount = $this->db->escape($creditAmount);
+ $this->db->query("UPDATE credit SET credit=credit+' . $creditAmount . ' WHERE userId=' . $userId . '");
+ }
+}
diff --git a/src/Repository/HistoryRepository.php b/src/Repository/HistoryRepository.php
new file mode 100644
index 00000000..879c084f
--- /dev/null
+++ b/src/Repository/HistoryRepository.php
@@ -0,0 +1,35 @@
+db = $db;
+ }
+
+ public function addItem(
+ int $userId,
+ int $bikeNum,
+ string $action,
+ string $parameter
+ ): void {
+ $userId = $this->db->escape($userId);
+ $bikeNum = $this->db->escape($bikeNum);
+ $action = $this->db->escape($action);
+ $parameter = $this->db->escape($parameter);
+
+ $this->db->query("
+ INSERT INTO history (userId, bikeNum, action, parameter)
+ VALUES ($userId, $bikeNum, '$action', '$parameter')
+ ");
+ }
+}
diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php
new file mode 100644
index 00000000..7feb083a
--- /dev/null
+++ b/src/Repository/UserRepository.php
@@ -0,0 +1,83 @@
+db = $db;
+ }
+
+ public function findAll(): array
+ {
+ $users = $this->db->query(
+ 'SELECT
+ users.userId,
+ username,
+ mail,
+ number,
+ privileges,
+ credit,
+ userLimit
+ FROM users
+ LEFT JOIN credit ON users.userId=credit.userId
+ LEFT JOIN limits ON users.userId=limits.userId
+ ORDER BY username'
+ )->fetchAllAssoc();
+
+
+ return $users;
+ }
+
+ public function findItem(int $userId): array
+ {
+ $user = $this->db->query(
+ 'SELECT
+ users.userId,
+ username,
+ mail,
+ number,
+ privileges,
+ credit,
+ userLimit
+ FROM users
+ LEFT JOIN credit ON users.userId=credit.userId
+ LEFT JOIN limits ON users.userId=limits.userId
+ WHERE users.userId=' . $userId
+ )->fetchAssoc();
+
+ return $user;
+ }
+
+ public function updateItem(
+ int $userId,
+ string $username,
+ string $email,
+ string $number,
+ int $privileges,
+ int $userLimit
+ ): void {
+ $this->db->query(
+ 'UPDATE users
+ SET username="' . $username . '",
+ mail="' . $email . '",
+ number="' . $number . '",
+ privileges=' . $privileges . '
+ WHERE userId=' . $userId
+ );
+
+ $this->db->query(
+ 'UPDATE limits
+ SET userLimit=' . $userLimit . '
+ WHERE userId=' . $userId
+ );
+ }
+}
diff --git a/templates/admin/credit.html.twig b/templates/admin/credit.html.twig
index 75cb5bca..60394af7 100644
--- a/templates/admin/credit.html.twig
+++ b/templates/admin/credit.html.twig
@@ -1,15 +1,13 @@
{% block credit %}
-
- {{ 'Generate'|trans }} {{ creditSystem.getMinRequiredCredit() }} {{ creditSystem.getCreditCurrency() }} {{ 'coupons'|trans }}
-
-
- {{ 'Generate'|trans }} {{ creditSystem.getMinRequiredCredit() * 5 }} {{ creditSystem.getCreditCurrency() }} {{ 'coupons'|trans }}
-
-
- {{ 'Generate'|trans }} {{ creditSystem.getMinRequiredCredit() * 10 }} {{ creditSystem.getCreditCurrency() }} {{ 'coupons'|trans }}
-
+ {% set multipliers = [1, 5, 10] %}
+ {% for multiplier in multipliers %}
+
+
+ {{ 'Generate'|trans }} {{ creditSystem.getMinRequiredCredit() * multiplier }} {{ creditSystem.getCreditCurrency() }} {{ 'coupons'|trans }}
+
+ {% endfor %}
diff --git a/templates/admin/fleet.html.twig b/templates/admin/fleet.html.twig
index ebbaabb4..47bb9051 100644
--- a/templates/admin/fleet.html.twig
+++ b/templates/admin/fleet.html.twig
@@ -52,9 +52,9 @@
-
+
diff --git a/templates/admin/index.html.twig b/templates/admin/index.html.twig
index e1a6f15a..6cb8977c 100644
--- a/templates/admin/index.html.twig
+++ b/templates/admin/index.html.twig
@@ -78,44 +78,7 @@
-
-
-
- {{ 'User list'|trans }}
-
-
-
-
-
+ {% include ('admin/user.html.twig') %}
{% if creditSystem.isEnabled %}
diff --git a/templates/admin/user.html.twig b/templates/admin/user.html.twig
new file mode 100644
index 00000000..44b8401d
--- /dev/null
+++ b/templates/admin/user.html.twig
@@ -0,0 +1,78 @@
+{% block user %}
+
+
+
+
+
+
+
+
+
+ {{ 'User'|trans }}
+ {{ 'Privileges'|trans }}
+ {{ 'Limit'|trans }}
+ {% if creditSystem.isEnabled %}
+ {{ 'Credit'|trans }}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/base.html.twig b/templates/base.html.twig
index 2a52c1b4..7f054602 100644
--- a/templates/base.html.twig
+++ b/templates/base.html.twig
@@ -63,9 +63,9 @@
-
-
-
+
+
+
{% block scripts %}{% endblock %}