Compare commits

..
3 Commits
Author SHA1 Message Date
nikola abf00ac401 Working on UI 2026-09-11 16:06:49 +02:00
nikola fcd4d4743b Theme switcher and BULMA 2026-09-11 13:30:02 +02:00
nikola fb152bd023 small changes 2026-09-11 10:34:44 +02:00
25 changed files with 338 additions and 87 deletions
+1 -2
View File
@@ -4,5 +4,4 @@
session.use_strict_mode=1 session.use_strict_mode=1
``` ```
https://github.com/ldaptools/ldaptools https://bulma.io/
+34 -4
View File
@@ -6,7 +6,7 @@
"name": "portal-vue", "name": "portal-vue",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "7.3.1", "@fortawesome/fontawesome-free": "7.3.1",
"bootstrap": "5.3.8", "bulma": "^1.0.4",
"vue": "^3.5.40", "vue": "^3.5.40",
"vue-i18n": "^11.4.10", "vue-i18n": "^11.4.10",
"vue-multiselect": "^3.5.0", "vue-multiselect": "^3.5.0",
@@ -150,7 +150,31 @@
"@oxc-project/types": ["@oxc-project/types@0.148.0", "", {}, "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A=="], "@oxc-project/types": ["@oxc-project/types@0.148.0", "", {}, "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A=="],
"@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="], "@parcel/watcher": ["@parcel/watcher@2.6.0", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.4" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.6.0", "@parcel/watcher-darwin-arm64": "2.6.0", "@parcel/watcher-darwin-x64": "2.6.0", "@parcel/watcher-freebsd-x64": "2.6.0", "@parcel/watcher-linux-arm-glibc": "2.6.0", "@parcel/watcher-linux-arm-musl": "2.6.0", "@parcel/watcher-linux-arm64-glibc": "2.6.0", "@parcel/watcher-linux-arm64-musl": "2.6.0", "@parcel/watcher-linux-x64-glibc": "2.6.0", "@parcel/watcher-linux-x64-musl": "2.6.0", "@parcel/watcher-win32-arm64": "2.6.0", "@parcel/watcher-win32-x64": "2.6.0" } }, "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w=="],
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.6.0", "", { "os": "android", "cpu": "arm64" }, "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA=="],
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.6.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw=="],
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.6.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw=="],
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.6.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ=="],
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.6.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg=="],
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.6.0", "", { "os": "linux", "cpu": "arm" }, "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw=="],
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.6.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g=="],
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.6.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA=="],
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.6.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A=="],
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.6.0", "", { "os": "linux", "cpu": "x64" }, "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw=="],
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.6.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ=="],
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.6.0", "", { "os": "win32", "cpu": "x64" }, "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A=="],
"@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.7", "", { "os": "android", "cpu": "arm" }, "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ=="], "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.7", "", { "os": "android", "cpu": "arm" }, "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ=="],
@@ -250,12 +274,12 @@
"birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="],
"bootstrap": ["bootstrap@5.3.8", "", { "peerDependencies": { "@popperjs/core": "^2.11.8" } }, "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg=="],
"brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="], "brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"bulma": ["bulma@1.0.4", "", {}, "sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g=="],
"cacheable": ["cacheable@2.5.0", "", { "dependencies": { "@cacheable/memory": "^2.2.0", "@cacheable/utils": "^2.5.0", "hookified": "^1.15.0", "keyv": "^5.6.0", "qified": "^0.10.1" } }, "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g=="], "cacheable": ["cacheable@2.5.0", "", { "dependencies": { "@cacheable/memory": "^2.2.0", "@cacheable/utils": "^2.5.0", "hookified": "^1.15.0", "keyv": "^5.6.0", "qified": "^0.10.1" } }, "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g=="],
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
@@ -352,6 +376,8 @@
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"immutable": ["immutable@5.1.9", "", {}, "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
@@ -420,6 +446,8 @@
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
"nostics": ["nostics@1.2.0", "", {}, "sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg=="], "nostics": ["nostics@1.2.0", "", {}, "sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg=="],
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
@@ -464,6 +492,8 @@
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
"sass": ["sass@1.104.0", "", { "dependencies": { "chokidar": "^5.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-btHMApW2bgolvClhRW8AlQJzgI9lUB3pPSofBQQT+E46GWvf9o0TVQ13SYv5riWZVFyPN+JNz3TKW9XhBlc10w=="],
"scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="], "scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="],
"semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
+3 -2
View File
@@ -4,8 +4,9 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NASLOV</title> <title>CMMS</title>
<link rel="icon" type="image/x-icon" href="/vue/favicon.ico">
<script defer type="module" src="/vue/main.js"></script> <script defer type="module" src="/vue/main.js"></script>
</head> </head>
+3 -2
View File
@@ -4,11 +4,12 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "vite build && cp -R php dist/php && cp -R vendor dist/vendor", "build": "vite build && cp -R php dist/php && cp -R vendor dist/vendor",
"serve": "concurrently \"vite\" \"php -S localhost:8000\" \"mailpit\" \"caddy run --config Caddyfile\"" "serve": "concurrently \"vite\" \"php -S localhost:8000\" \"mailpit\" \"caddy run --config Caddyfile\"",
"php": "concurrently \"php -t dist -S localhost:8000\" \"mailpit\" \"caddy reverse-proxy -t localhost:8000\""
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "7.3.1", "@fortawesome/fontawesome-free": "7.3.1",
"bootstrap": "5.3.8", "bulma": "^1.0.4",
"vue": "^3.5.40", "vue": "^3.5.40",
"vue-i18n": "^11.4.10", "vue-i18n": "^11.4.10",
"vue-multiselect": "^3.5.0", "vue-multiselect": "^3.5.0",
+2 -1
View File
@@ -11,7 +11,8 @@ try {
$conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch(PDOException $e) { } catch(PDOException $e) {
echo "Connection failed: " . $e->getMessage(); echo json_encode(["error" => "error.connect.faild"]);
exit;
} }
+11 -24
View File
@@ -12,7 +12,7 @@ $nimrodLDAP = new Connection([
]); ]);
if (!isset($_POST['uporabnik']) || !isset($_POST['geslo'])) { if (!isset($_POST['uporabnik']) || !isset($_POST['geslo'])) {
echo json_encode(["napaka" => "manjkajo podatki"]); echo json_encode(["error" => "error.user.login.missing"]);
exit; exit;
} }
@@ -22,17 +22,22 @@ $geslo = $_POST['geslo'];
try { try {
$nimrodLDAP->connect(); $nimrodLDAP->connect();
} catch (Exception $e) { } catch (Exception $e) {
echo 'napaka pri povezavi na LDap'; echo json_encode(["error" => "error.user.login.connect"]);
echo $e;
exit; exit;
} }
$LUser = $nimrodLDAP->query()->findBy('samaccountname', $username); $LUser = $nimrodLDAP->query()->findBy('samaccountname', $username);
if (empty($LUser)) { if (empty($LUser)) {
echo json_encode(["napaka" => "uporabnik ni najden"]); echo json_encode(["error" => "error.user.login.notFound"]);
exit; exit;
} }
$emailUserja = $LUser['userprincipalname'][0];
$user = $LUser['distinguishedname'][0];
if (!$nimrodLDAP->auth()->attempt($user, $geslo, $stayAuthenticated = true)) {
echo json_encode(["error" => "error.user.login.wrongPassword"]);
exit;
}
$vGrupah = $LUser['memberof']; $vGrupah = $LUser['memberof'];
$dGrupe = [ $dGrupe = [
@@ -44,14 +49,7 @@ $razlikaGrup = array_intersect(
array_map('strtolower', $dGrupe) array_map('strtolower', $dGrupe)
); );
if (count($razlikaGrup) <= 0) { if (count($razlikaGrup) <= 0) {
echo json_encode(["napaka" => "nimaš pravic skupinskih"]); echo json_encode(["error" => "error.user.login.groupRight"]);
exit;
}
$emailUserja = $LUser['userprincipalname'][0];
$user = $LUser['distinguishedname'][0];
if (!$nimrodLDAP->auth()->attempt($user, $geslo, $stayAuthenticated = true)) {
echo json_encode(["napaka" => "napacno geslo"]);
exit; exit;
} }
@@ -78,15 +76,4 @@ if ($obstojeciUporabnik) {
$_SESSION['displayname'] = $uporabnik; $_SESSION['displayname'] = $uporabnik;
$_SESSION['email'] = $email; $_SESSION['email'] = $email;
echo json_encode(["res" => "uspesno"]); echo json_encode(["res" => "res.user.login.true"]);
+2 -1
View File
@@ -1,2 +1,3 @@
<?php <?php
session_destroy(); session_destroy();
echo json_encode(["res" => "res.user.loginout.true"]);
+53 -5
View File
@@ -38,9 +38,31 @@ declare module 'vue-router/auto-routes' {
Record<never, never>, Record<never, never>,
| never | never
>, >,
'/test': RouteRecordInfo< '/settings': RouteRecordInfo<
'/test', '/settings',
'/test', '/settings',
Record<never, never>,
Record<never, never>,
| '/settings/groups'
| '/settings/users'
>,
'/settings/groups': RouteRecordInfo<
'/settings/groups',
'/settings/groups',
Record<never, never>,
Record<never, never>,
| never
>,
'/settings/users': RouteRecordInfo<
'/settings/users',
'/settings/users',
Record<never, never>,
Record<never, never>,
| never
>,
'/user_settings': RouteRecordInfo<
'/user_settings',
'/user_settings',
Record<never, never>, Record<never, never>,
Record<never, never>, Record<never, never>,
| never | never
@@ -66,9 +88,35 @@ declare module 'vue-router/auto-routes' {
pathParamNames: pathParamNames:
| never | never
} }
'vue/routes/test.vue': { 'vue/routes/settings.vue': {
routes: routes:
| '/test' | '/settings'
| '/settings/groups'
| '/settings/users'
views:
| 'default'
pathParamNames:
| never
}
'vue/routes/settings/groups.vue': {
routes:
| '/settings/groups'
views:
| never
pathParamNames:
| never
}
'vue/routes/settings/users.vue': {
routes:
| '/settings/users'
views:
| never
pathParamNames:
| never
}
'vue/routes/user_settings.vue': {
routes:
| '/user_settings'
views: views:
| never | never
pathParamNames: pathParamNames:
+1
View File
@@ -12,6 +12,7 @@ export default defineConfig(() => ({
VueRouter({ VueRouter({
routesFolder: 'vue/routes', routesFolder: 'vue/routes',
importMode: 'sync', importMode: 'sync',
exclude: ['vue/routes/**/comp/**'],
}), }),
vue(), vue(),
], ],
+11 -8
View File
@@ -1,15 +1,18 @@
<template> <template>
<LanguageSwitcher /> <div class="is-flex">
<p> <Sidebar />
{{ t('app.hello') }} <main class="p-5 is-flex-grow-1">
</p> <RouterView />
</main>
<RouterView /> </div>
</template> </template>
<script setup> <script setup>
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import LanguageSwitcher from '@/components/LanguageSwitcher.vue' import { applyTheme } from '@/components/theme';
import Sidebar from '@/components/Sidebar.vue';
applyTheme();
const { t } = useI18n(); const { t } = useI18n();
</script> </script>
+13 -9
View File
@@ -1,13 +1,17 @@
<template> <template>
<div class="dropdown"> <div class="dropdown is-hoverable">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown"> <div class="dropdown-trigger">
{{ locale.toUpperCase() }} <button class="button">
</button> <span>{{ locale.toUpperCase() }}</span>
<ul class="dropdown-menu"> </button>
<li v-for="loc in SUPPORT_LOCALES" :key="loc"> </div>
<a class="dropdown-item" href="#" @click.prevent="setLocale(loc)">{{ loc.toUpperCase() }}</a> <div class="dropdown-menu">
</li> <div class="dropdown-content">
</ul> <button v-for="loc in SUPPORT_LOCALES" :key="loc" @click.prevent="setLocale(loc)" class="dropdown-item">
{{ loc.toUpperCase() }}
</button>
</div>
</div>
</div> </div>
</template> </template>
+53
View File
@@ -0,0 +1,53 @@
<template>
<aside class="menu m-3" :class="{ 'is-collapsed': collapsed }">
<div class="level">
<p class="level-item" v-if="!collapsed">CMMS</p>
<div class="level-item">
<button class="button" :title="collapsed ? 'Expand menu' : 'Collapse menu'" @click="toggleSidebar">
<span class="icon">
<i class="fa-solid" :class="collapsed ? 'fa-angles-right' : 'fa-angles-left'"></i>
</span>
</button>
</div>
</div>
<ul class="menu-list">
<li v-for="item in items" :key="item.to">
<RouterLink :to="item.to" active-class="is-active" :class="{ 'has-text-centered': collapsed }"
:title="collapsed ? item.label : null">
<span class="icon">
<i class="fa-solid" :class="item.icon"></i>
</span>
<span v-if="!collapsed"> {{ t(item.label) }}</span>
</RouterLink>
</li>
</ul>
</aside>
</template>
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { RouterLink } from 'vue-router';
const { t } = useI18n()
const collapsed = ref(localStorage.getItem('sidebarCollapsed') === 'true');
function toggleSidebar() {
collapsed.value = !collapsed.value;
localStorage.setItem('sidebarCollapsed', collapsed.value);
}
// Example items — replace `to` with real routes as pages are added.
const items = [
{ label: 'menu.dashboard', icon: 'fa-gauge-high', to: '/' },
// { label: 'Assets', icon: 'fa-boxes-stacked', to: '/assets' },
// { label: 'Work Orders', icon: 'fa-clipboard-list', to: '/work-orders' },
// { label: 'Maintenance', icon: 'fa-screwdriver-wrench', to: '/maintenance' },
// { label: 'Reports', icon: 'fa-chart-line', to: '/reports' },
{ label: 'menu.userSettings', icon: 'fa-user-gear', to: '/user_settings' },
{ label: 'menu.settings', icon: 'fa-gear', to: '/settings' },
];
</script>
+23
View File
@@ -0,0 +1,23 @@
<template>
<div class="dropdown is-hoverable">
<div class="dropdown-trigger">
<button class="button">
<span>Barvna tema</span>
</button>
</div>
<div class="dropdown-menu">
<div class="dropdown-content">
<button class="dropdown-item" @click.prevent="setTheme('light')">
Svetlo
</button>
<button class="dropdown-item" @click.prevent="setTheme('dark')">
Temno
</button>
</div>
</div>
</div>
</template>
<script setup>
import { setTheme } from './theme';
</script>
+21
View File
@@ -0,0 +1,21 @@
import { ref } from 'vue';
const theme = ref(localStorage.getItem('theme') || 'auto');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
export function applyTheme() {
const resolved = theme.value === 'auto' ? (prefersDark.matches ? 'dark' : 'light') : theme.value;
document.documentElement.setAttribute('data-theme', resolved);
}
export function setTheme(value) {
theme.value = value;
localStorage.setItem('theme', value);
applyTheme();
}
prefersDark.addEventListener('change', () => {
if (theme.value === 'auto') applyTheme();
});
+22
View File
@@ -0,0 +1,22 @@
@import 'vue-multiselect/dist/vue-multiselect.css';
@import '@fortawesome/fontawesome-free/css/all.min.css';
@import 'bulma/css/bulma.min.css';
html {
background-color: var(--bulma-scheme-main-ter);
}
.dropdown {
--bulma-dropdown-menu-min-width: 4rem;
}
a.dropdown-item,
button.dropdown-item {
padding-inline-end: 1rem;
}
[data-theme=dark],
.theme-dark {
--bulma-scheme-main-ter-l: 20%;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

+10 -1
View File
@@ -1,4 +1,13 @@
{ {
"menu": {
"settings": "Settings",
"userSettings": "User settings",
"dashboard": "Dashboard"
},
"settings":{
"users":"Users",
"groups":"Groups"
},
"common": { "common": {
"save": "Save", "save": "Save",
"cancel": "Cancel", "cancel": "Cancel",
@@ -13,4 +22,4 @@
"hello": "HELLO", "hello": "HELLO",
"world": "WORLD" "world": "WORLD"
} }
} }
+6 -1
View File
@@ -1,4 +1,9 @@
{ {
"menu": {
"settings": "Nastavitve",
"userSettings": "Uporabniške nastavitve",
"dashboard": "Nadzorna plošča"
},
"common": { "common": {
"save": "Shrani", "save": "Shrani",
"cancel": "Prekliči", "cancel": "Prekliči",
@@ -13,4 +18,4 @@
"hello": "ZDRAVO", "hello": "ZDRAVO",
"world": "SVET" "world": "SVET"
} }
} }
+1 -5
View File
@@ -3,11 +3,7 @@ import { createRouter, createWebHashHistory } from 'vue-router'
import { routes, handleHotUpdate } from 'vue-router/auto-routes' import { routes, handleHotUpdate } from 'vue-router/auto-routes'
import { i18n, setLocale, initialLocale } from './i18n.js' import { i18n, setLocale, initialLocale } from './i18n.js'
import '@fortawesome/fontawesome-free/css/all.min.css' import './css/all.css'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.bundle.min.js'
import 'vue-multiselect/dist/vue-multiselect.css';
import App from './app.vue' import App from './app.vue'
+2 -14
View File
@@ -1,22 +1,10 @@
<template> <template>
<p>{{ t('app.world') }}</p> <h1 class="title">{{ t('menu.dashboard') }}</h1>
<p v-for="value in j">
{{ value }}
</p>
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n';
const { t } = useI18n(); const { t } = useI18n();
const j = ref([]);
onMounted(async () => {
const res = await fetch("/php/notifications.php")
j.value = await res.json();
});
</script> </script>
+26
View File
@@ -0,0 +1,26 @@
<template>
<h1 class="title">{{ t('menu.settings') }}</h1>
<div class="tabs">
<ul>
<li v-for="i in routes" :class="{ 'is-active': route.path === i.path }">
<RouterLink :to="i.path">
{{ t(i.title) }}
</RouterLink>
</li>
</ul>
</div>
<RouterView />
</template>
<script setup>
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
const { t } = useI18n();
const route = useRoute();
const routes = [
{"title": "settings.users", "path": "/settings/users"},
{"title": "settings.groups", "path": "/settings/groups"}
];
</script>
+8
View File
@@ -0,0 +1,8 @@
<template>
</template>
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
+16
View File
@@ -0,0 +1,16 @@
<template>
<table class="table">
<thead>
<tr>
<th>user</th>
<th>email</th>
<th>group</th>
</tr>
</thead>
</table>
</template>
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
-8
View File
@@ -1,8 +0,0 @@
<template>
{{ t('common.save') }}
</template>
<script setup>
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
</script>
+16
View File
@@ -0,0 +1,16 @@
<template>
<h1 class="title">{{ t('menu.userSettings') }}</h1>
<LanguageSwitcher />
<ThemeSwitcher />
</template>
<script setup>
import ThemeSwitcher from '@/components/ThemeSwitcher.vue';
import LanguageSwitcher from '@/components/LanguageSwitcher.vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>