com.gudang2go.UI 2.3.6
Collection of Gudang2go Reusable UI Block
Table Of Content
Add Imports
```
@using com.gudang2go.UI.Components
@using com.gudang2go.UI.Components.Navigation
@using com.gudang2go.UI.Components.ProgressViewer
@using com.gudang2go.UI.Layouts
```
into `_Imports.razor`.
Layout
Default Layout
Usage:
- Add
builder.Services.AddHttpContextAccessor();intoProgram.cs. The purpose is for getting darkmode value in cookies. - In
Program.csdon't forget also to add:var app = builder.Build(); app.UsePathBase("/{YOUR_BASE_PATH}"); // <-- This change to your basepath and just use 1 slash in front of it. // And also this line is under var app = builder.Build() and run before // Middleware under it. // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseRouting(); // <-- Add this line also to handle routing and static files app.UseStaticFiles(); // <-- Add this line also to handle routing and static files app.UseStatusCodePagesWithRedirects("~/not-found"); // <-- Add this line to use fallback route app.MapRazorComponents<App>() .AddInteractiveServerRenderMode(c => c.DisableWebSocketCompression = true) // <-- Add this to suppress websocket .AddAdditionalAssemblies(typeof(com.gudang2go.UI.Components.ErrorPage.NotFound).Assembly); // <-- Add this line to register fallback page - Add cookies checker for darkmode in
App.razor
then add@inject IHttpContextAccessor HttpContextAccessor @{ var darkMode = HttpContextAccessor.HttpContext?.Request.Cookies["darkmode"]; var bodyClass = darkMode == "true" ? "dark" : ""; }@bodyClassand make sure use<base href="/{YOUR_BASE_PATH}/" />e.g. /SettingWeb/, /DistributorWeb/<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <base href="/{YOUR_BASE_PATH}/" /> <link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" /> <link rel="stylesheet" href="@Assets["app.css"]" /> <HeadOutlet /> <ImportMap /> <link rel="icon" type="image/png" href="favicon.png" /> </head> <body class="@bodyClass"> <Routes /> <script src="_framework/blazor.web.js"></script> </body> - Remove and replace your MainLayout:
@inherits LayoutComponentBase @namespace com.gudang2go.Setting.Web.Components.Layout @using com.gudang2go.OpenID.Interfaces @inject IOpenIDConnectUserInfo service <DefaultLayout Header="Distributor" Username="@User"> @* If you want to add CDN or anything *@ <HeadExtra> </HeadExtra> <NavbarMenu> @* If you want to add customized menu for navbar *@ @* Use <NavbarItem Route="" Color=""><Icon>Your Icon</Icon></NavbarItem> Component *@ @* Add this toggler to apply darkmode *@ <DarkModeToggle @rendermode="InteractiveServer"/> @if (User == "Guest") { <a class="btn text-white d-inline-flex align-items-center gap-2" href="OnSignIn" style="background-color: #8DD334;"><i class="bi bi-box-arrow-in-left fs-4"></i> Log In</a> } else { <a class="btn text-white" href="OnSignOut" style="background-color: #DC3545;"><i class="bi bi-box-arrow-right fs-4"></i></a> } </NavbarMenu> <NavSideMenu> <NavItem Name="Dashboard" Route="/" Color="#499D0A"> <Icon> <i class="ms-Icon ms-Icon--BIDashboard fs-4"></i> </Icon> </NavItem> <NavItem Name="Sales" Route="/sales" Color="#00FF6A"> <Icon> <i class="bi bi-tag fs-4"></i> </Icon> <Submenu> <NavLinkCustom Name="Sales Order" Route="/sales/order" /> ... </Submenu> </NavItem> ... </NavSideMenu> <Body> @Body </Body> </DefaultLayout> @code { private string User = "Guest"; protected override async Task OnInitializedAsync() { var obj = await service.GetUserInfo(); User = string.IsNullOrEmpty(obj?.UserName) ? "Guest" : obj?.UserName ?? ""; } } - Pages routing using href make to not include any slash in front of it
This because blazor will read it as relative path instead of root path, but if there is subpath you can do like<NavItem Name="Custom Transaction Code" Route="{THIS_ROUTE e.g. transaction-code instead of /transaction-code}" Color="#499D0A"> <Icon><i class="bi bi-code fs-4"></i></Icon> </NavItem>transaction-code/custom, this is okay because it will run in relative path. Note: make sure not to change Routes.Razor layout
- Add
Logo
- Logo needed to be added manually from this g2g-ui module through ./img/Logo/.., default Logo & Favicon is g2g-ui Logo. You need to add the logo route through appsettings
"AppMetadata": { "Theme": "", // Theme name in .css "Logo": "", // Logo uri in /Logo folder "Language": "en" // Language for layout },- If want to access the logo image directly, add line below in your pages or component
private DomainContext Data { get; set; } = new(); protected override void OnInitialized() => Data = Config.GetDomainData();Hide when Not Auth
- Add this into Routes.razor:
@using Microsoft.AspNetCore.Components.Authorization <CascadingAuthenticationState> <Router AppAssembly="typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" /> <FocusOnNavigate RouteData="routeData" Selector="h1" /> </Found> </Router> </CascadingAuthenticationState>
Components
NavItem
Usage:
- Inside your layout:
@* Don't use slash infront of the route path *@ <NavItem Name="{YOUR_NAV_NAME}" Route="{YOUR_NAV_ROUTE e.g. transaction/sales}" />NavLinkCustom
Usage:
- As default NavLink
<NavLinkCustom Name="User Mapping" Route="" Color="#DA007F"> <Icon><i class="bi bi-bezier fs-4"></i></Icon> <Submenu> @* Your submenu *@ @* You can use <NavLink> or <NavItem> *@ </Submenu> </NavLinkCustom>Pagination
Usage:
<Pagination StartIndex=StartIndex ItemsPerPage=PageSize TotalItems=TotalItems OnPageChanged=HandleOnValueUpdate />DecimalInput
Usage:
- Include the js CDN first in your
_Host.cshtmlorApp.razor:
<script src="https://unpkg.com/inputmask@latest/dist/inputmask.min.js"></script>- Use the component in your page or layout:
<DecimalInput @bind-Value="yourVar" Digits=2 GroupSeparator="." OnInputChanged="() => OnInputChangeAsync(floor, level)" />- Acceptable parameters:
bind-Value: decimal variable to bindGroupSeparator: group separator for thousands, can be "." or ","Digits: number of digits after decimal point, default is 0OnInputChanged: to trigger Event on Update Value
- Include the js CDN first in your
Popup
Don't forget to use InteractiveServer. Usage:
<PopupModal @ref="modal" Title="Your Title" ModalCssClass="Bootstrap modal class e.g. => modal-lg"> <Body> ... Add your modal item here </Body> </PopupModal> @code { private PopupModal? modal; }If your other component need to trigger close modal:
- Component A
@code { [Parameter] public EventCallback OnCancel { get; set; } private async Task OnCancelClicked() { // Use invoke to notify parent if (OnCancel.HasDelegate) await OnCancel.InvokeAsync(); } }- Your page
<PopupModal @ref="modal" Title="..." HeaderBackgroundColor="..." ModalCssClass="..."> <Body> @* Pass the close trigger to the component. Identical in React when you pass state of UseState to a component *@ <ComponentA OnCancel="() => modal?.Close()" /> </Body> </PopupModal> @code { private PopupModal? modal; }Progress Viewer
Usage:
- Front End: Create ProgressViewerConfig class that implements IProgressViewerConfig interface from this UI nuget, this is used to set SignalR Hub URI for Progress Viewer.
record ProgressViewerConfig(string BaseAddress) : IProgressViewerConfig { public string SignalRHubUri => $"{BaseAddress}"; } - Front End: Register IProgressViewerConfig to program.cs as Singleton (note baseAddress to match API base path)
builder.Services.AddSingleton<IProgressViewerConfig>( new ProgressViewerConfig($"{baseAddress}/ProgressViewer") ); - Front End: Use the component in your project
@using com.gudang2go.UI.ProgressViewer <ProgressViewer TaskId="@TaskId" OnClose="Close" IsShowCloseButton="true" /> @code { private string? TaskId; private async Task OnClick() { @* Make sure to give progress viewer the TaskId *@ @* It will be better to add some loading mechanism or some delay *@ TaskId = await service.GetPid(); } @* Callback function for Progress Viewer close button, use to erase TaskId if needed *@ private void Close() { @* There is callback for parent using this function, if need the callback use StateHasChanged() *@ } } - Back End: insert following to program.cs and make sure to have RabbitMQ:host in appsettings
builder.Services.AddSingleton<IPublisher>(p => { var hostname = configuration["RabbitMQ:host"] ?? throw new ArgumentNullException(configuration["RabbitMQ:host"], "RabbitMQ host not configured!"); return Publisher.InitiatePublisher(hostname).GetAwaiter().GetResult(); }); - Back End: Usage in a service that needed progress viewer:
public class ExcelJob(IServiceProvider provider) { public async Task<string> Run(IFormFile excelFile, String editor, bool doCommit = false) { //Generate ID for Process Tracking Guid tk = Guid.CreateVersion7(); //Convert to String to make easy for send custom message String tkn = tk.ToString(); //Trigger Long Processing Task Here _ = Task.Run(async () => { using var scope = provider.CreateScope(); var publisher = scope.ServiceProvider.GetRequiredService<IPublisher>(); try { await publisher.PublishMessage(tkn.GeneralMessage("Process is running")); . .Insert Logic Here... . await publisher.PublishMessage(tkn.FinishedMessage("Process is finished")); } catch (Exception ex) { await publisher.PublishMessage(tkn.ErrorMessage(ex.Message)); } }); //this is the TaskId that will be used in ProgressViewer Front End return tkn; } } - There is 4 message type: GeneralMessage, ErrorMessage, WarningMessage, and FinishedMessage, available as extension function from String class.
- If you need close callback from ProgressViewer make sure to return FinishedMessage in every process. You just need to map the processId to each message that you needed.
- Front End: Create ProgressViewerConfig class that implements IProgressViewerConfig interface from this UI nuget, this is used to set SignalR Hub URI for Progress Viewer.
Dark Mode Toggle
Usage: read the DefaultLayout section. Just to make sure to use
@rendermode="InteractiveServer"inside the component.
No packages depend on com.gudang2go.UI.
.NET 9.0
- Microsoft.AspNetCore.Components.Authorization (>= 9.0.10)
- Microsoft.AspNetCore.Components.Web (>= 9.0.10)
- Microsoft.AspNetCore.SignalR.Client (>= 9.0.10)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Options (>= 9.0.10)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.10)
- com.gudang2go.Localizer (>= 2.0.1)
- com.gudang2go.ProgressViewer.Common (>= 2.0.0)
- radzen.blazor (>= 8.1.0)
| Version | Downloads | Last updated |
|---|---|---|
| 3.0.5 | 374 | 03/30/2026 |
| 3.0.4 | 31 | 03/17/2026 |
| 3.0.3 | 1 | 03/17/2026 |
| 3.0.2 | 1 | 03/17/2026 |
| 3.0.1 | 6 | 03/16/2026 |
| 3.0.0 | 10 | 03/13/2026 |
| 2.7.0 | 10 | 03/04/2026 |
| 2.6.2 | 91 | 01/19/2026 |
| 2.6.1 | 2 | 01/18/2026 |
| 2.6.0 | 0 | 01/18/2026 |
| 2.5.11 | 61 | 12/01/2025 |
| 2.5.10 | 8 | 12/01/2025 |
| 2.5.9 | 25 | 12/01/2025 |
| 2.5.8 | 23 | 11/28/2025 |
| 2.5.7 | 5 | 11/28/2025 |
| 2.5.6 | 24 | 11/22/2025 |
| 2.5.5 | 14 | 11/18/2025 |
| 2.5.4 | 27 | 11/18/2025 |
| 2.5.3 | 21 | 11/18/2025 |
| 2.5.2 | 0 | 11/18/2025 |
| 2.5.1 | 4 | 11/18/2025 |
| 2.5.0 | 0 | 11/18/2025 |
| 2.4.8 | 11 | 11/17/2025 |
| 2.4.7 | 14 | 11/14/2025 |
| 2.4.6 | 111 | 11/05/2025 |
| 2.4.5 | 20 | 11/04/2025 |
| 2.4.4 | 15 | 10/31/2025 |
| 2.4.3 | 3 | 10/30/2025 |
| 2.4.2 | 2 | 10/30/2025 |
| 2.4.1 | 2 | 10/30/2025 |
| 2.4.0 | 1 | 10/30/2025 |
| 2.3.6 | 38 | 10/25/2025 |
| 2.3.5 | 7 | 10/24/2025 |
| 2.3.4 | 12 | 10/24/2025 |
| 2.3.3 | 4 | 10/23/2025 |
| 2.3.2 | 2 | 10/23/2025 |
| 2.3.1 | 1 | 10/23/2025 |
| 2.3.0 | 1 | 10/23/2025 |
| 2.2.5 | 4 | 10/22/2025 |
| 2.2.4 | 16 | 10/21/2025 |
| 2.2.3 | 3 | 10/21/2025 |
| 2.2.2 | 3 | 10/21/2025 |
| 2.2.1 | 1 | 10/20/2025 |
| 2.2.0 | 4 | 10/20/2025 |
| 2.1.0 | 16 | 10/17/2025 |
| 2.0.5 | 39 | 10/08/2025 |
| 2.0.4 | 32 | 10/07/2025 |
| 2.0.3 | 38 | 10/02/2025 |
| 2.0.2 | 0 | 10/02/2025 |
| 2.0.1 | 25 | 09/29/2025 |
| 2.0.0 | 32 | 09/22/2025 |
| 0.0.104 | 0 | 09/29/2025 |
| 0.0.103 | 17 | 09/19/2025 |
| 0.0.102 | 14 | 09/15/2025 |
| 0.0.101 | 41 | 09/03/2025 |
| 0.0.100 | 8 | 09/02/2025 |
| 0.0.99 | 0 | 09/02/2025 |
| 0.0.98 | 4 | 09/02/2025 |
| 0.0.97 | 23 | 08/28/2025 |
| 0.0.96 | 19 | 08/26/2025 |
| 0.0.95 | 2 | 08/26/2025 |
| 0.0.94 | 2 | 08/26/2025 |
| 0.0.93 | 1 | 08/26/2025 |
| 0.0.92 | 12 | 08/24/2025 |
| 0.0.91 | 1 | 08/24/2025 |
| 0.0.90 | 83 | 07/30/2025 |
| 0.0.89 | 41 | 07/24/2025 |
| 0.0.88 | 34 | 07/18/2025 |
| 0.0.87 | 16 | 07/17/2025 |
| 0.0.86 | 6 | 07/16/2025 |
| 0.0.85 | 13 | 07/14/2025 |
| 0.0.84 | 2 | 07/14/2025 |
| 0.0.83 | 1 | 07/14/2025 |
| 0.0.82 | 3 | 07/14/2025 |
| 0.0.81 | 16 | 07/14/2025 |
| 0.0.80 | 0 | 07/14/2025 |
| 0.0.79 | 6 | 07/14/2025 |
| 0.0.78 | 4 | 07/14/2025 |
| 0.0.77 | 3 | 07/14/2025 |
| 0.0.76 | 1 | 07/14/2025 |
| 0.0.75 | 20 | 07/09/2025 |
| 0.0.74 | 24 | 07/02/2025 |
| 0.0.73 | 3 | 07/02/2025 |
| 0.0.72 | 29 | 06/27/2025 |
| 0.0.71 | 22 | 06/24/2025 |
| 0.0.70 | 3 | 06/23/2025 |
| 0.0.69 | 14 | 06/20/2025 |
| 0.0.68 | 19 | 06/20/2025 |
| 0.0.67 | 6 | 06/20/2025 |
| 0.0.66 | 11 | 06/18/2025 |
| 0.0.65 | 2 | 06/18/2025 |
| 0.0.64 | 2 | 06/18/2025 |
| 0.0.63 | 2 | 06/18/2025 |
| 0.0.62 | 2 | 06/17/2025 |
| 0.0.61 | 1 | 06/17/2025 |
| 0.0.60 | 2 | 06/17/2025 |
| 0.0.59 | 3 | 06/17/2025 |
| 0.0.58 | 2 | 06/17/2025 |
| 0.0.57 | 2 | 06/17/2025 |
| 0.0.56 | 20 | 06/13/2025 |
| 0.0.55 | 2 | 06/13/2025 |
| 0.0.54 | 9 | 06/13/2025 |
| 0.0.53 | 16 | 06/12/2025 |
| 0.0.52 | 3 | 06/11/2025 |
| 0.0.51 | 4 | 06/11/2025 |
| 0.0.50 | 1 | 06/11/2025 |
| 0.0.49 | 10 | 06/10/2025 |
| 0.0.48 | 2 | 06/10/2025 |
| 0.0.47 | 27 | 06/09/2025 |
| 0.0.46 | 1 | 06/09/2025 |
| 0.0.45 | 23 | 06/04/2025 |
| 0.0.44 | 27 | 06/03/2025 |
| 0.0.43 | 1 | 06/03/2025 |
| 0.0.42 | 27 | 06/01/2025 |
| 0.0.41 | 0 | 06/01/2025 |
| 0.0.40 | 10 | 05/30/2025 |
| 0.0.39 | 5 | 05/28/2025 |
| 0.0.38 | 21 | 05/28/2025 |
| 0.0.37 | 0 | 05/28/2025 |
| 0.0.36 | 14 | 05/27/2025 |
| 0.0.35 | 2 | 05/26/2025 |
| 0.0.34 | 2 | 05/26/2025 |
| 0.0.33 | 2 | 05/26/2025 |
| 0.0.32 | 2 | 05/26/2025 |
| 0.0.31 | 17 | 05/26/2025 |
| 0.0.30 | 3 | 05/26/2025 |
| 0.0.29 | 8 | 05/24/2025 |
| 0.0.28 | 13 | 05/22/2025 |
| 0.0.27 | 1 | 05/22/2025 |
| 0.0.26 | 5 | 05/21/2025 |
| 0.0.25 | 1 | 05/21/2025 |
| 0.0.24 | 2 | 05/21/2025 |
| 0.0.23 | 1 | 05/21/2025 |
| 0.0.22 | 1 | 05/21/2025 |
| 0.0.21 | 2 | 05/21/2025 |
| 0.0.20 | 1 | 05/21/2025 |
| 0.0.19 | 2 | 05/21/2025 |
| 0.0.18 | 1 | 05/21/2025 |
| 0.0.17 | 1 | 05/21/2025 |
| 0.0.16 | 1 | 05/21/2025 |
| 0.0.15 | 1 | 05/21/2025 |
| 0.0.14 | 1 | 05/21/2025 |
| 0.0.13 | 1 | 05/21/2025 |
| 0.0.12 | 1 | 05/21/2025 |
| 0.0.11 | 5 | 05/21/2025 |
| 0.0.10 | 3 | 05/21/2025 |
| 0.0.9 | 3 | 05/21/2025 |
| 0.0.8 | 3 | 05/21/2025 |
| 0.0.7 | 4 | 05/21/2025 |
| 0.0.6 | 2 | 05/21/2025 |
| 0.0.5 | 11 | 05/12/2025 |
| 0.0.4 | 3 | 05/09/2025 |
| 0.0.3 | 1 | 05/09/2025 |
| 0.0.2 | 1 | 05/09/2025 |