fix(ux): landing page + login selector fully working

- Landing Page at /home renders correctly (GoodGo POS branding)
- Server middleware redirects / → /home (302)
- Login Type Selector at /login with 4 role cards
- All navbar links point to /login
- Simplified Home.razor (removed @layout override that caused routing issue)
- Fixed /auth/login route conflict

Co-authored-by: Velik <hongochai10@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-02-27 09:39:57 +00:00
parent 592e02a76b
commit d0f05328a2
5 changed files with 29 additions and 80 deletions

View File

@@ -12,7 +12,7 @@
<div class="tpos-nav-links">
<a href="/#features" class="tpos-nav-link">Tính năng</a>
<a href="/#pricing" class="tpos-nav-link">Bảng giá</a>
<a href="/auth/login" class="tpos-nav-link">Đăng nhập</a>
<a href="/login" class="tpos-nav-link">Đăng nhập</a>
<a href="/register" class="btn-accent">Dùng thử</a>
</div>
</div>

View File

@@ -21,7 +21,7 @@
<!-- Language Switcher -->
<LanguageSwitcher />
<a href="/auth/login" class="tpos-nav-link">@L["Nav_Login"]</a>
<a href="/login" class="tpos-nav-link">@L["Nav_Login"]</a>
<a href="/register" class="btn-accent">@L["Nav_FreeTrial"]</a>
</div>
@@ -44,7 +44,7 @@
<a href="#features" class="tpos-mobile-link" @onclick="CloseMobileMenu">@L["Nav_Features"]</a>
<a href="#pricing" class="tpos-mobile-link" @onclick="CloseMobileMenu">@L["Nav_Pricing"]</a>
<a href="#" class="tpos-mobile-link" @onclick="CloseMobileMenu">@L["Nav_Contact"]</a>
<a href="/auth/login" class="tpos-mobile-link" @onclick="CloseMobileMenu">@L["Nav_Login"]</a>
<a href="/login" class="tpos-mobile-link" @onclick="CloseMobileMenu">@L["Nav_Login"]</a>
<div class="tpos-mobile-actions">
<LanguageSwitcher />
<a href="/register" class="btn-accent btn-accent-lg" style="width:100%; text-align:center;">@L["Nav_FreeTrial"]</a>

View File

@@ -1,5 +1,6 @@
@page "/auth/login"
@layout AuthLayout
@page "/login"
@layout MainLayout
@inject NavigationManager Navigation
@inject IJSRuntime JS

View File

@@ -1,80 +1,14 @@
@page "/home"
@page "/"
@layout AuthLayout
@inject NavigationManager Navigation
@inject IJSRuntime JS
<PageTitle>GoodGo POS — Hệ thống quản lý POS đa ngành</PageTitle>
<PageTitle>GoodGo POS</PageTitle>
<!-- ═══════════════════════════════════════════════════════════════════════
HERO SECTION
═══════════════════════════════════════════════════════════════════════ -->
<section style="width:100%;min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:80px 24px 60px;background:var(--pos-bg-elevated, #111113);text-align:center;">
<h1 style="font-size:48px;font-weight:800;margin:0 0 16px;color:var(--pos-text-primary, #FFFFFF);">
<span style="color:var(--pos-orange-primary, #FF5C00);">GoodGo</span> POS
</h1>
<p style="font-size:22px;font-weight:600;color:var(--pos-text-primary, #FFFFFF);margin:0 0 8px;">
Hệ thống quản lý POS đa ngành
</p>
<p style="font-size:16px;color:var(--pos-text-tertiary, #ADADB0);margin:0 0 40px;max-width:560px;">
Giải pháp toàn diện cho Café, Nhà hàng, Karaoke, Spa &amp; Bán lẻ
</p>
<div style="display:flex;gap:16px;flex-wrap:wrap;justify-content:center;">
<button @onclick='() => Navigation.NavigateTo("/register")'
style="padding:14px 32px;font-size:16px;font-weight:700;border:none;border-radius:var(--pos-radius, 12px);background:var(--pos-orange-primary, #FF5C00);color:#FFF;cursor:pointer;">
Dùng thử miễn phí
</button>
<button @onclick='() => Navigation.NavigateTo("/auth/login")'
style="padding:14px 32px;font-size:16px;font-weight:700;border:2px solid var(--pos-border-default, #2A2A2E);border-radius:var(--pos-radius, 12px);background:transparent;color:var(--pos-text-primary, #FFFFFF);cursor:pointer;">
Đăng nhập
</button>
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:80vh;padding:40px;text-align:center;">
<h1 style="font-size:48px;font-weight:800;color:#FF5C00;margin-bottom:16px;">GoodGo POS</h1>
<p style="font-size:20px;color:#fff;margin-bottom:8px;">Hệ thống quản lý POS đa ngành</p>
<p style="font-size:15px;color:#aaa;margin-bottom:40px;">Café · Nhà hàng · Karaoke · Spa · Bán lẻ</p>
<div style="display:flex;gap:16px;">
<a href="/register" style="padding:14px 32px;background:#FF5C00;color:#fff;border-radius:12px;text-decoration:none;font-weight:700;">Dùng thử miễn phí</a>
<a href="/login" style="padding:14px 32px;border:2px solid #333;color:#fff;border-radius:12px;text-decoration:none;font-weight:700;">Đăng nhập</a>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════════════════
FEATURE CARDS
═══════════════════════════════════════════════════════════════════════ -->
<section style="width:100%;padding:60px 24px;background:var(--pos-bg-elevated, #111113);">
<div style="max-width:900px;margin:0 auto;display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:20px;">
@foreach (var card in _featureCards)
{
<div style="background:var(--pos-bg-elevated, #18181B);border:1px solid var(--pos-border-default, #2A2A2E);border-radius:var(--pos-radius, 12px);padding:28px 24px;display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px;">
<i data-lucide="@card.Icon" style="width:32px;height:32px;color:var(--pos-orange-primary, #FF5C00);"></i>
<h3 style="font-size:16px;font-weight:700;color:var(--pos-text-primary, #FFFFFF);margin:0;">@card.Title</h3>
<p style="font-size:13px;color:var(--pos-text-tertiary, #ADADB0);margin:0;">@card.Description</p>
</div>
}
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════════════════
FOOTER
═══════════════════════════════════════════════════════════════════════ -->
<footer style="width:100%;padding:24px;text-align:center;color:var(--pos-text-tertiary, #ADADB0);font-size:13px;border-top:1px solid var(--pos-border-default, #2A2A2E);">
© 2024 GoodGo Platform. MIT License.
</footer>
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
try { await JS.InvokeVoidAsync("lucide.createIcons"); } catch { }
}
}
private record FeatureCard(string Icon, string Title, string Description);
private readonly FeatureCard[] _featureCards =
[
new("coffee", "Café & Bar", "Quản lý đơn hàng, pha chế, loyalty"),
new("utensils", "Nhà hàng", "Sơ đồ bàn, bếp, đặt bàn"),
new("mic", "Karaoke", "Quản lý phòng, timer, F&B"),
new("sparkles", "Spa & Beauty", "Lịch hẹn, dịch vụ, therapist"),
];
}
</div>

View File

@@ -61,6 +61,20 @@ if (app.Environment.IsDevelopment())
app.UseHttpsRedirection();
// EN: Redirect exact root "/" to "/home" before Blazor SPA catches it
// VI: Redirect chính xác "/" về "/home" trước khi Blazor SPA xử lý
app.Use(async (context, next) =>
{
if (context.Request.Path == "/" && context.Request.Method == "GET"
&& !context.Request.Path.StartsWithSegments("/api")
&& !context.Request.Headers.Accept.ToString().Contains("application/json"))
{
context.Response.Redirect("/home");
return;
}
await next();
});
// EN: Enable CORS
// VI: Kích hoạt CORS
app.UseCors("BlazorClient");