Files
SQLLint/SQLLinter/Infrastructure/Reporters/HtmlReportFormatter_v1.cs
FrigaT 0267d52d28
All checks were successful
CI / build-test (push) Successful in 38s
Release / pack-and-publish (release) Successful in 39s
Добавлена минификация для старого формата html и поправлены стили
2025-12-26 22:56:12 +03:00

149 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using SQLLinter.Common;
using SQLLinter.Infrastructure.Diagram;
using System.Text;
namespace SQLLinter.Infrastructure.Reporters;
public class HtmlReportFormatter_v1 : IReportFormatter
{
public string Format(List<IRuleViolation> violations)
=> Format(violations, null);
public string Format(List<IRuleViolation> violations, BpmnDiagram? diagram)
{
if (violations.Count == 0)
{
return "<p><em>Нет нарушений</em></p>";
}
var groupedByFile = violations
.GroupBy(v => v.FileName)
.OrderBy(g => g.Key);
var sb = new StringBuilder();
sb.AppendLine("<!DOCTYPE html>");
sb.AppendLine("<html lang=\"ru\">");
sb.AppendLine("<head>");
sb.AppendLine("<meta charset=\"UTF-8\">");
sb.AppendLine("<title>Отчёт по SQLпроверкам</title>");
sb.AppendLine("<style>");
sb.AppendLine("body { font-family: 'Segoe UI', Arial, sans-serif; background-color: #f5f5f5; margin: 0; padding: 0; color: #000; }");
sb.AppendLine("h1 { padding: 20px; margin: 0; background-color: #0078d4; color: white; }");
sb.AppendLine("h2 { margin-top: 20px; color: #333; }");
sb.AppendLine("h3 { margin-top: 15px; color: #555; }");
sb.AppendLine("table { border-collapse: collapse; width: 100%; margin: 20px 0; box-shadow: 0 2px 6px rgba(0,0,0,0.1); background-color: white; border-radius: 4px; overflow: hidden; }");
sb.AppendLine("th, td { border: 1px solid #e0e0e0; padding: 4px 8px; text-align: left; line-height: 1.2; }");
sb.AppendLine("th { background-color: #fafafa; font-weight: 600; }");
sb.AppendLine("td.line, td.column { width: 60px; text-align: center; }");
sb.AppendLine("td.rule { width: 300px; text-align: left; }");
sb.AppendLine("td.index { width: 40px; text-align: center; }");
sb.AppendLine(".critical { border-left: 4px solid #d13438; padding: 10px; margin-bottom: 20px; margin-top: 20px; background-color: #fde7e9; }");
sb.AppendLine(".warning { border-left: 4px solid #ffaa44; padding: 10px; margin-bottom: 20px; background-color: #fff4ce; }");
sb.AppendLine(".info { border-left: 4px solid #0078d4; padding: 10px; margin-bottom: 20px; background-color: #deecf9; }");
sb.AppendLine(".tabs { position: fixed; bottom: 0; left: 0; right: 0; background-color: #ffffff; border-top: 1px solid #ccc; padding: 10px; display: flex; overflow-x: auto; scrollbar-width: thin; justify-content: flex-start; box-shadow: 0 -2px 6px rgba(0,0,0,0.1); }");
sb.AppendLine(".tab { margin-right: 10px; padding: 8px 16px; border-radius: 4px; background-color: #f3f2f1; cursor: pointer; transition: background-color 0.2s; }");
sb.AppendLine(".tab:hover { background-color: #e1dfdd; }");
sb.AppendLine(".tab.active { background-color: #0078d4; color: white; }");
sb.AppendLine(".file-report { display: none; padding: 0 0; }");
sb.AppendLine(".file-report.active { display: block; }");
// Тёмная тема
sb.AppendLine("@media (prefers-color-scheme: dark) {");
sb.AppendLine(" body { background-color: #1e1e1e; color: #ddd; }");
sb.AppendLine(" h1 { background-color: #005a9e; }");
sb.AppendLine(" h2, h3 { color: #ddd; }");
sb.AppendLine(" table { background-color: #2d2d2d; box-shadow: none; }");
sb.AppendLine(" th { background-color: #3c3c3c; color: #fff; }");
sb.AppendLine(" td { border: 1px solid #444; }");
sb.AppendLine(" .tabs { background-color: #2d2d2d; border-top: 1px solid #444; }");
sb.AppendLine(" .tab { background-color: #3c3c3c; color: #ddd; }");
sb.AppendLine(" .tab:hover { background-color: #555; }");
sb.AppendLine(" .tab.active { background-color: #0078d4; color: white; }");
sb.AppendLine(" .critical { background-color: #4d1f1f; border-left-color: #d13438; }");
sb.AppendLine(" .warning { background-color: #4d3b1f; border-left-color: #ffaa44; }");
sb.AppendLine(" .info { background-color: #1f3b4d; border-left-color: #0078d4; }");
sb.AppendLine("}");
sb.AppendLine("</style>");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
//sb.AppendLine("<h1>Отчёт по SQLпроверкам</h1>");
int fileIndex = 0;
foreach (var fileGroup in groupedByFile)
{
string divId = $"file_{fileIndex}";
sb.AppendLine($"<div id=\"{divId}\" class=\"file-report{(fileIndex == 0 ? " active" : "")}\">");
sb.AppendLine($"<h1>Файл: {fileGroup.Key}</h1>");
var groupedBySeverity = fileGroup
.GroupBy(v => v.Severity)
.OrderByDescending(g => g.Key);
foreach (var severityGroup in groupedBySeverity)
{
string severityClass = severityGroup.Key switch
{
RuleViolationSeverity.Critical => "critical",
RuleViolationSeverity.Warning => "warning",
RuleViolationSeverity.Info => "info",
_ => ""
};
sb.AppendLine($"<div class=\"{severityClass}\">");
sb.AppendLine($"<h3>{severityGroup.Key}</h3>");
sb.AppendLine("<table>");
sb.AppendLine("<thead>");
sb.AppendLine("<tr><th>#</th><th>Строка</th><th>Колонка</th><th>Правило</th><th>Описание</th></tr>");
sb.AppendLine("</thead>");
sb.AppendLine("<tbody>");
int rowIndex = 1;
foreach (var v in severityGroup
.OrderBy(x => x.Line)
.ThenBy(x => x.Column))
{
sb.AppendLine($"<tr><td class=\"index\">{rowIndex}</td><td class=\"line\">{v.Line}</td><td class=\"column\">{v.Column}</td><td class=\"rule\">{v.RuleName}</td><td>{v.Text}</td></tr>");
rowIndex++;
}
sb.AppendLine("</tbody>");
sb.AppendLine("</table>");
sb.AppendLine("</div>");
}
sb.AppendLine("</div>");
fileIndex++;
}
// Табы снизу
sb.AppendLine("<div class=\"tabs\">");
fileIndex = 0;
foreach (var fileGroup in groupedByFile)
{
sb.AppendLine($"<div class=\"tab{(fileIndex == 0 ? " active" : "")}\" onclick=\"showReport('file_{fileIndex}', this)\">{fileGroup.Key}</div>");
fileIndex++;
}
sb.AppendLine("</div>");
// JS для переключения
sb.AppendLine("<script>");
sb.AppendLine("function showReport(id, tab) {");
sb.AppendLine(" document.querySelectorAll('.file-report').forEach(el => el.classList.remove('active'));");
sb.AppendLine(" document.getElementById(id).classList.add('active');");
sb.AppendLine(" document.querySelectorAll('.tab').forEach(el => el.classList.remove('active'));");
sb.AppendLine(" tab.classList.add('active');");
sb.AppendLine(" window.scrollTo({ top: 0, behavior: 'smooth' });");
sb.AppendLine("}");
sb.AppendLine("document.querySelector('.tabs').addEventListener('wheel', function(e) {");
sb.AppendLine(" e.preventDefault();");
sb.AppendLine(" this.scrollLeft += e.deltaY;");
sb.AppendLine("});");
sb.AppendLine("</script>");
sb.AppendLine("</body>");
sb.AppendLine("</html>");
return HtmlMinifier.MinifyHtml(sb.ToString());
}
}