Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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