Adding vue and stats
This commit is contained in:
108
pointMaster/Controllers/StatsApiController.cs
Normal file
108
pointMaster/Controllers/StatsApiController.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using pointMaster.Data;
|
||||||
|
using pointMaster.Models;
|
||||||
|
|
||||||
|
namespace pointMaster.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api")]
|
||||||
|
public class StatsApiController : ControllerBase
|
||||||
|
{
|
||||||
|
readonly DataContext dataContext;
|
||||||
|
|
||||||
|
public StatsApiController(DataContext dataContext)
|
||||||
|
{
|
||||||
|
this.dataContext = dataContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = Roles.Editor)]
|
||||||
|
[HttpGet("GetPointsOverTime")]
|
||||||
|
public async Task<object> GetList()
|
||||||
|
{
|
||||||
|
var pointData = await dataContext.Points.Include(x => x.Patrulje).Include(x => x.Poster).ToListAsync();
|
||||||
|
|
||||||
|
var pointGrouped = pointData.OrderBy(x => x.DateCreated).GroupBy(x => x.Patrulje.Name).ToList();
|
||||||
|
|
||||||
|
var retval = new List<PointChartModel>();
|
||||||
|
|
||||||
|
var lastChange = DateTime.Now.ToString("MM/d/yyyy H:m:s").Replace('.', ':');
|
||||||
|
|
||||||
|
foreach (var group in pointGrouped)
|
||||||
|
{
|
||||||
|
var data = new List<PointChartDataModel>();
|
||||||
|
|
||||||
|
var total = 0;
|
||||||
|
foreach (var point in group)
|
||||||
|
{
|
||||||
|
var date = DateTime.Parse(point.DateCreated.ToString());
|
||||||
|
|
||||||
|
total += point.Points + point.Turnout;
|
||||||
|
|
||||||
|
data.Add(new PointChartDataModel
|
||||||
|
{
|
||||||
|
x = date.ToString("MM/d/yyyy H:m:s").Replace('.', ':'),
|
||||||
|
y = total
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Add(new PointChartDataModel
|
||||||
|
{
|
||||||
|
x = lastChange,
|
||||||
|
y = total
|
||||||
|
});
|
||||||
|
|
||||||
|
retval.Add(new PointChartModel
|
||||||
|
{
|
||||||
|
Name = group.Key,
|
||||||
|
Data = data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var rval = JsonConvert.SerializeObject(retval, new JsonSerializerSettings()
|
||||||
|
{
|
||||||
|
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("getstats")]
|
||||||
|
public async Task<IActionResult> GetStats()
|
||||||
|
{
|
||||||
|
var vm = new List<StatModel>();
|
||||||
|
|
||||||
|
var pointData = await dataContext.Points.ToListAsync();
|
||||||
|
|
||||||
|
vm.Add(new StatModel
|
||||||
|
{
|
||||||
|
Title = "Points givet",
|
||||||
|
Value = pointData.Sum(x => x.Points + x.Turnout).ToString()
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(JsonConvert.SerializeObject(vm));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PointChartModel
|
||||||
|
{
|
||||||
|
[JsonProperty("name")]
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
[JsonProperty("data")]
|
||||||
|
public List<PointChartDataModel> Data { get; set; } = null!;
|
||||||
|
}
|
||||||
|
public class PointChartDataModel
|
||||||
|
{
|
||||||
|
public string x { get; set; } = null!;
|
||||||
|
public int y { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StatModel
|
||||||
|
{
|
||||||
|
public string Title { get; set; } = null!;
|
||||||
|
public string Value { get; set; } = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
pointMaster/Controllers/StatsController.cs
Normal file
22
pointMaster/Controllers/StatsController.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using pointMaster.Data;
|
||||||
|
using pointMaster.Models;
|
||||||
|
|
||||||
|
namespace pointMaster.Controllers
|
||||||
|
{
|
||||||
|
public class StatsController : Controller
|
||||||
|
{
|
||||||
|
private readonly DataContext dataContext;
|
||||||
|
|
||||||
|
public StatsController(DataContext dataContext)
|
||||||
|
{
|
||||||
|
this.dataContext = dataContext;
|
||||||
|
}
|
||||||
|
public async Task<IActionResult> Index()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,13 @@ COPY . .
|
|||||||
WORKDIR "/src/pointMaster"
|
WORKDIR "/src/pointMaster"
|
||||||
RUN dotnet build "./pointMaster.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
RUN dotnet build "./pointMaster.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM node:20 AS node-build
|
||||||
|
WORKDIR /js-app
|
||||||
|
COPY ["js/package.json", "js/package-lock.json*", "./"]
|
||||||
|
RUN npm install
|
||||||
|
COPY js/ .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
# This stage is used to publish the service project to be copied to the final stage
|
# This stage is used to publish the service project to be copied to the final stage
|
||||||
FROM build AS publish
|
FROM build AS publish
|
||||||
ARG BUILD_CONFIGURATION=Release
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -11,8 +12,8 @@ using pointMaster.Data;
|
|||||||
namespace pointMaster.Migrations
|
namespace pointMaster.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(DataContext))]
|
[DbContext(typeof(DataContext))]
|
||||||
[Migration("20241116003844_initial")]
|
[Migration("20241121135108_DateTime")]
|
||||||
partial class initial
|
partial class DateTime
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@@ -32,6 +33,9 @@ namespace pointMaster.Migrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
@@ -52,6 +56,9 @@ namespace pointMaster.Migrations
|
|||||||
b.Property<int>("Age")
|
b.Property<int>("Age")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
@@ -74,6 +81,9 @@ namespace pointMaster.Migrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<int>("PatruljeId")
|
b.Property<int>("PatruljeId")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
@@ -103,6 +113,9 @@ namespace pointMaster.Migrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
@@ -6,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace pointMaster.Migrations
|
namespace pointMaster.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class initial : Migration
|
public partial class DateTime : Migration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
@@ -17,7 +18,8 @@ namespace pointMaster.Migrations
|
|||||||
{
|
{
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
Name = table.Column<string>(type: "text", nullable: false)
|
Name = table.Column<string>(type: "text", nullable: false),
|
||||||
|
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -32,7 +34,8 @@ namespace pointMaster.Migrations
|
|||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
Name = table.Column<string>(type: "text", nullable: false),
|
Name = table.Column<string>(type: "text", nullable: false),
|
||||||
Location = table.Column<string>(type: "text", nullable: false),
|
Location = table.Column<string>(type: "text", nullable: false),
|
||||||
Description = table.Column<string>(type: "text", nullable: false)
|
Description = table.Column<string>(type: "text", nullable: false),
|
||||||
|
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -47,7 +50,8 @@ namespace pointMaster.Migrations
|
|||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
Name = table.Column<string>(type: "text", nullable: false),
|
Name = table.Column<string>(type: "text", nullable: false),
|
||||||
Age = table.Column<int>(type: "integer", nullable: false),
|
Age = table.Column<int>(type: "integer", nullable: false),
|
||||||
PatruljeId = table.Column<int>(type: "integer", nullable: false)
|
PatruljeId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -69,7 +73,8 @@ namespace pointMaster.Migrations
|
|||||||
Points = table.Column<int>(type: "integer", nullable: false),
|
Points = table.Column<int>(type: "integer", nullable: false),
|
||||||
Turnout = table.Column<int>(type: "integer", nullable: false),
|
Turnout = table.Column<int>(type: "integer", nullable: false),
|
||||||
PatruljeId = table.Column<int>(type: "integer", nullable: false),
|
PatruljeId = table.Column<int>(type: "integer", nullable: false),
|
||||||
PosterId = table.Column<int>(type: "integer", nullable: false)
|
PosterId = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
@@ -29,6 +30,9 @@ namespace pointMaster.Migrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
@@ -49,6 +53,9 @@ namespace pointMaster.Migrations
|
|||||||
b.Property<int>("Age")
|
b.Property<int>("Age")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
@@ -71,6 +78,9 @@ namespace pointMaster.Migrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<int>("PatruljeId")
|
b.Property<int>("PatruljeId")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
@@ -100,6 +110,9 @@ namespace pointMaster.Migrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateCreated")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<link rel="stylesheet" href="~/pointMaster.styles.css" asp-append-version="true" />
|
<link rel="stylesheet" href="~/pointMaster.styles.css" asp-append-version="true" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="app">
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
@@ -37,9 +38,11 @@
|
|||||||
© 2024 - pointMaster - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
© 2024 - pointMaster - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||||
</div>
|
</div>
|
||||||
</footer> *@
|
</footer> *@
|
||||||
|
</div>
|
||||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
|
<script src="~/js/dist/app.js" asp-append-version="true"></script>
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
7
pointMaster/Views/Stats/Index.cshtml
Normal file
7
pointMaster/Views/Stats/Index.cshtml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
@*
|
||||||
|
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||||
|
*@
|
||||||
|
@{
|
||||||
|
ViewData["title"] = "Stats";
|
||||||
|
}
|
||||||
|
<hello-world></hello-world>
|
||||||
24
pointMaster/js/package.json
Normal file
24
pointMaster/js/package.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "website",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"watch": "vue-cli-service build --mode development --watch",
|
||||||
|
"build": "vue-cli-service build"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||||
|
"@vue/cli-service": "^5.0.8",
|
||||||
|
"apexcharts": "^4.0.0",
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-class-component": "^7.2.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vue-cli-service": "^5.0.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
104
pointMaster/js/src/components/HelloWorld.vue
Normal file
104
pointMaster/js/src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stat-dashboard">
|
||||||
|
<div>
|
||||||
|
<h1>Stats</h1>
|
||||||
|
<div class="stats" v-for="data in StatData">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="stat-title">Point over tid</p>
|
||||||
|
<div id="chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import ApexCharts, { ApexOptions } from 'apexcharts';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { defineComponent, onMounted } from 'vue';
|
||||||
|
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'HelloWorld',
|
||||||
|
setup() {
|
||||||
|
var StatData: Stat[] = [];
|
||||||
|
|
||||||
|
const GetStats = async () => {
|
||||||
|
var data = (await axios.get("/api/getstats")).data as Stat[];
|
||||||
|
|
||||||
|
StatData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await GetStats();
|
||||||
|
await Chart();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Chart = async () => {
|
||||||
|
var options: ApexOptions = {
|
||||||
|
chart: {
|
||||||
|
type: "line",
|
||||||
|
toolbar: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
type: "datetime"
|
||||||
|
},
|
||||||
|
series: []
|
||||||
|
};
|
||||||
|
const chart = new ApexCharts(document.querySelector("#chart"), options);
|
||||||
|
chart.render();
|
||||||
|
|
||||||
|
const data = (await axios.get("/api/GetPointsOverTime")).data;
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
chart.updateOptions({
|
||||||
|
series: data
|
||||||
|
} as ApexOptions)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css">
|
||||||
|
.stat-dashboard {
|
||||||
|
display: flex;
|
||||||
|
height: 60vh;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chart {
|
||||||
|
height: 300px;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 50vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 10px;
|
||||||
|
height: 7rem;
|
||||||
|
width: 10rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat .stat-value {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
110
pointMaster/js/src/components/Stats.vue
Normal file
110
pointMaster/js/src/components/Stats.vue
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stat-dashboard">
|
||||||
|
<div>
|
||||||
|
<h1>Stats</h1>
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat" v-for="(data, index) in StatData" :key="index">
|
||||||
|
<p class="stat-title">{{ data.Title }}</p>
|
||||||
|
<p class="stat-value">{{ data.Value }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="stat-title">Point over tid</p>
|
||||||
|
<div id="chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import ApexCharts, { ApexOptions } from 'apexcharts';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { defineComponent, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Stats",
|
||||||
|
setup() {
|
||||||
|
const StatData = ref<Stat[]>([]);
|
||||||
|
|
||||||
|
const GetStats = async () => {
|
||||||
|
const data = (await axios.get("/api/getstats")).data as Stat[]
|
||||||
|
|
||||||
|
StatData.value = data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initializeChart = async () => {
|
||||||
|
const options: ApexOptions = {
|
||||||
|
chart: {
|
||||||
|
type: "line",
|
||||||
|
toolbar: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
type: "datetime"
|
||||||
|
},
|
||||||
|
series: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const chart = new ApexCharts(document.querySelector("#chart"), options);
|
||||||
|
chart.render();
|
||||||
|
|
||||||
|
const data = (await axios.get("/api/GetPointsOverTime")).data;
|
||||||
|
|
||||||
|
chart.updateOptions({
|
||||||
|
series: data
|
||||||
|
} as ApexOptions);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await GetStats();
|
||||||
|
await initializeChart();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
StatData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css">
|
||||||
|
.stat-dashboard {
|
||||||
|
display: flex;
|
||||||
|
height: 60vh;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chart {
|
||||||
|
height: 300px;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 50vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 10px;
|
||||||
|
height: 7rem;
|
||||||
|
width: 10rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0px 0px 5px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat .stat-value {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
11
pointMaster/js/src/main.ts
Normal file
11
pointMaster/js/src/main.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { createApp } from "vue";
|
||||||
|
import HelloWorld from "./components/HelloWorld.vue";
|
||||||
|
import Stats from "./components/Stats.vue";
|
||||||
|
|
||||||
|
const app = createApp({});
|
||||||
|
|
||||||
|
app.component("hello-world", Stats);
|
||||||
|
|
||||||
|
(window as any).app = app;
|
||||||
|
|
||||||
|
app.mount("#app");
|
||||||
6
pointMaster/js/src/shims-vue.d.ts
vendored
Normal file
6
pointMaster/js/src/shims-vue.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue'
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
||||||
13
pointMaster/js/src/typings.d.ts
vendored
Normal file
13
pointMaster/js/src/typings.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
type Point = {
|
||||||
|
Id: number;
|
||||||
|
Points: number;
|
||||||
|
Turnout: number;
|
||||||
|
postName: string;
|
||||||
|
patruljeName: string;
|
||||||
|
DateCreated: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stat = {
|
||||||
|
Title: string;
|
||||||
|
Value: string;
|
||||||
|
}
|
||||||
42
pointMaster/js/tsconfig.json
Normal file
42
pointMaster/js/tsconfig.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"importHelpers": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"types": [
|
||||||
|
"webpack-env"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue",
|
||||||
|
"tests/**/*.ts",
|
||||||
|
"tests/**/*.tsx",
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
19
pointMaster/js/vue.config.js
Normal file
19
pointMaster/js/vue.config.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
module.exports = {
|
||||||
|
outputDir: "../wwwroot/js/dist", // Output everything into the `dist` folder
|
||||||
|
assetsDir: "", // Prevent nested `assets` folder
|
||||||
|
publicPath: "./", // Use relative paths for assets
|
||||||
|
filenameHashing: false, // Disable hash in filenames
|
||||||
|
runtimeCompiler: true, // Enable runtime compilation
|
||||||
|
configureWebpack: {
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.vue', '.js', '.json'],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: '[name].js', // Keep the main app filename dynamic
|
||||||
|
chunkFilename: '[name].js', // Ensure chunk filenames are unique
|
||||||
|
},
|
||||||
|
},
|
||||||
|
chainWebpack: config => {
|
||||||
|
config.optimization.splitChunks(false); // Disable chunk splitting
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -18,11 +18,13 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.7" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.7" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Migrations\" />
|
<Folder Include="Migrations\" />
|
||||||
|
<Folder Include="js\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user