mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 09:33:13 +00:00
Replace all hardcoded credentials with configuration-injected values:
- SQL Server connection strings -> ${CONNECTION_STRING} env-var placeholders (4 appsettings files)
- SendGrid API keys -> ${SENDGRID_API_KEY} (incl. commented copies in SendMessage.cs)
- JWT signing secret -> ${JWT_SECRET} (3 appsettings files)
- AWS access key pair in UploadFileHp.cs -> DI-injected IAmazonS3 (SDK default credential chain)
- Google Maps API keys in App.razor / Home.razor -> IConfiguration lookup
- Legacy SMTP credentials in SendMessage.cs comments -> placeholders
Add .env.example documenting required environment variables and a
Configuration & Secrets section in BACKEND_ARCHITECTURE.md.
All exposed credentials were rotated 2026-06-05 prior to this scrub.
Source: github-audit-report.md Criticals 1-2 (Agent A4).
Verified: dotnet build 0 errors; secret-pattern grep clean.
587 lines
27 KiB
Text
587 lines
27 KiB
Text
@page "/"
|
|
<Header pagename="Home" icon='<i class="material-icons-round opacity-10">home</i>'></Header>
|
|
<style>
|
|
.rz-progressbar-circular-md {
|
|
width: 5rem;
|
|
height: 5rem;
|
|
}
|
|
</style>
|
|
<AuthorizeView Roles="Admin,Manager">
|
|
<Authorized>
|
|
<div class="row">
|
|
<div class="col-md-8 col-lg-8 position-relative mt-3">
|
|
<div class="" style="background-image:url('./assets/img/bg.png'); background-size: cover; background-position: center;height: 140px; width:100%; background-repeat:no-repeat; border-radius:10px;">
|
|
<div class="text-white px-4 py-3">
|
|
<span class="d-block mb-2 fw-bold">
|
|
Welcome Back,
|
|
<img src="./assets/img/waving-hand-svgrepo-com.png" alt="Waving Hand" style="height: 24px; margin-left: 5px;" />
|
|
</span>
|
|
<span class="d-block fw-bold text-info">John Doe,</span>
|
|
<span class="d-block text-sm">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 col-lg-4 px-2 py-3">
|
|
<div class="card">
|
|
<div class="row row px-3 py-3">
|
|
<div class="col-7">
|
|
<span class="d-block fw-bold text-xs">In Progress Work Orders</span>
|
|
<span class="d-block fw-bolder pt-3" style="font-size: 22px;">@DashboardViewModel.Inprogress</span>
|
|
</div>
|
|
<div class="col-5">
|
|
<img class="img-fluid pt-3" src="./assets/img/Progress-Chart-Sparkline.png" width="72" />
|
|
</div>
|
|
</div>
|
|
<div class="d-flex px-2 py-2">
|
|
<img src="./assets/img/arrow-icon.png" class="me-2" />
|
|
<p class="mb-0 text-sm"> <span class="text-white"></span> Updated Now </p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12 pt-3">
|
|
<div class="card">
|
|
<div class="row g-3 px-3 py-3">
|
|
<div class="col-12 col-md d-flex align-items-center justify-content-center">
|
|
<div class="me-3">
|
|
<RadzenProgressBarCircular ProgressBarStyle="ProgressBarStyle.Success" @bind-Value="@DashboardViewModel.Sev1Per" />
|
|
</div>
|
|
<h6 class="mt-2 text-center">
|
|
<span>@DashboardViewModel.Sev1</span>
|
|
<span class="d-block text-body text-sm">Sev 1</span>
|
|
</h6>
|
|
</div>
|
|
|
|
<div class="col-12 col-md d-flex align-items-center justify-content-center">
|
|
<div class="me-3">
|
|
<RadzenProgressBarCircular ProgressBarStyle="ProgressBarStyle.Primary" @bind-Value="@DashboardViewModel.Sev2Per" />
|
|
</div>
|
|
<h6 class="mt-2 text-center">
|
|
<span>@DashboardViewModel.Sev2</span>
|
|
<span class="d-block text-body text-sm">Sev 2</span>
|
|
</h6>
|
|
</div>
|
|
|
|
<div class="col-12 col-md d-flex align-items-center justify-content-center">
|
|
<div class="me-3">
|
|
<RadzenProgressBarCircular ProgressBarStyle="ProgressBarStyle.Info" @bind-Value="@DashboardViewModel.Sev3Per" />
|
|
</div>
|
|
<h6 class="mt-2 text-center">
|
|
<span>@DashboardViewModel.Sev3</span>
|
|
<span class="d-block text-body text-sm">Sev 3</span>
|
|
</h6>
|
|
</div>
|
|
|
|
<div class="col-12 col-md d-flex align-items-center justify-content-center">
|
|
<div class="me-3">
|
|
<RadzenProgressBarCircular ProgressBarStyle="ProgressBarStyle.Warning" @bind-Value="@DashboardViewModel.Sev4Per" />
|
|
</div>
|
|
<h6 class="mt-2 text-center">
|
|
<span>@DashboardViewModel.Sev4</span>
|
|
<span class="d-block text-body text-sm">Sev 4</span>
|
|
</h6>
|
|
</div>
|
|
|
|
<div class="col-12 col-md d-flex align-items-center justify-content-center">
|
|
<div class="me-3">
|
|
<RadzenProgressBarCircular ProgressBarStyle="ProgressBarStyle.Danger" @bind-Value="@DashboardViewModel.Sev5Per" />
|
|
</div>
|
|
<h6 class="mt-2 text-center">
|
|
<span>@DashboardViewModel.Sev5</span>
|
|
<span class="d-block text-body text-sm">Sev 5</span>
|
|
</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4 col-lg-4 pt-3">
|
|
<div class="card">
|
|
<div class="row row px-3 py-3">
|
|
<div class="col-7">
|
|
<span class="d-block fw-bold text-xs">Completed Work Orders</span>
|
|
<span class="d-block fw-bolder pt-3" style="font-size: 22px;">@DashboardViewModel.Completed</span>
|
|
</div>
|
|
<div class="col-5">
|
|
<img class="img-fluid pt-3" src="./assets/img/Complete-Chart-Sparkline.png" width="72" />
|
|
</div>
|
|
</div>
|
|
<div class="d-flex px-2 py-2">
|
|
<img src="./assets/img/arrow-icon.png" class="me-2" />
|
|
<p class="mb-0 text-sm"> <span class="text-white"> </span> Updated Now </p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 col-lg-4 px-2 py-3">
|
|
<div class="card">
|
|
<div class="row row px-3 py-3">
|
|
<div class="col-7">
|
|
<span class="d-block fw-bold text-xs">Open Work Orders</span>
|
|
<span class="d-block fw-bolder pt-3" style="font-size: 22px;">@DashboardViewModel.Open</span>
|
|
</div>
|
|
<div class="col-5">
|
|
<img class="img-fluid pt-3" src="./assets/img/Open-Chart-Sparkline.png" width="72" />
|
|
</div>
|
|
</div>
|
|
<div class="d-flex px-2 py-2">
|
|
<img src="./assets/img/arrow-icon.png" class="me-2" />
|
|
<p class="mb-0 text-sm"> <span class="text-white"></span> Updated Now </p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 col-lg-4 pt-3">
|
|
<div class="card">
|
|
<div class="row row px-3 py-3">
|
|
<div class="col-7">
|
|
<span class="d-block fw-bold text-xs">On Hold Work Orders</span>
|
|
<span class="d-block fw-bolder pt-3" style="font-size: 22px;">@DashboardViewModel.Onhold</span>
|
|
</div>
|
|
<div class="col-5">
|
|
<img class="img-fluid pt-3" src="./assets/img/Hold-Chart-Sparkline.png" width="72" />
|
|
</div>
|
|
</div>
|
|
<div class="d-flex px-2 py-2">
|
|
<img src="./assets/img/arrow-icon.png" class="me-2" />
|
|
<p class="mb-0 text-sm"> <span class="text-white"></span> Updated Now </p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row">
|
|
<div class="col-md-8 px-2 pb-3">
|
|
<div class="card mt-3">
|
|
<div class="card-header p-3 pt-2">
|
|
<div class="d-block d-md-flex">
|
|
<div class="me-auto">
|
|
<h6 class="mb-0" style="opacity:0.6 !important;">Work Order Status</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-3 pt-0">
|
|
<div class="chart">
|
|
<canvas id="pChart" class="chart-canvas" height="331"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@* <div class="col-md-3 col-lg-3 px-2 py-3">
|
|
<div class="card h-100">
|
|
<div class="card-header pb-0 p-3">
|
|
<h6 class="mb-0">Orders</h6>
|
|
</div>
|
|
<div class="card-body mb-5">
|
|
<div class="row">
|
|
<div class="col-12 text-center">
|
|
<div class="chart">
|
|
<canvas id="chart-consumption" class="chart-canvas" height="190" @ref="chartCanvas"></canvas>
|
|
</div>
|
|
<h4 class="font-weight-bold mt-n8">
|
|
<span class="d-block text-body text-sm">Total</span>
|
|
<span>@DashboardViewModel.Total</span>
|
|
</h4>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer p-3">
|
|
<div class="d-flex">
|
|
<span class="badge badge-md badge-dot me-2 d-block text-start">
|
|
<i class="bg-info"></i>
|
|
<span class="text-xs">Open</span>
|
|
</span>
|
|
<span class="badge badge-md badge-dot me-2 d-block text-start">
|
|
<i class="bg-danger"></i>
|
|
<span class="text-xs">On Hold</span>
|
|
</span>
|
|
<span class="badge badge-md badge-dot me-2 d-block text-start">
|
|
<i class="bg-dark"></i>
|
|
<span class="text-xs">In Progress</span>
|
|
</span>
|
|
<span class="badge badge-md badge-dot me-2 d-block text-start">
|
|
<i class="bg-warning"></i>
|
|
<span class="text-xs">Done</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
*@ <div class="col-md-4 py-3">
|
|
<div class="card">
|
|
<h6 class="px-2 py-2" style="opacity:0.6 !important;">Work Orders</h6>
|
|
<div class="">
|
|
<table class="table align-items-center ">
|
|
<tbody>
|
|
@if (DashboardViewModel != null && DashboardViewModel.Dashboardworkorders != null)
|
|
{
|
|
foreach (var itm in DashboardViewModel.Dashboardworkorders)
|
|
{
|
|
var userNameInitials = "";
|
|
if (itm.UserName != null && !string.IsNullOrEmpty(itm.UserName))
|
|
{
|
|
var nameParts = itm.UserName.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
|
if (nameParts.Length > 1)
|
|
{
|
|
// Take the first letter of the first two words
|
|
userNameInitials = string.Concat(nameParts[0][0], nameParts[1][0]).ToUpper();
|
|
}
|
|
else
|
|
{
|
|
// Take the first letter of the single word
|
|
userNameInitials = nameParts[0][0].ToString().ToUpper() + nameParts[0][1].ToString().ToUpper();
|
|
}
|
|
}
|
|
<tr>
|
|
<td class="w-80">
|
|
<div class="d-flex px-2 py-1 align-items-center">
|
|
<div>
|
|
@if (userNameInitials != "" && userNameInitials != null)
|
|
{
|
|
<span class="text-xs" style="border-radius:50%;background: #7dcced;color: black;padding:8px">@userNameInitials</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="text-xs" style="border-radius:50%;background: #7dcced;color: black;padding:8px">NA</span>
|
|
}
|
|
|
|
</div>
|
|
<div class="ms-4">
|
|
<p class="text-xs font-weight-bold mb-0 " style=" max-width: 180px; overflow: hidden; text-overflow: ellipsis;">@itm.Name</p>
|
|
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="w-20">
|
|
<div class="text-end">
|
|
@if (itm.Status == "Open")
|
|
{
|
|
<div class="btn btn-sm btn-outline-success ">Open</div>
|
|
}
|
|
else if (itm.Status == "On Hold")
|
|
{
|
|
<div class="btn btn-sm btn-outline-warning ">On Hold</div>
|
|
}
|
|
else if (itm.Status == "In Progress")
|
|
{
|
|
<div class="btn btn-sm btn-outline-danger">In Progress</div>
|
|
}
|
|
else if (itm.Status == "Done")
|
|
{
|
|
<div class="btn btn-sm btn-outline-info ">Done</div>
|
|
}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
}
|
|
}
|
|
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="row">
|
|
<div class="col-6">
|
|
<h6 class="px-4 pt-3" style="opacity:0.6 !important;">Map</h6>
|
|
<p class="px-4 pb-3 fst-italic">Lorem ipsum is a dummy text</p>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="input-group input-group-outline px-4 pt-3 pb-3">
|
|
<RadzenTextBox style="width: 100%; " @ref="addressBoxRadzenTextBox" Name="BusinessAddress1" value="@mapval" Change=@GoogleAddressAutoComplete />
|
|
</div>
|
|
</div>
|
|
<div class="col-12 px-5 mb-4">
|
|
<RadzenGoogleMap ApiKey="@Configuration["GoogleMaps:ApiKey"]" @ref=map style="height: 400px" Options=@(new Dictionary<string, object> { { "disableDoubleClickZoom", true } })
|
|
Zoom=@zoom Center=@(new GoogleMapPosition() { Lat = 42.6977, Lng = 23.3219 }) MapClick=@OnMapClick>
|
|
<Markers>
|
|
@foreach (var itm in setmappins)
|
|
{
|
|
<RadzenGoogleMapMarker Title="@itm.title" Label="@itm.title" Position=@(new GoogleMapPosition() { Lat = itm.lat, Lng = itm.lon }) />
|
|
}
|
|
<RadzenGoogleMapMarker Title="Paris " Label="Paris" Position=@(new GoogleMapPosition() { Lat = 48.8566, Lng = 2.3522 }) />
|
|
@if (showMadridMarker)
|
|
{
|
|
<RadzenGoogleMapMarker Title="Khayaban e Sir Syed, Rawalpindi, Punjab, Pakistan " Label="Madrid" Position=@(new GoogleMapPosition() { Lat = 40.4168, Lng = -3.7038 }) />
|
|
}
|
|
</Markers>
|
|
</RadzenGoogleMap>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</Authorized>
|
|
|
|
</AuthorizeView>
|
|
@if (isloading)
|
|
{
|
|
<div class="loading">Loading…</div>
|
|
}
|
|
|
|
@using GoogleMapsComponents.Maps.Places
|
|
@using Microsoft.AspNetCore.Authorization
|
|
@using Radzen
|
|
@using Radzen.Blazor
|
|
@using Microsoft.JSInterop
|
|
@using System.Text.Json
|
|
@using System.Globalization
|
|
@using SeaHavenIndustries.Data.IRepository
|
|
@using SeaHavenIndustries.Helper.ViewModels
|
|
@using global::Data.SeaHavenIndustries
|
|
@inject IJSRuntime JSRuntime
|
|
@attribute [Authorize]
|
|
@inject NavigationManager Navigation
|
|
@inject IWorkOrderRepository _service;
|
|
@inject IAccountRepository _aservice;
|
|
@inject ISettingsRepository _Seservice;
|
|
@inject HttpClient Http
|
|
@inject IConfiguration Configuration
|
|
|
|
@code {
|
|
private IEnumerable<ApplicationUser>? users { get; set; }
|
|
private bool isloading = false;
|
|
private IEnumerable<LocationViewModel>? droplocations { get; set; }
|
|
private IEnumerable<LocationViewModel>? locationslist { get; set; }
|
|
private DashboardViewModel DashboardViewModel = new DashboardViewModel();
|
|
private ElementReference chartCanvas;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
isloading = true;
|
|
await Task.Delay(50);
|
|
DashboardViewModel = await _Seservice.Getdashboarddata();
|
|
isloading = false;
|
|
}
|
|
|
|
RadzenDataGrid<Locations> grid;
|
|
int count;
|
|
int eid;
|
|
|
|
private ElementReference addressBox;
|
|
private RadzenTextBox? addressBoxRadzenTextBox;
|
|
private Autocomplete? autocomplete;
|
|
private string? placeData;
|
|
private string? formattedAddress;
|
|
IEnumerable<Locations> locations;
|
|
List<setmappin> setmappins = new List<setmappin>();
|
|
bool isLoading = false;
|
|
private Locations model = new Locations();
|
|
string mapval = "";
|
|
List<string> titles = new List<string> { "Sales Representative", "Vice President, Sales", "Sales Manager", "Inside Sales Coordinator" };
|
|
IEnumerable<string> selectedTitles;
|
|
int zoom = 3;
|
|
bool showMadridMarker;
|
|
// EventConsole console;
|
|
RadzenGoogleMap map;
|
|
|
|
void OnMapClick(GoogleMapClickEventArgs args)
|
|
{
|
|
// Check if any marker was clicked
|
|
var clickedMarker = setmappins.FirstOrDefault(marker =>
|
|
Math.Round(marker.lat, 4) == Math.Round(args.Position.Lat, 4) &&
|
|
Math.Round(marker.lon, 4) == Math.Round(args.Position.Lng, 4));
|
|
|
|
if (clickedMarker != null)
|
|
{
|
|
// Access the title of the clicked marker
|
|
string clickedTitle = clickedMarker.title;
|
|
|
|
// Perform actions based on the clicked title
|
|
Console.WriteLine($"Marker with title '{clickedTitle}' clicked.");
|
|
|
|
// Add additional logic as needed
|
|
}
|
|
else
|
|
{
|
|
// No marker was clicked directly on the map
|
|
Console.WriteLine("No marker clicked directly on the map.");
|
|
}
|
|
}
|
|
|
|
|
|
// async Task OnMarkerClick(RadzenGoogleMapMarker marker)
|
|
// {
|
|
// var message = $"Custom information about <b>{marker.Title}</b>";
|
|
|
|
// var code = $@"
|
|
// var map = Radzen['{map.UniqueID}'].instance;
|
|
// var marker = map.markers.find(m => m.title == '{marker.Title}');
|
|
// if(window.infoWindow) {{window.infoWindow.close();}}
|
|
// window.infoWindow = new google.maps.InfoWindow({{content: '{message}'}});
|
|
// setTimeout(() => window.infoWindow.open(map, marker), 200);
|
|
// ";
|
|
|
|
// await JSRuntime.InvokeVoidAsync("eval", code);
|
|
// }
|
|
|
|
async Task OnSelectedTitlesChange(object value)
|
|
{
|
|
if (selectedTitles != null && !selectedTitles.Any())
|
|
{
|
|
selectedTitles = null;
|
|
}
|
|
|
|
await grid.FirstPage();
|
|
}
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
{
|
|
|
|
await JSRuntime.InvokeVoidAsync("initScripts");
|
|
addressBox = addressBoxRadzenTextBox.Element;
|
|
await SetupGoogleAddressAutoComplete();
|
|
await EnsureDataLoadedAndInvokeJS();
|
|
|
|
}
|
|
}
|
|
private async Task EnsureDataLoadedAndInvokeJS()
|
|
{
|
|
while (DashboardViewModel.Sev1 == null || DashboardViewModel.Sev1Per == null)
|
|
{
|
|
await Task.Delay(10);
|
|
}
|
|
|
|
// Once data is loaded, call the JavaScript function
|
|
await JSRuntime.InvokeVoidAsync("CircleGraph", DashboardViewModel.Sev1, DashboardViewModel.Sev1Per);
|
|
await JSRuntime.InvokeVoidAsync("CircleGraph2", DashboardViewModel.Sev2, DashboardViewModel.Sev2Per);
|
|
await JSRuntime.InvokeVoidAsync("CircleGraph3", DashboardViewModel.Sev3, DashboardViewModel.Sev3Per);
|
|
await JSRuntime.InvokeVoidAsync("CircleGraph4", DashboardViewModel.Sev4, DashboardViewModel.Sev4Per);
|
|
await JSRuntime.InvokeVoidAsync("CircleGraph5", DashboardViewModel.Sev5, DashboardViewModel.Sev5Per);
|
|
List<int> list = new List<int>();
|
|
list.Add(DashboardViewModel.Open.Value);
|
|
list.Add(DashboardViewModel.Onhold.Value);
|
|
list.Add(DashboardViewModel.Inprogress.Value);
|
|
list.Add(DashboardViewModel.Completed.Value);
|
|
//await JSRuntime.InvokeVoidAsync("ConsumptionGraph", list);
|
|
await JSRuntime.InvokeVoidAsync("PerformanceChartHome", list);
|
|
|
|
}
|
|
protected async Task UpdateChart()
|
|
{
|
|
await JSRuntime.InvokeVoidAsync("initScripts");
|
|
addressBox = addressBoxRadzenTextBox.Element;
|
|
await SetupGoogleAddressAutoComplete();
|
|
await JSRuntime.InvokeVoidAsync("ConsumptionGraph");
|
|
await JSRuntime.InvokeVoidAsync("CircleGraph", DashboardViewModel.Sev1, DashboardViewModel.Sev1Per);
|
|
await JSRuntime.InvokeVoidAsync("CircleGraph2");
|
|
await JSRuntime.InvokeVoidAsync("PerformanceChartHome");
|
|
|
|
}
|
|
|
|
|
|
private async Task GoogleAddressAutoComplete()
|
|
{
|
|
|
|
System.Threading.Thread.Sleep(500); // this is a hack. with no wait place was null first time
|
|
|
|
var place = await this.autocomplete.GetPlace();
|
|
var placeDataTest = JsonSerializer.Serialize(place);
|
|
if (place?.AddressComponents == null)
|
|
{
|
|
// this.message = "No results available for " + place?.Name;
|
|
}
|
|
else
|
|
{
|
|
var placesd = await this.autocomplete.GetPlace();
|
|
|
|
model.Name = place.FormattedAddress;
|
|
|
|
var placeId = place.PlaceId;
|
|
var details = await GetLatLng(placeId);
|
|
var streetNumber = place.AddressComponents.FirstOrDefault(a => a.Types != null && a.Types.Contains("street_number"))?.LongName;
|
|
var street = place.AddressComponents.FirstOrDefault(a => a.Types != null && (a.Types.Contains("route") || a.Types.Contains("street_address")))?.LongName;
|
|
var city = place.AddressComponents.FirstOrDefault(a => a.Types != null && (a.Types.Contains("locality") || a.Types.Contains("neighborhood")))?.LongName;
|
|
var state = place.AddressComponents.FirstOrDefault(a => a.Types != null && a.Types.Contains("administrative_area_level_1"))?.LongName;
|
|
var country = place.AddressComponents.FirstOrDefault(a => a.Types != null && a.Types.Contains("country"))?.LongName;
|
|
var postcode = place.AddressComponents.FirstOrDefault(a => a.Types != null && a.Types.Contains("postal_code"))?.LongName;
|
|
formattedAddress = place.FormattedAddress;
|
|
// model.Title = place.AddressComponents.FirstOrDefault()?.LongName;
|
|
//setmappins.Add(new setmappin{title=place.Name,lat=place.l})
|
|
placeData = JsonSerializer.Serialize(place);
|
|
}
|
|
}
|
|
private async Task<(double Latitude, double Longitude)> GetLatLng(string placeId)
|
|
{
|
|
var apiKey = Configuration["GoogleMaps:ApiKey"]; // Injected from configuration (env var GoogleMaps__ApiKey / user-secrets)
|
|
//var apiUrl = $"https://places.googleapis.com/v1/places/{placeId}";
|
|
var apiUrl = $"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=Vict&language=pt_BR&types=%28cities%29&key={apiKey}";
|
|
// var apiUrl = $"https://places.googleapis.com/v1/places/{placeId}?fields=id,displayName&key={apiKey}";
|
|
|
|
|
|
try
|
|
{
|
|
var response = await Http.GetFromJsonAsync<GeocodingApiResponse>(apiUrl);
|
|
|
|
if (response?.status == "OK" && response.results?.Count > 0)
|
|
{
|
|
var location = response.results[0].geometry?.location;
|
|
if (location != null)
|
|
{
|
|
return (location.lat, location.lng);
|
|
}
|
|
}
|
|
|
|
Console.WriteLine($"Error retrieving geocode. Status: {response?.status}");
|
|
return default;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error fetching geocode: {ex.Message}");
|
|
return default;
|
|
}
|
|
}
|
|
public class GeocodingApiResponse
|
|
{
|
|
public string status { get; set; }
|
|
public List<GeocodingResult> results { get; set; }
|
|
}
|
|
public class GeocodingResult
|
|
{
|
|
public Geometry geometry { get; set; }
|
|
}
|
|
|
|
|
|
public class Geometry
|
|
{
|
|
public Location location { get; set; }
|
|
}
|
|
|
|
public class Location
|
|
{
|
|
public double lat { get; set; }
|
|
public double lng { get; set; }
|
|
}
|
|
private async Task SetupGoogleAddressAutoComplete()
|
|
{
|
|
this.autocomplete = await Autocomplete.CreateAsync(JSRuntime, addressBox, new AutocompleteOptions
|
|
{
|
|
StrictBounds = false,
|
|
});
|
|
await this.autocomplete.SetFields(new[] { "address_components", "place_id", "formatted_address" });
|
|
}
|
|
@inject NorthwindService service
|
|
|
|
public void Export(string type)
|
|
{
|
|
service.Export("locations", type, new Query()
|
|
{
|
|
OrderBy = grid.Query.OrderBy,
|
|
Filter = grid.Query.Filter,
|
|
Select = string.Join(",", grid.ColumnsCollection.Where(c => c.GetVisible() && !string.IsNullOrEmpty(c.Property))
|
|
.Select(c => c.Property.Contains(".") ? $"{c.Property} as {c.Property.Replace(".", "_")}" : c.Property))
|
|
});
|
|
}
|
|
public class setmappin
|
|
{
|
|
public string title { get; set; }
|
|
public double lat { get; set; }
|
|
public double lon { get; set; }
|
|
}
|
|
}
|