Add project
This commit is contained in:
30
.dockerignore
Normal file
30
.dockerignore
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
!**/.gitignore
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/config
|
||||||
|
!.git/packed-refs
|
||||||
|
!.git/refs/heads/**
|
||||||
82
.gitea/workflows/deploy.yaml
Normal file
82
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
name: SanStudent Multi-Project Deployment
|
||||||
|
run-name: ${{ github.actor }} wdraża system SanStudent 🚀
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'Api/**'
|
||||||
|
- 'FrontAdmin/**'
|
||||||
|
- 'FrontStudent/**'
|
||||||
|
- 'Common/**'
|
||||||
|
- 'sanstudent.sln'
|
||||||
|
- '.gitea/workflows/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ==========================================
|
||||||
|
# 1. API
|
||||||
|
# ==========================================
|
||||||
|
deploy-api:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout kodu
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Build API Image
|
||||||
|
run: docker build -t sanstudent-api:latest -f Api/Dockerfile .
|
||||||
|
|
||||||
|
- name: Deploy API Container
|
||||||
|
run: |
|
||||||
|
docker rm -f api-container || true
|
||||||
|
docker network create san-network || true
|
||||||
|
docker run -d \
|
||||||
|
--name api-container \
|
||||||
|
--network san-network \
|
||||||
|
-p 8083:8080 \
|
||||||
|
-e ConnectionStrings__DefaultConnection="${{ secrets.DB_CONNECTION_STRING }}" \
|
||||||
|
-e ConnectionStrings__RedisCache="${{ secrets.REDIS_CONNECTION_STRING }}" \
|
||||||
|
--restart always \
|
||||||
|
sanstudent-api:latest
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 2. FRONTADMIN (Blazor WebAssembly)
|
||||||
|
# ==========================================
|
||||||
|
deploy-frontadmin:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout kodu
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Build FrontAdmin Image
|
||||||
|
run: docker build -t sanstudent-frontadmin:latest -f FrontAdmin/Dockerfile .
|
||||||
|
|
||||||
|
- name: Deploy FrontAdmin Container
|
||||||
|
run: |
|
||||||
|
docker rm -f frontadmin-container || true
|
||||||
|
docker run -d \
|
||||||
|
--name frontadmin-container \
|
||||||
|
-p 8081:80 \
|
||||||
|
--restart always \
|
||||||
|
sanstudent-frontadmin:latest
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 3. FRONTSTUDENT (Blazor WebAssembly)
|
||||||
|
# ==========================================
|
||||||
|
deploy-frontstudent:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout kodu
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Build FrontStudent Image
|
||||||
|
run: docker build -t sanstudent-frontstudent:latest -f FrontStudent/Dockerfile .
|
||||||
|
|
||||||
|
- name: Deploy FrontStudent Container
|
||||||
|
run: |
|
||||||
|
docker rm -f frontstudent-container || true
|
||||||
|
docker run -d \
|
||||||
|
--name frontstudent-container \
|
||||||
|
-p 8082:80 \
|
||||||
|
--restart always \
|
||||||
|
sanstudent-frontstudent:latest
|
||||||
30
Api/.dockerignore
Normal file
30
Api/.dockerignore
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
!**/.gitignore
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/config
|
||||||
|
!.git/packed-refs
|
||||||
|
!.git/refs/heads/**
|
||||||
33
Api/Api.csproj
Normal file
33
Api/Api.csproj
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>0ba4ac51-a551-4875-80a9-1d0b702565c2</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
<Version>1.0.0.2</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.3">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.3">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
|
||||||
|
<PackageReference Include="Scalar.AspNetCore" Version="2.12.54" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
37
Api/Controllers/SeasonController.cs
Normal file
37
Api/Controllers/SeasonController.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using Api.Services.Interfaces;
|
||||||
|
using Common.Dtos.Season;
|
||||||
|
using Common.Dtos.Student;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
public class SeasonController(ISeasonService service) : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ISeasonService _service = service;
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("admin/season/create")]
|
||||||
|
public async Task<ActionResult> CreateAsync([FromBody] CreateSeasonRequest request)
|
||||||
|
{
|
||||||
|
var result = await _service.CreateAsync(request);
|
||||||
|
return result.Ok ? Ok(result.CreateSeasonResult) : Conflict(result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("admin/season")]
|
||||||
|
public async Task<ActionResult> GetAllAsync()
|
||||||
|
{
|
||||||
|
var result = await _service.GetAllAsync();
|
||||||
|
return result.Length != 0 ? Ok(result) : NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("admin/season/paged")]
|
||||||
|
public async Task<ActionResult> GetAllPagedAsync([FromQuery] GetSeasonsRequest request)
|
||||||
|
{
|
||||||
|
var result = await _service.GetAllPagedAsync(request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Api/Controllers/SpecializationController.cs
Normal file
36
Api/Controllers/SpecializationController.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Api.Services.Interfaces;
|
||||||
|
using Common.Dtos.Season;
|
||||||
|
using Common.Dtos.Specialization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
public class SpecializationController(ISpecializationService service) : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ISpecializationService _service = service;
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("admin/specialization/create")]
|
||||||
|
public async Task<ActionResult> CreateAsync([FromBody] CreateSpecializationRequest request)
|
||||||
|
{
|
||||||
|
var result = await _service.CreateAsync(request);
|
||||||
|
return result.Ok ? Ok(result.CreateSpecializationResult) : Conflict(result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("admin/specialization")]
|
||||||
|
public async Task<ActionResult> GetAllAsync()
|
||||||
|
{
|
||||||
|
var result = await _service.GetAllAsync();
|
||||||
|
return result.Length != 0 ? Ok(result) : NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("admin/specialization/paged")]
|
||||||
|
public async Task<ActionResult> GetAllPagedAsync([FromQuery] GetSpecializationsRequest request)
|
||||||
|
{
|
||||||
|
var result = await _service.GetAllPagedAsync(request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Api/Controllers/SubjectController.cs
Normal file
36
Api/Controllers/SubjectController.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Api.Services.Interfaces;
|
||||||
|
using Common.Dtos.Subject;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Api.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
public class SubjectController(ISubjectService service) : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ISubjectService _service = service;
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("admin/subject/create")]
|
||||||
|
public async Task<ActionResult> CreateAsync([FromBody] CreateSubjectRequest request)
|
||||||
|
{
|
||||||
|
var result = await _service.CreateAsync(request);
|
||||||
|
return result.Ok ? Ok(result.CreateSubjectResult) : Conflict(result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("admin/subject")]
|
||||||
|
public async Task<ActionResult> GetAllAsync()
|
||||||
|
{
|
||||||
|
var result = await _service.GetAllAsync();
|
||||||
|
return result.Length != 0 ? Ok(result) : NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("admin/subject/paged")]
|
||||||
|
public async Task<ActionResult> GetAllPagedAsync([FromQuery] GetSubjectsRequest request)
|
||||||
|
{
|
||||||
|
var result = await _service.GetAllPagedAsync(request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Api/Controllers/VersionController.cs
Normal file
32
Api/Controllers/VersionController.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Api.Extensions;
|
||||||
|
using Api.Services.Interfaces;
|
||||||
|
using Common.Dtos.Student;
|
||||||
|
using Common.Extensions;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
public class VersionController() : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
[Route("version")]
|
||||||
|
public async Task<ActionResult> GetVersion()
|
||||||
|
{
|
||||||
|
var version = GetVersionNumber();
|
||||||
|
return Ok(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetVersionNumber()
|
||||||
|
{
|
||||||
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
var version = new VersionResponse { Version = assembly.GetName().Version!.ToString() };
|
||||||
|
return version.ToJson();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VersionResponse
|
||||||
|
{
|
||||||
|
public string Version { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
12
Api/Database/Entities/BaseEntity.cs
Normal file
12
Api/Database/Entities/BaseEntity.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Api.Database.Entities;
|
||||||
|
|
||||||
|
public abstract class BaseEntity
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Column("Id")]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
18
Api/Database/Entities/BaseEntityGuid.cs
Normal file
18
Api/Database/Entities/BaseEntityGuid.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Api.Database.Entities;
|
||||||
|
|
||||||
|
public abstract class BaseEntityGuid
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[Column("Id")]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||||
|
public Guid Id { get; set; } = Guid.CreateVersion7();
|
||||||
|
|
||||||
|
[Column("CreatedAt")]
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
[Column("LastModifiedAt")]
|
||||||
|
public DateTime LastModifiedAt { get; set; } = DateTime.Now;
|
||||||
|
}
|
||||||
22
Api/Database/Entities/Season.cs
Normal file
22
Api/Database/Entities/Season.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Api.Database.Entities;
|
||||||
|
|
||||||
|
[Table("Seasons")]
|
||||||
|
public class Season : BaseEntity
|
||||||
|
{
|
||||||
|
[Column("Name")]
|
||||||
|
[NotNull]
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[Column("StartDate")]
|
||||||
|
[NotNull]
|
||||||
|
public DateOnly StartDate { get; set; }
|
||||||
|
|
||||||
|
[Column("EndDate")]
|
||||||
|
[NotNull]
|
||||||
|
public DateOnly EndDate { get; set; }
|
||||||
|
}
|
||||||
19
Api/Database/Entities/Specialization.cs
Normal file
19
Api/Database/Entities/Specialization.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Api.Database.Entities;
|
||||||
|
|
||||||
|
[Table("Specializations")]
|
||||||
|
public class Specialization : BaseEntity
|
||||||
|
{
|
||||||
|
[Column("Name")]
|
||||||
|
[NotNull]
|
||||||
|
[MaxLength(200)]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[Column("ShortName")]
|
||||||
|
[NotNull]
|
||||||
|
[MaxLength(10)]
|
||||||
|
public string? ShortName { get; set; }
|
||||||
|
}
|
||||||
24
Api/Database/Entities/Student.cs
Normal file
24
Api/Database/Entities/Student.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Api.Database.Entities;
|
||||||
|
|
||||||
|
[Table("Students")]
|
||||||
|
public class Student : BaseEntityGuid
|
||||||
|
{
|
||||||
|
[Column("FirstName")]
|
||||||
|
[NotNull]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
|
||||||
|
[Column("LastName")]
|
||||||
|
[NotNull]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
|
||||||
|
[Column("AlbumNumber")]
|
||||||
|
[NotNull]
|
||||||
|
[MaxLength(6)]
|
||||||
|
public string? AlbumNumber { get; set; }
|
||||||
|
}
|
||||||
19
Api/Database/Entities/Subject.cs
Normal file
19
Api/Database/Entities/Subject.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Api.Database.Entities;
|
||||||
|
|
||||||
|
[Table("Subjects")]
|
||||||
|
public class Subject : BaseEntity
|
||||||
|
{
|
||||||
|
[Column("Name")]
|
||||||
|
[NotNull]
|
||||||
|
[MaxLength(200)]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[Column("ShortName")]
|
||||||
|
[NotNull]
|
||||||
|
[MaxLength(10)]
|
||||||
|
public string? ShortName { get; set; }
|
||||||
|
}
|
||||||
70
Api/Database/Migrations/20260301222003_CreateDb.Designer.cs
generated
Normal file
70
Api/Database/Migrations/20260301222003_CreateDb.Designer.cs
generated
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Api.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Api.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SanStudentContext))]
|
||||||
|
[Migration("20260301222003_CreateDb")]
|
||||||
|
partial class CreateDb
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.3")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Student", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
b.Property<string>("AlbumNumber")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(6)
|
||||||
|
.HasColumnType("nvarchar(6)")
|
||||||
|
.HasColumnName("AlbumNumber");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("CreatedAt");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("FirstName");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastModifiedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("LastModifiedAt");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("LastName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AlbumNumber")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Students");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Api/Database/Migrations/20260301222003_CreateDb.cs
Normal file
44
Api/Database/Migrations/20260301222003_CreateDb.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Api.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class CreateDb : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Students",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
FirstName = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||||
|
LastName = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||||
|
AlbumNumber = table.Column<string>(type: "nvarchar(6)", maxLength: 6, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
LastModifiedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Students", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Students_AlbumNumber",
|
||||||
|
table: "Students",
|
||||||
|
column: "AlbumNumber",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Students");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
150
Api/Database/Migrations/20260305195643_SpecsSubjectsAndSeasons.Designer.cs
generated
Normal file
150
Api/Database/Migrations/20260305195643_SpecsSubjectsAndSeasons.Designer.cs
generated
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Api.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Api.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SanStudentContext))]
|
||||||
|
[Migration("20260305195643_SpecsSubjectsAndSeasons")]
|
||||||
|
partial class SpecsSubjectsAndSeasons
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.3")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Season", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateOnly>("EndDate")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("EndDate");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)")
|
||||||
|
.HasColumnName("Name");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("StartDate")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("StartDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Seasons");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Specialization", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("Name");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("nvarchar(10)")
|
||||||
|
.HasColumnName("ShortName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Specializations");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Student", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
b.Property<string>("AlbumNumber")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(6)
|
||||||
|
.HasColumnType("nvarchar(6)")
|
||||||
|
.HasColumnName("AlbumNumber");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("CreatedAt");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("FirstName");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastModifiedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("LastModifiedAt");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("LastName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AlbumNumber")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Students");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Subject", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("Name");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("nvarchar(10)")
|
||||||
|
.HasColumnName("ShortName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Subjects");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Api.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class SpecsSubjectsAndSeasons : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Seasons",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
Name = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
|
||||||
|
StartDate = table.Column<DateOnly>(type: "date", nullable: false),
|
||||||
|
EndDate = table.Column<DateOnly>(type: "date", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Seasons", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Specializations",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
ShortName = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Specializations", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Subjects",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
ShortName = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Subjects", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Seasons");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Specializations");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Subjects");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
147
Api/Database/Migrations/SanStudentContextModelSnapshot.cs
Normal file
147
Api/Database/Migrations/SanStudentContextModelSnapshot.cs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Api.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Api.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SanStudentContext))]
|
||||||
|
partial class SanStudentContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.3")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Season", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateOnly>("EndDate")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("EndDate");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)")
|
||||||
|
.HasColumnName("Name");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("StartDate")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("StartDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Seasons");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Specialization", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("Name");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("nvarchar(10)")
|
||||||
|
.HasColumnName("ShortName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Specializations");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Student", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
b.Property<string>("AlbumNumber")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(6)
|
||||||
|
.HasColumnType("nvarchar(6)")
|
||||||
|
.HasColumnName("AlbumNumber");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("CreatedAt");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("FirstName");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastModifiedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("LastModifiedAt");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("LastName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AlbumNumber")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Students");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Api.Database.Entities.Subject", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("Id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("Name");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("nvarchar(10)")
|
||||||
|
.HasColumnName("ShortName");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Subjects");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Api/Database/SanStudentContext.cs
Normal file
22
Api/Database/SanStudentContext.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Api.Database.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Api.Database;
|
||||||
|
|
||||||
|
public class SanStudentContext : DbContext
|
||||||
|
{
|
||||||
|
public SanStudentContext(DbContextOptions<SanStudentContext> options) : base(options) { }
|
||||||
|
|
||||||
|
public required virtual DbSet<Specialization> Specializations { get; set; }
|
||||||
|
public required virtual DbSet<Subject> Subjects { get; set; }
|
||||||
|
public virtual required DbSet<Season> Seasons { get; set; }
|
||||||
|
public required virtual DbSet<Student> Students { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Student>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasIndex(e => e.AlbumNumber).IsUnique();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Api/Dockerfile
Normal file
21
Api/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY ["Api/Api.csproj", "Api/"]
|
||||||
|
COPY ["Common/Common.csproj", "Common/"]
|
||||||
|
|
||||||
|
RUN dotnet restore "Api/Api.csproj"
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
WORKDIR /src/Api
|
||||||
|
|
||||||
|
|
||||||
|
RUN dotnet publish "Api.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["dotnet", "Api.dll"]
|
||||||
16
Api/Extensions/CacheExtensions.cs
Normal file
16
Api/Extensions/CacheExtensions.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Common.Extensions;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Api.Extensions;
|
||||||
|
|
||||||
|
public static class CacheExtensions
|
||||||
|
{
|
||||||
|
public static async Task SetWithExpirationTimeAsync<T>(this IDistributedCache cache, string key, T value, int minutes = 10)
|
||||||
|
{
|
||||||
|
var options = new DistributedCacheEntryOptions()
|
||||||
|
.SetAbsoluteExpiration(TimeSpan.FromMinutes(minutes));
|
||||||
|
|
||||||
|
await cache.SetStringAsync(key, value.ToJson(), options);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Api/Extensions/PagingExtensions.cs
Normal file
19
Api/Extensions/PagingExtensions.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Api.Extensions;
|
||||||
|
|
||||||
|
public static class PagingExtensions
|
||||||
|
{
|
||||||
|
public static async Task<PagedList<T>> ToPagedListAsync<T>(this IQueryable<T> queryable, int pageNumber, int pageSize)
|
||||||
|
{
|
||||||
|
var query = queryable;
|
||||||
|
int totalItemsCount = await queryable.CountAsync();
|
||||||
|
int totalPagesCount = (totalItemsCount / pageSize) + 1;
|
||||||
|
var number = Math.Min(pageNumber, totalPagesCount);
|
||||||
|
int skip = (number - 1) * pageSize;
|
||||||
|
|
||||||
|
var items = await query.Skip(skip).Take(pageSize).ToArrayAsync();
|
||||||
|
return new PagedList<T>(items, pageNumber, pageSize, totalItemsCount, totalPagesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
Api/Mapping/Mapper.cs
Normal file
52
Api/Mapping/Mapper.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using Api.Database.Entities;
|
||||||
|
using Common.Dtos.Season;
|
||||||
|
using Common.Dtos.Specialization;
|
||||||
|
using Common.Dtos.Student;
|
||||||
|
using Common.Dtos.Subject;
|
||||||
|
|
||||||
|
namespace Api.Mapping;
|
||||||
|
|
||||||
|
public static class Mapper
|
||||||
|
{
|
||||||
|
public static SpecializationDto ToSpecializationDto(this Specialization specialization)
|
||||||
|
{
|
||||||
|
return new SpecializationDto
|
||||||
|
{
|
||||||
|
Id = specialization.Id,
|
||||||
|
Name = specialization.Name,
|
||||||
|
ShortName = specialization.ShortName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SubjectDto ToSubjectDto(this Subject subject)
|
||||||
|
{
|
||||||
|
return new SubjectDto
|
||||||
|
{
|
||||||
|
Id = subject.Id,
|
||||||
|
Name = subject.Name,
|
||||||
|
ShortName = subject.ShortName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SeasonDto ToSeasonDto(this Season season)
|
||||||
|
{
|
||||||
|
return new SeasonDto
|
||||||
|
{
|
||||||
|
Id = season.Id,
|
||||||
|
Name = season.Name,
|
||||||
|
StartDate = season.StartDate,
|
||||||
|
EndDate = season.EndDate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StudentBasicDto ToStudentBasicDto(this Student student)
|
||||||
|
{
|
||||||
|
return new StudentBasicDto
|
||||||
|
{
|
||||||
|
Id = student.Id,
|
||||||
|
FirstName = student.FirstName,
|
||||||
|
LastName = student.LastName,
|
||||||
|
AlbumNumber = student.AlbumNumber
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
61
Api/Program.cs
Normal file
61
Api/Program.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using Api.Database;
|
||||||
|
using Api.Services.Implementation;
|
||||||
|
using Api.Services.Interfaces;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Scalar.AspNetCore;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("AllowBlazorFrontends",
|
||||||
|
policy =>
|
||||||
|
{
|
||||||
|
policy.WithOrigins("https://sanstudent.aherman.eu",
|
||||||
|
"https://sanstudent.eu",
|
||||||
|
"https://www.sanstudent.eu",
|
||||||
|
"https://localhost:7001",
|
||||||
|
"https://localhost:7000"
|
||||||
|
)
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowAnyMethod();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddDbContext<SanStudentContext>(options =>
|
||||||
|
{
|
||||||
|
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddStackExchangeRedisCache(options =>
|
||||||
|
{
|
||||||
|
options.Configuration = builder.Configuration.GetConnectionString("RedisCache");
|
||||||
|
options.InstanceName = "SanStudentApi_";
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddScoped<ISpecializationService, SpecializationService>();
|
||||||
|
builder.Services.AddScoped<ISubjectService, SubjectService>();
|
||||||
|
builder.Services.AddScoped<ISeasonService, SeasonService>();
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
builder.Services.AddOpenApi();
|
||||||
|
var app = builder.Build();
|
||||||
|
app.UseCors("AllowBlazorFrontends");
|
||||||
|
|
||||||
|
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.MapOpenApi();
|
||||||
|
app.MapScalarApiReference(options =>
|
||||||
|
{
|
||||||
|
options.Title = "SanStudent API";
|
||||||
|
options.Theme = ScalarTheme.BluePlanet;
|
||||||
|
options.DefaultHttpClient = new(ScalarTarget.CSharp, ScalarClient.HttpClient);
|
||||||
|
options.CustomCss = "";
|
||||||
|
options.ShowSidebar = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.MapControllers();
|
||||||
|
app.Run();
|
||||||
33
Api/Properties/launchSettings.json
Normal file
33
Api/Properties/launchSettings.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "http://localhost:5001"
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "scalar/v1",
|
||||||
|
"applicationUrl": "https://localhost:5000;http://localhost:5001"
|
||||||
|
},
|
||||||
|
"Container (Dockerfile)": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_HTTPS_PORTS": "8081",
|
||||||
|
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||||
|
},
|
||||||
|
"publishAllPorts": true,
|
||||||
|
"useSSL": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json"
|
||||||
|
}
|
||||||
156
Api/Services/Implementation/SeasonService.cs
Normal file
156
Api/Services/Implementation/SeasonService.cs
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
using Api.Database;
|
||||||
|
using Api.Database.Entities;
|
||||||
|
using Api.Extensions;
|
||||||
|
using Api.Mapping;
|
||||||
|
using Api.Services.Interfaces;
|
||||||
|
using Common.Dtos.Common;
|
||||||
|
using Common.Dtos.Season;
|
||||||
|
using Common.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
|
||||||
|
namespace Api.Services.Implementation;
|
||||||
|
|
||||||
|
public class SeasonService(SanStudentContext context, IDistributedCache cache) : ISeasonService, IServiceHelper<CreateSeasonRequest>
|
||||||
|
{
|
||||||
|
private readonly SanStudentContext _context = context;
|
||||||
|
private readonly IDistributedCache _cache = cache;
|
||||||
|
private List<string> _cacheKeys = [];
|
||||||
|
|
||||||
|
public async Task<CreateSeasonResponse> CreateAsync(CreateSeasonRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var problems = await Validate(request);
|
||||||
|
if (problems.Errors.Count > 0)
|
||||||
|
{
|
||||||
|
return new CreateSeasonResponse
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
CreateSeasonResult = string.Join(", ", problems.Errors)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var season = new Season()
|
||||||
|
{
|
||||||
|
Name = request.Name!.Trim(),
|
||||||
|
StartDate = request.StartDate,
|
||||||
|
EndDate = request.EndDate
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Seasons.Add(season);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
await RemoveCache();
|
||||||
|
return new CreateSeasonResponse
|
||||||
|
{
|
||||||
|
Ok = true,
|
||||||
|
CreateSeasonResult = $"Zapisano nowy semestr \"{season.Name}\"",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
return new CreateSeasonResponse
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
CreateSeasonResult = $"Błąd podczas próby zapisu semestru: {error.Message}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SeasonDto[]> GetAllAsync()
|
||||||
|
{
|
||||||
|
var key = "seasons";
|
||||||
|
AddKeyToCache(key);
|
||||||
|
var cachedSeasons = await _cache.GetStringAsync(key);
|
||||||
|
if (!string.IsNullOrEmpty(cachedSeasons))
|
||||||
|
return cachedSeasons.FromJson<SeasonDto[]>();
|
||||||
|
|
||||||
|
var query = _context.Seasons as IQueryable<Season>;
|
||||||
|
var result = await query
|
||||||
|
.Where(s => s.EndDate > DateOnly.FromDateTime(DateTime.Now))
|
||||||
|
.OrderBy(s => s.StartDate)
|
||||||
|
.ThenBy(s => s.Name)
|
||||||
|
.Select(s => s.ToSeasonDto()).ToArrayAsync();
|
||||||
|
|
||||||
|
await _cache.SetWithExpirationTimeAsync(key, result, 131487);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PagedList<SeasonDto>> GetAllPagedAsync(GetSeasonsRequest request)
|
||||||
|
{
|
||||||
|
var key = $"seasons_{request.PageNumber}_{request.PageSize}";
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.Name))
|
||||||
|
key += $"_{request.Name.ToLower()}";
|
||||||
|
|
||||||
|
if (request.HideObsolete.HasValue && request.HideObsolete.Value)
|
||||||
|
key += $"_{true}";
|
||||||
|
|
||||||
|
AddKeyToCache(key);
|
||||||
|
var cachedSeasons = await _cache.GetStringAsync(key);
|
||||||
|
if (!string.IsNullOrEmpty(cachedSeasons))
|
||||||
|
return cachedSeasons.FromJson<PagedList<SeasonDto>>();
|
||||||
|
|
||||||
|
var query = _context.Seasons as IQueryable<Season>;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.Name))
|
||||||
|
query = query.Where(s => s.Name.ToLower().StartsWith(request.Name.ToLower()));
|
||||||
|
|
||||||
|
if (request.HideObsolete.HasValue && request.HideObsolete.Value)
|
||||||
|
query = query.Where(s => s.EndDate > DateOnly.FromDateTime(DateTime.Now));
|
||||||
|
|
||||||
|
var data = query
|
||||||
|
.OrderBy(s => s.StartDate)
|
||||||
|
.ThenBy(s => s.Name)
|
||||||
|
.Select(s => s.ToSeasonDto());
|
||||||
|
|
||||||
|
var result = await data.ToPagedListAsync(request.PageNumber, request.PageSize);
|
||||||
|
await _cache.SetWithExpirationTimeAsync(key, result, 131487);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ValidationProblems> Validate(CreateSeasonRequest request)
|
||||||
|
{
|
||||||
|
var problems = new ValidationProblems();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Name))
|
||||||
|
problems.Errors.Add("Brak nazwy semestru");
|
||||||
|
|
||||||
|
if (problems.Errors.Count > 0)
|
||||||
|
return problems;
|
||||||
|
|
||||||
|
|
||||||
|
var season = await _context.Seasons
|
||||||
|
.FirstOrDefaultAsync(s => s.Name.ToLower() == request.Name!.ToLower());
|
||||||
|
|
||||||
|
if (season is not null)
|
||||||
|
problems.Errors.Add("Semestr o podanej nazwie już istnieje");
|
||||||
|
|
||||||
|
season = await _context.Seasons
|
||||||
|
.FirstOrDefaultAsync(s => s.StartDate == request.StartDate);
|
||||||
|
|
||||||
|
if (season is not null)
|
||||||
|
problems.Errors.Add("Semestr o podanej dacie rozpoczęcia już istnieje");
|
||||||
|
|
||||||
|
season = await _context.Seasons
|
||||||
|
.FirstOrDefaultAsync(s => s.EndDate == request.EndDate);
|
||||||
|
|
||||||
|
if (season is not null)
|
||||||
|
problems.Errors.Add("Semestr o podanej dacie zakończenia już istnieje");
|
||||||
|
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveCache()
|
||||||
|
{
|
||||||
|
foreach (var cacheKey in _cacheKeys)
|
||||||
|
await _cache.RemoveAsync(cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddKeyToCache(string key)
|
||||||
|
{
|
||||||
|
if (!_cacheKeys.Contains(key))
|
||||||
|
_cacheKeys.Add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
146
Api/Services/Implementation/SpecializationService.cs
Normal file
146
Api/Services/Implementation/SpecializationService.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using Api.Database;
|
||||||
|
using Api.Database.Entities;
|
||||||
|
using Api.Extensions;
|
||||||
|
using Api.Mapping;
|
||||||
|
using Api.Services.Interfaces;
|
||||||
|
using Common.Dtos.Common;
|
||||||
|
using Common.Dtos.Season;
|
||||||
|
using Common.Dtos.Specialization;
|
||||||
|
using Common.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
|
||||||
|
namespace Api.Services.Implementation;
|
||||||
|
|
||||||
|
public class SpecializationService(SanStudentContext context, IDistributedCache cache)
|
||||||
|
: ISpecializationService, IServiceHelper<CreateSpecializationRequest>
|
||||||
|
{
|
||||||
|
private readonly SanStudentContext _context = context;
|
||||||
|
private readonly IDistributedCache _cache = cache;
|
||||||
|
private List<string> _cacheKeys = [];
|
||||||
|
|
||||||
|
public async Task<CreateSpecializationResponse> CreateAsync(CreateSpecializationRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var problems = await Validate(request);
|
||||||
|
if (problems.Errors.Count > 0)
|
||||||
|
{
|
||||||
|
return new CreateSpecializationResponse
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
CreateSpecializationResult = string.Join(", ", problems.Errors)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var specialization = new Specialization()
|
||||||
|
{
|
||||||
|
Name = request.Name!.Trim(),
|
||||||
|
ShortName = request.ShortName!.Trim()
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Specializations.Add(specialization);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
await RemoveCache();
|
||||||
|
return new CreateSpecializationResponse
|
||||||
|
{
|
||||||
|
Ok = true,
|
||||||
|
CreateSpecializationResult = $"Zapisano nową specializację \"{specialization.Name}\"",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
return new CreateSpecializationResponse
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
CreateSpecializationResult = $"Błąd podczas próby zapisu specializacji: {error.Message}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SpecializationDto[]> GetAllAsync()
|
||||||
|
{
|
||||||
|
var key = "specializations";
|
||||||
|
AddKeyToCache(key);
|
||||||
|
var cachedSpecs = await _cache.GetStringAsync(key);
|
||||||
|
if (!string.IsNullOrEmpty(cachedSpecs))
|
||||||
|
return cachedSpecs.FromJson<SpecializationDto[]>();
|
||||||
|
|
||||||
|
var query = _context.Specializations as IQueryable<Specialization>;
|
||||||
|
var result = await query
|
||||||
|
.OrderBy(s => s.Name)
|
||||||
|
.ThenBy(s => s.ShortName)
|
||||||
|
.Select(s => s.ToSpecializationDto()).ToArrayAsync();
|
||||||
|
|
||||||
|
await _cache.SetWithExpirationTimeAsync(key, result, 262487);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PagedList<SpecializationDto>> GetAllPagedAsync(GetSpecializationsRequest request)
|
||||||
|
{
|
||||||
|
var key = $"specializations_{request.PageNumber}_{request.PageSize}";
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.Name))
|
||||||
|
key += $"_{request.Name.ToLower()}";
|
||||||
|
|
||||||
|
AddKeyToCache(key);
|
||||||
|
var cachedSpecs = await _cache.GetStringAsync(key);
|
||||||
|
if (!string.IsNullOrEmpty(cachedSpecs))
|
||||||
|
return cachedSpecs.FromJson<PagedList<SpecializationDto>>();
|
||||||
|
|
||||||
|
var query = _context.Specializations as IQueryable<Specialization>;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.Name))
|
||||||
|
query = query.Where(s => s.Name.ToLower().StartsWith(request.Name.ToLower()));
|
||||||
|
|
||||||
|
var data = query
|
||||||
|
.OrderBy(s => s.Name)
|
||||||
|
.ThenBy(s => s.ShortName)
|
||||||
|
.Select(s => s.ToSpecializationDto());
|
||||||
|
|
||||||
|
var result = await data.ToPagedListAsync(request.PageNumber, request.PageSize);
|
||||||
|
await _cache.SetWithExpirationTimeAsync(key, result, 262487);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<ValidationProblems> Validate(CreateSpecializationRequest request)
|
||||||
|
{
|
||||||
|
var problems = new ValidationProblems();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Name))
|
||||||
|
problems.Errors.Add("Brak nazwy specializacji");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.ShortName))
|
||||||
|
problems.Errors.Add("Brak nazwy skróconej specializacji");
|
||||||
|
|
||||||
|
if (problems.Errors.Count > 0)
|
||||||
|
return problems;
|
||||||
|
|
||||||
|
|
||||||
|
var specialization = await _context.Specializations
|
||||||
|
.FirstOrDefaultAsync(s => s.Name.ToLower() == request.Name!.ToLower());
|
||||||
|
|
||||||
|
if (specialization is not null)
|
||||||
|
problems.Errors.Add("Specializacja o podanej nazwie już istnieje");
|
||||||
|
|
||||||
|
specialization = await _context.Specializations
|
||||||
|
.FirstOrDefaultAsync(s => s.ShortName.ToLower() == request.ShortName!.ToLower());
|
||||||
|
|
||||||
|
if (specialization is not null)
|
||||||
|
problems.Errors.Add("Specializacja o podanej nazwie skróconej już istnieje");
|
||||||
|
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveCache()
|
||||||
|
{
|
||||||
|
foreach (var cacheKey in _cacheKeys)
|
||||||
|
await _cache.RemoveAsync(cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddKeyToCache(string key)
|
||||||
|
{
|
||||||
|
if (!_cacheKeys.Contains(key))
|
||||||
|
_cacheKeys.Add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
144
Api/Services/Implementation/SubjectService.cs
Normal file
144
Api/Services/Implementation/SubjectService.cs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
using Api.Database;
|
||||||
|
using Api.Database.Entities;
|
||||||
|
using Api.Extensions;
|
||||||
|
using Api.Mapping;
|
||||||
|
using Api.Services.Interfaces;
|
||||||
|
using Common.Dtos.Common;
|
||||||
|
using Common.Dtos.Subject;
|
||||||
|
using Common.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
|
||||||
|
namespace Api.Services.Implementation;
|
||||||
|
|
||||||
|
public class SubjectService(SanStudentContext context, IDistributedCache cache)
|
||||||
|
: ISubjectService, IServiceHelper<CreateSubjectRequest>
|
||||||
|
{
|
||||||
|
private readonly SanStudentContext _context = context;
|
||||||
|
private readonly IDistributedCache _cache = cache;
|
||||||
|
private List<string> _cacheKeys = [];
|
||||||
|
|
||||||
|
public async Task<CreateSubjectResponse> CreateAsync(CreateSubjectRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var problems = await Validate(request);
|
||||||
|
if (problems.Errors.Count > 0)
|
||||||
|
{
|
||||||
|
return new CreateSubjectResponse
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
CreateSubjectResult = string.Join(", ", problems.Errors)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var subject = new Subject()
|
||||||
|
{
|
||||||
|
Name = request.Name!.Trim(),
|
||||||
|
ShortName = request.ShortName!.Trim()
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Subjects.Add(subject);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
await RemoveCache();
|
||||||
|
return new CreateSubjectResponse
|
||||||
|
{
|
||||||
|
Ok = true,
|
||||||
|
CreateSubjectResult = $"Zapisano nowy przedmiot \"{subject.Name}\"",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
return new CreateSubjectResponse
|
||||||
|
{
|
||||||
|
Ok = false,
|
||||||
|
CreateSubjectResult = $"Błąd podczas próby zapisu przedmiotu: {error.Message}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SubjectDto[]> GetAllAsync()
|
||||||
|
{
|
||||||
|
var key = "subjects";
|
||||||
|
AddKeyToCache(key);
|
||||||
|
var cachedSubjects = await _cache.GetStringAsync(key);
|
||||||
|
if (!string.IsNullOrEmpty(cachedSubjects))
|
||||||
|
return cachedSubjects.FromJson<SubjectDto[]>();
|
||||||
|
|
||||||
|
var query = _context.Subjects as IQueryable<Subject>;
|
||||||
|
var result = await query
|
||||||
|
.OrderBy(s => s.Name)
|
||||||
|
.ThenBy(s => s.ShortName)
|
||||||
|
.Select(s => s.ToSubjectDto()).ToArrayAsync();
|
||||||
|
|
||||||
|
await _cache.SetWithExpirationTimeAsync(key, result, 262487);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PagedList<SubjectDto>> GetAllPagedAsync(GetSubjectsRequest request)
|
||||||
|
{
|
||||||
|
var key = $"subjects_{request.PageNumber}_{request.PageSize}";
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.Name))
|
||||||
|
key += $"_{request.Name.ToLower()}";
|
||||||
|
|
||||||
|
AddKeyToCache(key);
|
||||||
|
var cachedSubjects = await _cache.GetStringAsync(key);
|
||||||
|
if (!string.IsNullOrEmpty(cachedSubjects))
|
||||||
|
return cachedSubjects.FromJson<PagedList<SubjectDto>>();
|
||||||
|
|
||||||
|
var query = _context.Subjects as IQueryable<Subject>;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.Name))
|
||||||
|
query = query.Where(s => s.Name.ToLower().StartsWith(request.Name.ToLower()));
|
||||||
|
|
||||||
|
var data = query
|
||||||
|
.OrderBy(s => s.Name)
|
||||||
|
.ThenBy(s => s.ShortName)
|
||||||
|
.Select(s => s.ToSubjectDto());
|
||||||
|
|
||||||
|
var result = await data.ToPagedListAsync(request.PageNumber, request.PageSize);
|
||||||
|
await _cache.SetWithExpirationTimeAsync(key, result, 262487);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<ValidationProblems> Validate(CreateSubjectRequest request)
|
||||||
|
{
|
||||||
|
var problems = new ValidationProblems();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Name))
|
||||||
|
problems.Errors.Add("Brak nazwy przedmiotu");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.ShortName))
|
||||||
|
problems.Errors.Add("Brak nazwy skróconej przedmiotu");
|
||||||
|
|
||||||
|
if (problems.Errors.Count > 0)
|
||||||
|
return problems;
|
||||||
|
|
||||||
|
var subject = await _context.Subjects
|
||||||
|
.FirstOrDefaultAsync(s => s.Name.ToLower() == request.Name!.ToLower());
|
||||||
|
|
||||||
|
if (subject is not null)
|
||||||
|
problems.Errors.Add("Przedmiot o podanej nazwie już istnieje");
|
||||||
|
|
||||||
|
subject = await _context.Subjects
|
||||||
|
.FirstOrDefaultAsync(s => s.ShortName.ToLower() == request.ShortName!.ToLower());
|
||||||
|
|
||||||
|
if (subject is not null)
|
||||||
|
problems.Errors.Add("Przedmiot o podanej nazwie skróconej już istnieje");
|
||||||
|
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddKeyToCache(string key)
|
||||||
|
{
|
||||||
|
if (!_cacheKeys.Contains(key))
|
||||||
|
_cacheKeys.Add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveCache()
|
||||||
|
{
|
||||||
|
foreach (var cacheKey in _cacheKeys)
|
||||||
|
await _cache.RemoveAsync(cacheKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Api/Services/Interfaces/ISeasonService.cs
Normal file
11
Api/Services/Interfaces/ISeasonService.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
using Common.Dtos.Season;
|
||||||
|
|
||||||
|
namespace Api.Services.Interfaces;
|
||||||
|
|
||||||
|
public interface ISeasonService
|
||||||
|
{
|
||||||
|
Task<CreateSeasonResponse> CreateAsync(CreateSeasonRequest request);
|
||||||
|
Task<PagedList<SeasonDto>> GetAllPagedAsync(GetSeasonsRequest request);
|
||||||
|
Task<SeasonDto[]> GetAllAsync();
|
||||||
|
}
|
||||||
11
Api/Services/Interfaces/IServiceHelper.cs
Normal file
11
Api/Services/Interfaces/IServiceHelper.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
|
||||||
|
namespace Api.Services.Interfaces
|
||||||
|
{
|
||||||
|
public interface IServiceHelper<T>
|
||||||
|
{
|
||||||
|
Task<ValidationProblems> Validate(T request);
|
||||||
|
Task RemoveCache();
|
||||||
|
void AddKeyToCache(string key);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Api/Services/Interfaces/ISpecializationService.cs
Normal file
12
Api/Services/Interfaces/ISpecializationService.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
using Common.Dtos.Season;
|
||||||
|
using Common.Dtos.Specialization;
|
||||||
|
|
||||||
|
namespace Api.Services.Interfaces;
|
||||||
|
|
||||||
|
public interface ISpecializationService
|
||||||
|
{
|
||||||
|
Task<CreateSpecializationResponse> CreateAsync(CreateSpecializationRequest request);
|
||||||
|
Task<PagedList<SpecializationDto>> GetAllPagedAsync(GetSpecializationsRequest request);
|
||||||
|
Task<SpecializationDto[]> GetAllAsync();
|
||||||
|
}
|
||||||
12
Api/Services/Interfaces/ISubjectService.cs
Normal file
12
Api/Services/Interfaces/ISubjectService.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
using Common.Dtos.Specialization;
|
||||||
|
using Common.Dtos.Subject;
|
||||||
|
|
||||||
|
namespace Api.Services.Interfaces;
|
||||||
|
|
||||||
|
public interface ISubjectService
|
||||||
|
{
|
||||||
|
Task<CreateSubjectResponse> CreateAsync(CreateSubjectRequest request);
|
||||||
|
Task<PagedList<SubjectDto>> GetAllPagedAsync(GetSubjectsRequest request);
|
||||||
|
Task<SubjectDto[]> GetAllAsync();
|
||||||
|
}
|
||||||
12
Api/appsettings.Development.json
Normal file
12
Api/appsettings.Development.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Server=89.167.52.197;Database=SanStudent;User ID=sanstudentuser;Password=@sanStudent2026@;TrustServerCertificate=True;",
|
||||||
|
"RedisCache": "localhost:6379,password=#PanTadeusz1973#,abortConnect=false"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Api/appsettings.json
Normal file
9
Api/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
18
Common/Common.csproj
Normal file
18
Common/Common.csproj
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Requests\**" />
|
||||||
|
<Compile Remove="Responses\**" />
|
||||||
|
<EmbeddedResource Remove="Requests\**" />
|
||||||
|
<EmbeddedResource Remove="Responses\**" />
|
||||||
|
<None Remove="Requests\**" />
|
||||||
|
<None Remove="Responses\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
9
Common/Dtos/Common/BaseResponse.cs
Normal file
9
Common/Dtos/Common/BaseResponse.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Common;
|
||||||
|
|
||||||
|
public abstract class BaseResponse
|
||||||
|
{
|
||||||
|
public bool Ok { get; set; }
|
||||||
|
public string? Error { get; set; }
|
||||||
|
}
|
||||||
22
Common/Dtos/Common/PagedList.cs
Normal file
22
Common/Dtos/Common/PagedList.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
namespace Common.Dtos.Common;
|
||||||
|
|
||||||
|
public class PagedList<T>
|
||||||
|
{
|
||||||
|
public PagedList(T[] items, int pageNumber, int pageSize, int totalItemsCount, int numberOfPages)
|
||||||
|
{
|
||||||
|
Items = items;
|
||||||
|
PageNumber = pageNumber;
|
||||||
|
PageSize = pageSize;
|
||||||
|
TotalItemsCount = totalItemsCount;
|
||||||
|
NumberOfPages = numberOfPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PagedList<T> Empty(int pagedNumber, int pageSize)
|
||||||
|
=> new([], pagedNumber, pageSize, 0, 0);
|
||||||
|
|
||||||
|
public T[] Items { get; }
|
||||||
|
public int PageNumber { get; }
|
||||||
|
public int PageSize { get; }
|
||||||
|
public int TotalItemsCount { get; }
|
||||||
|
public int NumberOfPages { get; }
|
||||||
|
}
|
||||||
7
Common/Dtos/Common/PagedRequest.cs
Normal file
7
Common/Dtos/Common/PagedRequest.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Common.Dtos.Common;
|
||||||
|
|
||||||
|
public abstract class PagedRequest
|
||||||
|
{
|
||||||
|
public int PageNumber { get; set; }
|
||||||
|
public int PageSize { get; set; }
|
||||||
|
}
|
||||||
11
Common/Dtos/Common/ValidationProblems.cs
Normal file
11
Common/Dtos/Common/ValidationProblems.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Common.Dtos.Common;
|
||||||
|
|
||||||
|
public class ValidationProblems
|
||||||
|
{
|
||||||
|
public ValidationProblems()
|
||||||
|
{
|
||||||
|
Errors = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> Errors { get; set; }
|
||||||
|
}
|
||||||
8
Common/Dtos/Season/CreateSeasonRequest.cs
Normal file
8
Common/Dtos/Season/CreateSeasonRequest.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Common.Dtos.Season;
|
||||||
|
|
||||||
|
public class CreateSeasonRequest
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public DateOnly StartDate { get; set; }
|
||||||
|
public DateOnly EndDate { get; set; }
|
||||||
|
}
|
||||||
8
Common/Dtos/Season/CreateSeasonResponse.cs
Normal file
8
Common/Dtos/Season/CreateSeasonResponse.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Season;
|
||||||
|
|
||||||
|
public class CreateSeasonResponse : BaseResponse
|
||||||
|
{
|
||||||
|
public string? CreateSeasonResult { get; set; }
|
||||||
|
}
|
||||||
9
Common/Dtos/Season/GetSeasonsRequest.cs
Normal file
9
Common/Dtos/Season/GetSeasonsRequest.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Season;
|
||||||
|
|
||||||
|
public class GetSeasonsRequest : PagedRequest
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public bool? HideObsolete { get; set; }
|
||||||
|
}
|
||||||
10
Common/Dtos/Season/SeasonDto.cs
Normal file
10
Common/Dtos/Season/SeasonDto.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Common.Dtos.Season;
|
||||||
|
|
||||||
|
public class SeasonDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public DateOnly StartDate { get; set; }
|
||||||
|
public DateOnly EndDate { get; set; }
|
||||||
|
public bool IsCurrent => StartDate <= DateOnly.FromDateTime(DateTime.Now) && EndDate >= DateOnly.FromDateTime(DateTime.Now);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Common.Dtos.Specialization;
|
||||||
|
|
||||||
|
public class CreateSpecializationRequest
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? ShortName { get; set; }
|
||||||
|
}
|
||||||
12
Common/Dtos/Specialization/CreateSpecializationResponse.cs
Normal file
12
Common/Dtos/Specialization/CreateSpecializationResponse.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Specialization
|
||||||
|
{
|
||||||
|
public class CreateSpecializationResponse : BaseResponse
|
||||||
|
{
|
||||||
|
public string? CreateSpecializationResult { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Common/Dtos/Specialization/GetSpecializationsRequest.cs
Normal file
8
Common/Dtos/Specialization/GetSpecializationsRequest.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Specialization;
|
||||||
|
|
||||||
|
public class GetSpecializationsRequest : PagedRequest
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
||||||
15
Common/Dtos/Specialization/SpecializationDto.cs
Normal file
15
Common/Dtos/Specialization/SpecializationDto.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Specialization;
|
||||||
|
|
||||||
|
public class SpecializationDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? ShortName { get; set; }
|
||||||
|
}
|
||||||
8
Common/Dtos/Student/CreateStudentRequest.cs
Normal file
8
Common/Dtos/Student/CreateStudentRequest.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Common.Dtos.Student;
|
||||||
|
|
||||||
|
public class CreateStudentRequest
|
||||||
|
{
|
||||||
|
public required string FirstName { get; set; }
|
||||||
|
public required string LastName { get; set; }
|
||||||
|
public required string AlbumNumber { get; set; }
|
||||||
|
}
|
||||||
8
Common/Dtos/Student/CreateStudentResponse.cs
Normal file
8
Common/Dtos/Student/CreateStudentResponse.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Student;
|
||||||
|
|
||||||
|
public class CreateStudentResponse : BaseResponse
|
||||||
|
{
|
||||||
|
public string? CreateStudentResult { get; set; }
|
||||||
|
}
|
||||||
10
Common/Dtos/Student/StudentBasicDto.cs
Normal file
10
Common/Dtos/Student/StudentBasicDto.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Common.Dtos.Student;
|
||||||
|
|
||||||
|
public class StudentBasicDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
public string? AlbumNumber { get; set; }
|
||||||
|
public string FullNameReverese => $"{LastName} {FirstName} ({AlbumNumber})";
|
||||||
|
}
|
||||||
7
Common/Dtos/Subject/CreateSubjectRequest.cs
Normal file
7
Common/Dtos/Subject/CreateSubjectRequest.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Common.Dtos.Subject;
|
||||||
|
|
||||||
|
public class CreateSubjectRequest
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? ShortName { get; set; }
|
||||||
|
}
|
||||||
12
Common/Dtos/Subject/CreateSubjectResponse.cs
Normal file
12
Common/Dtos/Subject/CreateSubjectResponse.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Subject
|
||||||
|
{
|
||||||
|
public class CreateSubjectResponse : BaseResponse
|
||||||
|
{
|
||||||
|
public string? CreateSubjectResult { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Common/Dtos/Subject/GetSubjectsRequest.cs
Normal file
8
Common/Dtos/Subject/GetSubjectsRequest.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Common.Dtos.Common;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Subject;
|
||||||
|
|
||||||
|
public class GetSubjectsRequest : PagedRequest
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
||||||
12
Common/Dtos/Subject/SubjectDto.cs
Normal file
12
Common/Dtos/Subject/SubjectDto.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common.Dtos.Subject;
|
||||||
|
|
||||||
|
public class SubjectDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? ShortName { get; set; }
|
||||||
|
}
|
||||||
11
Common/Extensions/JsonExtensions.cs
Normal file
11
Common/Extensions/JsonExtensions.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Common.Extensions
|
||||||
|
{
|
||||||
|
public static class JsonExtensions
|
||||||
|
{
|
||||||
|
private static JsonSerializerOptions _jso = new() { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
||||||
|
public static string ToJson<T>(this T source) => JsonSerializer.Serialize(source, _jso);
|
||||||
|
public static T FromJson<T>(this string source) => JsonSerializer.Deserialize<T>(source, _jso) ?? default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Common/Extensions/StringExtensions.cs
Normal file
20
Common/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Common.Pagination;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common.Extensions;
|
||||||
|
|
||||||
|
public static class StringExtensions
|
||||||
|
{
|
||||||
|
public static IDictionary<string, object?> AsDictionary(this object source,
|
||||||
|
BindingFlags bindingAttr = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
{
|
||||||
|
return source.GetType().GetProperties(bindingAttr).ToDictionary
|
||||||
|
(
|
||||||
|
propInfo => propInfo.Name,
|
||||||
|
propInfo => propInfo.GetValue(source, null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Common/Pagination/PaginationExtensions.cs
Normal file
15
Common/Pagination/PaginationExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common.Pagination;
|
||||||
|
|
||||||
|
public static class PaginationExtensions
|
||||||
|
{
|
||||||
|
public static string ToPaginationSummary(this PaginationProperties props)
|
||||||
|
{
|
||||||
|
var before = (props.PageSize * (props.PageNumber - 1)) + 1;
|
||||||
|
var after = Math.Min((props.PageSize * props.PageNumber), props.TotalItems);
|
||||||
|
return props.TotalItems == 0 ? $"{props.Text}: brak" : $"{props.Text}: {before}-{after} z {props.TotalItems}";
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Common/Pagination/PaginationProperties.cs
Normal file
21
Common/Pagination/PaginationProperties.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common.Pagination;
|
||||||
|
|
||||||
|
public class PaginationProperties
|
||||||
|
{
|
||||||
|
public PaginationProperties(string text, int pageSize)
|
||||||
|
{
|
||||||
|
Text = text;
|
||||||
|
PageNumber = 1;
|
||||||
|
PageSize = pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PageNumber { get; set; }
|
||||||
|
public int PageSize { get; set; }
|
||||||
|
public int TotalItems { get; set; }
|
||||||
|
public int NumberOfPages { get; set; }
|
||||||
|
public string? Text { get; set; }
|
||||||
|
}
|
||||||
30
FrontAdmin/.dockerignore
Normal file
30
FrontAdmin/.dockerignore
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
!**/.gitignore
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/config
|
||||||
|
!.git/packed-refs
|
||||||
|
!.git/refs/heads/**
|
||||||
6
FrontAdmin/App.razor
Normal file
6
FrontAdmin/App.razor
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<Router AppAssembly="@typeof(App).Assembly" NotFoundPage="typeof(Pages.NotFound)">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
||||||
|
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
</Router>
|
||||||
26
FrontAdmin/Dockerfile
Normal file
26
FrontAdmin/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY ["FrontAdmin/FrontAdmin.csproj", "FrontAdmin/"]
|
||||||
|
COPY ["Common/Common.csproj", "Common/"]
|
||||||
|
RUN dotnet restore -s https://api.nuget.org/v3/index.json "FrontAdmin/FrontAdmin.csproj"
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/FrontAdmin"
|
||||||
|
|
||||||
|
RUN find . -type d -name "bin" -o -name "obj" | xargs rm -rf
|
||||||
|
|
||||||
|
RUN dotnet publish "FrontAdmin.csproj" -c Release -o /app/publish
|
||||||
|
|
||||||
|
FROM nginx:alpine AS final
|
||||||
|
WORKDIR /usr/share/nginx/html
|
||||||
|
|
||||||
|
RUN rm -rf ./*
|
||||||
|
|
||||||
|
COPY --from=build /app/publish/wwwroot .
|
||||||
|
|
||||||
|
COPY FrontAdmin/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
19
FrontAdmin/FrontAdmin.csproj
Normal file
19
FrontAdmin/FrontAdmin.csproj
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.3" PrivateAssets="all" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
16
FrontAdmin/Layout/MainLayout.razor
Normal file
16
FrontAdmin/Layout/MainLayout.razor
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
@inherits LayoutComponentBase
|
||||||
|
<div class="page">
|
||||||
|
<div class="sidebar">
|
||||||
|
<NavMenu />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="top-row px-4">
|
||||||
|
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<article class="content px-4">
|
||||||
|
@Body
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
77
FrontAdmin/Layout/MainLayout.razor.css
Normal file
77
FrontAdmin/Layout/MainLayout.razor.css
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
.page {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border-bottom: 1px solid #d6d5d5;
|
||||||
|
justify-content: flex-end;
|
||||||
|
height: 3.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row ::deep a:first-child {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640.98px) {
|
||||||
|
.top-row {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 641px) {
|
||||||
|
.page {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
height: 100vh;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row.auth ::deep a:first-child {
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row, article {
|
||||||
|
padding-left: 2rem !important;
|
||||||
|
padding-right: 1.5rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
FrontAdmin/Layout/NavMenu.razor
Normal file
39
FrontAdmin/Layout/NavMenu.razor
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<div class="top-row ps-3 navbar navbar-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="">FrontAdmin</a>
|
||||||
|
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="@NavMenuCssClass nav-scrollable" @onclick="ToggleNavMenu">
|
||||||
|
<nav class="nav flex-column">
|
||||||
|
<div class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||||
|
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
<div class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="counter">
|
||||||
|
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
<div class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="weather">
|
||||||
|
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool collapseNavMenu = true;
|
||||||
|
|
||||||
|
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
|
||||||
|
|
||||||
|
private void ToggleNavMenu()
|
||||||
|
{
|
||||||
|
collapseNavMenu = !collapseNavMenu;
|
||||||
|
}
|
||||||
|
}
|
||||||
83
FrontAdmin/Layout/NavMenu.razor.css
Normal file
83
FrontAdmin/Layout/NavMenu.razor.css
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
.navbar-toggler {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
min-height: 3.5rem;
|
||||||
|
background-color: rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
top: -1px;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-house-door-fill-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-plus-square-fill-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-list-nested-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:first-of-type {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:last-of-type {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep a {
|
||||||
|
color: #d7d7d7;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 3rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep a.active {
|
||||||
|
background-color: rgba(255,255,255,0.37);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep a:hover {
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 641px) {
|
||||||
|
.navbar-toggler {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse {
|
||||||
|
/* Never collapse the sidebar for wide screens */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-scrollable {
|
||||||
|
/* Allow sidebar to scroll for tall menus */
|
||||||
|
height: calc(100vh - 3.5rem);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
FrontAdmin/Pages/Counter.razor
Normal file
18
FrontAdmin/Pages/Counter.razor
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@page "/counter"
|
||||||
|
|
||||||
|
<PageTitle>Counter</PageTitle>
|
||||||
|
|
||||||
|
<h1>Counter</h1>
|
||||||
|
|
||||||
|
<p role="status">Current count: @currentCount</p>
|
||||||
|
|
||||||
|
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private int currentCount = 0;
|
||||||
|
|
||||||
|
private void IncrementCount()
|
||||||
|
{
|
||||||
|
currentCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
FrontAdmin/Pages/Home.razor
Normal file
7
FrontAdmin/Pages/Home.razor
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
@page "/"
|
||||||
|
|
||||||
|
<PageTitle>Home</PageTitle>
|
||||||
|
|
||||||
|
<h1>Hello, world!</h1>
|
||||||
|
|
||||||
|
Welcome to your new app.
|
||||||
5
FrontAdmin/Pages/NotFound.razor
Normal file
5
FrontAdmin/Pages/NotFound.razor
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@page "/not-found"
|
||||||
|
@layout MainLayout
|
||||||
|
|
||||||
|
<h3>Not Found</h3>
|
||||||
|
<p>Sorry, the content you are looking for does not exist.</p>
|
||||||
57
FrontAdmin/Pages/Weather.razor
Normal file
57
FrontAdmin/Pages/Weather.razor
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
@page "/weather"
|
||||||
|
@inject HttpClient Http
|
||||||
|
|
||||||
|
<PageTitle>Weather</PageTitle>
|
||||||
|
|
||||||
|
<h1>Weather</h1>
|
||||||
|
|
||||||
|
<p>This component demonstrates fetching data from the server.</p>
|
||||||
|
|
||||||
|
@if (forecasts == null)
|
||||||
|
{
|
||||||
|
<p><em>Loading...</em></p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Date</th>
|
||||||
|
<th aria-label="Temperature in Celsius">Temp. (C)</th>
|
||||||
|
<th aria-label="Temperature in Fahrenheit">Temp. (F)</th>
|
||||||
|
<th>Summary</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var forecast in forecasts)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@forecast.Date.ToShortDateString()</td>
|
||||||
|
<td>@forecast.TemperatureC</td>
|
||||||
|
<td>@forecast.TemperatureF</td>
|
||||||
|
<td>@forecast.Summary</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private WeatherForecast[]? forecasts;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WeatherForecast
|
||||||
|
{
|
||||||
|
public DateOnly Date { get; set; }
|
||||||
|
|
||||||
|
public int TemperatureC { get; set; }
|
||||||
|
|
||||||
|
public string? Summary { get; set; }
|
||||||
|
|
||||||
|
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
FrontAdmin/Program.cs
Normal file
11
FrontAdmin/Program.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using FrontAdmin;
|
||||||
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
|
|
||||||
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
|
builder.RootComponents.Add<App>("#app");
|
||||||
|
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||||
|
|
||||||
|
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||||
|
|
||||||
|
await builder.Build().RunAsync();
|
||||||
25
FrontAdmin/Properties/launchSettings.json
Normal file
25
FrontAdmin/Properties/launchSettings.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||||
|
"applicationUrl": "http://localhost:5159",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||||
|
"applicationUrl": "https://localhost:7290;http://localhost:5159",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
FrontAdmin/_Imports.razor
Normal file
10
FrontAdmin/_Imports.razor
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
@using System.Net.Http
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.AspNetCore.Components.WebAssembly.Http
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using FrontAdmin
|
||||||
|
@using FrontAdmin.Layout
|
||||||
5
FrontAdmin/libman.json
Normal file
5
FrontAdmin/libman.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "3.0",
|
||||||
|
"defaultProvider": "cdnjs",
|
||||||
|
"libraries": []
|
||||||
|
}
|
||||||
24
FrontAdmin/nginx.conf
Normal file
24
FrontAdmin/nginx.conf
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
events { }
|
||||||
|
http {
|
||||||
|
include mime.types;
|
||||||
|
types {
|
||||||
|
application/wasm wasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# To jest najwa¿niejsza czêœæ dla Blazora:
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Opcjonalnie: cache dla plików statycznych
|
||||||
|
location ~* \.(jpg|jpeg|png|gif|ico|css|js|wasm|dll)$ {
|
||||||
|
expires 7d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
115
FrontAdmin/wwwroot/css/app.css
Normal file
115
FrontAdmin/wwwroot/css/app.css
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
html, body {
|
||||||
|
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, .btn-link {
|
||||||
|
color: #0071c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
border-color: #1861ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
|
||||||
|
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.valid.modified:not([type=checkbox]) {
|
||||||
|
outline: 1px solid #26b050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid {
|
||||||
|
outline: 1px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.validation-message {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blazor-error-ui {
|
||||||
|
color-scheme: light only;
|
||||||
|
background: lightyellow;
|
||||||
|
bottom: 0;
|
||||||
|
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: none;
|
||||||
|
left: 0;
|
||||||
|
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blazor-error-ui .dismiss {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: 0.75rem;
|
||||||
|
top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blazor-error-boundary {
|
||||||
|
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||||
|
padding: 1rem 1rem 1rem 3.7rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blazor-error-boundary::after {
|
||||||
|
content: "An error has occurred."
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-progress {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
width: 8rem;
|
||||||
|
height: 8rem;
|
||||||
|
inset: 20vh 0 auto 0;
|
||||||
|
margin: 0 auto 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-progress circle {
|
||||||
|
fill: none;
|
||||||
|
stroke: #e0e0e0;
|
||||||
|
stroke-width: 0.6rem;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-progress circle:last-child {
|
||||||
|
stroke: #1b6ec2;
|
||||||
|
stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
|
||||||
|
transition: stroke-dasharray 0.05s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-progress-text {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-progress-text:after {
|
||||||
|
content: var(--blazor-load-percentage-text, "Loading");
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: #c02d76;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
BIN
FrontAdmin/wwwroot/favicon.png
Normal file
BIN
FrontAdmin/wwwroot/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
FrontAdmin/wwwroot/icon-192.png
Normal file
BIN
FrontAdmin/wwwroot/icon-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
34
FrontAdmin/wwwroot/index.html
Normal file
34
FrontAdmin/wwwroot/index.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>FrontAdmin</title>
|
||||||
|
<base href="/" />
|
||||||
|
<link rel="preload" id="webassembly" />
|
||||||
|
<link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||||
|
<link rel="stylesheet" href="css/app.css" />
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
|
<link href="FrontAdmin.styles.css" rel="stylesheet" />
|
||||||
|
<script type="importmap"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<svg class="loading-progress">
|
||||||
|
<circle r="40%" cx="50%" cy="50%" />
|
||||||
|
<circle r="40%" cx="50%" cy="50%" />
|
||||||
|
</svg>
|
||||||
|
<div class="loading-progress-text"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="blazor-error-ui">
|
||||||
|
An unhandled error has occurred.
|
||||||
|
<a href="." class="reload">Reload</a>
|
||||||
|
<span class="dismiss">🗙</span>
|
||||||
|
</div>
|
||||||
|
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
4085
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
vendored
Normal file
4085
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
vendored
Normal file
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
4084
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css
vendored
Normal file
4084
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css
vendored
Normal file
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
597
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
vendored
Normal file
597
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
vendored
Normal file
@@ -0,0 +1,597 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2024 The Bootstrap Authors
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
*/
|
||||||
|
:root,
|
||||||
|
[data-bs-theme=light] {
|
||||||
|
--bs-blue: #0d6efd;
|
||||||
|
--bs-indigo: #6610f2;
|
||||||
|
--bs-purple: #6f42c1;
|
||||||
|
--bs-pink: #d63384;
|
||||||
|
--bs-red: #dc3545;
|
||||||
|
--bs-orange: #fd7e14;
|
||||||
|
--bs-yellow: #ffc107;
|
||||||
|
--bs-green: #198754;
|
||||||
|
--bs-teal: #20c997;
|
||||||
|
--bs-cyan: #0dcaf0;
|
||||||
|
--bs-black: #000;
|
||||||
|
--bs-white: #fff;
|
||||||
|
--bs-gray: #6c757d;
|
||||||
|
--bs-gray-dark: #343a40;
|
||||||
|
--bs-gray-100: #f8f9fa;
|
||||||
|
--bs-gray-200: #e9ecef;
|
||||||
|
--bs-gray-300: #dee2e6;
|
||||||
|
--bs-gray-400: #ced4da;
|
||||||
|
--bs-gray-500: #adb5bd;
|
||||||
|
--bs-gray-600: #6c757d;
|
||||||
|
--bs-gray-700: #495057;
|
||||||
|
--bs-gray-800: #343a40;
|
||||||
|
--bs-gray-900: #212529;
|
||||||
|
--bs-primary: #0d6efd;
|
||||||
|
--bs-secondary: #6c757d;
|
||||||
|
--bs-success: #198754;
|
||||||
|
--bs-info: #0dcaf0;
|
||||||
|
--bs-warning: #ffc107;
|
||||||
|
--bs-danger: #dc3545;
|
||||||
|
--bs-light: #f8f9fa;
|
||||||
|
--bs-dark: #212529;
|
||||||
|
--bs-primary-rgb: 13, 110, 253;
|
||||||
|
--bs-secondary-rgb: 108, 117, 125;
|
||||||
|
--bs-success-rgb: 25, 135, 84;
|
||||||
|
--bs-info-rgb: 13, 202, 240;
|
||||||
|
--bs-warning-rgb: 255, 193, 7;
|
||||||
|
--bs-danger-rgb: 220, 53, 69;
|
||||||
|
--bs-light-rgb: 248, 249, 250;
|
||||||
|
--bs-dark-rgb: 33, 37, 41;
|
||||||
|
--bs-primary-text-emphasis: #052c65;
|
||||||
|
--bs-secondary-text-emphasis: #2b2f32;
|
||||||
|
--bs-success-text-emphasis: #0a3622;
|
||||||
|
--bs-info-text-emphasis: #055160;
|
||||||
|
--bs-warning-text-emphasis: #664d03;
|
||||||
|
--bs-danger-text-emphasis: #58151c;
|
||||||
|
--bs-light-text-emphasis: #495057;
|
||||||
|
--bs-dark-text-emphasis: #495057;
|
||||||
|
--bs-primary-bg-subtle: #cfe2ff;
|
||||||
|
--bs-secondary-bg-subtle: #e2e3e5;
|
||||||
|
--bs-success-bg-subtle: #d1e7dd;
|
||||||
|
--bs-info-bg-subtle: #cff4fc;
|
||||||
|
--bs-warning-bg-subtle: #fff3cd;
|
||||||
|
--bs-danger-bg-subtle: #f8d7da;
|
||||||
|
--bs-light-bg-subtle: #fcfcfd;
|
||||||
|
--bs-dark-bg-subtle: #ced4da;
|
||||||
|
--bs-primary-border-subtle: #9ec5fe;
|
||||||
|
--bs-secondary-border-subtle: #c4c8cb;
|
||||||
|
--bs-success-border-subtle: #a3cfbb;
|
||||||
|
--bs-info-border-subtle: #9eeaf9;
|
||||||
|
--bs-warning-border-subtle: #ffe69c;
|
||||||
|
--bs-danger-border-subtle: #f1aeb5;
|
||||||
|
--bs-light-border-subtle: #e9ecef;
|
||||||
|
--bs-dark-border-subtle: #adb5bd;
|
||||||
|
--bs-white-rgb: 255, 255, 255;
|
||||||
|
--bs-black-rgb: 0, 0, 0;
|
||||||
|
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||||
|
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||||
|
--bs-body-font-size: 1rem;
|
||||||
|
--bs-body-font-weight: 400;
|
||||||
|
--bs-body-line-height: 1.5;
|
||||||
|
--bs-body-color: #212529;
|
||||||
|
--bs-body-color-rgb: 33, 37, 41;
|
||||||
|
--bs-body-bg: #fff;
|
||||||
|
--bs-body-bg-rgb: 255, 255, 255;
|
||||||
|
--bs-emphasis-color: #000;
|
||||||
|
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||||
|
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||||
|
--bs-secondary-color-rgb: 33, 37, 41;
|
||||||
|
--bs-secondary-bg: #e9ecef;
|
||||||
|
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||||
|
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||||
|
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||||
|
--bs-tertiary-bg: #f8f9fa;
|
||||||
|
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||||
|
--bs-heading-color: inherit;
|
||||||
|
--bs-link-color: #0d6efd;
|
||||||
|
--bs-link-color-rgb: 13, 110, 253;
|
||||||
|
--bs-link-decoration: underline;
|
||||||
|
--bs-link-hover-color: #0a58ca;
|
||||||
|
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||||
|
--bs-code-color: #d63384;
|
||||||
|
--bs-highlight-color: #212529;
|
||||||
|
--bs-highlight-bg: #fff3cd;
|
||||||
|
--bs-border-width: 1px;
|
||||||
|
--bs-border-style: solid;
|
||||||
|
--bs-border-color: #dee2e6;
|
||||||
|
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-border-radius: 0.375rem;
|
||||||
|
--bs-border-radius-sm: 0.25rem;
|
||||||
|
--bs-border-radius-lg: 0.5rem;
|
||||||
|
--bs-border-radius-xl: 1rem;
|
||||||
|
--bs-border-radius-xxl: 2rem;
|
||||||
|
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||||
|
--bs-border-radius-pill: 50rem;
|
||||||
|
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||||
|
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||||
|
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
|
--bs-focus-ring-width: 0.25rem;
|
||||||
|
--bs-focus-ring-opacity: 0.25;
|
||||||
|
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||||
|
--bs-form-valid-color: #198754;
|
||||||
|
--bs-form-valid-border-color: #198754;
|
||||||
|
--bs-form-invalid-color: #dc3545;
|
||||||
|
--bs-form-invalid-border-color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme=dark] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bs-body-color: #dee2e6;
|
||||||
|
--bs-body-color-rgb: 222, 226, 230;
|
||||||
|
--bs-body-bg: #212529;
|
||||||
|
--bs-body-bg-rgb: 33, 37, 41;
|
||||||
|
--bs-emphasis-color: #fff;
|
||||||
|
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||||
|
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||||
|
--bs-secondary-color-rgb: 222, 226, 230;
|
||||||
|
--bs-secondary-bg: #343a40;
|
||||||
|
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||||
|
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||||
|
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||||
|
--bs-tertiary-bg: #2b3035;
|
||||||
|
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||||
|
--bs-primary-text-emphasis: #6ea8fe;
|
||||||
|
--bs-secondary-text-emphasis: #a7acb1;
|
||||||
|
--bs-success-text-emphasis: #75b798;
|
||||||
|
--bs-info-text-emphasis: #6edff6;
|
||||||
|
--bs-warning-text-emphasis: #ffda6a;
|
||||||
|
--bs-danger-text-emphasis: #ea868f;
|
||||||
|
--bs-light-text-emphasis: #f8f9fa;
|
||||||
|
--bs-dark-text-emphasis: #dee2e6;
|
||||||
|
--bs-primary-bg-subtle: #031633;
|
||||||
|
--bs-secondary-bg-subtle: #161719;
|
||||||
|
--bs-success-bg-subtle: #051b11;
|
||||||
|
--bs-info-bg-subtle: #032830;
|
||||||
|
--bs-warning-bg-subtle: #332701;
|
||||||
|
--bs-danger-bg-subtle: #2c0b0e;
|
||||||
|
--bs-light-bg-subtle: #343a40;
|
||||||
|
--bs-dark-bg-subtle: #1a1d20;
|
||||||
|
--bs-primary-border-subtle: #084298;
|
||||||
|
--bs-secondary-border-subtle: #41464b;
|
||||||
|
--bs-success-border-subtle: #0f5132;
|
||||||
|
--bs-info-border-subtle: #087990;
|
||||||
|
--bs-warning-border-subtle: #997404;
|
||||||
|
--bs-danger-border-subtle: #842029;
|
||||||
|
--bs-light-border-subtle: #495057;
|
||||||
|
--bs-dark-border-subtle: #343a40;
|
||||||
|
--bs-heading-color: inherit;
|
||||||
|
--bs-link-color: #6ea8fe;
|
||||||
|
--bs-link-hover-color: #8bb9fe;
|
||||||
|
--bs-link-color-rgb: 110, 168, 254;
|
||||||
|
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||||
|
--bs-code-color: #e685b5;
|
||||||
|
--bs-highlight-color: #dee2e6;
|
||||||
|
--bs-highlight-bg: #664d03;
|
||||||
|
--bs-border-color: #495057;
|
||||||
|
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||||
|
--bs-form-valid-color: #75b798;
|
||||||
|
--bs-form-valid-border-color: #75b798;
|
||||||
|
--bs-form-invalid-color: #ea868f;
|
||||||
|
--bs-form-invalid-border-color: #ea868f;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
font-size: var(--bs-body-font-size);
|
||||||
|
font-weight: var(--bs-body-font-weight);
|
||||||
|
line-height: var(--bs-body-line-height);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: inherit;
|
||||||
|
border: 0;
|
||||||
|
border-top: var(--bs-border-width) solid;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6, h5, h4, h3, h2, h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--bs-heading-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
-webkit-text-decoration-skip-ink: none;
|
||||||
|
text-decoration-skip-ink: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding: 0.1875em;
|
||||||
|
color: var(--bs-highlight-color);
|
||||||
|
background-color: var(--bs-highlight-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: var(--bs-font-monospace);
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-code-color);
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
a > code {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.1875rem 0.375rem;
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-body-bg);
|
||||||
|
background-color: var(--bs-body-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
kbd kbd {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
caption-side: bottom;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus:not(:focus-visible) {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
select:disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button:not(:disabled),
|
||||||
|
[type=button]:not(:disabled),
|
||||||
|
[type=reset]:not(:disabled),
|
||||||
|
[type=submit]:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
legend {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legend + * {
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper,
|
||||||
|
::-webkit-datetime-edit-text,
|
||||||
|
::-webkit-datetime-edit-minute,
|
||||||
|
::-webkit-datetime-edit-hour-field,
|
||||||
|
::-webkit-datetime-edit-day-field,
|
||||||
|
::-webkit-datetime-edit-month-field,
|
||||||
|
::-webkit-datetime-edit-year-field {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=search] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rtl:raw:
|
||||||
|
[type="tel"],
|
||||||
|
[type="url"],
|
||||||
|
[type="email"],
|
||||||
|
[type="number"] {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
||||||
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
vendored
Normal file
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
594
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css
vendored
Normal file
594
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css
vendored
Normal file
@@ -0,0 +1,594 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2024 The Bootstrap Authors
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
*/
|
||||||
|
:root,
|
||||||
|
[data-bs-theme=light] {
|
||||||
|
--bs-blue: #0d6efd;
|
||||||
|
--bs-indigo: #6610f2;
|
||||||
|
--bs-purple: #6f42c1;
|
||||||
|
--bs-pink: #d63384;
|
||||||
|
--bs-red: #dc3545;
|
||||||
|
--bs-orange: #fd7e14;
|
||||||
|
--bs-yellow: #ffc107;
|
||||||
|
--bs-green: #198754;
|
||||||
|
--bs-teal: #20c997;
|
||||||
|
--bs-cyan: #0dcaf0;
|
||||||
|
--bs-black: #000;
|
||||||
|
--bs-white: #fff;
|
||||||
|
--bs-gray: #6c757d;
|
||||||
|
--bs-gray-dark: #343a40;
|
||||||
|
--bs-gray-100: #f8f9fa;
|
||||||
|
--bs-gray-200: #e9ecef;
|
||||||
|
--bs-gray-300: #dee2e6;
|
||||||
|
--bs-gray-400: #ced4da;
|
||||||
|
--bs-gray-500: #adb5bd;
|
||||||
|
--bs-gray-600: #6c757d;
|
||||||
|
--bs-gray-700: #495057;
|
||||||
|
--bs-gray-800: #343a40;
|
||||||
|
--bs-gray-900: #212529;
|
||||||
|
--bs-primary: #0d6efd;
|
||||||
|
--bs-secondary: #6c757d;
|
||||||
|
--bs-success: #198754;
|
||||||
|
--bs-info: #0dcaf0;
|
||||||
|
--bs-warning: #ffc107;
|
||||||
|
--bs-danger: #dc3545;
|
||||||
|
--bs-light: #f8f9fa;
|
||||||
|
--bs-dark: #212529;
|
||||||
|
--bs-primary-rgb: 13, 110, 253;
|
||||||
|
--bs-secondary-rgb: 108, 117, 125;
|
||||||
|
--bs-success-rgb: 25, 135, 84;
|
||||||
|
--bs-info-rgb: 13, 202, 240;
|
||||||
|
--bs-warning-rgb: 255, 193, 7;
|
||||||
|
--bs-danger-rgb: 220, 53, 69;
|
||||||
|
--bs-light-rgb: 248, 249, 250;
|
||||||
|
--bs-dark-rgb: 33, 37, 41;
|
||||||
|
--bs-primary-text-emphasis: #052c65;
|
||||||
|
--bs-secondary-text-emphasis: #2b2f32;
|
||||||
|
--bs-success-text-emphasis: #0a3622;
|
||||||
|
--bs-info-text-emphasis: #055160;
|
||||||
|
--bs-warning-text-emphasis: #664d03;
|
||||||
|
--bs-danger-text-emphasis: #58151c;
|
||||||
|
--bs-light-text-emphasis: #495057;
|
||||||
|
--bs-dark-text-emphasis: #495057;
|
||||||
|
--bs-primary-bg-subtle: #cfe2ff;
|
||||||
|
--bs-secondary-bg-subtle: #e2e3e5;
|
||||||
|
--bs-success-bg-subtle: #d1e7dd;
|
||||||
|
--bs-info-bg-subtle: #cff4fc;
|
||||||
|
--bs-warning-bg-subtle: #fff3cd;
|
||||||
|
--bs-danger-bg-subtle: #f8d7da;
|
||||||
|
--bs-light-bg-subtle: #fcfcfd;
|
||||||
|
--bs-dark-bg-subtle: #ced4da;
|
||||||
|
--bs-primary-border-subtle: #9ec5fe;
|
||||||
|
--bs-secondary-border-subtle: #c4c8cb;
|
||||||
|
--bs-success-border-subtle: #a3cfbb;
|
||||||
|
--bs-info-border-subtle: #9eeaf9;
|
||||||
|
--bs-warning-border-subtle: #ffe69c;
|
||||||
|
--bs-danger-border-subtle: #f1aeb5;
|
||||||
|
--bs-light-border-subtle: #e9ecef;
|
||||||
|
--bs-dark-border-subtle: #adb5bd;
|
||||||
|
--bs-white-rgb: 255, 255, 255;
|
||||||
|
--bs-black-rgb: 0, 0, 0;
|
||||||
|
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||||
|
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||||
|
--bs-body-font-size: 1rem;
|
||||||
|
--bs-body-font-weight: 400;
|
||||||
|
--bs-body-line-height: 1.5;
|
||||||
|
--bs-body-color: #212529;
|
||||||
|
--bs-body-color-rgb: 33, 37, 41;
|
||||||
|
--bs-body-bg: #fff;
|
||||||
|
--bs-body-bg-rgb: 255, 255, 255;
|
||||||
|
--bs-emphasis-color: #000;
|
||||||
|
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||||
|
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||||
|
--bs-secondary-color-rgb: 33, 37, 41;
|
||||||
|
--bs-secondary-bg: #e9ecef;
|
||||||
|
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||||
|
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||||
|
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||||
|
--bs-tertiary-bg: #f8f9fa;
|
||||||
|
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||||
|
--bs-heading-color: inherit;
|
||||||
|
--bs-link-color: #0d6efd;
|
||||||
|
--bs-link-color-rgb: 13, 110, 253;
|
||||||
|
--bs-link-decoration: underline;
|
||||||
|
--bs-link-hover-color: #0a58ca;
|
||||||
|
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||||
|
--bs-code-color: #d63384;
|
||||||
|
--bs-highlight-color: #212529;
|
||||||
|
--bs-highlight-bg: #fff3cd;
|
||||||
|
--bs-border-width: 1px;
|
||||||
|
--bs-border-style: solid;
|
||||||
|
--bs-border-color: #dee2e6;
|
||||||
|
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-border-radius: 0.375rem;
|
||||||
|
--bs-border-radius-sm: 0.25rem;
|
||||||
|
--bs-border-radius-lg: 0.5rem;
|
||||||
|
--bs-border-radius-xl: 1rem;
|
||||||
|
--bs-border-radius-xxl: 2rem;
|
||||||
|
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||||
|
--bs-border-radius-pill: 50rem;
|
||||||
|
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||||
|
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||||
|
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||||
|
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||||
|
--bs-focus-ring-width: 0.25rem;
|
||||||
|
--bs-focus-ring-opacity: 0.25;
|
||||||
|
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||||
|
--bs-form-valid-color: #198754;
|
||||||
|
--bs-form-valid-border-color: #198754;
|
||||||
|
--bs-form-invalid-color: #dc3545;
|
||||||
|
--bs-form-invalid-border-color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme=dark] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bs-body-color: #dee2e6;
|
||||||
|
--bs-body-color-rgb: 222, 226, 230;
|
||||||
|
--bs-body-bg: #212529;
|
||||||
|
--bs-body-bg-rgb: 33, 37, 41;
|
||||||
|
--bs-emphasis-color: #fff;
|
||||||
|
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||||
|
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||||
|
--bs-secondary-color-rgb: 222, 226, 230;
|
||||||
|
--bs-secondary-bg: #343a40;
|
||||||
|
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||||
|
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||||
|
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||||
|
--bs-tertiary-bg: #2b3035;
|
||||||
|
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||||
|
--bs-primary-text-emphasis: #6ea8fe;
|
||||||
|
--bs-secondary-text-emphasis: #a7acb1;
|
||||||
|
--bs-success-text-emphasis: #75b798;
|
||||||
|
--bs-info-text-emphasis: #6edff6;
|
||||||
|
--bs-warning-text-emphasis: #ffda6a;
|
||||||
|
--bs-danger-text-emphasis: #ea868f;
|
||||||
|
--bs-light-text-emphasis: #f8f9fa;
|
||||||
|
--bs-dark-text-emphasis: #dee2e6;
|
||||||
|
--bs-primary-bg-subtle: #031633;
|
||||||
|
--bs-secondary-bg-subtle: #161719;
|
||||||
|
--bs-success-bg-subtle: #051b11;
|
||||||
|
--bs-info-bg-subtle: #032830;
|
||||||
|
--bs-warning-bg-subtle: #332701;
|
||||||
|
--bs-danger-bg-subtle: #2c0b0e;
|
||||||
|
--bs-light-bg-subtle: #343a40;
|
||||||
|
--bs-dark-bg-subtle: #1a1d20;
|
||||||
|
--bs-primary-border-subtle: #084298;
|
||||||
|
--bs-secondary-border-subtle: #41464b;
|
||||||
|
--bs-success-border-subtle: #0f5132;
|
||||||
|
--bs-info-border-subtle: #087990;
|
||||||
|
--bs-warning-border-subtle: #997404;
|
||||||
|
--bs-danger-border-subtle: #842029;
|
||||||
|
--bs-light-border-subtle: #495057;
|
||||||
|
--bs-dark-border-subtle: #343a40;
|
||||||
|
--bs-heading-color: inherit;
|
||||||
|
--bs-link-color: #6ea8fe;
|
||||||
|
--bs-link-hover-color: #8bb9fe;
|
||||||
|
--bs-link-color-rgb: 110, 168, 254;
|
||||||
|
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||||
|
--bs-code-color: #e685b5;
|
||||||
|
--bs-highlight-color: #dee2e6;
|
||||||
|
--bs-highlight-bg: #664d03;
|
||||||
|
--bs-border-color: #495057;
|
||||||
|
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||||
|
--bs-form-valid-color: #75b798;
|
||||||
|
--bs-form-valid-border-color: #75b798;
|
||||||
|
--bs-form-invalid-color: #ea868f;
|
||||||
|
--bs-form-invalid-border-color: #ea868f;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
font-size: var(--bs-body-font-size);
|
||||||
|
font-weight: var(--bs-body-font-weight);
|
||||||
|
line-height: var(--bs-body-line-height);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
text-align: var(--bs-body-text-align);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: inherit;
|
||||||
|
border: 0;
|
||||||
|
border-top: var(--bs-border-width) solid;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6, h5, h4, h3, h2, h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--bs-heading-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: calc(1.375rem + 1.5vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: calc(1.325rem + 0.9vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc(1.3rem + 0.6vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h3 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
cursor: help;
|
||||||
|
-webkit-text-decoration-skip-ink: none;
|
||||||
|
text-decoration-skip-ink: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
address {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
padding-right: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
dl {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol ol,
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ul ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
padding: 0.1875em;
|
||||||
|
color: var(--bs-highlight-color);
|
||||||
|
background-color: var(--bs-highlight-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: var(--bs-font-monospace);
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-code-color);
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
a > code {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.1875rem 0.375rem;
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--bs-body-bg);
|
||||||
|
background-color: var(--bs-body-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
kbd kbd {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
caption-side: bottom;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus:not(:focus-visible) {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
optgroup,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
select:disabled {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button:not(:disabled),
|
||||||
|
[type=button]:not(:disabled),
|
||||||
|
[type=reset]:not(:disabled),
|
||||||
|
[type=submit]:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
float: right;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: calc(1.275rem + 0.3vw);
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
legend {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
legend + * {
|
||||||
|
clear: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper,
|
||||||
|
::-webkit-datetime-edit-text,
|
||||||
|
::-webkit-datetime-edit-minute,
|
||||||
|
::-webkit-datetime-edit-hour-field,
|
||||||
|
::-webkit-datetime-edit-day-field,
|
||||||
|
::-webkit-datetime-edit-month-field,
|
||||||
|
::-webkit-datetime-edit-year-field {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=search] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type="tel"],
|
||||||
|
[type="url"],
|
||||||
|
[type="email"],
|
||||||
|
[type="number"] {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
::file-selector-button {
|
||||||
|
font: inherit;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
||||||
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css
vendored
Normal file
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
5402
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css
vendored
Normal file
5402
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css
vendored
Normal file
6
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map
vendored
Normal file
1
FrontAdmin/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user