com.gudang2go.UI 2.4.4

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:

    1. Add builder.Services.AddHttpContextAccessor(); into Program.cs. The purpose is for getting darkmode value in cookies.
    2. In Program.cs don'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
      
    3. Add cookies checker for darkmode in App.razor
      @inject IHttpContextAccessor HttpContextAccessor
      
      @{
          var darkMode = HttpContextAccessor.HttpContext?.Request.Cookies["darkmode"];
          var bodyClass = darkMode == "true" ? "dark" : "";
      }
      
      then add @bodyClass and 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>
      
    4. 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 ?? "";
          }
      }
      
    5. Pages routing using href make to not include any slash in front of it
      <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>
      
      This because blazor will read it as relative path instead of root path, but if there is subpath you can do like transaction-code/custom, this is okay because it will run in relative path. Note: make sure not to change Routes.Razor layout
    1. 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
        },
    
    1. 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

    1. 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

  • 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}" />
    
  • 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.cshtml or App.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 bind
      • GroupSeparator: group separator for thousands, can be "." or ","
      • Digits: number of digits after decimal point, default is 0
      • OnInputChanged : to trigger Event on Update Value
  • 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:

    1. 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}"; }
      
    2. Front End: Register IProgressViewerConfig to program.cs as Singleton (note baseAddress to match API base path)
      builder.Services.AddSingleton<IProgressViewerConfig>(
          new ProgressViewerConfig($"{baseAddress}/ProgressViewer")
      );
      
    3. 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()
              *@
          }
      }
      
    4. 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();
      });
      
    5. 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;
          }
      }
      
    6. There is 4 message type: GeneralMessage, ErrorMessage, WarningMessage, and FinishedMessage, available as extension function from String class.
    7. 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.
  • 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.

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