Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"name": "myblog",
"private": true,
"scripts": {
"tw:build": "npx @tailwindcss/cli -i ./src/Web/Styles/input.css -o ./src/Web/wwwroot/css/tailwind.css --minify",
"tw:build": "npx @tailwindcss/cli -i ./src/Web/Styles/input.css -o ./src/Web/wwwroot/css/tailwind.css",
"tw:build:prod": "npx @tailwindcss/cli -i ./src/Web/Styles/input.css -o ./src/Web/wwwroot/css/tailwind.css --minify",
"tw:watch": "npx @tailwindcss/cli -i ./src/Web/Styles/input.css -o ./src/Web/wwwroot/css/tailwind.css --watch"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/Web/Components/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@inherits LayoutComponentBase


<div class="min-h-screen flex flex-col bg-primary-400 dark:bg-primary-800 text-primary-800 dark:text-primary-300">
<div class="min-h-screen flex flex-col bg-primary-100 dark:bg-primary-800 text-primary-900 dark:text-primary-100">
<NavMenu/>

<main id="main-content" tabindex="-1" class="mx-auto w-full max-w-7xl flex-1 px-4 pt-20 pb-8 outline-none focus:outline-none focus-visible:outline-none">
Expand Down
6 changes: 3 additions & 3 deletions src/Web/Components/Layout/NavMenu.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<header>
<nav class="fixed top-0 left-0 right-0 z-50
bg-primary-600 dark:bg-primary-600
border-b-2 border-primary-200 dark:border-primary-200" aria-label="Main navigation">
bg-primary-500 dark:bg-primary-900
border-b-2 border-primary-300 dark:border-primary-700" aria-label="Main navigation">
<div class="mx-auto max-w-7xl px-4">
<div class="flex items-center justify-between h-16">

Expand All @@ -11,7 +11,7 @@
@* Mobile hamburger (pure CSS peer toggle — no JS required) *@
<input type="checkbox" id="menu-toggle" class="peer sr-only" aria-hidden="true" />
<label for="menu-toggle"
class="md:hidden cursor-pointer p-2 rounded hover:bg-primary-500 dark:hover:bg-primary-600 text-white"
class="md:hidden cursor-pointer p-2 rounded hover:bg-primary-400 dark:hover:bg-primary-800 text-primary-900 dark:text-primary-500"
aria-label="Toggle navigation" role="button" tabindex="0">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
Expand Down
26 changes: 13 additions & 13 deletions src/Web/Features/BlogPosts/Create/Create.razor
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,33 @@

@if (_error is not null)
{
<div class="rounded-lg border border-red-300 bg-red-50 text-red-700 px-4 py-3 text-sm mb-4 flex items-center justify-between" role="alert">
<div class="alert-error" role="alert">
<span>@_error</span>
<button type="button" class="ml-4 cursor-pointer text-red-700 hover:text-red-900" @onclick="() => _error = null">&times;</button>
<button type="button" class="alert-dismiss" @onclick="() => _error = null">&times;</button>
</div>
}

<div class="rounded-lg shadow p-6 bg-white dark:bg-gray-800 max-w-2xl">
<div class="form-panel">
<EditForm Model="_model" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<ValidationSummary class="mb-4" />

<div class="mb-4">
<label class="block text-sm font-medium text-gray-900 dark:text-gray-50 mb-1">Title</label>
<InputText class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-50 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-400" @bind-Value="_model.Title" />
<div class="form-group">
<label class="form-label">Title</label>
<InputText class="form-input" @bind-Value="_model.Title" />
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-900 dark:text-gray-50 mb-1">Author</label>
<InputText class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-50 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-400" @bind-Value="_model.Author" />
<div class="form-group">
<label class="form-label">Author</label>
<InputText class="form-input" @bind-Value="_model.Author" />
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-900 dark:text-gray-50 mb-1">Content</label>
<InputTextArea class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-50 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-400" rows="8" @bind-Value="_model.Content" />
<div class="form-group">
<label class="form-label">Content</label>
<InputTextArea class="form-input" rows="8" @bind-Value="_model.Content" />
</div>

<div class="flex gap-2">
<button type="submit" class="btn-primary">Create</button>
<a href="/blog" class="px-4 py-2 rounded font-medium border border-gray-300 dark:border-gray-600 text-gray-900 dark:text-gray-50 hover:bg-gray-100 dark:hover:bg-gray-700 transition">Cancel</a>
<a href="/blog" class="btn-secondary">Cancel</a>
</div>
</EditForm>
</div>
Expand Down
24 changes: 12 additions & 12 deletions src/Web/Features/BlogPosts/Edit/Edit.razor
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@

@if (_error is not null)
{
<div class="rounded-lg border border-red-300 bg-red-50 text-red-700 px-4 py-3 text-sm mb-4 flex items-center justify-between" role="alert">
<div class="alert-error" role="alert">
<span>@_error</span>
<button type="button" class="ml-4 cursor-pointer text-red-700 hover:text-red-900" @onclick="() => _error = null">&times;</button>
<button type="button" class="alert-dismiss" @onclick="() => _error = null">&times;</button>
</div>
}

@if (_concurrencyError)
{
<div class="rounded-lg border border-yellow-300 bg-yellow-50 text-yellow-700 px-4 py-3 text-sm mb-4 flex items-center justify-between" role="alert">
<div class="alert-warning" role="alert">
<div>
<strong class="font-semibold">Concurrency Conflict:</strong> This post was modified by another user.
Please reload the page to get the latest version.
</div>
<button type="button" class="ml-4 cursor-pointer text-yellow-700 hover:text-yellow-900" @onclick="() => _concurrencyError = false">&times;</button>
<button type="button" class="alert-dismiss" @onclick="() => _concurrencyError = false">&times;</button>
</div>
}

Expand All @@ -34,23 +34,23 @@
}
else if (_model is not null)
{
<div class="rounded-lg shadow p-6 bg-white dark:bg-gray-800 max-w-2xl">
<div class="form-panel">
<EditForm Model="_model" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<ValidationSummary class="mb-4" />

<div class="mb-4">
<label class="block text-sm font-medium text-gray-900 dark:text-gray-50 mb-1">Title</label>
<InputText class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-50 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-400" @bind-Value="_model.Title" />
<div class="form-group">
<label class="form-label">Title</label>
<InputText class="form-input" @bind-Value="_model.Title" />
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-900 dark:text-gray-50 mb-1">Content</label>
<InputTextArea class="w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-50 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-400" rows="8" @bind-Value="_model.Content" />
<div class="form-group">
<label class="form-label">Content</label>
<InputTextArea class="form-input" rows="8" @bind-Value="_model.Content" />
</div>

<div class="flex gap-2">
<button type="submit" class="btn-primary">Save</button>
<a href="/blog" class="px-4 py-2 rounded font-medium border border-gray-300 dark:border-gray-600 text-gray-900 dark:text-gray-50 hover:bg-gray-100 dark:hover:bg-gray-700 transition">Cancel</a>
<a href="/blog" class="btn-secondary">Cancel</a>
</div>
</EditForm>
</div>
Expand Down
38 changes: 19 additions & 19 deletions src/Web/Features/BlogPosts/List/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@

@if (_error is not null)
{
<div class="rounded-lg border border-red-300 bg-red-50 text-red-700 px-4 py-3 text-sm mb-4 flex items-center justify-between" role="alert">
<div class="alert-error" role="alert">
<span>@_error</span>
<button type="button" class="ml-4 cursor-pointer text-red-700 hover:text-red-900" @onclick="() => _error = null">&times;</button>
<button type="button" class="alert-dismiss" @onclick="() => _error = null">&times;</button>
</div>
}

@if (_concurrencyError)
{
<div class="rounded-lg border border-yellow-300 bg-yellow-50 text-yellow-700 px-4 py-3 text-sm mb-4 flex items-center justify-between" role="alert">
<div class="alert-warning" role="alert">
<div>
<strong class="font-semibold">Concurrency Conflict:</strong> This post was modified by another user.
Please reload the page to get the latest version.
</div>
<button type="button" class="ml-4 cursor-pointer text-yellow-700 hover:text-yellow-900" @onclick="() => _concurrencyError = false">&times;</button>
<button type="button" class="alert-dismiss" @onclick="() => _concurrencyError = false">&times;</button>
</div>
}

Expand All @@ -44,33 +44,33 @@ else if (!_posts.Any())
}
else
{
<div class="rounded-lg shadow bg-white dark:bg-gray-800 overflow-hidden">
<table class="w-full text-sm text-left text-gray-900 dark:text-gray-50">
<thead class="bg-gray-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700">
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th class="px-4 py-3">Title</th>
<th class="px-4 py-3">Author</th>
<th class="px-4 py-3">Published</th>
<th class="px-4 py-3">Created</th>
<th>Title</th>
<th>Author</th>
<th>Published</th>
<th>Created</th>
<AuthorizeView Roles="Author,Admin">
<Authorized>
<th class="px-4 py-3"></th>
<th></th>
</Authorized>
</AuthorizeView>
</tr>
</thead>
<tbody>
@foreach (var post in _posts)
{
<tr class="odd:bg-white odd:dark:bg-gray-800 even:bg-gray-50 even:dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700 hover:bg-primary-50 dark:hover:bg-primary-950 transition">
<td class="px-4 py-3">@post.Title</td>
<td class="px-4 py-3">@post.Author</td>
<td class="px-4 py-3">@(post.IsPublished ? "Yes" : "No")</td>
<td class="px-4 py-3">@post.CreatedAt.ToShortDateString()</td>
<tr>
<td>@post.Title</td>
<td>@post.Author</td>
<td>@(post.IsPublished ? "Yes" : "No")</td>
<td>@post.CreatedAt.ToShortDateString()</td>
<AuthorizeView Roles="Author,Admin">
<Authorized>
<td class="px-4 py-3">
<a href="/blog/edit/@post.Id" class="inline-block px-3 py-1 text-sm rounded font-medium border border-gray-300 dark:border-gray-600 text-gray-900 dark:text-gray-50 hover:bg-gray-100 dark:hover:bg-gray-700 transition mr-1">Edit</a>
<td>
<a href="/blog/edit/@post.Id" class="btn-secondary inline-block mr-1">Edit</a>
<button class="inline-block px-3 py-1 text-sm rounded font-medium bg-red-600 text-white hover:bg-red-700 transition"
@onclick="() => ConfirmDelete(post)">Delete</button>
</td>
Expand Down
28 changes: 14 additions & 14 deletions src/Web/Features/UserManagement/ManageRoles.razor
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ else
{
@if (_error is not null)
{
<div class="rounded-lg border border-red-300 bg-red-50 text-red-700 px-4 py-3 text-sm mb-4 flex items-center justify-between" role="alert">
<div class="alert-error" role="alert">
<span>@_error</span>
<button type="button" class="ml-4 cursor-pointer text-red-700 hover:text-red-900" aria-label="Close error" @onclick="() => _error = null">&times;</button>
<button type="button" class="alert-dismiss" aria-label="Close error" @onclick="() => _error = null">&times;</button>
</div>
}

Expand All @@ -26,24 +26,24 @@ else
<p class="text-gray-600 dark:text-gray-400 mb-4">Available roles: @string.Join(", ", _availableRoles.Select(r => r.Name))</p>
}

<div class="rounded-lg shadow bg-white dark:bg-gray-800 overflow-hidden">
<table class="w-full text-sm text-left text-gray-900 dark:text-gray-50">
<thead class="bg-gray-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700">
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th class="px-4 py-3">User</th>
<th class="px-4 py-3">Email</th>
<th class="px-4 py-3">Current Roles</th>
<th class="px-4 py-3">Actions</th>
<th>User</th>
<th>Email</th>
<th>Current Roles</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in _users)
{
<tr class="odd:bg-white odd:dark:bg-gray-800 even:bg-gray-50 even:dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700 hover:bg-primary-50 dark:hover:bg-primary-950 transition">
<td class="px-4 py-3">@user.Name</td>
<td class="px-4 py-3">@user.Email</td>
<td class="px-4 py-3">@string.Join(", ", user.Roles)</td>
<td class="px-4 py-3">
<tr>
<td>@user.Name</td>
<td>@user.Email</td>
<td>@string.Join(", ", user.Roles)</td>
<td>
@foreach (var role in _availableRoles.Where(r => !user.Roles.Contains(r.Name)))
{
<button class="inline-block px-3 py-1 text-sm rounded border border-green-600 text-green-600 hover:bg-green-50 dark:hover:bg-green-900/20 transition mr-1 mb-1"
Expand Down
89 changes: 81 additions & 8 deletions src/Web/Styles/input.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,20 @@
@layer components {
/* ─── Nav Links ───────────────────────────────────────────────────────── */
.nav-link {
@apply text-primary-100 dark:text-primary-100
hover:text-primary-400 dark:hover:text-primary-400
@apply text-primary-900 dark:text-primary-500
hover:text-primary-700 dark:hover:text-primary-200
transition-colors;
}

.nav-link.active {
@apply font-bold border-b-2 border-white;
@apply font-bold border-b-2 border-primary-900 dark:border-primary-500;
}

/* ─── Footer ─────────────────────────────────────────────────────────── */
footer {
@apply border-t-2 border-primary-200 dark:border-primary-200
bg-primary-600 dark:bg-primary-600
text-primary-50 dark:text-primary-100
@apply border-t-2 border-primary-300 dark:border-primary-700
bg-primary-500 dark:bg-primary-900
text-primary-900 dark:text-primary-500
py-3 text-center text-sm font-medium shadow-lg;
}

Expand All @@ -85,13 +85,86 @@
rounded-lg shadow;
}

/* ─── Tables ──────────────────────────────────────────────────────────── */
.table-wrapper {
@apply rounded-lg shadow bg-white dark:bg-gray-800 overflow-hidden;
}

.data-table {
@apply w-full text-sm text-left text-gray-900 dark:text-gray-50;

Comment on lines +88 to +95
& thead {
@apply bg-primary-500 dark:bg-primary-900
text-primary-900 dark:text-primary-500
border-b border-primary-300 dark:border-primary-700;
}

& thead th {
@apply px-4 py-3;
}

& tbody tr {
@apply odd:bg-white odd:dark:bg-gray-800 even:bg-gray-50 even:dark:bg-gray-900
border-b border-gray-200 dark:border-gray-700
hover:bg-primary-100 dark:hover:bg-primary-800 transition;
}

& tbody td {
@apply px-4 py-3;
}
}

/* ─── Forms ───────────────────────────────────────────────────────────── */
.form-panel {
@apply rounded-lg shadow p-6 bg-white dark:bg-gray-800 max-w-2xl;
}

.form-group {
@apply mb-4;
}

.form-label {
@apply block text-sm font-medium text-gray-900 dark:text-gray-50 mb-1;
}

.form-input {
@apply w-full rounded border border-gray-300 dark:border-gray-600
bg-white dark:bg-gray-700
text-gray-900 dark:text-gray-50
px-3 py-2 text-sm
focus:outline-none focus:ring-2 focus:ring-primary-400;
}

/* ─── Secondary button (Cancel / neutral action) ──────────────────────── */
.btn-secondary {
@apply px-4 py-2 rounded font-medium
border border-gray-300 dark:border-gray-600
text-gray-900 dark:text-gray-50
hover:bg-gray-100 dark:hover:bg-gray-700 transition;
}

/* ─── Alert banners ───────────────────────────────────────────────────── */
.alert-error {
@apply rounded-lg border border-red-300 bg-red-50 text-red-700
px-4 py-3 text-sm mb-4 flex items-center justify-between;
}

.alert-warning {
@apply rounded-lg border border-yellow-300 bg-yellow-50 text-yellow-700
px-4 py-3 text-sm mb-4 flex items-center justify-between;
}

.alert-dismiss {
@apply ml-4 cursor-pointer hover:opacity-75;
}

/* ─── Blazor form validation ──────────────────────────────────────────── */
.valid.modified:not([type=checkbox]) {
@apply outline outline-1 outline-green-500;
@apply outline-1 outline-green-500;
}

.invalid {
@apply outline outline-1 outline-red-500;
@apply outline-1 outline-red-500;
}
Comment on lines 161 to 168

.validation-message {
Expand Down
Loading
Loading