initial commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
bin/
|
||||
obj/
|
||||
.vs/
|
||||
*.db*
|
||||
23
ApplianceRepair.csproj
Normal file
23
ApplianceRepair.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>9bafa5c2-cfce-4fd2-89f6-91b06916b038</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter" Version="9.0.12" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.12" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.12" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.12" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.12">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.12" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
8
ApplianceRepair.csproj.user
Normal file
8
ApplianceRepair.csproj.user
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_SelectedScaffolderID>BlazorCRUDScaffolder</_SelectedScaffolderID>
|
||||
<_SelectedScaffolderCategoryPath>root/Common/Blazor/RazorComponent</_SelectedScaffolderCategoryPath>
|
||||
<WebStackScaffolding_ControllerDialogWidth>650</WebStackScaffolding_ControllerDialogWidth>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
3
ApplianceRepair.slnx
Normal file
3
ApplianceRepair.slnx
Normal file
@@ -0,0 +1,3 @@
|
||||
<Solution>
|
||||
<Project Path="ApplianceRepair.csproj" />
|
||||
</Solution>
|
||||
19
Components/App.razor
Normal file
19
Components/App.razor
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="@Assets["app.css"]" />
|
||||
<link rel="stylesheet" href="@Assets["ApplianceRepair.styles.css"]" />
|
||||
<ImportMap />
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes />
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
9
Components/Layout/MainLayout.razor
Normal file
9
Components/Layout/MainLayout.razor
Normal file
@@ -0,0 +1,9 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@Body
|
||||
|
||||
<div id="blazor-error-ui" data-nosnippet>
|
||||
An unhandled error has occurred.
|
||||
<a href="." class="reload">Reload</a>
|
||||
<span class="dismiss">🗙</span>
|
||||
</div>
|
||||
20
Components/Layout/MainLayout.razor.css
Normal file
20
Components/Layout/MainLayout.razor.css
Normal file
@@ -0,0 +1,20 @@
|
||||
#blazor-error-ui {
|
||||
color-scheme: light only;
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
36
Components/Pages/Error.razor
Normal file
36
Components/Pages/Error.razor
Normal file
@@ -0,0 +1,36 @@
|
||||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() =>
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
48
Components/Pages/Home.Razor.cs
Normal file
48
Components/Pages/Home.Razor.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace ApplianceRepair.Components.Pages
|
||||
{
|
||||
public partial class Home(IMemoryCache cache, HomePageReader homePageReader, ContentCardReader contentCardReader)
|
||||
{
|
||||
private HomePageModel? Model;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (!cache.TryGetValue(nameof(HomePageModel), out Model))
|
||||
{
|
||||
Model = Defaults.DefaultHomePageContent;
|
||||
|
||||
var homePageRecord = await homePageReader.ReadLatestRecord();
|
||||
if (homePageRecord != null)
|
||||
{
|
||||
Model = new HomePageModel(homePageRecord);
|
||||
|
||||
var serviceCardRecords = await contentCardReader.ReadAllByPageAndGroup(nameof(Home), "Services");
|
||||
if (serviceCardRecords != null)
|
||||
{
|
||||
foreach (var record in serviceCardRecords)
|
||||
{
|
||||
Model.ServicesCards!.Add(new ContentCardModel(record));
|
||||
}
|
||||
}
|
||||
|
||||
var trustCardRecords = await contentCardReader.ReadAllByPageAndGroup(nameof(Home), "Trust");
|
||||
if (trustCardRecords != null)
|
||||
{
|
||||
foreach (var record in trustCardRecords)
|
||||
{
|
||||
Model.TrustCards!.Add(new ContentCardModel(record));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cacheOptions = new MemoryCacheEntryOptions()
|
||||
.SetAbsoluteExpiration(TimeSpan.FromHours(24))
|
||||
.SetSlidingExpiration(TimeSpan.FromHours(2));
|
||||
|
||||
cache.Set(nameof(HomePageModel), Model, cacheOptions);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
67
Components/Pages/Home.razor
Normal file
67
Components/Pages/Home.razor
Normal file
@@ -0,0 +1,67 @@
|
||||
@page "/"
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
||||
@if (Model == null)
|
||||
{
|
||||
<p>Loading...</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<div class="logo">@Model.BusinessName</div>
|
||||
<a href="@Model.PhoneNumberCallLink" class="nav-phone">📞 @Model.FormattedPhoneNumber</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<header class="hero">
|
||||
<div class="container">
|
||||
<h1>@Model.HeaderLine1 <br><span>@Model.HeaderLine2</span></h1>
|
||||
<p>@Model.HeaderText</p>
|
||||
<div class="cta-group">
|
||||
<a href="@Model.HeaderButton1Link" class="btn btn-primary">@Model.HeaderButton1Text</a>
|
||||
<a href="@Model.HeaderButton2Link" class="btn btn-secondary">@Model.HeaderButton2Text</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="services">
|
||||
<div class="container">
|
||||
<h2 class="section-title">@Model.SecondaryHeaderText</h2>
|
||||
<div class="service-grid">
|
||||
@if (Model.ServicesCards != null)
|
||||
{
|
||||
foreach (var serviceCard in Model.ServicesCards)
|
||||
{
|
||||
<div class="service-card">
|
||||
<h3>@serviceCard.Header</h3>
|
||||
<p>@serviceCard.Text</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="trust">
|
||||
<div class="container">
|
||||
@if (Model.TrustCards != null)
|
||||
{
|
||||
foreach (var trustCard in Model.TrustCards)
|
||||
{
|
||||
<div class="trust-item">
|
||||
<strong>@trustCard.Header</strong>
|
||||
<p>@trustCard.Text</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>@Model.CopyrightText</p>
|
||||
</footer>
|
||||
}
|
||||
|
||||
148
Components/Pages/Home.razor.css
Normal file
148
Components/Pages/Home.razor.css
Normal file
@@ -0,0 +1,148 @@
|
||||
|
||||
/* General Styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/* Navbar */
|
||||
.navbar {
|
||||
background: #fff;
|
||||
padding: 1rem 0;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.navbar .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.logo span {
|
||||
color: #e63946;
|
||||
}
|
||||
|
||||
.nav-phone {
|
||||
text-decoration: none;
|
||||
color: #0056b3;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
background: linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('https://images.unsplash.com/photo-1581092918056-0c4c3acd3789?auto=format&fit=crop&w=1200&q=80');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
color: #fff;
|
||||
padding: 100px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.hero h1 span {
|
||||
color: #4cc9f0;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
margin: 10px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #e63946;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Services */
|
||||
.services {
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.service-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
background: #fff;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
border-bottom: 4px solid #0056b3;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
/* Trust Section */
|
||||
.trust {
|
||||
background: #0056b3;
|
||||
color: #fff;
|
||||
padding: 40px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trust .container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.trust-item {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
font-size: 0.9rem;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
/* Mobile Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.hero h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
6
Components/Routes.razor
Normal file
6
Components/Routes.razor
Normal file
@@ -0,0 +1,6 @@
|
||||
<Router AppAssembly="typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
||||
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||
</Found>
|
||||
</Router>
|
||||
10
Components/_Imports.razor
Normal file
10
Components/_Imports.razor
Normal file
@@ -0,0 +1,10 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using ApplianceRepair
|
||||
@using ApplianceRepair.Components
|
||||
47
Data.cs
Normal file
47
Data.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace ApplianceRepair
|
||||
{
|
||||
public class HomePageRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
public string? HeaderLine1 { get; set; }
|
||||
public string? HeaderLine2 { get; set; }
|
||||
|
||||
public string? HeaderText { get; set; }
|
||||
|
||||
public string? HeaderButton1Text { get; set; }
|
||||
public string? HeaderButton2Text { get; set; }
|
||||
|
||||
public string? HeaderButton1Link { get; set; }
|
||||
public string? HeaderButton2Link { get; set; }
|
||||
|
||||
public string? SecondaryHeaderText { get; set; }
|
||||
|
||||
public string? CopyrightText { get; set; }
|
||||
}
|
||||
|
||||
public class ContentCardRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
public string? BelongsToPage { get; set; }
|
||||
public string? Group { get; set; }
|
||||
public string? Header { get; set; }
|
||||
public string? Text { get; set; }
|
||||
}
|
||||
|
||||
public class BusinessConfigRecord
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
public string? PhoneNumber { get; set; }
|
||||
public string? SupportEmail { get; set; }
|
||||
}
|
||||
}
|
||||
11
DatabaseContext.cs
Normal file
11
DatabaseContext.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ApplianceRepair
|
||||
{
|
||||
public class DatabaseContext(DbContextOptions<DatabaseContext> options) : DbContext(options)
|
||||
{
|
||||
public DbSet<HomePageRecord> HomePage { get; set; }
|
||||
public DbSet<ContentCardRecord> ContentCards { get; set; }
|
||||
public DbSet<BusinessConfigRecord> BusinessConfig { get; set; }
|
||||
}
|
||||
}
|
||||
40
Defaults.cs
Normal file
40
Defaults.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace ApplianceRepair
|
||||
{
|
||||
public static class Defaults
|
||||
{
|
||||
public static readonly HomePageModel DefaultHomePageContent = new()
|
||||
{
|
||||
BusinessName = "Appliance Pro",
|
||||
FormattedPhoneNumber = "(555) 012-3456",
|
||||
PhoneNumberCallLink = "tel:5550123456",
|
||||
|
||||
HeaderLine1 = "Expert Appliance Repair,",
|
||||
HeaderLine2 = "Done Right Today.",
|
||||
|
||||
HeaderText = "Fast, affordable repairs for all major brands. Serving the Greater Metro Area.",
|
||||
|
||||
HeaderButton1Text = "Call for Same-Day Service",
|
||||
HeaderButton1Link = "tel:5550123456",
|
||||
HeaderButton2Text = "Book Online",
|
||||
HeaderButton2Link = "#booking",
|
||||
|
||||
SecondaryHeaderText = "What We Fix",
|
||||
|
||||
ServicesCards =
|
||||
[
|
||||
new() { Header = "Refrigerators", Text = "Cooling issues, leaks, and compressor repairs." },
|
||||
new() { Header = "Washers & Dryers", Text = "Fixing drum issues, drainage, and heating elements." },
|
||||
new() { Header = "Ovens & Ranges", Text = "Electrical igniters, gas flow, and temperature control." }
|
||||
],
|
||||
|
||||
TrustCards =
|
||||
[
|
||||
new() { Header = "90-Day Warranty", Text = "Quality parts and labor guaranteed." },
|
||||
new() { Header = "Certified Techs", Text = "Licensed, bonded, and background-checked." },
|
||||
new() { Header = "Fair Pricing", Text = "No hidden fees or diagnostic surprises." }
|
||||
],
|
||||
|
||||
CopyrightText = $"© {DateTime.Now.Year} Appliance Pro. All rights reserved."
|
||||
};
|
||||
}
|
||||
}
|
||||
124
Migrations/20260202020516_InitialCreate.Designer.cs
generated
Normal file
124
Migrations/20260202020516_InitialCreate.Designer.cs
generated
Normal file
@@ -0,0 +1,124 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using ApplianceRepair;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ApplianceRepair.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20260202020516_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.12");
|
||||
|
||||
modelBuilder.Entity("ApplianceRepair.BusinessConfigRecord", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SupportEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BusinessConfig");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ApplianceRepair.ContentCardRecord", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("BelongsToPage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Group")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Header")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ContentCards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ApplianceRepair.HomePageRecord", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CopyrightText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderButton1Link")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderButton1Text")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderButton2Link")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderButton2Text")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderLine1")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderLine2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SecondaryHeaderText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("HomePage");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
86
Migrations/20260202020516_InitialCreate.cs
Normal file
86
Migrations/20260202020516_InitialCreate.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ApplianceRepair.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BusinessConfig",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||
PhoneNumber = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SupportEmail = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BusinessConfig", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ContentCards",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
BelongsToPage = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Group = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Header = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Text = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ContentCards", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "HomePage",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
HeaderLine1 = table.Column<string>(type: "TEXT", nullable: true),
|
||||
HeaderLine2 = table.Column<string>(type: "TEXT", nullable: true),
|
||||
HeaderText = table.Column<string>(type: "TEXT", nullable: true),
|
||||
HeaderButton1Text = table.Column<string>(type: "TEXT", nullable: true),
|
||||
HeaderButton2Text = table.Column<string>(type: "TEXT", nullable: true),
|
||||
HeaderButton1Link = table.Column<string>(type: "TEXT", nullable: true),
|
||||
HeaderButton2Link = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SecondaryHeaderText = table.Column<string>(type: "TEXT", nullable: true),
|
||||
CopyrightText = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_HomePage", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "BusinessConfig");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ContentCards");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "HomePage");
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Migrations/DatabaseContextModelSnapshot.cs
Normal file
121
Migrations/DatabaseContextModelSnapshot.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using ApplianceRepair;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ApplianceRepair.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
partial class DatabaseContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.12");
|
||||
|
||||
modelBuilder.Entity("ApplianceRepair.BusinessConfigRecord", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SupportEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BusinessConfig");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ApplianceRepair.ContentCardRecord", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("BelongsToPage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Group")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Header")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ContentCards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ApplianceRepair.HomePageRecord", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CopyrightText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderButton1Link")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderButton1Text")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderButton2Link")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderButton2Text")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderLine1")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderLine2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HeaderText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SecondaryHeaderText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("HomePage");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Models.cs
Normal file
76
Models.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
namespace ApplianceRepair
|
||||
{
|
||||
public class ContentCardModel : ContentCardRecord
|
||||
{
|
||||
public ContentCardModel()
|
||||
{
|
||||
BelongsToPage = string.Empty;
|
||||
Group = string.Empty;
|
||||
Header = string.Empty;
|
||||
Text = string.Empty;
|
||||
}
|
||||
|
||||
public ContentCardModel(ContentCardRecord record)
|
||||
{
|
||||
BelongsToPage = record.BelongsToPage;
|
||||
Group = record.Group;
|
||||
Header = record.Header;
|
||||
Text = record.Text;
|
||||
}
|
||||
}
|
||||
|
||||
public class HomePageModel : HomePageRecord
|
||||
{
|
||||
public string BusinessName { get; set; }
|
||||
public string FormattedPhoneNumber { get; set; }
|
||||
public string PhoneNumberCallLink { get; set; }
|
||||
|
||||
public List<ContentCardModel> ServicesCards { get; set; }
|
||||
public List<ContentCardModel> TrustCards { get; set; }
|
||||
|
||||
public HomePageModel()
|
||||
{
|
||||
HeaderLine1 = string.Empty;
|
||||
HeaderLine2 = string.Empty;
|
||||
HeaderText = string.Empty;
|
||||
HeaderButton1Text = string.Empty;
|
||||
HeaderButton1Link = string.Empty;
|
||||
HeaderButton2Text = string.Empty;
|
||||
HeaderButton2Link = string.Empty;
|
||||
SecondaryHeaderText = string.Empty;
|
||||
CopyrightText = string.Empty;
|
||||
|
||||
BusinessName = "Appliance Pro";
|
||||
FormattedPhoneNumber = "(555) 555-5555";
|
||||
PhoneNumberCallLink = $"tel:{FormattedPhoneNumber}";
|
||||
|
||||
ServicesCards = [];
|
||||
TrustCards = [];
|
||||
}
|
||||
|
||||
public HomePageModel(HomePageRecord record)
|
||||
{
|
||||
HeaderLine1 = record.HeaderLine1;
|
||||
HeaderLine2 = record.HeaderLine2;
|
||||
HeaderText = record.HeaderText;
|
||||
HeaderButton1Text = record.HeaderButton1Text;
|
||||
HeaderButton1Link = record.HeaderButton1Link;
|
||||
HeaderButton2Text = record.HeaderButton2Text;
|
||||
HeaderButton2Link = record.HeaderButton2Link;
|
||||
SecondaryHeaderText = record.SecondaryHeaderText;
|
||||
CopyrightText = record.CopyrightText;
|
||||
|
||||
BusinessName = "Appliance Pro";
|
||||
FormattedPhoneNumber = "(555) 555-5555";
|
||||
PhoneNumberCallLink = $"tel:{FormattedPhoneNumber}";
|
||||
|
||||
ServicesCards = [];
|
||||
TrustCards = [];
|
||||
}
|
||||
}
|
||||
|
||||
public class BusinessConfig : BusinessConfigRecord
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
50
Program.cs
Normal file
50
Program.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using ApplianceRepair;
|
||||
using ApplianceRepair.Components;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
|
||||
builder.Services.AddDbContext<DatabaseContext>(options =>
|
||||
options.UseSqlite("Data Source=site.db"));
|
||||
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.AddLogging();
|
||||
|
||||
builder.Services.AddScoped<BusinessConfigReader>();
|
||||
builder.Services.AddScoped<ContentCardReader>();
|
||||
builder.Services.AddScoped<HomePageReader>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
try
|
||||
{
|
||||
services.GetRequiredService<DatabaseContext>().Database.EnsureCreated();
|
||||
services.GetRequiredService<DatabaseContext>().Database
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||
logger.LogError(ex, "An error occurred creating the DB.");
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||
}
|
||||
|
||||
app.UseAntiforgery();
|
||||
|
||||
app.MapStaticAssets();
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
|
||||
app.Run();
|
||||
14
Properties/launchSettings.json
Normal file
14
Properties/launchSettings.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5037",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Properties/serviceDependencies.json
Normal file
7
Properties/serviceDependencies.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"secrets1": {
|
||||
"type": "secrets"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Properties/serviceDependencies.local.json
Normal file
7
Properties/serviceDependencies.local.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"secrets1": {
|
||||
"type": "secrets.user"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Properties/serviceDependencies.local.json.user
Normal file
9
Properties/serviceDependencies.local.json.user
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"secrets1": {
|
||||
"restored": true,
|
||||
"restoreTime": "2026-02-02T01:11:05.5247555Z"
|
||||
}
|
||||
},
|
||||
"parameters": {}
|
||||
}
|
||||
33
Services.cs
Normal file
33
Services.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ApplianceRepair
|
||||
{
|
||||
public class HomePageReader(DatabaseContext db)
|
||||
{
|
||||
public async Task<HomePageRecord?> ReadLatestRecord()
|
||||
{
|
||||
return await db.HomePage.OrderByDescending(page => page.Id).FirstAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public class ContentCardReader(DatabaseContext db)
|
||||
{
|
||||
public async Task<List<ContentCardRecord>?> ReadAllByPageAndGroup(string belongsToPage, string group)
|
||||
{
|
||||
return await db.ContentCards.Where(card => card.BelongsToPage == belongsToPage && card.Group == group).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ContentCardRecord>?> ReadAllByPage(string belongsToPage)
|
||||
{
|
||||
return await db.ContentCards.Where(card => card.BelongsToPage == belongsToPage).ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public class BusinessConfigReader(DatabaseContext db)
|
||||
{
|
||||
public async Task<BusinessConfigRecord?> ReadLatestRecord()
|
||||
{
|
||||
return await db.BusinessConfig.OrderByDescending(page => page.Id).FirstOrDefaultAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
8
appsettings.Development.json
Normal file
8
appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
appsettings.json
Normal file
9
appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
13
dotnet-tools.json
Normal file
13
dotnet-tools.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "10.0.2",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
||||
47
wwwroot/app.css
Normal file
47
wwwroot/app.css
Normal file
@@ -0,0 +1,47 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@600;700&family=Open+Sans:wght@400;600&display=swap');
|
||||
|
||||
h1:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.valid.modified:not([type=checkbox]) {
|
||||
outline: 1px solid #26b050;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
outline: 1px solid #e50000;
|
||||
}
|
||||
|
||||
.validation-message {
|
||||
color: #e50000;
|
||||
}
|
||||
|
||||
.blazor-error-boundary {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||
padding: 1rem 1rem 1rem 3.7rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.blazor-error-boundary::after {
|
||||
content: "An error has occurred."
|
||||
}
|
||||
|
||||
.darker-border-checkbox.form-check-input {
|
||||
border-color: #929292;
|
||||
}
|
||||
|
||||
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
|
||||
color: var(--bs-secondary-color);
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
Reference in New Issue
Block a user