Ready To Blazor

Bienvenido a Ready To Blazor, un starter-kit basado en Blazor Server que acelera la creación de aplicaciones empresariales conectadas a Dinaup. En este artículo encontrarás una introducción gradual—desde la pila de componentes hasta ejemplos de código listos para copiar—para que puedas publicar tu primera pantalla en minutos.

Conceptos

Ready To Blazor usa Radzen

Ready To Blazor adopta la colección de componentes Radzen Blazor para proporcionar :

  • DataGrid, Tabs, DialogService, notificaciones y decenas de controles preparados para producción.

  • Un look & feel consistente y personalizable mediante los temas de Radzen.

  • Integración directa con código C# (sin generadores externos).

DinaZen → puente entre Radzen y Dinaup

Para simplificar la comunicación con la API de Dinaup hemos publicado DinaZen: un paquete NuGet que añade helpers de extensión y componentes visuales que encajan como un guante con Radzen. Basta con añadir la referencia y un @using DinaZen para disponer de:

  • LoaderU: indicador de carga reactivo.

  • Extensiones como Extensions.LoadReportDataAsync para rellenar DataGrids en una línea.

  • Conversores y validadores listos para usar.

Autenticación integrada (login / alta / recuperación)

Ready To Blazor trae el flujo de identidad completo:

Pantalla
Archivo
Descripción

Inicio de sesión

Pages/Sesion/LoginForm.razor

Inicia sesión y recuerda al usuario.

Alta de cuenta

LoginForm.razor (pestaña Crear Cuenta)

Valida email, fuerza política de contraseña y envía correo de activación.

Recuperar contraseña

LoginForm.razor (pestaña Recuperar Contraseña)

Envía enlace de recuperación y permite cambiarla con código.

Todo el flujo se apoya en los servicios CurrentUserService y SMTPService, comentados más abajo.

Configura tus credenciales SMTP

El envío de correos (activación y recuperación) requiere un servidor SMTP válido; esto se configura desde appsettings.json .

Conecta tu instancia MyDinaup

Ready To Blazor asume que tu organización dispone de un entorno MyDinaup. Solo necesitas:

  1. URL base de tu API Dinaup.

  2. Un API Key / Secret con permisos de lectura y escritura.

Ejemplos

Archivo de Configuración

Ejemplo de archivo appsettings.json

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft.AspNetCore": "Warning"
        }
    },
    "AllowedHosts": "*",
    "Dinaup": {
        "APIBaseUrl": "https://api.dinaup.com/v2/****",
        "APIKey": "****",
        "APISecret": "********"
    },
    "Smtp": {
        "Host": "smtp.resend.com",
        "Port": 2587,
        "EnableSsl": true,
        "UserName": "resend",
        "SenderName": "****",
        "SenderEmail": "****",
        "Password": "****"
    }
}

Cargar Informe Manual

Antes de entrar en herramientas de más alto nivel, en Dinaup casi todo se basa en "Reports" o "Informes" APIPaisesC es una clase de MyDinaup , es decir, no tendrás que programarla simplemente instanciarla.

Ejemplo de como cargar 100 países.

var rpt = new APIPaisesC();
await rpt.ExecuteQueryAsync(DinaupClient, 1, 100);

foreach (var row in rpt.Rows)
    Console.WriteLine($"{row.TextoPrincipal} – {row.CodAlfabetico2}");

Ejemplo 1 – Carga directa en memoria

Este ejemplo muestra cómo cargar todos los registros de un informe de Dinaup de una sola vez al iniciar la página. Es una opción sencilla y rápida para conjuntos de datos pequeños o medianos que se consulten con poca frecuencia.

DataGrid es un componente de Radzen para mostrar tablas de datos con paginación, ordenación y filtrado integrado. En este ejemplo lo usamos para conectar dinámicamente con datos de informes.

@page "/Ej1"

@if (Paises.IsNull())
{
    <LoaderU />
    
}else{

    <RadzenDataGrid PageSize="10" AllowPaging="true" [email protected]() Data=@Paises>
        <Columns>
	    <RadzenDataGridColumn Property=@(nameof(APIPaisesC.APIPaises_RowC.TextoPrincipal)) Title="Nombre Pais" Width="200px" />
	    <RadzenDataGridColumn Property=@(nameof(APIPaisesC.APIPaises_RowC.CodAlfabetico2)) Title="Código Pais" Width="100px" />
        </Columns>
    </RadzenDataGrid>
}

@code {
    private IEnumerable<APIPaisesC.APIPaises_RowC> Paises;
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        var rpt = new APIPaisesC();
        await rpt.ExecuteQueryAsync(DinaupClient, 1, 1000);
        Paises = rpt.Rows;
    }
}

Ejemplo 2 – Carga bajo demanda

En este caso, el RadzenDataGrid se conecta al informe Dinaup usando el evento LoadData. Solo se recuperan los datos necesarios para la página actual, y se aplican filtros y ordenaciones desde el servidor. Ideal para trabajar con grandes volúmenes de datos de forma eficiente.

La función Extensions.LoadReportDataAsync forma parte del helper DinaZen y es la pieza clave que conecta un RadzenDataGrid con cualquier informe de Dinaup.

APISeccionDePruebasAPIC es una sección de prueba dentro de Dinaup pensada para experimentar con la gestión de datos. Permite agregar, editar y eliminar registros libremente, y probar diferentes tipos de campos (fechas, textos, números, etc.) en un entorno seguro.

@page "/Ej2"
@using Dinaup
@using static DemoUp.MyDinaup.Reports.FuncionalidadD.APISeccionDePruebasAPIC


<RadzenDataGrid Data=@ReportData?.Rows
				Count=@(ReportData?.TotalResults ?? 0)
				PageSize=@PageSize
				AllowSorting=true
				AllowPaging="true"
				AllowFiltering="true"
				[email protected]()
				LoadData="@LoadData">
	<Columns>
		<RadzenDataGridColumn Property="@(nameof(APISeccionDePruebasAPI_RowC.TextoPrincipal))" Title="Nombre País" Width="200px" />
		<RadzenDataGridColumn Property="@(nameof(APISeccionDePruebasAPI_RowC.FechaIA))" Title="Fecha" Width="100px" />
		<RadzenDataGridColumn Property="@(nameof(APISeccionDePruebasAPI_RowC.Valorentero))" Title="Valorentero" Width="100px" />
		<RadzenDataGridColumn Property="@(nameof(APISeccionDePruebasAPI_RowC.Textodeprueba))" Title="Textodeprueba" Width="100px" />
	</Columns>
</RadzenDataGrid>


@code {

    private int PageSize = 10;
    private DemoUp.MyDinaup.Reports.FuncionalidadD.APISeccionDePruebasAPIC ReportData = new();
    private RadzenDataGrid<APIPaisesC.APIPaises_RowC> grid;

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        ReportData.RequestParams.ResultsPerPage = PageSize;
        ReportData.RequestParams.CurrentPage = 1;
        await LoadData(null);
    }
    
    private async Task LoadData(LoadDataArgs args)
    {
        await Extensions.LoadReportDataAsync(DinaupClient, CurrentUserService.User, ReportData, args);
    }

}

Operaciones de Escritura

Las operaciones de escritura se realiza utilizando WriteOperation. Aquí un ejemplo mínimo.

// Crear una nueva operación de escritura (nuevo registro, por eso Guid.Empty)
var wOp = new WriteOperation(Guid.Empty);

// Agregar los datos al registro. Se puede añadir cualquier campo definido en la sección.
wOp.DataMainRow.Add(SeccionDePruebasAPIES.TextoPrincipal, "Prueba");

// Ejecutar la operación de escritura contra la sección deseada
var result = await DinaupClient.RunWriteOperationAsync(
    CurrentUserService.User, // Sesión del usuario actual
    SeccionDePruebasAPID._SectionIDGUID, // GUID de la sección
    wOp,
    false // false = escritura directa (sin ejecutar scripts ni recalcular)
);

// Validar que la operación se haya realizado correctamente
result.EnsureSuccess();

// Obtener el ID del nuevo registro insertado
var resultId = wOp.WriteOperationResult.RowID;
Console.WriteLine($"Registro creado con éxito. ID: {resultId}");

Ejemplo 3 - Agregado rápido

Aquí tienes un ejemplo mínimo y claro para ejecutar una WriteOperation

<RadzenTextBox @bind-Value=@editingText Style="width: 100%;" />
<RadzenButton Icon="add" Text="Guardar" Click=@AddTestData IsBusy=@addTestDataIsBusy ></RadzenButton>

@code {

   string editingText;

   private bool addTestDataIsBusy { get; set; }

   private async Task AddTestData()
   {
      addTestDataIsBusy = true;

      try
      {
         var wOp = new WriteOperation(Guid.Empty);
         wOp.DataMainRow.Add(SeccionDePruebasAPIES.TextoPrincipal, editingText);
         var result = await DinaupClient.RunWriteOperationAsync(CurrentUserService.User, SeccionDePruebasAPID._SectionIDGUID, wOp, false);
         result.EnsureSuccess();
         editingText = "";

      } catch (Exception ex) {
         NotificationService.Notify(NotificationSeverity.Error, "Ups.", ex.Message);
      }

     addTestDataIsBusy = false;


   }
}

Anotaciones Internas [Subir archivo + añadir comentario]

Este ejemplo permite escribir un comentario al usuario, adjuntar una imagen desde cámara o galería, y vincular ambos a una fila (rowId) y sección (sectionId). Al guardar, se ejecuta Annotation_PutAsync, y se notifica al componente padre con OnAnnotationSended.

El tipo de anotación se define con AnnotationTypeE, que puede ser:

  • Files: para documentación interna, como fichas técnicas o manuales.

  • Comments: para notas internas o interacción entre miembros del equipo.

  • PublicGallery: para archivos públicos como fotos de un producto. Cuidado: pueden ser accesibles públicamente e indexados por buscadores.

Ideal para añadir contexto o evidencias visuales en cualquier flujo de trabajo.



<RadzenCard class="d-flex justify-content-between gap-1  align-items-center px-3  align-items-center align-content-center">
	<div class="d-flex flex-grow-1 flex-column">
		<span class="rz-text-overline">Nuevo comentario</span>
		<RadzenTextArea Style="width: 100%" @bind-Value=@message />

		@if (selectedFileName.IsNotEmpty())
		{
			<div class="d-flex">

				<FileNameU FileName=@selectedFileName></FileNameU>
				<RadzenButton Variant="Variant.Text" ButtonStyle="ButtonStyle.Danger" Icon="close" Click=@RemoveFile />
			</div>
		}
		else
		{
			<RadzenUpload ChooseText="Adjuntar archivo..." Multiple="false" Accept="image/*;capture=camera" Complete=@OnComplete Url="file/upload/single" />
		}
	</div>
	<div>
		<RadzenButton Icon="send" Click=@SaveAnnotation />
	</div>
</RadzenCard>





@code {



	[Parameter] public Action OnAnnotationSended { get; set; }



	[Parameter] public System.Guid rowId { get; set; }
	[Parameter] public System.Guid sectionId { get; set; }
	[Parameter] public AnnotationTypeE Type { get; set; }


	public string  message;
	public System.Guid selectedFileID;
	public string selectedFileName;


	async Task OnComplete(UploadCompleteEventArgs args)
	{

		var jsonData = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(args.RawResponse);
		var id = jsonData.GetM("id");
		var filename = jsonData.GetM("filename");
		selectedFileID = id.ToGUID();
		selectedFileName = filename;
		InvokeAsync(this.StateHasChanged);



	}

	async Task RemoveFile()
	{
		selectedFileID = Guid.Empty;
		selectedFileName = "";
		InvokeAsync(this.StateHasChanged);

	}


	async Task SaveAnnotation()
	{
		try
		{
			await SessionPlay.Annotation_PutAsync(SessionPlay, sectionId, rowId.STR(), selectedFileID, message, Type);
			message = "";
			selectedFileID = Guid.Empty;
			selectedFileName = "";
	
			try
			{
				OnAnnotationSended.Invoke();
			}
			catch (Exception e2x)
			{
			}
	
		}
		catch (Exception ex)
		{
	
			ex.Notify(NotificationService);
		}
	}




}

Convención

  • Uso de nameof Siempre que se indique una propiedad por su nombre (por ejemplo, en columnas, filtros o bindings), debe usarse nameof para mantener el código seguro ante refactors y facilitar la navegación.

  • Botones asíncronos con IsBusy Todos los botones que ejecuten acciones asíncronas mediante el evento Click deben incluir el indicador IsBusy para proporcionar feedback visual al usuario mientras se procesa la acción.

  • Nombres de secciones según licencia del usuario Se deben conservar los nombres de las secciones tal como aparecen en la licencia del usuario. Por ejemplo: GetPaises es correcto y no se considera una mezcla de idiomas, ya que "Paises" es el identificador personalizado del usuario en su entorno MyDinaup.

  • Fechas y Horas siempre UTC

  • Fechas sin Horas Siempre DateOnly

Última actualización