summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/src/Database/RangedWeaponsAccessLayer.zig2
-rw-r--r--backend/src/Handler.zig7
-rw-r--r--backend/src/Models/RangedWeapon.zig2
-rw-r--r--backend/src/Models/User.zig6
-rw-r--r--backend/src/main.zig12
-rw-r--r--front/package-lock.json37
-rw-r--r--front/package.json1
-rw-r--r--front/src/App.css2
-rw-r--r--front/src/Config.ts1
-rw-r--r--front/src/Emelents/Elements.css31
-rw-r--r--front/src/Fonts.css2
-rw-r--r--front/src/Locales/ru_RU.ts1
-rw-r--r--front/src/Models/RangedWeapon.ts31
-rw-r--r--front/src/Pages/Weapons.tsx91
-rw-r--r--front/src/index.css1
15 files changed, 214 insertions, 13 deletions
diff --git a/backend/src/Database/RangedWeaponsAccessLayer.zig b/backend/src/Database/RangedWeaponsAccessLayer.zig
index 8d86840..34c71ea 100644
--- a/backend/src/Database/RangedWeaponsAccessLayer.zig
+++ b/backend/src/Database/RangedWeaponsAccessLayer.zig
@@ -7,7 +7,7 @@ const pg = @import("pg");
const model = @import("../Models/RangedWeapon.zig");
pub fn GetAll(alloc: std.mem.Allocator) !std.ArrayList(model.RangedWeaponType) {
- const query = "SELECT * FROM RangedWeapons";
+ const query = "SELECT * FROM RangedWeapons ORDER BY weapon_type";
var result = try conn.pool.query(query, .{});
defer _ = result.deinit();
diff --git a/backend/src/Handler.zig b/backend/src/Handler.zig
index dfd884d..3e40cf8 100644
--- a/backend/src/Handler.zig
+++ b/backend/src/Handler.zig
@@ -8,7 +8,7 @@ pub const RequestData = struct {
pub fn Init(req: *httpz.Request) !RequestData {
return .{
- .User = getUser(req),
+ .User = try getUser(req),
};
}
@@ -27,6 +27,11 @@ pub const RequestData = struct {
return parsed.value;
}
+
+ pub fn CanUserAccess(self: RequestData, minimalRole: userModel.Role) bool {
+ if (self.User == null) return false;
+ return self.User.?.Role >= minimalRole;
+ }
};
pub const Handler = struct {
diff --git a/backend/src/Models/RangedWeapon.zig b/backend/src/Models/RangedWeapon.zig
index b2c15cd..36e750e 100644
--- a/backend/src/Models/RangedWeapon.zig
+++ b/backend/src/Models/RangedWeapon.zig
@@ -11,7 +11,7 @@ pub const RangedWeaponType = struct {
Avaliability: []const u8,
Damage: []const u8,
Ammunition: []const u8,
- NumberOfShots: u31,
+ NumberOfShots: u32,
RateOfFire: u32,
Reliability: []const u8,
diff --git a/backend/src/Models/User.zig b/backend/src/Models/User.zig
index a52186d..e7550b7 100644
--- a/backend/src/Models/User.zig
+++ b/backend/src/Models/User.zig
@@ -11,9 +11,9 @@ const RolesMap = std.static_string_map.StaticStringMap(Role).initComptime(.{
.{ "editor", .editor },
});
-pub const Role = enum {
- user,
- editor,
+pub const Role = enum (u8) {
+ user = 0,
+ editor = 1,
pub fn ToString(self: Role) []const u8 {
return switch (self) {
diff --git a/backend/src/main.zig b/backend/src/main.zig
index 6def1a1..86e84cb 100644
--- a/backend/src/main.zig
+++ b/backend/src/main.zig
@@ -26,12 +26,22 @@ pub fn main() !void {
.address = .all(6969),
}, &httpHandler);
+ const cors = try server.middleware(httpz.middleware.Cors, .{
+ .origin = "*",
+ .methods = "GET,POST,PUT,DELETE,OPTIONS",
+ .headers = "authorization,content-type",
+ });
+
defer {
server.stop();
server.deinit();
}
- var router = try server.router(.{});
+ var router = try server.router(.{
+ .middlewares = &.{
+ cors
+ }
+ });
router.get("/", index, .{});
@import("API/WeaponsAPI.zig").RegisterEndpoints(router);
diff --git a/front/package-lock.json b/front/package-lock.json
index fe858e6..6943f13 100644
--- a/front/package-lock.json
+++ b/front/package-lock.json
@@ -16,6 +16,7 @@
"@types/node": "^16.18.126",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
+ "axios": "^1.15.2",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-router": "^7.14.0",
@@ -4834,6 +4835,33 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz",
+ "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -13508,6 +13536,15 @@
"node": ">= 0.10"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/psl": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
diff --git a/front/package.json b/front/package.json
index b705d58..234d168 100644
--- a/front/package.json
+++ b/front/package.json
@@ -11,6 +11,7 @@
"@types/node": "^16.18.126",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
+ "axios": "^1.15.2",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-router": "^7.14.0",
diff --git a/front/src/App.css b/front/src/App.css
index 5cbbeb8..dcfa013 100644
--- a/front/src/App.css
+++ b/front/src/App.css
@@ -13,7 +13,7 @@
padding-left: 10px;
}
-.AppContents > div > h1 {
+.AppContents > div > h1, .AppContents > div > span > h1 {
background-color: var(--colorscheme-black);
color: var(--colorscheme-white);
padding-left: 10px;
diff --git a/front/src/Config.ts b/front/src/Config.ts
new file mode 100644
index 0000000..78f4e0d
--- /dev/null
+++ b/front/src/Config.ts
@@ -0,0 +1 @@
+export const BackendURL = "http://localhost:6969";
diff --git a/front/src/Emelents/Elements.css b/front/src/Emelents/Elements.css
index 1f4d60f..31f0fd2 100644
--- a/front/src/Emelents/Elements.css
+++ b/front/src/Emelents/Elements.css
@@ -65,6 +65,10 @@
display: flex;
}
+.ContentsList > li > span {
+ display: flex;
+}
+
.ContentsList > li > span > span {
flex-grow: 1;
font-family: "Rubik Dirt", system-ui;
@@ -88,3 +92,30 @@
font-size: 15px;
font-family: "Rubik Dirt", system-ui;
}
+
+.WeaponsDiv {
+ flex-grow: 1;
+}
+
+.WeaponsDiv > span {
+ display: flex;
+}
+
+.WeaponTable {
+ width: 100%;
+}
+
+.WeaponTable > tbody > tr > td {
+ font-family: "Wix Madefor Text", sans-serif;
+}
+
+.WeaponTable, .WeaponTable > tbody, .WeaponTable > tbody > tr > td, .WeaponTable > tbody > tr {
+ border: 0px solid red;
+ border-collapse: collapse;
+}
+
+.CategoryName {
+ padding-top: 10px;
+ font-family: "Rubik Dirt", system-ui !important;
+ font-size: 20px;
+}
diff --git a/front/src/Fonts.css b/front/src/Fonts.css
index c3b986b..c7f3bb6 100644
--- a/front/src/Fonts.css
+++ b/front/src/Fonts.css
@@ -1,4 +1,4 @@
-@import url('https://fonts.googleapis.com/css2?family=Rubik+Dirt&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Rubik+Dirt&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap');
h1, h2 {
font-family: "Rubik Dirt", system-ui;
diff --git a/front/src/Locales/ru_RU.ts b/front/src/Locales/ru_RU.ts
index 6c804b9..39c45d0 100644
--- a/front/src/Locales/ru_RU.ts
+++ b/front/src/Locales/ru_RU.ts
@@ -9,6 +9,7 @@ lang.LocalizedStrings = new Map<string, string>([
["classes list", "Список классов"],
["weapons", "Оружие"],
["weapons index", "Список вооружения"],
+ ["ranged weapons", "Дальнобойное оружие"],
]);
export default lang;
diff --git a/front/src/Models/RangedWeapon.ts b/front/src/Models/RangedWeapon.ts
new file mode 100644
index 0000000..2bc9995
--- /dev/null
+++ b/front/src/Models/RangedWeapon.ts
@@ -0,0 +1,31 @@
+export type RangedWeapon = {
+ Id: string,
+ Name: string,
+
+ WeaponType: string,
+ Accuracy: number,
+ Concealability: string,
+ Avaliability: string,
+ Damage: string,
+ Ammunition: string,
+ NumberOfShots: number,
+ RateOfFire: number,
+ Reliability: string,
+
+ CreatedAt: number,
+ UpdatedAt: number,
+};
+
+export type RangedWeaponRequest = {
+ Id: string,
+ Name: string,
+ WeaponType: string,
+ Accuracy: number,
+ Concealability: string,
+ Avaliability: string,
+ Damage: string,
+ Ammunition: string,
+ NumberOfShots: number,
+ RateOfFire: number,
+ Reliability: string,
+};
diff --git a/front/src/Pages/Weapons.tsx b/front/src/Pages/Weapons.tsx
index d16762c..f918232 100644
--- a/front/src/Pages/Weapons.tsx
+++ b/front/src/Pages/Weapons.tsx
@@ -1,18 +1,101 @@
-import { useContext } from "react";
+import axios from "axios";
+import { useContext, useState } from "react";
+import { BackendURL } from "../Config";
import { LanguageContext } from "../Locales/Context";
import { GetLocalizedString } from "../Locales/Locales";
+import { RangedWeapon } from "../Models/RangedWeapon";
+
+const RangedWeaponsURL = `${BackendURL}/weapons/ranged`;
function WeaponsIndex() {
- var lang = useContext(LanguageContext);
+ const lang = useContext(LanguageContext);
+ const [rangedWeapons, setRangedWeapons] = useState<RangedWeapon[] | null>(null);
+
+ useState(() => {
+ getRangedWeapons().then(data => setRangedWeapons(data));
+ });
return (
- <div>
+ <div className="WeaponsDiv">
+ <span>
<h1> {GetLocalizedString("Weapons index", lang)} </h1>
+ </span>
+ <h2> {GetLocalizedString("Ranged weapons", lang)} </h2>
+ {generatedRangedWeaponsList(rangedWeapons)}
</div>
);
}
-function WeaponsType() {
+function generatedRangedWeaponsList(rangedWeapons: RangedWeapon[] | null): any {
+ if (!rangedWeapons) {
+ return (
+ <div> </div>
+ );
+ }
+
+ const categorized = new Map<string, RangedWeapon[]>();
+
+ rangedWeapons.forEach(el => {
+ if (!categorized.has(el.WeaponType)) {
+ categorized.set(el.WeaponType, new Array<RangedWeapon>());
+ }
+ categorized.get(el.WeaponType)?.push(el);
+ });
+
+ const keys = Array.from(categorized.keys());
+ const list = keys.map(x => {
+ const elements = categorized.get(x)?.map(w => {
+ return (
+ <tr key={w.Id}>
+ <td> {w.Name} </td>
+ <td> {w.WeaponType} </td>
+ <td> {w.Accuracy} </td>
+ <td> {w.Concealability} </td>
+ <td> {w.Avaliability} </td>
+ <td> {w.Damage}({w.Ammunition}) </td>
+ <td> {w.NumberOfShots} </td>
+ <td> {w.RateOfFire} </td>
+ <td> {w.Reliability} </td>
+ </tr>
+ )
+ });
+
+ return (
+ <tbody key={x}>
+ <tr>
+ <td className="CategoryName"> {`${x}s`} </td>
+ </tr>
+ {elements}
+ </tbody>
+ );
+ });
+
+ return (
+ <table className="WeaponTable">
+ {list}
+ </table>
+ );
+}
+
+async function getRangedWeapons(): Promise<RangedWeapon[]> {
+ try {
+ const {data, status} = await axios.get<RangedWeapon[]>(
+ RangedWeaponsURL,
+ {
+ headers: { Accept: "application/json" }
+ }
+ );
+
+ if (status != 200) {
+ return new Array<RangedWeapon>();
+ }
+
+ return data;
+
+ } catch (err) {
+ console.log(`Failed to get ranged weapons: ${err}`);
+ return new Array<RangedWeapon>();
+ }
}
export default WeaponsIndex;
diff --git a/front/src/index.css b/front/src/index.css
index 4e7932e..0193f3b 100644
--- a/front/src/index.css
+++ b/front/src/index.css
@@ -3,6 +3,7 @@
--colorscheme-blue: #00F0FF;
--colorscheme-white: #FAFAFA;
--colorscheme-gray: #3C3C3C;
+ --colorscheme-light-gray: #909090F0;
}
* {