Compare commits
129 Commits
main
...
RestsrtCou
| Author | SHA1 | Date | |
|---|---|---|---|
| 974a8aa0ad | |||
| 87782c3269 | |||
| b421e07926 | |||
| 818a173440 | |||
| aeeb6e0d91 | |||
| 9eb2540c51 | |||
| cda6529985 | |||
| b5b7979cdf | |||
| 26b5ef7b73 | |||
| a85c557f43 | |||
| 036e6d4e67 | |||
| 1fc894921a | |||
| 049ea0aa3c | |||
| ab12053850 | |||
| 675d6a21aa | |||
| f912109eb4 | |||
| 581ae44776 | |||
| bda854b917 | |||
| 8d26b95832 | |||
| 2450b0ba22 | |||
| dfa9163ce9 | |||
| 23c52f096e | |||
| c6516678fd | |||
| 15074cc3d2 | |||
| f6c9cf3044 | |||
| ba9543df1b | |||
| e9090577d6 | |||
| 59749a4a67 | |||
| 6f32e2c251 | |||
| e8f925972c | |||
| a9790a9d88 | |||
| 7e108ee216 | |||
| a20bfbb3e2 | |||
| 60029d7e1b | |||
| da84992d30 | |||
| 2f4efc2dba | |||
| 430319f50f | |||
| 39af7fd785 | |||
| 0dc5b4880a | |||
| ba16aa6f10 | |||
| e5b9d581ca | |||
| cacbcc5d53 | |||
| df9d449e4c | |||
| 19c217dd68 | |||
| 854cbb2a87 | |||
| 845f96acb4 | |||
| c137ef9606 | |||
| f77e81a6c6 | |||
| f6585ce267 | |||
| 9590375c03 | |||
| 4c06445980 | |||
| 7b9d550445 | |||
| 1868b80b84 | |||
| d846a6b5ce | |||
| 6558ef8f2d | |||
| 352701d94f | |||
| 8c2612df4a | |||
| b1a6d92df4 | |||
| a3ff31280f | |||
| 40e26a0120 | |||
| aa58e0817d | |||
| 0cedc4a00a | |||
| 02bf7c34ef | |||
| b1c96467cc | |||
| ed481a5e62 | |||
| cee5905b67 | |||
| bf4e4a0d18 | |||
| 9eb709a3ee | |||
| 7ad22682fb | |||
| 011151dba0 | |||
| 786f9ef7b1 | |||
| 3620b7fe99 | |||
| 8b976b7523 | |||
| bb76ebcd83 | |||
| 33e2641318 | |||
| b406dea953 | |||
| 7b678ef445 | |||
| 4d44610c2f | |||
| 3fcaec39e9 | |||
| b554fcc2f3 | |||
| 3facbf9d28 | |||
| 443b29e70b | |||
| 141d39b42e | |||
| 69392019bb | |||
| 2c62e282f9 | |||
| 91951c9ef6 | |||
| 245cb9e87e | |||
| e1f5c49c8a | |||
| 26cf1ae0c1 | |||
| b7038b508c | |||
| 9c86534470 | |||
| 655601f7c7 | |||
| 545cacbe9a | |||
| e9c6b7ff3a | |||
| 18af25be0b | |||
| adbf6eb004 | |||
| b9e8a3257f | |||
| e78f937fc8 | |||
| 483b945269 | |||
| 5f77287037 | |||
| 176786f5eb | |||
|
|
9de7e5e480 | ||
|
|
f98e2a9df8 | ||
|
|
50fabdaf55 | ||
|
|
e34abad173 | ||
|
|
9a6963d2c5 | ||
| 85c3a97d23 | |||
| b44784854a | |||
| 30852efca6 | |||
| 9132f6d7c3 | |||
| 651f3d5564 | |||
| 1ec2a956d3 | |||
| 142d9d2bf8 | |||
|
|
e66849ac08 | ||
|
|
046921f18d | ||
| 84b2f754d1 | |||
|
|
d9123e9f2c | ||
| 38ecc483c2 | |||
|
|
6156a55dd9 | ||
| 0688934faf | |||
|
|
d80e031d74 | ||
| 1925bb4fcc | |||
| 89e4449b96 | |||
| 3a6f28ecfb | |||
|
|
f3cf508702 | ||
|
|
134f277016 | ||
|
|
5bbf76d13b | ||
|
|
0279d05a36 | ||
|
|
d7109524d1 |
43
UniversityAllExpelledClient/UniversityAllExpelled.sln
Normal file
43
UniversityAllExpelledClient/UniversityAllExpelled.sln
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.13.35806.99
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversityAllExpelled_Models", "UniversityAllExpelled_Models\UniversityAllExpelled_Models.csproj", "{B2364D1E-38B8-4DF7-90DB-49F8463A1B40}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversityAllExpelled_DataBase", "UniversityAllExpelled_DataBase\UniversityAllExpelled_DataBase.csproj", "{23807279-75F8-4BEC-831B-24AA27ABA57A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversityAllExpelled_BusinessLogic", "UniversityAllExpelled_BusinessLogic\UniversityAllExpelled_BusinessLogic.csproj", "{6C4551E0-879E-4D28-9C2A-019929E0211A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversityAllExpelled_WebApi", "UniversityAllExpelled_WebApi\UniversityAllExpelled_WebApi.csproj", "{F2CB59D0-8BC0-477E-8BC6-5A230AE04694}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B2364D1E-38B8-4DF7-90DB-49F8463A1B40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B2364D1E-38B8-4DF7-90DB-49F8463A1B40}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B2364D1E-38B8-4DF7-90DB-49F8463A1B40}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B2364D1E-38B8-4DF7-90DB-49F8463A1B40}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{23807279-75F8-4BEC-831B-24AA27ABA57A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23807279-75F8-4BEC-831B-24AA27ABA57A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23807279-75F8-4BEC-831B-24AA27ABA57A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23807279-75F8-4BEC-831B-24AA27ABA57A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6C4551E0-879E-4D28-9C2A-019929E0211A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6C4551E0-879E-4D28-9C2A-019929E0211A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6C4551E0-879E-4D28-9C2A-019929E0211A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6C4551E0-879E-4D28-9C2A-019929E0211A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2CB59D0-8BC0-477E-8BC6-5A230AE04694}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F2CB59D0-8BC0-477E-8BC6-5A230AE04694}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F2CB59D0-8BC0-477E-8BC6-5A230AE04694}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2CB59D0-8BC0-477E-8BC6-5A230AE04694}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {256411BF-AC51-436B-B254-59E602A9AE79}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UniversityAllExpelled_Models\UniversityAllExpelled_Models.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="UniversityAllExpelled_WebApi" />
|
||||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Implementations\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using UniversityAllExpelled_Database.Models;
|
||||
|
||||
namespace UniversityAllExpelled_DataBase
|
||||
{
|
||||
public interface IUniversityDbContext
|
||||
{
|
||||
DbSet<User> Users { get; }
|
||||
DbSet<Student> Students { get; }
|
||||
DbSet<Teacher> Teachers { get; }
|
||||
DbSet<Payment> Payments { get; }
|
||||
DbSet<Lesson> Lessons { get; }
|
||||
DbSet<Salary> Salaries { get; }
|
||||
DbSet<Education> Educations { get; }
|
||||
DbSet<EducationLessons> EducationLessons { get; }
|
||||
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using UniversityAllExpelled_Models.DataModels.Worker;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
|
||||
public class Education
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
public double Amount { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public bool Active { get; set; }
|
||||
|
||||
[ForeignKey("EducationId")]
|
||||
public virtual List<EducationLessons> EducationLessons { get; set; } = [];
|
||||
public virtual List<Payment> Payments { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using UniversityAllExpelled_Models.DataModels.Worker;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
|
||||
public class EducationLessons()
|
||||
{
|
||||
public required string EducationId { get; set; }
|
||||
public required string LessonId { get; set; }
|
||||
public Education? Education { get; set; }
|
||||
public Lesson? Lesson { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
|
||||
public class Payment
|
||||
{
|
||||
|
||||
public required string Id { get; set; }
|
||||
public required string StudentUserId { get; set; }
|
||||
public required string LessonId { get; set; }
|
||||
public double PaidAmount { get; set; }
|
||||
public double Arrears { get; set; }
|
||||
public DateTime DateOfPayment { get; set; }
|
||||
public bool IsPaid { get; set; }
|
||||
|
||||
public Student? Student { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
|
||||
public class Student
|
||||
{
|
||||
public required string UserId { get; set; }
|
||||
|
||||
public string Faculty { get; set; } = "ФИСТ";
|
||||
public int Course { get; set; }
|
||||
public required User User { get; set; }
|
||||
public List<Payment>? Payments { get; set; }
|
||||
public List<Education>? Educations { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
using UniversityAllExpelled_Models.DataModels.Worker;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
public class Teacher
|
||||
{
|
||||
public required string UserId { get; set; }
|
||||
public string LevelPost { get; set; } = "Доцент";
|
||||
public DateTime DateHiring { get; set; }
|
||||
public required User User { get; set; }
|
||||
public List<Lesson>? Lessons { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Data;
|
||||
using UniversityAllExpelled_Models.DataModels;
|
||||
using UniversityAllExpelled_Models.Enums;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
public class User
|
||||
{
|
||||
public required string Id { get; set;}
|
||||
public required string Login { get; set; }
|
||||
public required string Password { get; set; }
|
||||
public required SystemRoleType Role { get; set; }
|
||||
public required string FIO { get; set; }
|
||||
public DateTime BirthDate { get; set; }
|
||||
public required string PhoneNumber { get; set; }
|
||||
public required string Email { get; set; }
|
||||
public Student? Student { get; set; }
|
||||
public Teacher? Teacher { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using UniversityAllExpelled_Database.Models;
|
||||
using UniversityAllExpelled_DataBase;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_Database;
|
||||
|
||||
public class UniversityAllExpelledDbContext : DbContext, IUniversityDbContext
|
||||
{
|
||||
private readonly IConfigurationDatabase? _configurationDatabase;
|
||||
|
||||
public UniversityAllExpelledDbContext(IConfigurationDatabase? configurationDatabase = null)
|
||||
{
|
||||
_configurationDatabase = configurationDatabase;
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (_configurationDatabase == null || string.IsNullOrEmpty(_configurationDatabase.ConnectionString))
|
||||
throw new InvalidOperationException("Database configuration is not set");
|
||||
|
||||
optionsBuilder.UseNpgsql(_configurationDatabase.ConnectionString, o => o.SetPostgresVersion(12, 2));
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<User>()
|
||||
.HasIndex(u => u.PhoneNumber)
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<User>()
|
||||
.HasOne(u => u.Student)
|
||||
.WithOne(s => s.User)
|
||||
.HasForeignKey<Student>(s => s.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<User>()
|
||||
.HasOne(u => u.Teacher)
|
||||
.WithOne(t => t.User)
|
||||
.HasForeignKey<Teacher>(t => t.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<Lesson>()
|
||||
.HasOne(l => l.Student)
|
||||
.WithMany(s => s.Lessons)
|
||||
.HasForeignKey(l => l.StudentId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<Lesson>()
|
||||
.HasOne(l => l.Salary)
|
||||
.WithMany(s => s.Lessons)
|
||||
.HasForeignKey(l => l.SalaryId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
modelBuilder.Entity<Lesson>()
|
||||
.HasMany(l => l.EducationLessons)
|
||||
.WithOne(el => el.Lesson)
|
||||
.HasForeignKey(el => el.LessonId);
|
||||
|
||||
modelBuilder.Entity<Education>()
|
||||
.HasOne(e => e.Teacher)
|
||||
.WithMany(t => t.Educations)
|
||||
.HasForeignKey(e => e.TeacherId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<Education>()
|
||||
.HasMany(e => e.EducationLessons)
|
||||
.WithOne(el => el.Education)
|
||||
.HasForeignKey(el => el.EducationId);
|
||||
|
||||
modelBuilder.Entity<EducationLessons>()
|
||||
.HasKey(el => new { el.EducationId, el.LessonId });
|
||||
|
||||
modelBuilder.Entity<Payment>()
|
||||
.HasOne(p => p.Student)
|
||||
.WithMany(s => s.Payments)
|
||||
.HasForeignKey(p => p.StudentUserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<Payment>()
|
||||
.HasOne(p => p.Education)
|
||||
.WithMany(e => e.Payments)
|
||||
.HasForeignKey(p => p.EducationId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<Student> Students { get; set; }
|
||||
public DbSet<Teacher> Teachers { get; set; }
|
||||
public DbSet<Lesson> Lessons { get; set; }
|
||||
public DbSet<Payment> Payments { get; set; }
|
||||
public DbSet<Salary> Salaries { get; set; }
|
||||
public DbSet<Education> Educations { get; set; }
|
||||
public DbSet<EducationLessons> EducationLessons { get; set; }
|
||||
|
||||
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
=> await base.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UniversityAllExpelled_Models\UniversityAllExpelled_Models.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="UniversityAllExpelled_WebApi" />
|
||||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Implementations\" />
|
||||
<Folder Include="Infrastructure\" />
|
||||
<Folder Include="Migrations\" />
|
||||
<Folder Include="Models\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using UniversityAllExpelled_Models.DataModels.Worker;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
|
||||
public class Education
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
public double Amount { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public bool Active { get; set; }
|
||||
|
||||
[ForeignKey("EducationId")]
|
||||
public virtual List<EducationLessons> EducationLessons { get; set; } = [];
|
||||
public virtual List<Payment> Payments { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using UniversityAllExpelled_Models.DataModels.Worker;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
|
||||
public class EducationLessons()
|
||||
{
|
||||
public required string EducationId { get; set; }
|
||||
public required string LessonId { get; set; }
|
||||
public Education? Education { get; set; }
|
||||
public Lesson? Lesson { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
public class Lesson
|
||||
{
|
||||
public required string Id { get; set;}
|
||||
|
||||
public required string TeacherUserId { get; set;}
|
||||
public required string LessonName { get; set;}
|
||||
public double Price { get; set; }
|
||||
public DateTime LessonDate { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
[ForeignKey("LessonId")]
|
||||
public List<EducationLessons>? EducationLessons { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
|
||||
public class Payment
|
||||
{
|
||||
|
||||
public required string Id { get; set; }
|
||||
public required string StudentUserId { get; set; }
|
||||
public required string LessonId { get; set; }
|
||||
public double PaidAmount { get; set; }
|
||||
public double Arrears { get; set; }
|
||||
public DateTime DateOfPayment { get; set; }
|
||||
public bool IsPaid { get; set; }
|
||||
|
||||
public Student? Student { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
|
||||
public class Student
|
||||
{
|
||||
public required string UserId { get; set; }
|
||||
|
||||
public string Faculty { get; set; } = "ФИСТ";
|
||||
public int Course { get; set; }
|
||||
public required User User { get; set; }
|
||||
public List<Payment>? Payments { get; set; }
|
||||
public List<Education>? Educations { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
using UniversityAllExpelled_Models.DataModels.Worker;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
public class Teacher
|
||||
{
|
||||
public required string UserId { get; set; }
|
||||
public string LevelPost { get; set; } = "Доцент";
|
||||
public DateTime DateHiring { get; set; }
|
||||
public required User User { get; set; }
|
||||
public List<Lesson>? Lessons { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Data;
|
||||
using UniversityAllExpelled_Models.DataModels;
|
||||
using UniversityAllExpelled_Models.Enums;
|
||||
|
||||
namespace UniversityAllExpelled_Database.Models;
|
||||
|
||||
public class User
|
||||
{
|
||||
public required string Id { get; set;}
|
||||
public required string Login { get; set; }
|
||||
public required string Password { get; set; }
|
||||
public required SystemRoleType Role { get; set; }
|
||||
public required string FIO { get; set; }
|
||||
public DateTime BirthDate { get; set; }
|
||||
public required string PhoneNumber { get; set; }
|
||||
public required string Email { get; set; }
|
||||
public Student? Student { get; set; }
|
||||
public Teacher? Teacher { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using UniversityAllExpelled_Models.Exceptions;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_Models.DataModels.Worker
|
||||
{
|
||||
public class EducationDataModel(string id, double amount, DateTime startDate, DateTime endDate, bool active) : IValidation
|
||||
{
|
||||
public string Id { get; set; } = id;
|
||||
public double Amount { get; set; } = amount;
|
||||
public DateTime StartDate { get; set; } = startDate;
|
||||
public DateTime EndDate { get; set; } = endDate;
|
||||
public bool Active { get; set; } = active;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Id))
|
||||
throw new ValidationException("Education record ID cannot be empty");
|
||||
|
||||
if (!Guid.TryParse(Id, out _))
|
||||
throw new ValidationException("Education record ID must be in valid GUID format");
|
||||
|
||||
if (Amount <= 0)
|
||||
throw new ValidationException("Education amount must be greater than zero");
|
||||
|
||||
if (StartDate == default)
|
||||
throw new ValidationException("Start date must be specified");
|
||||
|
||||
if (EndDate == default)
|
||||
throw new ValidationException("End date must be specified");
|
||||
|
||||
if (StartDate > EndDate)
|
||||
throw new ValidationException("Start date cannot be after end date");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using UniversityAllExpelled_Models.Extensions;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_Models.DataModels.Worker;
|
||||
|
||||
public class EducationLessonsDataModel(string educationId, string lessonId) : IValidation
|
||||
{
|
||||
public virtual string EducationId { get; set; } = educationId;
|
||||
public virtual string LessonId { get; set; } = lessonId;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (EducationId.IsEmpty())
|
||||
throw new ValidationException("Education ID cannot be empty");
|
||||
|
||||
if (!EducationId.IsGuid())
|
||||
throw new ValidationException("Education ID must be in valid GUID format");
|
||||
|
||||
if (LessonId.IsEmpty())
|
||||
throw new ValidationException("Lesson ID cannot be empty");
|
||||
|
||||
if (!LessonId.IsGuid())
|
||||
throw new ValidationException("Lesson ID must be in valid GUID format");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using UniversityAllExpelled_Models.Exceptions;
|
||||
using UniversityAllExpelled_Models.Extensions;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_Models.DataModels;
|
||||
|
||||
public class LessonDataModel(string id, string lessonName, double price,DateTime lessonDate, bool isActive) : IValidation
|
||||
{
|
||||
public string Id { get; set; } = id;
|
||||
public string LessonName { get; set; } = lessonName;
|
||||
public double Price { get; set; } = price;
|
||||
public DateTime LessonDate { get; set; } = lessonDate;
|
||||
public bool IsActive { get; set; } = isActive;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (Id.IsEmpty())
|
||||
throw new ValidationException("Field Id is empty.");
|
||||
if (!Id.IsGuid())
|
||||
throw new ValidationException("Field Id is not a valid GUID.");
|
||||
|
||||
if (LessonName.IsEmpty())
|
||||
throw new ValidationException("Field Name is empty.");
|
||||
|
||||
if (Price <= 0)
|
||||
throw new ValidationException("Price must be greater than 0.");
|
||||
|
||||
if (LessonDate.Date > EndDate.Date)
|
||||
throw new ValidationException("StartDate cannot be later than EndDate.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using UniversityAllExpelled_Models.Exceptions;
|
||||
using UniversityAllExpelled_Models.Extensions;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_Models.DataModels;
|
||||
|
||||
public class PaymentDataModel(string id,string studentId,double paidAmount,
|
||||
double arrears,DateTime dateOfPayment, bool isPaid) : IValidation
|
||||
{
|
||||
public string Id { get; set; } = id;
|
||||
public string StudentId { get; set; } = studentId;
|
||||
public string EducationId { get; set; } = string.Empty;
|
||||
public double PaidAmount { get; set; } = paidAmount;
|
||||
public double Arrears { get; set; } = arrears;
|
||||
public DateTime DateOfPayment { get; set; } = dateOfPayment;
|
||||
public bool IsPaid { get; set; } = isPaid;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (Id.IsEmpty())
|
||||
throw new ValidationException("Field Id is empty.");
|
||||
if (!Id.IsGuid())
|
||||
throw new ValidationException("Field Id is not a valid GUID.");
|
||||
|
||||
if (StudentId.IsEmpty())
|
||||
throw new ValidationException("Field StudentId is empty.");
|
||||
if (!StudentId.IsGuid())
|
||||
throw new ValidationException("Field StudentId is not a valid GUID.");
|
||||
|
||||
if (!EducationId.IsEmpty() && !EducationId.IsGuid())
|
||||
throw new ValidationException("Field EducationId must be a valid GUID if provided.");
|
||||
|
||||
if (PaidAmount < 0)
|
||||
throw new ValidationException("PaidAmount cannot be negative.");
|
||||
|
||||
if (Arrears < 0)
|
||||
throw new ValidationException("Arrears cannot be negative.");
|
||||
|
||||
if (IsPaid && Arrears != 0)
|
||||
throw new ValidationException("If IsPaid is true, Arrears must be 0.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using UniversityAllExpelled_Models.Exceptions;
|
||||
using UniversityAllExpelled_Models.Extensions;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_Models.DataModels;
|
||||
|
||||
public class StudentDataModel(string userId, int course, string faculty = "ФИСТ") : IValidation
|
||||
{
|
||||
public string UserId { get; set; } = userId;
|
||||
public int Course { get; set; } = course;
|
||||
public string Faculty { get; set; } = faculty;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
|
||||
if (UserId.IsEmpty())
|
||||
throw new ValidationException("Field UserId is empty");
|
||||
|
||||
if (!UserId.IsGuid())
|
||||
throw new ValidationException("Field UserId is not a valid GUID");
|
||||
|
||||
if (Course < 1 || Course > 6)
|
||||
throw new ValidationException("Course must be between 1 and 6");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using UniversityAllExpelled_Models.Enums;
|
||||
using UniversityAllExpelled_Models.Extensions;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_Models.DataModels;
|
||||
|
||||
public class TeacherDataModel(string userId, DateTime dateHiring, string levelPost = "Доцент") : IValidation
|
||||
{
|
||||
public string UserId { get; set; } = userId;
|
||||
public string LevelPost { get; set; } = levelPost;
|
||||
public DateTime DateHiring { get; set; } = dateHiring;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (UserId.IsEmpty())
|
||||
throw new ValidationException("User ID cannot be empty");
|
||||
|
||||
if (!UserId.IsGuid())
|
||||
throw new ValidationException("User ID must be a valid GUID");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using UniversityAllExpelled_Models.Enums;
|
||||
using UniversityAllExpelled_Models.Exceptions;
|
||||
using UniversityAllExpelled_Models.Extensions;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_Models.DataModels;
|
||||
|
||||
public class UserDataModel(string id, string login, string password, SystemRoleType role,
|
||||
string fio, DateTime birthdate, string phoneNumber, string email, bool isDeleted) : IValidation
|
||||
{
|
||||
public string Id { get; set; } = id;
|
||||
public string Login { get; set; } = login;
|
||||
public string Password { get; set; } = password;
|
||||
public SystemRoleType Role { get; set; } = role;
|
||||
public string FIO { get; set; } = fio;
|
||||
public DateTime BirthDate { get; set; } = birthdate;
|
||||
public string PhoneNumber { get; set; } = phoneNumber;
|
||||
public string Email { get; set; } = email;
|
||||
public bool IsDeleted { get; set; } = isDeleted;
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (Id.IsEmpty())
|
||||
throw new ValidationException("Field Id is empty");
|
||||
|
||||
if (!Id.IsGuid())
|
||||
throw new ValidationException("The value in the field Id is not a valid GUID");
|
||||
|
||||
if (Login.IsEmpty())
|
||||
throw new ValidationException("Field Login is empty");
|
||||
|
||||
if (Password.IsEmpty())
|
||||
throw new ValidationException("Field Password is empty");
|
||||
|
||||
if (!Regex.IsMatch(Password, @"^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+{}\[\]:;'"",.<>?\/\\|`~\-=]).{8,}$"))
|
||||
throw new ValidationException("The Password field was entered incorrectly. It must contain at least one lowercase letter, one uppercase letter, one digit, and one special character.");
|
||||
|
||||
if (Role == SystemRoleType.None)
|
||||
throw new ValidationException("The Role field must not be None");
|
||||
|
||||
if (FIO.IsEmpty())
|
||||
throw new ValidationException("Field FIO is empty");
|
||||
|
||||
if (BirthDate > DateTime.Now.AddYears(-16))
|
||||
throw new ValidationException("User must be at least 16 years old");
|
||||
|
||||
if (!Regex.IsMatch(PhoneNumber, @"^(\+7|8)(-?\d{3}-?\d{3}-?\d{2}-?\d{2}|-?\d{10})$"))
|
||||
throw new ValidationException("The PhoneNumber field was entered incorrectly");
|
||||
|
||||
if (!Regex.IsMatch(Email, @"^[a-zA-Z0-9._%+-]+@(?:yandex\.[a-z]{2,}|mail\.ru|inbox\.ru|list\.ru|bk\.ru|gmail\.com)$"))
|
||||
throw new ValidationException("The Email field was entered incorrectly");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace UniversityAllExpelled_Models.Enums;
|
||||
|
||||
public enum SystemRoleType
|
||||
{
|
||||
None = 0,
|
||||
student = 1,
|
||||
teacher = 2
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace UniversityAllExpelled_Models.Exceptions;
|
||||
|
||||
public class DuplicateEmployeeException : Exception
|
||||
{
|
||||
public DuplicateEmployeeException(string id)
|
||||
: base($"Employee with ID {id} already exists") { }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace UniversityAllExpelled_Models.Exceptions
|
||||
{
|
||||
|
||||
[Serializable]
|
||||
public class DuplicateException : Exception
|
||||
{
|
||||
public DuplicateException()
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected DuplicateException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
/// <param name="entityName">Название сущности</param>
|
||||
/// <param name="fieldName">Название поля</param>
|
||||
/// <param name="fieldValue">Значение поля</param>
|
||||
|
||||
public static DuplicateException ForField(string entityName, string fieldName, string fieldValue)
|
||||
{
|
||||
return new DuplicateException(
|
||||
$"{entityName} with {fieldName} '{fieldValue}' already exists. {fieldName} must be unique.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace UniversityAllExpelled_Models.Exceptions;
|
||||
|
||||
public class ElementDeletedException : Exception
|
||||
{
|
||||
public ElementDeletedException(string id)
|
||||
: base($"Cannot modify a deleted item (id: {id})") { }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace UniversityAllExpelled_Models.Exceptions;
|
||||
|
||||
public class ElementExistsException : Exception
|
||||
{
|
||||
public string ParamName { get; }
|
||||
public string ParamValue { get; }
|
||||
|
||||
public ElementExistsException(string paramName, string paramValue)
|
||||
: base($"An element with value '{paramValue}' already exists for parameter '{paramName}'")
|
||||
{
|
||||
ParamName = paramName;
|
||||
ParamValue = paramValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace UniversityAllExpelled_Models.Exceptions;
|
||||
|
||||
public class ElementNotFoundException : Exception
|
||||
{
|
||||
public string Value { get; }
|
||||
|
||||
public ElementNotFoundException(string value)
|
||||
: base($"Element not found with value = {value}")
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace UniversityAllExpelled_Models.Exceptions;
|
||||
|
||||
public class IncorrectDatesException : Exception
|
||||
{
|
||||
public IncorrectDatesException(DateTime start, DateTime end)
|
||||
: base($"The end date must be later than the start date. StartDate: {start:dd.MM.yyyy}, EndDate: {end:dd.MM.yyyy}")
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace UniversityAllExpelled_Models.Exceptions;
|
||||
|
||||
public class NullListException : Exception
|
||||
{
|
||||
public NullListException(string context)
|
||||
: base($"Expected list was null or empty in context: {context}")
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace UniversityAllExpelled_Models.Exceptions;
|
||||
|
||||
public class StorageException : Exception
|
||||
{
|
||||
public StorageException(Exception ex)
|
||||
: base($"Error while working in storage: {ex.Message}", ex) { }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace UniversityAllExpelled_Models.Exceptions;
|
||||
|
||||
public class ValidationException : Exception
|
||||
{
|
||||
public ValidationException(string message) : base(message) { }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
namespace UniversityAllExpelled_Models.Extensions;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static bool IsEmpty(this string str)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(str);
|
||||
}
|
||||
|
||||
public static bool IsGuid(this string str)
|
||||
{
|
||||
return Guid.TryParse(str, out _);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
public interface IConfigurationDatabase
|
||||
{
|
||||
string ConnectionString { get; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
namespace UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
public interface IValidation
|
||||
{
|
||||
void Validate();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="UniversityAllExpelled_DataBase" />
|
||||
<InternalsVisibleTo Include="UniversityAllExpelled_BusinessLogic" />
|
||||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="UniversityAllExpelled_WebApi" />
|
||||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="BusinessLogicContracts\" />
|
||||
<Folder Include="StorageContracts\" />
|
||||
<Folder Include="ViewModels\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="NUnit" Version="4.2.2" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.4.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UniversityAllExpelled_Models\UniversityAllExpelled_Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="NUnit.Framework" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,10 @@
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
|
||||
namespace UniversityAllExpelled_WebApi.Infrastructure;
|
||||
|
||||
public class ConfigurationDatabase(IConfiguration configuration) : IConfigurationDatabase
|
||||
{
|
||||
private readonly string _connectionString = configuration["DataBaseSettings:ConnectionString"] ?? throw new InvalidDataException("DataBaseSettings:ConnectionString не найден в конфигурации");
|
||||
|
||||
public string ConnectionString => _connectionString;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace UniversityAllExpelled_WebApi.Infrastructure;
|
||||
|
||||
|
||||
public class DataBaseSettings
|
||||
{
|
||||
public required string ConnectionString { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using UniversityAllExpelled_BusinessLogic.Implementations;
|
||||
using UniversityAllExpelled_DataBase;
|
||||
using UniversityAllExpelled_DataBase.Implementations;
|
||||
using UniversityAllExpelled_Database.Implementations;
|
||||
using UniversityAllExpelled_Models.BusinessLogicContracts;
|
||||
using UniversityAllExpelled_Models.Infrastructure;
|
||||
using UniversityAllExpelled_Models.StorageContracts;
|
||||
using UniversityAllExpelled_WebApi.Infrastructure;
|
||||
using UniversityAllExpelled_Database;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> enum
|
||||
builder.Services.AddControllers()
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
|
||||
});
|
||||
|
||||
// JWT Authentication
|
||||
builder.Services.AddAuthorization();
|
||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = AuthOptions.ISSUER,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = AuthOptions.AUDIENCE,
|
||||
ValidateLifetime = true,
|
||||
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
|
||||
ValidateIssuerSigningKey = true,
|
||||
};
|
||||
});
|
||||
|
||||
// DbContext
|
||||
builder.Services.AddDbContext<UniversityAllExpelledDbContext>(options =>
|
||||
options.UseNpgsql(
|
||||
builder.Configuration["DataBaseSettings:ConnectionString"],
|
||||
x => x.MigrationsAssembly("UniversityAllExpelled_WebApi"))
|
||||
);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>-<2D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
builder.Services.AddTransient<IConfigurationDatabase, ConfigurationDatabase>();
|
||||
builder.Services.AddTransient<IUserBusinessLogicContract, UserBusinessLogicContract>();
|
||||
builder.Services.AddTransient<IStudentBusinessLogicContract, StudentBusinessLogicContract>();
|
||||
builder.Services.AddTransient<ITeacherBusinessLogicContract, TeacherBusinessLogicContract>();
|
||||
builder.Services.AddTransient<ILessonBusinessLogicContract, LessonBusinessLogicContract>();
|
||||
builder.Services.AddTransient<ISalaryBusinessLogicContract, SalaryBusinessLogicContract>();
|
||||
builder.Services.AddTransient<IEducationBusinessLogicContract, EducationBusinessLogicContract>();
|
||||
builder.Services.AddTransient<IEducationLessonsBusinessLogicContract, EducationLessonsBusinessLogicContract>();
|
||||
builder.Services.AddTransient<IPaymentBusinessLogicContract, PaymentBusinessLogicContract>();
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
builder.Services.AddTransient<IUserStorageContract, UserStorageContract>();
|
||||
builder.Services.AddTransient<IStudentStorageContract, StudentStorageContract>();
|
||||
builder.Services.AddTransient<ITeacherStorageContract, TeacherStorageContract>();
|
||||
builder.Services.AddTransient<ILessonStorageContract, LessonStorageContract>();
|
||||
builder.Services.AddTransient<ISalaryStorageContract, SalaryStorageContract>();
|
||||
builder.Services.AddTransient<IEducationStorageContract, EducationStorageContract>();
|
||||
builder.Services.AddTransient<IEducationLessonsStorageContract, EducationLessonsStorageContract>();
|
||||
builder.Services.AddTransient<IPaymentStorageContract, PaymentStorageContract>();
|
||||
|
||||
builder.Services.AddScoped<IUniversityDbContext, UniversityAllExpelledDbContext>();
|
||||
builder.Services.AddAutoMapper(typeof(Program));
|
||||
builder.Services.AddLogging();
|
||||
|
||||
// Swagger (OpenAPI)
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles(); // <20><><EFBFBD> wwwroot
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Swagger UI
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "University All Expelled API V1");
|
||||
c.RoutePrefix = "swagger";
|
||||
});
|
||||
|
||||
app.MapControllers();
|
||||
app.Run();
|
||||
|
||||
public class AuthOptions
|
||||
{
|
||||
public const string ISSUER = "MyAuthServer";
|
||||
public const string AUDIENCE = "MyAuthClient";
|
||||
const string KEY = "mysupersecret_secretsecretsecretkey!123";
|
||||
public static SymmetricSecurityKey GetSymmetricSecurityKey() =>
|
||||
new(Encoding.UTF8.GetBytes(KEY));
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5215",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:7226;http://localhost:5215",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.10.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UniversityAllExpelled_BusinessLogic\UniversityAllExpelled_BusinessLogic.csproj" />
|
||||
<ProjectReference Include="..\UniversityAllExpelled_DataBase\UniversityAllExpelled_DataBase.csproj" />
|
||||
<ProjectReference Include="..\UniversityAllExpelled_Models\UniversityAllExpelled_Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,6 @@
|
||||
@UniversityAllExpelled_WebApi_HostAddress = http://localhost:5215
|
||||
|
||||
GET {{UniversityAllExpelled_WebApi_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"DataBaseSettings": {
|
||||
"ConnectionString": "Host=localhost;Port=5432;Database=university;Username=postgres;password=daa200513dr"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/* Общие стили */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, #a2d4f1, #00c6d7);
|
||||
}
|
||||
|
||||
/* Основная структура */
|
||||
.dashboard-layout {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* Левая панель */
|
||||
.sidebar {
|
||||
width: 300px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-right: 2px solid #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 30px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.main-logo {
|
||||
max-width: 80%;
|
||||
height: auto;
|
||||
max-height: 120px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Контейнер кнопок */
|
||||
.nav-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
margin-bottom: 10px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border: 2px solid #00BFFF;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-button:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.35);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.nav-button i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* Правая часть */
|
||||
.student-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-left: 2px solid #fff;
|
||||
}
|
||||
|
||||
/* Контент секций */
|
||||
.content-section {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 30px 40px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
max-width: 700px;
|
||||
width: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Только профиль показываем при запуске */
|
||||
#profileSection {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.student-info h1 {
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
margin-bottom: 30px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.info-group {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.info-group p {
|
||||
margin: 12px 0;
|
||||
font-size: 1rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.info-group strong {
|
||||
color: #fff;
|
||||
min-width: 120px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.dashboard-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
flex: 1 1 120px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
padding: 20px;
|
||||
max-width: 100%;
|
||||
}
|
||||
@keyframes slideInUp {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutUp {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInDown {
|
||||
0% {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutDown {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.slide-in-up {
|
||||
animation: slideInUp 0.4s ease forwards;
|
||||
}
|
||||
|
||||
.slide-out-up {
|
||||
animation: slideOutUp 0.4s ease forwards;
|
||||
}
|
||||
|
||||
.slide-in-down {
|
||||
animation: slideInDown 0.4s ease forwards;
|
||||
}
|
||||
|
||||
.slide-out-down {
|
||||
animation: slideOutDown 0.4s ease forwards;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/* Общие базовые настройки */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
font-family: Arial, sans-serif;
|
||||
background: linear-gradient(to right, #a2d4f1, #00c6d7);
|
||||
}
|
||||
|
||||
/* Контейнер всей страницы с двумя колонками */
|
||||
.page-layout {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* Левая часть — логотип */
|
||||
.logo-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main-logo {
|
||||
max-width: 80%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Правая часть — форма авторизации/регистрации */
|
||||
.auth-container {
|
||||
width: 300px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-left: 2px solid #fff;
|
||||
padding: 50px 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 130vh;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Контейнер формы */
|
||||
.auth-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
min-height: 130vh;
|
||||
}
|
||||
|
||||
/* Заголовок формы */
|
||||
.auth-box h2 {
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
/* Форма — вертикальный стек элементов */
|
||||
.auth-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Универсальный стиль для всех input и select */
|
||||
.auth-input {
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
color: #000;
|
||||
box-sizing: border-box;
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.auth-input:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 5px 2px #00c6d7;
|
||||
}
|
||||
|
||||
/* Свитч роли — вертикальный стек радио */
|
||||
.role-switch {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.role-switch label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.role-switch input[type="radio"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Контейнер для дополнительных полей */
|
||||
.additional-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Подвал формы с кнопкой */
|
||||
.auth-footer {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Кнопка отправки */
|
||||
.login-btn {
|
||||
padding: 8px 20px;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border: 2px solid #00BFFF;
|
||||
color: #fff;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.login-btn:hover {
|
||||
background-color: #009ba0;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Сообщение об ошибке */
|
||||
.alert-danger {
|
||||
background-color: #f8d7da;
|
||||
border-color: #f5c6cb;
|
||||
color: #721c24;
|
||||
padding: 0.75rem 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.page-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
width: 100%;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.auth-box {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
body {
|
||||
text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/* Универсальные сбросы и базовая структура */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
font-family: Arial, sans-serif;
|
||||
background: linear-gradient(to right, #a2d4f1, #00c6d7);
|
||||
}
|
||||
|
||||
/* Кнопки (универсальные на всех страницах) */
|
||||
button,
|
||||
.login-btn,
|
||||
.nav-button {
|
||||
padding: 10px 16px;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Основной стиль навигационных кнопок */
|
||||
.nav-button {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border: 2px solid #00BFFF;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.nav-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.nav-button.active {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nav-button i {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Инпуты и поля */
|
||||
input,
|
||||
select,
|
||||
.auth-input {
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
color: #000;
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
.auth-input:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 5px 2px #00c6d7;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
flex: 1 1 150px;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/* Общие стили */
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, #a2d4f1, #00c6d7);
|
||||
}
|
||||
|
||||
/* Разметка */
|
||||
.payment-layout {
|
||||
display: flex;
|
||||
min-height: 100vh
|
||||
}
|
||||
|
||||
/* Боковая панель */
|
||||
.sidebar {
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
width: 150px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-right: 1px solid #00BFFF;
|
||||
display: flex;
|
||||
flex-direction: column; /* изменено: вертикальное расположение */
|
||||
align-items: center;
|
||||
padding: 20px 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Секция логотипа */
|
||||
.logo-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px; /* отступ от логотипа до кнопки */
|
||||
}
|
||||
|
||||
.main-logo {
|
||||
max-width: 100%;
|
||||
max-height: 60px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Кнопка "На Главную" */
|
||||
.back-button {
|
||||
padding: 10px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
color: #fff;
|
||||
border: 2px solid #003344;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* Контент */
|
||||
.payment-content {
|
||||
flex: 1;
|
||||
padding: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Заголовок */
|
||||
.section-title {
|
||||
font-size: 28px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
/* Информация по оплате */
|
||||
.payment-info-box {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 25px 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 25px;
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.payment-info-box p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* Элементы ввода */
|
||||
.payment-input {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Кнопка оплаты */
|
||||
.pay-button {
|
||||
padding: 12px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
color: #fff;
|
||||
border: 2px solid #003344;
|
||||
border-radius: 6px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.pay-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* Форма оплаты */
|
||||
.payment-form {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* Детали карты */
|
||||
#cardDetails {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* Номер карты — отдельной строкой */
|
||||
#cardNumber {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* MM/YY и CSV — в строку */
|
||||
.card-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
#cardExpiry,
|
||||
#cardCSV {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
@@ -0,0 +1,73 @@
|
||||
// education.js
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const studySection = document.getElementById("studySection");
|
||||
if (!studySection) return;
|
||||
|
||||
const educationList = document.getElementById("educationList");
|
||||
const createBtn = document.getElementById("createEducationBtn");
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
if (!userId) return;
|
||||
|
||||
async function loadStudentEducations() {
|
||||
educationList.innerHTML = "<div class='text-white'>Загрузка...</div>";
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/education/by-student/${userId}`);
|
||||
if (!res.ok) throw new Error("Ошибка запроса");
|
||||
|
||||
const educations = await res.json();
|
||||
educationList.innerHTML = "";
|
||||
|
||||
if (educations.length === 0) {
|
||||
educationList.innerHTML = "<div class='text-white'>У вас пока нет обучений.</div>";
|
||||
return;
|
||||
}
|
||||
|
||||
educations.forEach((edu, i) => {
|
||||
const item = document.createElement("div");
|
||||
item.className = "accordion-item";
|
||||
|
||||
item.innerHTML = `
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#edu-${i}">
|
||||
Обучение ${edu.id} | ${new Date(edu.startDate).toLocaleDateString()} — ${new Date(edu.endDate).toLocaleDateString()}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="edu-${i}" class="accordion-collapse collapse">
|
||||
<div class="accordion-body text-white" id="edu-lessons-${i}">
|
||||
Загрузка уроков...
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
educationList.appendChild(item);
|
||||
|
||||
// загружаем уроки при открытии панели
|
||||
const collapseEl = item.querySelector(`#edu-${i}`);
|
||||
collapseEl.addEventListener("show.bs.collapse", async () => {
|
||||
const lessonContainer = document.getElementById(`edu-lessons-${i}`);
|
||||
lessonContainer.textContent = "Загрузка...";
|
||||
const resp = await fetch(`/api/lesson/by-education/${edu.id}`);
|
||||
const lessons = await resp.json();
|
||||
if (lessons.length === 0) {
|
||||
lessonContainer.textContent = "Нет уроков.";
|
||||
return;
|
||||
}
|
||||
lessonContainer.innerHTML = lessons.map(l => `<div>${l.name} (${new Date(l.date).toLocaleDateString()})</div>`).join("");
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
educationList.innerHTML = `<div class='text-danger'>Ошибка загрузки обучений</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// загрузка при открытии секции "Обучение"
|
||||
document.querySelector('[data-section="study"]').addEventListener("click", loadStudentEducations);
|
||||
|
||||
// кнопка "Создать обучение"
|
||||
createBtn.addEventListener("click", () => {
|
||||
alert("Создание обучения пока не реализовано.");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const sectionBtn = document.querySelector('[data-section="teaching"]');
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
if (!sectionBtn || !userId) return;
|
||||
|
||||
sectionBtn.addEventListener("click", async () => {
|
||||
const list = document.getElementById("lessonList");
|
||||
list.innerHTML = "Загрузка...";
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/lesson/by-teacher/${userId}`);
|
||||
const lessons = await res.json();
|
||||
|
||||
list.innerHTML = lessons.map(l =>
|
||||
`<div class="accordion-item mb-2">
|
||||
<div class="accordion-header p-2 bg-white text-dark">
|
||||
Урок: ${new Date(l.date).toLocaleDateString()} — ${l.theme}
|
||||
</div>
|
||||
</div>`
|
||||
).join("");
|
||||
} catch {
|
||||
list.innerHTML = "Не удалось загрузить уроки.";
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("createLessonBtn").addEventListener("click", async () => {
|
||||
const theme = prompt("Введите тему урока:");
|
||||
const date = prompt("Введите дату (ГГГГ-ММ-ДД):");
|
||||
|
||||
if (!theme || !date) return;
|
||||
|
||||
const response = await fetch("/api/lesson/create", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ teacherId: userId, theme, date })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert("Урок создан");
|
||||
sectionBtn.click();
|
||||
} else {
|
||||
alert("Ошибка при создании урока");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const role = localStorage.getItem("userRole");
|
||||
|
||||
// Скрываем все секции кроме профиля
|
||||
document.querySelectorAll(".content-section").forEach(sec => sec.style.display = "none");
|
||||
const profile = document.getElementById("profileSection");
|
||||
if (profile) profile.style.display = "block";
|
||||
|
||||
// Подставляем данные в профиль
|
||||
document.getElementById("userFio").textContent = localStorage.getItem("userFio");
|
||||
document.getElementById("userEmail").textContent = localStorage.getItem("userEmail");
|
||||
document.getElementById("userPhone").textContent = localStorage.getItem("userPhone");
|
||||
document.getElementById("userRole").textContent = role === "student" ? "Студент" : "Преподаватель";
|
||||
|
||||
if (role === "student") {
|
||||
document.getElementById("studentCourse").textContent = localStorage.getItem("userCourse") ?? "—";
|
||||
} else if (role === "teacher") {
|
||||
const hireDate = new Date(localStorage.getItem("userDateHiring"));
|
||||
const now = new Date();
|
||||
let years = now.getFullYear() - hireDate.getFullYear();
|
||||
if (now.getMonth() < hireDate.getMonth() || (now.getMonth() === hireDate.getMonth() && now.getDate() < hireDate.getDate())) {
|
||||
years--;
|
||||
}
|
||||
document.getElementById("teacherExperience").textContent = `${years} лет`;
|
||||
}
|
||||
|
||||
// Навигация по секциям
|
||||
document.querySelectorAll(".nav-button").forEach(btn => {
|
||||
btn.addEventListener("click", () => {
|
||||
const sectionId = btn.dataset.section;
|
||||
document.querySelectorAll(".content-section").forEach(sec => sec.style.display = "none");
|
||||
const target = document.getElementById(sectionId + "Section");
|
||||
if (target) target.style.display = "block";
|
||||
document.querySelectorAll(".nav-button").forEach(b => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
});
|
||||
});
|
||||
|
||||
// Отображение блоков по ролям
|
||||
document.querySelectorAll("[data-role]").forEach(el => {
|
||||
el.style.display = el.dataset.role === role ? "" : "none";
|
||||
});
|
||||
|
||||
// Выход из аккаунта
|
||||
document.getElementById("logoutBtn").addEventListener("click", () => {
|
||||
localStorage.clear();
|
||||
window.location.href = "/login.html";
|
||||
});
|
||||
|
||||
// Выбор отчета
|
||||
const reportType = document.getElementById("reportType");
|
||||
const reportsBtn = document.querySelector('[data-section="reports"]');
|
||||
if (reportsBtn && reportType) {
|
||||
reportsBtn.addEventListener("click", () => {
|
||||
reportType.innerHTML = role === "student"
|
||||
? `<option value="education">По обучениям</option><option value="payment">По оплатам</option>`
|
||||
: `<option value="lesson">По дисциплинам</option><option value="salary">По заработку</option>`;
|
||||
});
|
||||
}
|
||||
|
||||
// Формирование отчета
|
||||
document.getElementById("generateReportBtn")?.addEventListener("click", async () => {
|
||||
const reportType = document.getElementById("reportType").value;
|
||||
const format = document.getElementById("formatSelect").value;
|
||||
const startDate = document.getElementById("startDate").value;
|
||||
const endDate = document.getElementById("endDate").value;
|
||||
const statusBox = document.getElementById("reportStatus");
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
statusBox.textContent = "Выберите даты начала и конца периода.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/report/generate", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ userId, role, type: reportType, format, startDate, endDate })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const blob = await response.blob();
|
||||
const fileName = `Отчет_${reportType}_${startDate}_${endDate}.${format}`;
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
statusBox.textContent = "Отчет успешно сформирован.";
|
||||
} else {
|
||||
statusBox.textContent = "Ошибка при формировании отчета.";
|
||||
}
|
||||
} catch (err) {
|
||||
statusBox.textContent = "Ошибка соединения: " + err.message;
|
||||
}
|
||||
});
|
||||
|
||||
// Отображение полей карты при выборе способа оплаты
|
||||
const methodSelect = document.getElementById("paymentMethod");
|
||||
const cardDetails = document.getElementById("cardDetails");
|
||||
if (methodSelect && cardDetails) {
|
||||
const updateCardVisibility = () => {
|
||||
cardDetails.style.display = methodSelect.value === "card" ? "block" : "none";
|
||||
};
|
||||
methodSelect.addEventListener("change", updateCardVisibility);
|
||||
updateCardVisibility(); // Инициализация при загрузке
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const form = document.getElementById("loginForm");
|
||||
const errorBox = document.getElementById("errorMessage");
|
||||
|
||||
form.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const login = document.getElementById("loginInput").value;
|
||||
const password = document.getElementById("passwordInput").value;
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/user/authorize", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ login, password })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const msg = await response.text();
|
||||
errorBox.classList.remove("d-none");
|
||||
errorBox.innerText = msg || "Ошибка входа";
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await response.json();
|
||||
|
||||
localStorage.setItem("userId", user.id);
|
||||
localStorage.setItem("userLogin", user.login);
|
||||
localStorage.setItem("userFio", user.fio);
|
||||
localStorage.setItem("userEmail", user.email);
|
||||
localStorage.setItem("userPhone", user.phoneNumber);
|
||||
localStorage.setItem("userRole", user.role);
|
||||
|
||||
if (user.course !== null && user.course !== undefined)
|
||||
localStorage.setItem("userCourse", user.course);
|
||||
|
||||
if (user.dateHiring !== null && user.dateHiring !== undefined)
|
||||
localStorage.setItem("userDateHiring", user.dateHiring);
|
||||
|
||||
window.location.href = "/lk.html";
|
||||
} catch (error) {
|
||||
errorBox.classList.remove("d-none");
|
||||
errorBox.innerText = "Ошибка подключения: " + error.message;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const sectionBtn = document.querySelector('[data-section="payments"]');
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
if (!sectionBtn || !userId) return;
|
||||
|
||||
sectionBtn.addEventListener("click", async () => {
|
||||
const eduSelect = document.getElementById("educationSelect");
|
||||
const paymentInfo = document.getElementById("paymentInfo");
|
||||
|
||||
eduSelect.innerHTML = "<option disabled selected>Загрузка...</option>";
|
||||
paymentInfo.innerHTML = "";
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/education/by-student/${userId}`);
|
||||
const educations = await res.json();
|
||||
|
||||
eduSelect.innerHTML = educations.map(e =>
|
||||
`<option value="${e.id}">Обучение ${e.id} (${new Date(e.startDate).toLocaleDateString()} — ${new Date(e.endDate).toLocaleDateString()})</option>`
|
||||
).join("");
|
||||
|
||||
eduSelect.onchange = async () => {
|
||||
const educationId = eduSelect.value;
|
||||
const r = await fetch(`/api/payment/status/${educationId}`);
|
||||
const { total, paid, due } = await r.json();
|
||||
|
||||
paymentInfo.innerHTML = `
|
||||
<p>Общая стоимость: <strong>${total} руб.</strong></p>
|
||||
<p>Уже оплачено: <strong>${paid} руб.</strong></p>
|
||||
<p>Осталось оплатить: <strong>${due} руб.</strong></p>
|
||||
`;
|
||||
};
|
||||
|
||||
eduSelect.dispatchEvent(new Event("change"));
|
||||
|
||||
document.getElementById("payBtn").onclick = async () => {
|
||||
const educationId = eduSelect.value;
|
||||
const amount = parseFloat(document.getElementById("paymentAmount").value);
|
||||
const method = document.getElementById("paymentMethod").value;
|
||||
|
||||
const response = await fetch(`/api/payment/make`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ studentId: userId, educationId, amount, method })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert("Оплата успешно проведена");
|
||||
eduSelect.dispatchEvent(new Event("change"));
|
||||
} else {
|
||||
alert("Ошибка оплаты");
|
||||
}
|
||||
};
|
||||
} catch {
|
||||
eduSelect.innerHTML = `<option disabled selected>Ошибка загрузки</option>`;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const form = document.getElementById("registerForm");
|
||||
const courseField = document.getElementById("studentFields");
|
||||
const teacherField = document.getElementById("teacherFields");
|
||||
const roleInputs = document.querySelectorAll("input[name='role']");
|
||||
|
||||
// Переключение видимости полей по роли
|
||||
function toggleRoleFields() {
|
||||
const role = document.querySelector("input[name='role']:checked").value;
|
||||
courseField.style.display = role === "student" ? "block" : "none";
|
||||
teacherField.style.display = role === "teacher" ? "block" : "none";
|
||||
}
|
||||
|
||||
roleInputs.forEach(input => {
|
||||
input.addEventListener("change", toggleRoleFields);
|
||||
});
|
||||
|
||||
toggleRoleFields(); // инициализация на старте
|
||||
|
||||
form.addEventListener("submit", async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const role = form.role.value;
|
||||
|
||||
const data = {
|
||||
login: form.login.value,
|
||||
password: form.password.value,
|
||||
email: form.email.value,
|
||||
phone: form.phone.value,
|
||||
fio: form.fio.value,
|
||||
birthDate: form.birthDate.value,
|
||||
role: role
|
||||
};
|
||||
|
||||
if (role === "student") {
|
||||
const course = parseInt(form.course.value);
|
||||
if (isNaN(course) || course < 1 || course > 6) {
|
||||
alert("Некорректное значение курса");
|
||||
return;
|
||||
}
|
||||
data.course = course;
|
||||
} else if (role === "teacher") {
|
||||
const hiringDate = form.dateHiring.value;
|
||||
if (!hiringDate) {
|
||||
alert("Введите дату приёма на работу");
|
||||
return;
|
||||
}
|
||||
data.dateHiring = hiringDate;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/user/register", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
alert("Ошибка регистрации: " + text);
|
||||
return;
|
||||
}
|
||||
|
||||
alert("Регистрация прошла успешно");
|
||||
window.location.href = "/login.html";
|
||||
} catch (error) {
|
||||
alert("Ошибка подключения: " + error.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const reportsButton = document.querySelector('[data-section="reports"]');
|
||||
const userRole = localStorage.getItem("userRole");
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
if (!reportsButton || !userRole || !userId) return;
|
||||
|
||||
reportsButton.addEventListener("click", () => {
|
||||
const reportTypeSelect = document.getElementById("reportType");
|
||||
|
||||
reportTypeSelect.innerHTML = userRole === "student"
|
||||
? `<option value="education">По обучениям</option><option value="payment">По оплатам</option>`
|
||||
: `<option value="education">По дисциплинам</option><option value="salary">По зарплате</option>`;
|
||||
});
|
||||
|
||||
document.getElementById("generateReportBtn").addEventListener("click", async () => {
|
||||
const reportType = document.getElementById("reportType").value;
|
||||
const format = document.getElementById("formatSelect").value;
|
||||
const startDate = document.getElementById("startDate").value;
|
||||
const endDate = document.getElementById("endDate").value;
|
||||
const statusBox = document.getElementById("reportStatus");
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
statusBox.textContent = "Выберите даты начала и конца периода.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/report/generate`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
userId,
|
||||
role: userRole,
|
||||
type: reportType,
|
||||
format,
|
||||
startDate,
|
||||
endDate
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const blob = await response.blob();
|
||||
const fileName = `Отчет_${reportType}_${startDate}_${endDate}.${format}`;
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
statusBox.textContent = "Отчет успешно сформирован.";
|
||||
} else {
|
||||
statusBox.textContent = "Ошибка при формировании отчета.";
|
||||
}
|
||||
} catch (err) {
|
||||
statusBox.textContent = "Ошибка соединения: " + err.message;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const salaryBtn = document.getElementById("calculateSalaryBtn");
|
||||
const resultBox = document.getElementById("salaryResult");
|
||||
const monthInput = document.getElementById("salaryMonth");
|
||||
const userId = localStorage.getItem("userId");
|
||||
|
||||
if (!salaryBtn || !monthInput || !userId) return;
|
||||
|
||||
salaryBtn.addEventListener("click", async () => {
|
||||
const month = monthInput.value;
|
||||
if (!month) {
|
||||
resultBox.textContent = "Выберите месяц.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/salary/by-month?teacherId=${userId}&month=${month}`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
resultBox.innerHTML = `<p>Зарплата за ${month}: <strong>${data.amount} руб.</strong></p>`;
|
||||
} else {
|
||||
resultBox.textContent = "Ошибка при расчете зарплаты.";
|
||||
}
|
||||
} catch {
|
||||
resultBox.textContent = "Сервер не отвечает.";
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Личный кабинет</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/css/lk.css" rel="stylesheet" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="dashboard-layout">
|
||||
<aside class="sidebar">
|
||||
<div class="logo-section">
|
||||
<img src="/image/logo.png" alt="Логотип" class="main-logo" />
|
||||
</div>
|
||||
<div class="nav-container">
|
||||
<button class="nav-button active" data-section="profile"><i class="bi bi-person"></i> Профиль</button>
|
||||
<button class="nav-button" data-section="study" data-role="student"><i class="bi bi-journal-bookmark"></i> Обучение</button>
|
||||
<button class="nav-button" data-section="payments" data-role="student"><i class="bi bi-credit-card"></i> Оплата</button>
|
||||
<button class="nav-button" data-section="teaching" data-role="teacher"><i class="bi bi-mortarboard"></i> Преподавание</button>
|
||||
<button class="nav-button" data-section="salary" data-role="teacher"><i class="bi bi-cash"></i> Моя зарплата</button>
|
||||
<button class="nav-button" data-section="reports"><i class="bi bi-graph-up"></i> Отчеты</button>
|
||||
<button class="nav-button" id="logoutBtn"><i class="bi bi-box-arrow-right"></i> Выход</button>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="student-info">
|
||||
<div id="profileSection" class="content-section">
|
||||
<h1>Профиль</h1>
|
||||
<div class="info-group">
|
||||
<p><strong>ФИО:</strong> <span id="userFio"></span></p>
|
||||
<p><strong>Email:</strong> <span id="userEmail"></span></p>
|
||||
<p><strong>Телефон:</strong> <span id="userPhone"></span></p>
|
||||
<p><strong>Роль:</strong> <span id="userRole"></span></p>
|
||||
<div data-role="student">
|
||||
<p><strong>Курс:</strong> <span id="studentCourse"></span></p>
|
||||
</div>
|
||||
<div data-role="teacher">
|
||||
<p><strong>Стаж:</strong> <span id="teacherExperience"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="studySection" class="content-section" data-role="student">
|
||||
<h2 style="color: #fff;">Мои обучения</h2>
|
||||
<div id="educationList" class="accordion mb-4"></div>
|
||||
<button id="createEducationBtn" class="btn btn-outline-light">Создать обучение</button>
|
||||
</div>
|
||||
|
||||
<div id="paymentsSection" class="content-section" data-role="student">
|
||||
<h2 style="color: #fff;">Оплата обучения</h2>
|
||||
<div class="mb-3">
|
||||
<label for="educationSelect" class="form-label text-white">Выберите обучение:</label>
|
||||
<select id="educationSelect" class="form-select mb-3"></select>
|
||||
</div>
|
||||
<div id="paymentInfo" class="text-white mb-3"></div>
|
||||
<div id="paymentForm">
|
||||
<label class="form-label text-white">Сумма оплаты:</label>
|
||||
<input type="number" id="paymentAmount" class="form-control mb-2" />
|
||||
<label class="form-label text-white">Способ оплаты:</label>
|
||||
<select id="paymentMethod" class="form-select mb-2">
|
||||
<option value="card">Банковская карта</option>
|
||||
<option value="sbp">СБП</option>
|
||||
</select>
|
||||
<div id="cardDetails" style="display: none;">
|
||||
<label class="form-label text-white">Номер карты:</label>
|
||||
<input type="text" id="cardNumber" class="form-control mb-2" placeholder="1234 5678 9012 3456" maxlength="19" />
|
||||
<label class="form-label text-white">Срок действия (MM/YY):</label>
|
||||
<input type="text" id="cardExpiry" class="form-control mb-2" placeholder="MM/YY" maxlength="5" />
|
||||
<label class="form-label text-white">CSV:</label>
|
||||
<input type="text" id="cardCsv" class="form-control mb-2" placeholder="123" maxlength="3" />
|
||||
</div>
|
||||
<button id="payBtn" class="btn btn-success">Оплатить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="teachingSection" class="content-section" data-role="teacher">
|
||||
<h2 style="color: #fff;">Мои занятия</h2>
|
||||
<div id="lessonList" class="accordion mb-4"></div>
|
||||
<button id="createLessonBtn" class="btn btn-outline-light">Создать урок</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="salarySection" class="content-section" data-role="teacher">
|
||||
<h2 style="color: #fff;">Моя зарплата</h2>
|
||||
<div class="mb-3">
|
||||
<label for="salaryMonth" class="form-label text-white">Выберите месяц:</label>
|
||||
<input type="month" id="salaryMonth" class="form-control mb-2" />
|
||||
<button id="calculateSalaryBtn" class="btn btn-outline-light">Рассчитать зарплату за месяц</button>
|
||||
</div>
|
||||
<div id="salaryResult" class="text-white mt-3"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="reportsSection" class="content-section">
|
||||
<h2 style="color: #fff;">Отчеты</h2>
|
||||
<div class="mb-3">
|
||||
<label for="reportType" class="form-label text-white">Тип отчета:</label>
|
||||
<select id="reportType" class="form-select"></select>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-wrap gap-2">
|
||||
<div>
|
||||
<label class="form-label text-white">С:</label>
|
||||
<input type="date" id="startDate" class="form-control" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label text-white">По:</label>
|
||||
<input type="date" id="endDate" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-white">Формат отчета:</label>
|
||||
<select id="formatSelect" class="form-select">
|
||||
<option value="doc">.doc</option>
|
||||
<option value="pdf">.pdf</option>
|
||||
<option value="xls">.xls</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button id="generateReportBtn" class="btn btn-outline-light">Сформировать</button>
|
||||
</div>
|
||||
<div id="reportStatus" class="text-white mt-3"></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script type="module" src="/js/lk.js"></script>
|
||||
<script type="module" src="/js/education.js"></script>
|
||||
<script type="module" src="/js/payment.js"></script>
|
||||
<script type="module" src="/js/report.js"></script>
|
||||
<script type="module" src="/js/lesson.js"></script>
|
||||
<script type="module" src="/js/salary.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>УГУ - Вход</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/css/login.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-layout">
|
||||
<div class="logo-section">
|
||||
<img src="/image/logo.png" alt="Логотип УГУ" class="main-logo" />
|
||||
</div>
|
||||
<div class="auth-container">
|
||||
<div class="auth-box">
|
||||
<h2>Вход</h2>
|
||||
<form id="loginForm" class="auth-form">
|
||||
<div id="errorMessage" class="alert alert-danger d-none"></div>
|
||||
<input type="text" id="loginInput" class="auth-input" placeholder="Логин" required />
|
||||
<input type="password" id="passwordInput" class="auth-input" placeholder="Пароль" required />
|
||||
<div class="auth-footer">
|
||||
<a href="/register.html" class="register-link">Регистрация</a>
|
||||
<button type="submit" class="btn btn-primary login-btn"style="margin-left: 30px;":>Войти</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/js/login.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Регистрация — УГУ</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/css/login.css" rel="stylesheet" />
|
||||
<style>
|
||||
body {
|
||||
text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: match-parent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-layout">
|
||||
<div class="logo-section">
|
||||
<img src="/image/logo.png" alt="Логотип" class="main-logo" />
|
||||
</div>
|
||||
<div class="auth-container">
|
||||
<div class="auth-box">
|
||||
<h2>Регистрация</h2>
|
||||
<form id="registerForm" class="auth-form" novalidate>
|
||||
<div class="role-switch">
|
||||
<label><input type="radio" name="role" value="student" checked /> Студент</label>
|
||||
<label><input type="radio" name="role" value="teacher" /> Преподаватель</label>
|
||||
</div>
|
||||
|
||||
<input type="text" name="fio" class="auth-input" placeholder="ФИО" required />
|
||||
<input type="text" name="login" class="auth-input" placeholder="Логин" required />
|
||||
<input type="email" name="email" class="auth-input" placeholder="Email" required />
|
||||
<input type="tel" name="phone" class="auth-input" placeholder="Телефон" required />
|
||||
<input type="date" name="birthDate" class="auth-input" required placeholder="Дата рождения" />
|
||||
<input type="password" name="password" class="auth-input" placeholder="Пароль" required />
|
||||
|
||||
<div id="studentFields" class="additional-fields">
|
||||
<input type="number" id="courseInput" name="course" class="auth-input"
|
||||
placeholder="Курс (1–6)" min="1" max="6" />
|
||||
</div>
|
||||
|
||||
<div id="teacherFields" class="additional-fields" style="display:none;">
|
||||
<input type="date" name="dateHiring" class="auth-input" placeholder="Дата приёма на работу" />
|
||||
</div>
|
||||
|
||||
<div class="auth-footer">
|
||||
<button type="submit" class="btn btn-primary login-btn">Зарегистрироваться</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/js/register.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user