Layout user and admin en repository updated
c8f5c6bb5fa60120bd525b68544333df6d343fa1
18 files changed
src/GitServer/Services/RepositoryService.cssrc/GitServer/gitserver.db-shmsrc/GitServer/gitserver.db-walsrc/GitServer/wwwroot/Admin/BlockedEmails.cshtmlsrc/GitServer/wwwroot/Admin/Users.cshtmlsrc/GitServer/wwwroot/Repo/Collaborators.cshtmlsrc/GitServer/wwwroot/Repo/Issues/New.cshtmlsrc/GitServer/wwwroot/Repo/New.cshtmlsrc/GitServer/wwwroot/Repo/New.cshtml.cssrc/GitServer/wwwroot/Repo/Settings.cshtmlsrc/GitServer/wwwroot/Shared/_AdminNav.cshtmlsrc/GitServer/wwwroot/Shared/_Layout.cshtmlsrc/GitServer/wwwroot/Shared/_ProfileHeader.cshtmlsrc/GitServer/wwwroot/Shared/_UserNav.cshtmlsrc/GitServer/wwwroot/User/Profile.cshtmlsrc/GitServer/wwwroot/User/Profile.cshtml.cssrc/GitServer/wwwroot/User/Settings.cshtmlsrc/GitServer/wwwroot/User/Settings.cshtml.cs
diff --git a/src/GitServer/Services/RepositoryService.cs b/src/GitServer/Services/RepositoryService.cs
index 092c592..5311613 100644
--- a/src/GitServer/Services/RepositoryService.cs
+++ b/src/GitServer/Services/RepositoryService.cs
@@ -83,7 +83,7 @@ public class RepositoryService(AppDbContext db,
.ToListAsync();
}
- public async Task<List<Repository>> GetUserReposAsync(string userId, bool includePrivate)
+ public async Task<List<Repository>> GetUserReposAsync(string userId, bool includePrivate, string? query = null)
{
var q = _db.Repositories
.Include(r => r.Owner)
@@ -92,6 +92,14 @@ public class RepositoryService(AppDbContext db,
if (!includePrivate)
q = q.Where(r => !r.IsPrivate);
+ if (!string.IsNullOrWhiteSpace(query))
+ {
+ var lower = query.Trim().ToLower();
+ q = q.Where(r =>
+ r.Name.ToLower().Contains(lower) ||
+ (r.Description != null && r.Description.ToLower().Contains(lower)));
+ }
+
return await q.OrderByDescending(r => r.UpdatedAt).ToListAsync();
}
diff --git a/src/GitServer/gitserver.db-shm b/src/GitServer/gitserver.db-shm
index 8743016..7ee0cb0 100644
Binary files a/src/GitServer/gitserver.db-shm and b/src/GitServer/gitserver.db-shm differ
diff --git a/src/GitServer/gitserver.db-wal b/src/GitServer/gitserver.db-wal
index 4dd27c0..dff709b 100644
Binary files a/src/GitServer/gitserver.db-wal and b/src/GitServer/gitserver.db-wal differ
diff --git a/src/GitServer/wwwroot/Admin/BlockedEmails.cshtml b/src/GitServer/wwwroot/Admin/BlockedEmails.cshtml
index c2a8782..bb6d44e 100644
--- a/src/GitServer/wwwroot/Admin/BlockedEmails.cshtml
+++ b/src/GitServer/wwwroot/Admin/BlockedEmails.cshtml
@@ -4,7 +4,11 @@
ViewData["Title"] = L["admin_blocked_emails_title"];
}
<div class="container">
- <h1>@L["admin_blocked_emails_title"]</h1>
+ <h1>@L["nav_admin"]</h1>
+
+ @await Html.PartialAsync("_AdminNav", "blocked-emails")
+
+ <h2>@L["admin_blocked_emails_title"]</h2>
<p class="text-muted">@L["admin_blocked_emails_hint"]</p>
@if (!string.IsNullOrEmpty(Model.Message))
diff --git a/src/GitServer/wwwroot/Admin/Users.cshtml b/src/GitServer/wwwroot/Admin/Users.cshtml
index 4447e59..befe6f3 100644
--- a/src/GitServer/wwwroot/Admin/Users.cshtml
+++ b/src/GitServer/wwwroot/Admin/Users.cshtml
@@ -4,7 +4,9 @@
ViewData["Title"] = L["admin_users_title"];
}
<div class="container">
- <h1>@L["admin_users_title"]</h1>
+ <h1>@L["nav_admin"]</h1>
+
+ @await Html.PartialAsync("_AdminNav", "users")
@if (!string.IsNullOrEmpty(Model.Message))
{
diff --git a/src/GitServer/wwwroot/Repo/Collaborators.cshtml b/src/GitServer/wwwroot/Repo/Collaborators.cshtml
index 6ec8a4e..35b3ce0 100644
--- a/src/GitServer/wwwroot/Repo/Collaborators.cshtml
+++ b/src/GitServer/wwwroot/Repo/Collaborators.cshtml
@@ -3,7 +3,7 @@
@{
ViewData["Title"] = $"{L["settings_collaborators"]} - {Model.UserName}/{Model.RepoName}";
}
-<div class="container container-sm">
+<div class="container">
<div class="repo-header">
<div class="repo-title">
<a href="/User/@Model.UserName">@Model.UserName</a> /
diff --git a/src/GitServer/wwwroot/Repo/Issues/New.cshtml b/src/GitServer/wwwroot/Repo/Issues/New.cshtml
index 0eced34..ee5890a 100644
--- a/src/GitServer/wwwroot/Repo/Issues/New.cshtml
+++ b/src/GitServer/wwwroot/Repo/Issues/New.cshtml
@@ -3,7 +3,7 @@
@{
ViewData["Title"] = $"{L["issues_new"]} — {Model.UserName}/{Model.RepoName}";
}
-<div class="container container-sm">
+<div class="container">
<div class="repo-header">
<div class="repo-title">
<a href="/User/@Model.UserName">@Model.UserName</a> /
diff --git a/src/GitServer/wwwroot/Repo/New.cshtml b/src/GitServer/wwwroot/Repo/New.cshtml
index 1815366..fe36b3a 100644
--- a/src/GitServer/wwwroot/Repo/New.cshtml
+++ b/src/GitServer/wwwroot/Repo/New.cshtml
@@ -3,7 +3,14 @@
@{
ViewData["Title"] = L["new_repo_page_title"];
}
-<div class="container container-sm">
+<div class="container">
+ @if (Model.CurrentUser != null)
+ {
+ @await Html.PartialAsync("_ProfileHeader", Model.CurrentUser)
+
+ @await Html.PartialAsync("_UserNav", (Model.CurrentUser.UserName!, "new", true))
+ }
+
<h1>@L["new_repo_title"]</h1>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
diff --git a/src/GitServer/wwwroot/Repo/New.cshtml.cs b/src/GitServer/wwwroot/Repo/New.cshtml.cs
index 8dcd469..b4b7dc5 100644
--- a/src/GitServer/wwwroot/Repo/New.cshtml.cs
+++ b/src/GitServer/wwwroot/Repo/New.cshtml.cs
@@ -19,20 +19,25 @@ public class NewModel(
[BindProperty] public string? Description { get; set; }
[BindProperty] public bool IsPrivate { get; set; }
public string? ErrorMessage { get; set; }
+ public AppUser? CurrentUser { get; set; }
- public void OnGet() { }
+ public async Task OnGetAsync()
+ {
+ CurrentUser = await userManager.GetUserAsync(User);
+ }
public async Task<IActionResult> OnPostAsync()
{
+ var user = await userManager.GetUserAsync(User);
+ if (user == null) return Challenge();
+ CurrentUser = user;
+
if (!Regex.IsMatch(Name, @"^[a-zA-Z0-9_\-\.]+$"))
{
ErrorMessage = L["error_invalid_repo_name"];
return Page();
}
- var user = await userManager.GetUserAsync(User);
- if (user == null) return Challenge();
-
try
{
var repo = await repos.CreateAsync(user.Id, user.UserName!, Name, Description, IsPrivate);
diff --git a/src/GitServer/wwwroot/Repo/Settings.cshtml b/src/GitServer/wwwroot/Repo/Settings.cshtml
index 7f78bd0..88a457c 100644
--- a/src/GitServer/wwwroot/Repo/Settings.cshtml
+++ b/src/GitServer/wwwroot/Repo/Settings.cshtml
@@ -3,7 +3,7 @@
@{
ViewData["Title"] = $"{L["repo_nav_settings"]} - {Model.UserName}/{Model.RepoName}";
}
-<div class="container container-sm">
+<div class="container">
<div class="repo-header">
<div class="repo-title">
<a href="/User/@Model.UserName">@Model.UserName</a> /
diff --git a/src/GitServer/wwwroot/Shared/_AdminNav.cshtml b/src/GitServer/wwwroot/Shared/_AdminNav.cshtml
new file mode 100644
index 0000000..b9be655
--- /dev/null
+++ b/src/GitServer/wwwroot/Shared/_AdminNav.cshtml
@@ -0,0 +1,8 @@
+@model string
+@inject GitServer.Services.LocalizationService L
+<div class="repo-nav">
+ <a class="repo-nav-link @(Model == "users" ? "active" : "")"
+ href="/Admin/Users">@L["admin_users_title"]</a>
+ <a class="repo-nav-link @(Model == "blocked-emails" ? "active" : "")"
+ href="/Admin/BlockedEmails">@L["nav_admin_blocked_emails"]</a>
+</div>
diff --git a/src/GitServer/wwwroot/Shared/_Layout.cshtml b/src/GitServer/wwwroot/Shared/_Layout.cshtml
index 1f8fb88..c8c5441 100644
--- a/src/GitServer/wwwroot/Shared/_Layout.cshtml
+++ b/src/GitServer/wwwroot/Shared/_Layout.cshtml
@@ -36,12 +36,10 @@
{
var currentUser = await UserManager.GetUserAsync(User);
<a class="nav-link" href="/User/@currentUser!.UserName">@currentUser!.UserName</a>
- <a class="nav-link" href="/Repo/New">@L["nav_new"]</a>
<a class="nav-link" href="/explore">@L["nav_explore"]</a>
@if (currentUser!.IsAdmin)
{
<a class="nav-link" href="/Admin/Users">@L["nav_admin"]</a>
- <a class="nav-link" href="/Admin/BlockedEmails">@L["nav_admin_blocked_emails"]</a>
}
<form method="post" action="/Auth/Logout" style="display:inline">
@Html.AntiForgeryToken()
diff --git a/src/GitServer/wwwroot/Shared/_ProfileHeader.cshtml b/src/GitServer/wwwroot/Shared/_ProfileHeader.cshtml
new file mode 100644
index 0000000..0f1a005
--- /dev/null
+++ b/src/GitServer/wwwroot/Shared/_ProfileHeader.cshtml
@@ -0,0 +1,16 @@
+@model GitServer.Models.AppUser
+@inject GitServer.Services.LocalizationService L
+<div class="profile-header">
+ <div class="profile-avatar">
+ <img src="@Model.EffectiveAvatarUrl" alt="@Model.UserName" class="avatar-img" />
+ </div>
+ <div class="profile-info">
+ <h1>@Model.DisplayName</h1>
+ <p class="text-muted">@@@Model.UserName</p>
+ @if (!string.IsNullOrEmpty(Model.Bio))
+ {
+ <p>@Model.Bio</p>
+ }
+ <p class="text-muted">@L["profile_member_since"] @Model.CreatedAt.ToString("MMMM yyyy")</p>
+ </div>
+</div>
diff --git a/src/GitServer/wwwroot/Shared/_UserNav.cshtml b/src/GitServer/wwwroot/Shared/_UserNav.cshtml
new file mode 100644
index 0000000..715335b
--- /dev/null
+++ b/src/GitServer/wwwroot/Shared/_UserNav.cshtml
@@ -0,0 +1,13 @@
+@model (string UserName, string ActiveTab, bool IsOwner)
+@inject GitServer.Services.LocalizationService L
+<div class="repo-nav">
+ <a class="repo-nav-link @(Model.ActiveTab == "repositories" ? "active" : "")"
+ href="/User/@Model.UserName">@L["profile_repos"]</a>
+ @if (Model.IsOwner)
+ {
+ <a class="repo-nav-link @(Model.ActiveTab == "new" ? "active" : "")"
+ href="/Repo/New">@L["nav_new"]</a>
+ <a class="repo-nav-link @(Model.ActiveTab == "profile" ? "active" : "")"
+ href="/User/Settings">@L["account_settings"]</a>
+ }
+</div>
diff --git a/src/GitServer/wwwroot/User/Profile.cshtml b/src/GitServer/wwwroot/User/Profile.cshtml
index c2dd8b0..52d652a 100644
--- a/src/GitServer/wwwroot/User/Profile.cshtml
+++ b/src/GitServer/wwwroot/User/Profile.cshtml
@@ -6,25 +6,24 @@
<div class="container">
@if (Model.ProfileUser != null)
{
- <div class="profile-header">
- <div class="profile-avatar">
- <img src="@Model.ProfileUser.EffectiveAvatarUrl" alt="@Model.ProfileUser.UserName" class="avatar-img" />
- </div>
- <div class="profile-info">
- <h1>@Model.ProfileUser.DisplayName</h1>
- <p class="text-muted">@@@Model.ProfileUser.UserName</p>
- @if (!string.IsNullOrEmpty(Model.ProfileUser.Bio))
- {
- <p>@Model.ProfileUser.Bio</p>
- }
- <p class="text-muted">@L["profile_member_since"] @Model.ProfileUser.CreatedAt.ToString("MMMM yyyy")</p>
- </div>
- </div>
+ @await Html.PartialAsync("_ProfileHeader", Model.ProfileUser)
+
+ @await Html.PartialAsync("_UserNav", (Model.ProfileUser.UserName!, "repositories", Model.IsOwner))
+
+ <form method="get" class="search-form search-form-full">
+ <input name="q" type="search" class="form-control" placeholder="@L["explore_search_placeholder"]"
+ value="@Model.Query" />
+ <button type="submit" class="btn btn-primary">@L["search_button"]</button>
+ </form>
+
+ @if (!string.IsNullOrEmpty(Model.Query))
+ {
+ <p class="search-results-info">@Model.Repos.Count @L["explore_results_for"] "<strong>@Model.Query</strong>"</p>
+ }
- <h2>@L["profile_repos"]</h2>
@if (!Model.Repos.Any())
{
- <div class="empty-state">@L["profile_no_repos"]</div>
+ <div class="empty-state">@(string.IsNullOrEmpty(Model.Query) ? L["profile_no_repos"] : L["explore_no_results"])</div>
}
else
{
diff --git a/src/GitServer/wwwroot/User/Profile.cshtml.cs b/src/GitServer/wwwroot/User/Profile.cshtml.cs
index 833e93a..3dd5ff7 100644
--- a/src/GitServer/wwwroot/User/Profile.cshtml.cs
+++ b/src/GitServer/wwwroot/User/Profile.cshtml.cs
@@ -10,16 +10,19 @@ public class ProfileModel(UserManager<AppUser> userManager, RepositoryService re
{
public AppUser? ProfileUser { get; set; }
public List<Repository> Repos { get; set; } = new();
+ public bool IsOwner { get; set; }
+ public string Query { get; set; } = "";
- public async Task<IActionResult> OnGetAsync(string username)
+ public async Task<IActionResult> OnGetAsync(string username, string? q)
{
ProfileUser = await userManager.FindByNameAsync(username);
if (ProfileUser == null) return NotFound();
var currentUserId = userManager.GetUserId(User);
- var isOwner = currentUserId == ProfileUser.Id;
+ IsOwner = currentUserId == ProfileUser.Id;
+ Query = q ?? "";
- Repos = await repos.GetUserReposAsync(ProfileUser.Id, includePrivate: isOwner);
+ Repos = await repos.GetUserReposAsync(ProfileUser.Id, includePrivate: IsOwner, query: Query);
return Page();
}
}
diff --git a/src/GitServer/wwwroot/User/Settings.cshtml b/src/GitServer/wwwroot/User/Settings.cshtml
index c4e2cc0..1484bd5 100644
--- a/src/GitServer/wwwroot/User/Settings.cshtml
+++ b/src/GitServer/wwwroot/User/Settings.cshtml
@@ -3,8 +3,13 @@
@{
ViewData["Title"] = L["account_settings"];
}
-<div class="container container-sm">
- <h1>@L["account_settings"]</h1>
+<div class="container">
+ @if (Model.CurrentUser != null)
+ {
+ @await Html.PartialAsync("_ProfileHeader", Model.CurrentUser)
+
+ @await Html.PartialAsync("_UserNav", (Model.CurrentUser.UserName!, "profile", true))
+ }
@if (!string.IsNullOrEmpty(Model.Message))
{
diff --git a/src/GitServer/wwwroot/User/Settings.cshtml.cs b/src/GitServer/wwwroot/User/Settings.cshtml.cs
index fb65a99..e34924c 100644
--- a/src/GitServer/wwwroot/User/Settings.cshtml.cs
+++ b/src/GitServer/wwwroot/User/Settings.cshtml.cs
@@ -10,6 +10,7 @@ namespace GitServer.wwwroot.User;
[Authorize]
public class UserSettingsModel(UserManager<AppUser> userManager, LocalizationService L) : PageModel
{
+ public AppUser? CurrentUser { get; set; }
public string DisplayName { get; set; } = "";
public string? Bio { get; set; }
public string? AvatarUrl { get; set; }
@@ -27,6 +28,7 @@ public class UserSettingsModel(UserManager<AppUser> userManager, LocalizationSer
{
var user = await userManager.GetUserAsync(User);
if (user == null) return;
+ CurrentUser = user;
DisplayName = user.DisplayName;
Bio = user.Bio;
AvatarUrl = user.AvatarUrl;
@@ -46,6 +48,7 @@ public class UserSettingsModel(UserManager<AppUser> userManager, LocalizationSer
Message = result.Succeeded ? L["success_profile_saved"] : string.Join(" ", result.Errors.Select(e => e.Description));
IsError = !result.Succeeded;
+ CurrentUser = user;
DisplayName = user.DisplayName; Bio = user.Bio; AvatarUrl = user.AvatarUrl;
return Page();
}
@@ -64,6 +67,7 @@ public class UserSettingsModel(UserManager<AppUser> userManager, LocalizationSer
Message = result.Succeeded ? L["success_password_changed"] : string.Join(" ", result.Errors.Select(e => e.Description));
IsError = !result.Succeeded;
+ CurrentUser = user;
DisplayName = user.DisplayName; Bio = user.Bio; AvatarUrl = user.AvatarUrl;
HasPassword = await userManager.HasPasswordAsync(user);
return Page();