Add grades
This commit is contained in:
parent
33d394a27b
commit
a64b866cfa
25 changed files with 362 additions and 207 deletions
|
|
@ -1,13 +1,16 @@
|
|||
<script lang="ts">
|
||||
import type { Authentication, DNSResults, ReportSummary } from "$lib/api/types.gen";
|
||||
import { getScoreColorClass } from "$lib/score";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
authentication: Authentication;
|
||||
authenticationGrade?: string;
|
||||
authenticationScore?: number;
|
||||
dnsResults?: DNSResults;
|
||||
}
|
||||
|
||||
let { authentication, authenticationScore, dnsResults }: Props = $props();
|
||||
let { authentication, authenticationGrade, authenticationScore, dnsResults }: Props = $props();
|
||||
|
||||
function getAuthResultClass(result: string): string {
|
||||
switch (result) {
|
||||
|
|
@ -57,11 +60,16 @@
|
|||
<i class="bi bi-shield-check me-2"></i>
|
||||
Authentication
|
||||
</span>
|
||||
{#if authenticationScore !== undefined}
|
||||
<span class="badge bg-secondary">
|
||||
{authenticationScore}%
|
||||
</span>
|
||||
{/if}
|
||||
<span>
|
||||
{#if authenticationScore !== undefined}
|
||||
<span class="badge bg-{getScoreColorClass(authenticationScore)}">
|
||||
{authenticationScore}%
|
||||
</span>
|
||||
{/if}
|
||||
{#if authenticationGrade !== undefined}
|
||||
<GradeDisplay grade={authenticationGrade} size="small" />
|
||||
{/if}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { RBLCheck } from "$lib/api/types.gen";
|
||||
import { getScoreColorClass } from "$lib/score";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
blacklists: Record<string, RBLCheck[]>;
|
||||
blacklistGrade?: string;
|
||||
blacklistScore?: number;
|
||||
}
|
||||
|
||||
let { blacklists, blacklistScore }: Props = $props();
|
||||
let { blacklists, blacklistGrade, blacklistScore }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
|
|
@ -16,11 +19,16 @@
|
|||
<i class="bi bi-shield-exclamation me-2"></i>
|
||||
Blacklist Checks
|
||||
</span>
|
||||
{#if blacklistScore !== undefined}
|
||||
<span class="badge bg-secondary">
|
||||
{blacklistScore}%
|
||||
</span>
|
||||
{/if}
|
||||
<span>
|
||||
{#if blacklistScore !== undefined}
|
||||
<span class="badge bg-{getScoreColorClass(blacklistScore)}">
|
||||
{blacklistScore}%
|
||||
</span>
|
||||
{/if}
|
||||
{#if blacklistGrade !== undefined}
|
||||
<GradeDisplay grade={blacklistGrade} size="small" />
|
||||
{/if}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { ContentAnalysis } from "$lib/api/types.gen";
|
||||
import { getScoreColorClass } from "$lib/score";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
contentAnalysis: ContentAnalysis;
|
||||
contentGrade?: string;
|
||||
contentScore?: number;
|
||||
}
|
||||
|
||||
let { contentAnalysis, contentScore }: Props = $props();
|
||||
let { contentAnalysis, contentGrade, contentScore }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
|
|
@ -16,11 +19,16 @@
|
|||
<i class="bi bi-file-text me-2"></i>
|
||||
Content Analysis
|
||||
</span>
|
||||
{#if contentScore !== undefined}
|
||||
<span class="badge bg-secondary">
|
||||
{contentScore}%
|
||||
</span>
|
||||
{/if}
|
||||
<span>
|
||||
{#if contentScore !== undefined}
|
||||
<span class="badge bg-{getScoreColorClass(contentScore)}">
|
||||
{contentScore}%
|
||||
</span>
|
||||
{/if}
|
||||
{#if contentGrade !== undefined}
|
||||
<GradeDisplay grade={contentGrade} size="small" />
|
||||
{/if}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { DNSResults } from "$lib/api/types.gen";
|
||||
import { getScoreColorClass } from "$lib/score";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
dnsResults?: DNSResults;
|
||||
dnsGrade?: string;
|
||||
dnsScore?: number;
|
||||
}
|
||||
|
||||
let { dnsResults, dnsScore }: Props = $props();
|
||||
let { dnsResults, dnsGrade, dnsScore }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
|
|
@ -16,11 +19,16 @@
|
|||
<i class="bi bi-diagram-3 me-2"></i>
|
||||
DNS Records
|
||||
</span>
|
||||
{#if dnsScore !== undefined}
|
||||
<span class="badge bg-secondary">
|
||||
{dnsScore}%
|
||||
</span>
|
||||
{/if}
|
||||
<span>
|
||||
{#if dnsScore !== undefined}
|
||||
<span class="badge bg-{getScoreColorClass(dnsScore)}">
|
||||
{dnsScore}%
|
||||
</span>
|
||||
{/if}
|
||||
{#if dnsGrade !== undefined}
|
||||
<GradeDisplay grade={dnsGrade} size="small" />
|
||||
{/if}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
|
|
|||
61
web/src/lib/components/GradeDisplay.svelte
Normal file
61
web/src/lib/components/GradeDisplay.svelte
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<script lang="ts">
|
||||
interface Props {
|
||||
grade?: string;
|
||||
score: number;
|
||||
size?: "small" | "medium" | "large";
|
||||
}
|
||||
|
||||
let { grade, score, size = "medium" }: Props = $props();
|
||||
|
||||
function getGradeColor(grade?: string): string {
|
||||
if (!grade) return "#6b7280"; // Gray for no grade
|
||||
|
||||
const baseLetter = grade.charAt(0).toUpperCase();
|
||||
const modifier = grade.length > 1 ? grade.charAt(1) : "";
|
||||
|
||||
// Gradient from green (A+) to red (F)
|
||||
switch (baseLetter) {
|
||||
case "A":
|
||||
if (modifier === "+") return "#22c55e"; // Bright green
|
||||
if (modifier === "-") return "#16a34a"; // Green
|
||||
return "#22c55e"; // Green
|
||||
case "B":
|
||||
if (modifier === "-") return "#65a30d"; // Darker lime
|
||||
return "#84cc16"; // Lime
|
||||
case "C":
|
||||
if (modifier === "-") return "#ca8a04"; // Darker yellow
|
||||
return "#eab308"; // Yellow
|
||||
case "D":
|
||||
return "#f97316"; // Orange
|
||||
case "E":
|
||||
return "#ea580c"; // Red
|
||||
case "F":
|
||||
return "#dc2626"; // Red
|
||||
default:
|
||||
return "#6b7280"; // Gray
|
||||
}
|
||||
}
|
||||
|
||||
function getSizeClass(size: "small" | "medium" | "large"): string {
|
||||
if (size === "small") return "fs-4";
|
||||
if (size === "large") return "display-1";
|
||||
return "fs-2";
|
||||
}
|
||||
</script>
|
||||
|
||||
<strong
|
||||
class={getSizeClass(size)}
|
||||
style="color: {getGradeColor(grade)}; font-weight: 700;"
|
||||
>
|
||||
{#if grade}
|
||||
{grade}
|
||||
{:else}
|
||||
{score}%
|
||||
{/if}
|
||||
</strong>
|
||||
|
||||
<style>
|
||||
strong {
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { HeaderAnalysis } from "$lib/api/types.gen";
|
||||
import { getScoreColorClass } from "$lib/score";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
headerAnalysis: HeaderAnalysis;
|
||||
headerGrade?: string;
|
||||
headerScore?: number;
|
||||
}
|
||||
|
||||
let { headerAnalysis, headerScore }: Props = $props();
|
||||
let { headerAnalysis, headerGrade, headerScore }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
|
|
@ -16,11 +19,16 @@
|
|||
<i class="bi bi-list-ul me-2"></i>
|
||||
Header Analysis
|
||||
</span>
|
||||
{#if headerScore !== undefined}
|
||||
<span class="badge bg-secondary">
|
||||
{headerScore}%
|
||||
</span>
|
||||
{/if}
|
||||
<span>
|
||||
{#if headerScore !== undefined}
|
||||
<span class="badge bg-{getScoreColorClass(headerScore)}">
|
||||
{headerScore}%
|
||||
</span>
|
||||
{/if}
|
||||
{#if headerGrade !== undefined}
|
||||
<GradeDisplay grade={headerGrade} size="small" />
|
||||
{/if}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import type { ScoreSummary } from "$lib/api/types.gen";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
grade: string;
|
||||
|
|
@ -9,14 +10,6 @@
|
|||
|
||||
let { grade, score, summary }: Props = $props();
|
||||
|
||||
function getScoreClass(score: number): string {
|
||||
if (score >= 90) return "score-excellent";
|
||||
if (score >= 70) return "score-good";
|
||||
if (score >= 50) return "score-warning";
|
||||
if (score >= 30) return "score-poor";
|
||||
return "score-bad";
|
||||
}
|
||||
|
||||
function getScoreLabel(score: number): string {
|
||||
if (score >= 90) return "Excellent";
|
||||
if (score >= 70) return "Good";
|
||||
|
|
@ -28,9 +21,9 @@
|
|||
|
||||
<div class="card shadow-lg bg-white">
|
||||
<div class="card-body p-5 text-center">
|
||||
<h1 class="display-1 fw-bold mb-3 {getScoreClass(score)}">
|
||||
{grade}
|
||||
</h1>
|
||||
<div class="mb-3">
|
||||
<GradeDisplay {grade} {score} size="large" />
|
||||
</div>
|
||||
<h3 class="fw-bold mb-2">{getScoreLabel(score)}</h3>
|
||||
<p class="text-muted mb-4">Overall Deliverability Score</p>
|
||||
|
||||
|
|
@ -38,84 +31,37 @@
|
|||
<div class="row g-3 text-start">
|
||||
<div class="col-md-6 col-lg">
|
||||
<div class="p-2 bg-light rounded text-center">
|
||||
<strong
|
||||
class="fs-2"
|
||||
class:text-success={summary.dns_score >= 100}
|
||||
class:text-warning={summary.dns_score < 100 &&
|
||||
summary.dns_score >= 50}
|
||||
class:text-danger={summary.dns_score < 50}
|
||||
>
|
||||
{summary.dns_score}%
|
||||
</strong>
|
||||
<GradeDisplay grade={summary.dns_grade} score={summary.dns_score} />
|
||||
<small class="text-muted d-block">DNS</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg">
|
||||
<div class="p-2 bg-light rounded text-center">
|
||||
<strong
|
||||
class="fs-2"
|
||||
class:text-success={summary.authentication_score >= 100}
|
||||
class:text-warning={summary.authentication_score < 100 &&
|
||||
summary.authentication_score >= 50}
|
||||
class:text-danger={summary.authentication_score < 50}
|
||||
>
|
||||
{summary.authentication_score}%
|
||||
</strong>
|
||||
<GradeDisplay grade={summary.authentication_grade} score={summary.authentication_score} />
|
||||
<small class="text-muted d-block">Authentication</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg">
|
||||
<div class="p-2 bg-light rounded text-center">
|
||||
<strong
|
||||
class="fs-2"
|
||||
class:text-success={summary.blacklist_score >= 100}
|
||||
class:text-warning={summary.blacklist_score < 100 &&
|
||||
summary.blacklist_score >= 50}
|
||||
class:text-danger={summary.blacklist_score < 50}
|
||||
>
|
||||
{summary.blacklist_score}%
|
||||
</strong>
|
||||
<GradeDisplay grade={summary.blacklist_grade} score={summary.blacklist_score} />
|
||||
<small class="text-muted d-block">Blacklists</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg">
|
||||
<div class="p-2 bg-light rounded text-center">
|
||||
<strong
|
||||
class="fs-2"
|
||||
class:text-success={summary.header_score >= 100}
|
||||
class:text-warning={summary.header_score < 100 &&
|
||||
summary.header_score >= 50}
|
||||
class:text-danger={summary.header_score < 50}
|
||||
>
|
||||
{summary.header_score}%
|
||||
</strong>
|
||||
<GradeDisplay grade={summary.header_grade} score={summary.header_score} />
|
||||
<small class="text-muted d-block">Headers</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg">
|
||||
<div class="p-2 bg-light rounded text-center">
|
||||
<strong
|
||||
class="fs-2"
|
||||
class:text-success={summary.spam_score >= 100}
|
||||
class:text-warning={summary.spam_score < 100 && summary.spam_score >= 50}
|
||||
class:text-danger={summary.spam_score < 50}
|
||||
>
|
||||
{summary.spam_score}%
|
||||
</strong>
|
||||
<GradeDisplay grade={summary.spam_grade} score={summary.spam_score} />
|
||||
<small class="text-muted d-block">Spam Score</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg">
|
||||
<div class="p-2 bg-light rounded text-center">
|
||||
<strong
|
||||
class="fs-2"
|
||||
class:text-success={summary.content_score >= 100}
|
||||
class:text-warning={summary.content_score < 100 &&
|
||||
summary.content_score >= 50}
|
||||
class:text-danger={summary.content_score < 50}
|
||||
>
|
||||
{summary.content_score}%
|
||||
</strong>
|
||||
<GradeDisplay grade={summary.content_grade} score={summary.content_score} />
|
||||
<small class="text-muted d-block">Content</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { SpamAssassinResult } from "$lib/api/types.gen";
|
||||
import { getScoreColorClass } from "$lib/score";
|
||||
import GradeDisplay from "./GradeDisplay.svelte";
|
||||
|
||||
interface Props {
|
||||
spamassassin: SpamAssassinResult;
|
||||
spamGrade: string;
|
||||
spamScore: number;
|
||||
}
|
||||
|
||||
let { spamassassin, spamScore }: Props = $props();
|
||||
let { spamassassin, spamGrade, spamScore }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
|
|
@ -16,11 +19,16 @@
|
|||
<i class="bi bi-bug me-2"></i>
|
||||
SpamAssassin Analysis
|
||||
</span>
|
||||
{#if spamScore !== undefined}
|
||||
<span class="badge bg-secondary">
|
||||
{spamScore}%
|
||||
</span>
|
||||
{/if}
|
||||
<span>
|
||||
{#if spamScore !== undefined}
|
||||
<span class="badge bg-{getScoreColorClass(spamScore)}">
|
||||
{spamScore}%
|
||||
</span>
|
||||
{/if}
|
||||
{#if spamGrade !== undefined}
|
||||
<GradeDisplay grade={spamGrade} size="small" />
|
||||
{/if}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
|
|
|||
5
web/src/lib/score.ts
Normal file
5
web/src/lib/score.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export function getScoreColorClass(percentage: number): string {
|
||||
if (percentage >= 85) return "success";
|
||||
if (percentage >= 50) return "warning";
|
||||
return "danger";
|
||||
}
|
||||
|
|
@ -81,12 +81,6 @@
|
|||
stopPolling();
|
||||
});
|
||||
|
||||
function getScoreColorClass(percentage: number): string {
|
||||
if (percentage >= 80) return "text-success";
|
||||
if (percentage >= 50) return "text-warning";
|
||||
return "text-danger";
|
||||
}
|
||||
|
||||
async function handleReanalyze() {
|
||||
if (!testId || reanalyzing) return;
|
||||
|
||||
|
|
@ -150,6 +144,7 @@
|
|||
<div class="col-12">
|
||||
<DnsRecordsCard
|
||||
dnsResults={report.dns_results}
|
||||
dnsGrade={report.summary?.dns_grade}
|
||||
dnsScore={report.summary?.dns_score}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -162,6 +157,7 @@
|
|||
<div class="col-12">
|
||||
<AuthenticationCard
|
||||
authentication={report.authentication}
|
||||
authenticationGrade={report.summary?.authentication_grade}
|
||||
authenticationScore={report.summary?.authentication_score}
|
||||
dnsResults={report.dns_results}
|
||||
/>
|
||||
|
|
@ -175,6 +171,7 @@
|
|||
<div class="col-12">
|
||||
<BlacklistCard
|
||||
blacklists={report.blacklists}
|
||||
blacklistGrade={report.summary?.blacklist_grade}
|
||||
blacklistScore={report.summary?.blacklist_score}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -187,6 +184,7 @@
|
|||
<div class="col-12">
|
||||
<HeaderAnalysisCard
|
||||
headerAnalysis={report.header_analysis}
|
||||
headerGrade={report.summary?.header_grade}
|
||||
headerScore={report.summary?.header_score}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -199,6 +197,7 @@
|
|||
<div class="col-12">
|
||||
<SpamAssassinCard
|
||||
spamassassin={report.spamassassin}
|
||||
spamGrade={report.summary?.spam_grade}
|
||||
spamScore={report.summary?.spam_score}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -211,6 +210,7 @@
|
|||
<div class="col-12">
|
||||
<ContentAnalysisCard
|
||||
contentAnalysis={report.content_analysis}
|
||||
contentGrade={report.summary?.content_grade}
|
||||
contentScore={report.summary?.content_score}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue