Adam's last changes
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"DeveloperDbConnection": "Data Source=194.164.235.47;Initial Catalog=TIAM_DEV;Trusted_Connection=false;Encrypt=false;TrustServerCertificate=True;Connect Timeout=200;User ID=Anata_Development_Team;Password=v6f_?xNfg9N1;MultipleActiveResultSets=true"
|
||||
"DeveloperDbConnection": "Data Source=195.26.231.218;Initial Catalog=TIAM_DEV;Trusted_Connection=false;Encrypt=false;TrustServerCertificate=True;Connect Timeout=200;User ID=sa;Password=v6f_?xNfg9N1;MultipleActiveResultSets=true"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
|
|
|||
|
|
@ -384,6 +384,15 @@ namespace TIAM.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Who we are.
|
||||
/// </summary>
|
||||
public static string Index_WhoWeAre {
|
||||
get {
|
||||
return ResourceManager.GetString("Index.WhoWeAre", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Login.
|
||||
/// </summary>
|
||||
|
|
@ -456,6 +465,24 @@ namespace TIAM.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Book now!.
|
||||
/// </summary>
|
||||
public static string NavMenu_BookNow {
|
||||
get {
|
||||
return ResourceManager.GetString("NavMenu.BookNow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Contact.
|
||||
/// </summary>
|
||||
public static string NavMenu_Contact {
|
||||
get {
|
||||
return ResourceManager.GetString("NavMenu.Contact", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Events.
|
||||
/// </summary>
|
||||
|
|
@ -465,6 +492,15 @@ namespace TIAM.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Explore the region.
|
||||
/// </summary>
|
||||
public static string NavMenu_ExploreTheRegion {
|
||||
get {
|
||||
return ResourceManager.GetString("NavMenu.ExploreTheRegion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to FormulaOne.
|
||||
/// </summary>
|
||||
|
|
@ -483,6 +519,15 @@ namespace TIAM.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to How it works.
|
||||
/// </summary>
|
||||
public static string NavMenu_HowItWorks {
|
||||
get {
|
||||
return ResourceManager.GetString("NavMenu.HowItWorks", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Login.
|
||||
/// </summary>
|
||||
|
|
@ -510,6 +555,15 @@ namespace TIAM.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Signature ride packages.
|
||||
/// </summary>
|
||||
public static string NavMenu_SignatureRidePackages {
|
||||
get {
|
||||
return ResourceManager.GetString("NavMenu.SignatureRidePackages", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sign out.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -249,15 +249,27 @@
|
|||
<data name="NavMenu.Admin" xml:space="preserve">
|
||||
<value>Adminisztráció</value>
|
||||
</data>
|
||||
<data name="NavMenu.BookNow" xml:space="preserve">
|
||||
<value>Foglalj most!</value>
|
||||
</data>
|
||||
<data name="NavMenu.Contact" xml:space="preserve">
|
||||
<value>Kapcsolat</value>
|
||||
</data>
|
||||
<data name="NavMenu.Events" xml:space="preserve">
|
||||
<value>Események</value>
|
||||
</data>
|
||||
<data name="NavMenu.ExploreTheRegion" xml:space="preserve">
|
||||
<value>Fedezd fel a térséget</value>
|
||||
</data>
|
||||
<data name="NavMenu.FormulaOne" xml:space="preserve">
|
||||
<value>Forma1</value>
|
||||
</data>
|
||||
<data name="NavMenu.Home" xml:space="preserve">
|
||||
<value>Főoldal</value>
|
||||
</data>
|
||||
<data name="NavMenu.HowItWorks" xml:space="preserve">
|
||||
<value>Miben más a TourIam</value>
|
||||
</data>
|
||||
<data name="NavMenu.Login" xml:space="preserve">
|
||||
<value>Belépés</value>
|
||||
</data>
|
||||
|
|
@ -267,6 +279,9 @@
|
|||
<data name="NavMenu.Settings" xml:space="preserve">
|
||||
<value>Beállítások</value>
|
||||
</data>
|
||||
<data name="NavMenu.SignatureRidePackages" xml:space="preserve">
|
||||
<value>Túrák</value>
|
||||
</data>
|
||||
<data name="NavMenu.SignOut" xml:space="preserve">
|
||||
<value>Kijelentkezés</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -225,6 +225,9 @@
|
|||
<data name="Index.Transfer.Desc" xml:space="preserve">
|
||||
<value>Do you need a lift? Book a transfer now!</value>
|
||||
</data>
|
||||
<data name="Index.WhoWeAre" xml:space="preserve">
|
||||
<value>Who we are</value>
|
||||
</data>
|
||||
<data name="Login" xml:space="preserve">
|
||||
<value>Login</value>
|
||||
</data>
|
||||
|
|
@ -249,15 +252,27 @@
|
|||
<data name="NavMenu.Admin" xml:space="preserve">
|
||||
<value>Admin</value>
|
||||
</data>
|
||||
<data name="NavMenu.BookNow" xml:space="preserve">
|
||||
<value>Book now!</value>
|
||||
</data>
|
||||
<data name="NavMenu.Contact" xml:space="preserve">
|
||||
<value>Contact</value>
|
||||
</data>
|
||||
<data name="NavMenu.Events" xml:space="preserve">
|
||||
<value>Events</value>
|
||||
</data>
|
||||
<data name="NavMenu.ExploreTheRegion" xml:space="preserve">
|
||||
<value>Explore the region</value>
|
||||
</data>
|
||||
<data name="NavMenu.FormulaOne" xml:space="preserve">
|
||||
<value>FormulaOne</value>
|
||||
</data>
|
||||
<data name="NavMenu.Home" xml:space="preserve">
|
||||
<value>Home</value>
|
||||
</data>
|
||||
<data name="NavMenu.HowItWorks" xml:space="preserve">
|
||||
<value>How it works</value>
|
||||
</data>
|
||||
<data name="NavMenu.Login" xml:space="preserve">
|
||||
<value>Login</value>
|
||||
</data>
|
||||
|
|
@ -267,6 +282,9 @@
|
|||
<data name="NavMenu.Settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="NavMenu.SignatureRidePackages" xml:space="preserve">
|
||||
<value>Signature ride packages</value>
|
||||
</data>
|
||||
<data name="NavMenu.SignOut" xml:space="preserve">
|
||||
<value>Sign out</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -19,10 +19,6 @@
|
|||
|
||||
At Tour I Am, we understand the importance of a seamless and comfortable journey for our valued customers. Whether you're arriving in Budapest for business or leisure, our dedicated team ensures that your transfer experience is hassle-free and enjoyable.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
<p class="mb-4 pe-xl-12 ">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
@page "/explore-the-region"
|
||||
@using TIAMSharedUI.Pages.Components
|
||||
@using TIAMSharedUI.Shared
|
||||
@using TIAMWebApp.Shared.Application.Models
|
||||
@using TIAMWebApp.Shared.Application.Models.ClientSide.UI
|
||||
@using TIAMWebApp.Shared.Application.Services
|
||||
@inject BlogService BlogService
|
||||
|
||||
<HeroSlider SliderItems="@sliders" PBottom="50px" Height="30vh"></HeroSlider>
|
||||
|
||||
<div class="container pb-5">
|
||||
<h1 class="mb-4">Blog Posts</h1>
|
||||
<p class="lead">
|
||||
Discover the hidden gems and breathtaking landscapes of Hungary! Our blog features inspiring destinations, scenic routes, and must-see spots that invite you to explore the natural beauty of this enchanting country. Whether you're planning your next adventure or simply dreaming of a getaway, let these stories guide your journey.
|
||||
</p>
|
||||
|
||||
@if (posts == null)
|
||||
{
|
||||
<p>Loading blog posts...</p>
|
||||
}
|
||||
else if (posts.Count == 0)
|
||||
{
|
||||
<p>No blog posts found.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* <section class="game-section" style="max-width: 100%; overflow-x:hidden"> *@
|
||||
<div class="row">
|
||||
@foreach (var post in posts)
|
||||
{
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card rounded h-100 shadow">
|
||||
@if (!string.IsNullOrEmpty(post.CoverImage))
|
||||
{
|
||||
<img src="@post.CoverImage" class="card-img-top" alt="@post.Title">
|
||||
}
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">@post.Title</h5>
|
||||
@if (!string.IsNullOrEmpty(post.Lead))
|
||||
{
|
||||
|
||||
<p class="card-title">@post.Lead</p>
|
||||
}
|
||||
@if (post.Tags?.Count > 0)
|
||||
{
|
||||
<p class="card-text text-muted small">
|
||||
@string.Join(", ", post.Tags)
|
||||
</p>
|
||||
}
|
||||
<a href="@($"/blogpost/{post.Id}")" class="btn btn-primary mt-auto">Read More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@* <a href="@($"/blogpost/{post.Id}")">
|
||||
<div class="item" style="background-image: url(@post.CoverImage);">
|
||||
<div class="item-desc" style="height: 100%; background-color: rgba(0,0,0,0.3);">
|
||||
<h3 class="card-title" style="margin-top: 80px;">@post.Title</h3>
|
||||
<p>@post.Tags</p>
|
||||
<p>@post.Lead</p>
|
||||
<p>@post.Id</p>
|
||||
<div class="glass" style="position: absolute; top:0px; left:0px; padding:10px; width:100%">
|
||||
<span>by</span>
|
||||
<svg width="30%" viewBox="0 0 112 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Logó"><path d="M99.636,13.719c-0.569,1.804 -1.048,3.323 -1.542,4.889l3.215,0c-0.549,-1.606 -1.062,-3.104 -1.673,-4.889m11.754,11.595c-2.575,0 -4.95,0.027 -7.323,-0.036c-0.267,-0.007 -0.637,-0.476 -0.76,-0.805c-0.592,-1.582 -0.567,-1.591 -2.263,-1.592c-1.452,0 -2.903,0 -4.316,0c-0.913,2.437 -0.913,2.437 -3.473,2.437l-3.674,0c2.395,-6.454 4.723,-12.727 7.07,-19.049c2.38,0 4.725,-0.037 7.065,0.044c0.312,0.01 0.736,0.565 0.884,0.953c2.099,5.504 4.159,11.022 6.228,16.537c0.16,0.426 0.318,0.854 0.562,1.511" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><path d="M89.146,6.271c-2.087,2.974 -4.038,5.753 -6.039,8.604c1.968,3.411 3.942,6.833 6.02,10.434c-2.908,0 -5.596,0.014 -8.284,-0.025c-0.228,-0.003 -0.526,-0.317 -0.663,-0.559c-0.559,-0.986 -1.068,-2.001 -1.698,-3.202c-0.649,1.062 -1.269,1.982 -1.786,2.957c-0.348,0.658 -0.797,0.87 -1.523,0.853c-1.934,-0.045 -3.869,-0.015 -6.019,-0.015c1.733,-2.622 3.245,-5.103 4.963,-7.431c0.923,-1.251 0.94,-2.138 0.063,-3.432c-1.751,-2.579 -3.285,-5.306 -4.969,-8.072c0.297,-0.045 0.516,-0.107 0.735,-0.108c2.344,-0.007 4.689,0.018 7.032,-0.019c0.622,-0.009 0.972,0.195 1.247,0.741c0.475,0.94 1.019,1.846 1.607,2.898c0.707,-1.095 1.311,-2.088 1.982,-3.033c0.192,-0.269 0.577,-0.558 0.879,-0.565c2.058,-0.05 4.117,-0.026 6.453,-0.026" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><path d="M59.654,6.335l0,5.691l-5.73,0l0,13.193l-7.515,0l0,-13.147l-5.731,0l0,-5.737l18.976,0Z" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><rect x="61.187" y="6.342" width="6.698" height="18.911" style="fill:#fff;fill-opacity:0.8;"></rect><path d="M28.354,21.366c0.306,4.69 -5.098,5.2 -6.831,2.855c-2.169,-2.934 -5.05,-2.797 -6.619,-1.438c-1.569,-1.359 -4.449,-1.496 -6.618,1.438c-1.733,2.345 -7.137,1.835 -6.831,-2.855c0,0 -2.763,4.258 0.611,8.055c3.263,3.671 8.689,3.784 12.032,0.918c0.305,-0.262 0.568,-0.53 0.806,-0.802c0.238,0.272 0.501,0.54 0.807,0.802c3.343,2.866 8.768,2.753 12.031,-0.918c3.375,3.796 0.612,-8.055 0.612,-8.055Z" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><path d="M28.354,11.37c0.306,-4.69 -5.098,-5.2 -6.831,-2.855c-2.169,2.934 -5.05,2.797 -6.619,1.438c-1.569,1.359 -4.449,1.496 -6.618,-1.438c-1.733,-2.345 -7.137,-1.835 -6.831,2.855c0,0 -2.763,-4.258 0.611,-8.055c3.263,-3.671 8.689,-3.784 12.032,-0.918c0.305,0.262 0.568,0.53 0.806,0.802c0.238,-0.272 0.501,-0.54 0.807,-0.802c3.343,-2.866 8.768,-2.753 12.031,0.918c3.375,3.796 0.612,8.055 0.612,8.055Z" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><circle cx="12.241" cy="16.368" r="1.332" style="fill:#fff;fill-opacity:0.8;"></circle><circle cx="16.902" cy="16.368" r="1.332" style="fill:#fff;fill-opacity:0.8;"></circle></g></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a> *@
|
||||
}
|
||||
@* </section> *@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<CallToActionComponent></CallToActionComponent>
|
||||
|
||||
@code {
|
||||
private List<BlogPostMetadata>? posts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
posts = await BlogService.GetAllPostsAsync();
|
||||
}
|
||||
|
||||
public List<HeroSliderItem> sliders = new()
|
||||
{
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1551867633-194f125bddfa?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Explore Our Programs",
|
||||
ButtonUrl= "/signature-ride-packages"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1549877452-9c387954fbc2?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Book now",
|
||||
ButtonUrl= "/transfer"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1507622560124-621e26755fb8?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "",
|
||||
ButtonUrl= ""
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
@page "/blogpost/{PostId}"
|
||||
@using TIAMSharedUI.Pages.Components
|
||||
@using TIAMSharedUI.Shared
|
||||
@using TIAMWebApp.Shared.Application.Models.ClientSide.UI
|
||||
@using TIAMWebApp.Shared.Application.Services
|
||||
@inject HttpClient Http
|
||||
@inject BlogService BlogService
|
||||
|
||||
<HeroSlider SliderItems="@sliders" PBottom="50px" Height="30vh"></HeroSlider>
|
||||
|
||||
@if (Content == null)
|
||||
{
|
||||
<p>Loading post...</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="container">
|
||||
|
||||
<div class="card p-3" @onclick:stopPropagation>
|
||||
@((MarkupString)Content)
|
||||
</div>
|
||||
|
||||
@if (Tags?.Count > 0)
|
||||
{
|
||||
<p class="mt-3 text-muted small">
|
||||
Tags: @string.Join(", ", Tags)
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<CallToActionComponent></CallToActionComponent>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string PostId { get; set; }
|
||||
|
||||
private string? Content;
|
||||
private string? Title;
|
||||
private string? CoverImage;
|
||||
private List<string>? Tags;
|
||||
|
||||
|
||||
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var post = await BlogService.GetPostByIdAsync(PostId);
|
||||
if (post != null)
|
||||
{
|
||||
Title = post.Title;
|
||||
CoverImage = post.CoverImage;
|
||||
Tags = post.Tags;
|
||||
|
||||
// ✅ Extract fileId from DriveLink:
|
||||
var fileId = ExtractGoogleDriveFileId(post.DriveLink);
|
||||
if (!string.IsNullOrEmpty(fileId))
|
||||
{
|
||||
// string apiKey = "AIzaSyBLKx4XFpgX97sULTbtpyKA2Ca_ANrjxxs"; // your existing key
|
||||
// // string downloadUrl = $"https://www.googleapis.com/drive/v3/files/{fileId}?alt=media&key={apiKey}";
|
||||
// string downloadUrl = $"https://drive.google.com/uc?export=download&id={fileId}";
|
||||
// Content = await Http.GetStringAsync(downloadUrl);
|
||||
|
||||
Content = null; // Optional: clear previous content while loading new
|
||||
|
||||
try
|
||||
{
|
||||
Content = await Http.GetStringAsync($"/api/blog/postcontent/{PostId}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Content = "<p class='text-danger'>Failed to load blog post.</p>";
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Content = "<p class='text-danger'>Invalid Google Drive link in post metadata.</p>";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
Content = "<p class='text-danger'>Failed to load blog post. " + e.ToString()+ "</p>";
|
||||
}
|
||||
}
|
||||
|
||||
private string? ExtractGoogleDriveFileId(string driveLink)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Works with typical Google Drive URLs
|
||||
var match = System.Text.RegularExpressions.Regex.Match(driveLink, @"\/d\/([^\/]+)");
|
||||
return match.Success ? match.Groups[1].Value : null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<HeroSliderItem> sliders = new()
|
||||
{
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1551867633-194f125bddfa?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Explore Our Programs",
|
||||
ButtonUrl= "/signature-ride-packages"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1549877452-9c387954fbc2?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Book now",
|
||||
ButtonUrl= "/transfer"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1507622560124-621e26755fb8?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "",
|
||||
ButtonUrl= ""
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
<div class="container text-center py-4 py-md-6 rounded-3">
|
||||
<div class="lc-block card border-0 text-center rounded p-4 p-lg-6" style="background:url(https://cdn.livecanvas.com/media/backgrounds/fffuelco/ffflux.svg) center / cover no-repeat;">
|
||||
<div class="container-fluid text-center pt-4 py-md-6">
|
||||
<div class="lc-block card border-0 text-center py-5" style="background: #4c327b; background-position: center; background-size: cover;">
|
||||
<div class="row card-body mb-3 mb-lg-4">
|
||||
<div class="col-xl-11 col-xxl-9 mx-auto">
|
||||
<div class="lc-block mb-4">
|
||||
<div editable="rich">
|
||||
<p class="text-white">JOIN OUR COMMUNITY</p>
|
||||
<p class="text-white">READY TO GO?</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lc-block">
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lc-block"><a class="btn btn-primary btn-lg" href="#" role="button">Get Started</a></div>
|
||||
<div class="lc-block"><a class="btn btn-primary btn-lg" href="#" role="button">Book now!</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<div class="item" style="background-image: url(@item.ImageUrl);">
|
||||
|
||||
<div class="item-desc" style="height: 100%; background-color: rgba(0,0,0,0.3);">
|
||||
<!--h3>item.Name</h3-->
|
||||
|
||||
|
||||
<h3 class="card-title" style="margin-top: 80px;">@item.Name</h3>
|
||||
<p>@item.DateAndTime</p>
|
||||
|
|
@ -37,14 +37,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="login">
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -102,4 +95,4 @@
|
|||
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
@page "/"
|
||||
@using AyCode.Interfaces.StorageHandlers;
|
||||
@using BlazorAnimation
|
||||
@using TIAMSharedUI.Shared.Components
|
||||
@using TIAMSharedUI.Shared.Components.BaseComponents
|
||||
@using TIAMWebApp.Shared.Application.Interfaces
|
||||
@using TIAMWebApp.Shared.Application.Models
|
||||
@using TIAMWebApp.Shared.Application.Models.ClientSide.UI
|
||||
@using AyCode.Services.Loggers
|
||||
@using TIAMSharedUI.Pages.Components;
|
||||
|
|
@ -24,8 +26,8 @@
|
|||
|
||||
<AuthComponent />
|
||||
|
||||
<HeroSlider SliderItems="@sliders" PBottom="50px" Height="30vh"></HeroSlider>
|
||||
@* <div class="container-fluid" style="position: relative; z-index: 2;">
|
||||
<HeroSlider SliderItems="@sliders" PBottom="50px" Height="70vh"></HeroSlider>
|
||||
@* <div class="container-fluid" style="position: relative; z-index: 2;">
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card bg-white p-3">
|
||||
|
|
@ -42,9 +44,7 @@
|
|||
</div> *@
|
||||
|
||||
|
||||
<div class="container-fluid" style="align-content: center;">
|
||||
|
||||
|
||||
@* <div class="container-fluid" style="align-content: center;">
|
||||
|
||||
<div class="text-center">
|
||||
|
||||
|
|
@ -52,49 +52,49 @@
|
|||
<h2>@localizer.GetString("Index.Subtitle")</h2>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
</div> *@
|
||||
|
||||
<NavLink class="nav-link col-md-6 col-lg-4 col-12" href="transfer">
|
||||
<Animation Effect="@Effect.FadeIn" Speed="@Speed.Fast" Delay="@TimeSpan.FromMilliseconds(250)">
|
||||
<div class="card m-3 my-card text-white">
|
||||
<img class="card-img" src="_content/TIAMSharedUI/images/m_transfer.jpg" alt="Card image">
|
||||
<div class="card-img-overlay">
|
||||
|
||||
<h3 class="card-title">@localizer.GetString("Index.Transfer")</h3>
|
||||
|
||||
<p class="card-text">@localizer.GetString("Index.Transfer.Desc")</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</Animation>
|
||||
</NavLink>
|
||||
<NavLink class="nav-link col-md-6 col-lg-4 col-12" href="tours">
|
||||
<Animation Effect="@Effect.FadeIn" Speed="@Speed.Fast" Delay="@TimeSpan.FromMilliseconds(250)">
|
||||
<div class="card m-3 my-card text-white">
|
||||
<img class="card-img" src="_content/TIAMSharedUI/images/m_tour.jpg" alt="Card image">
|
||||
<div class="card-img-overlay">
|
||||
<h3 class="card-title">@localizer.GetString("Index.Tours")</h3>
|
||||
<p class="card-text">@localizer.GetString("Index.Tours.Desc")</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</Animation>
|
||||
</NavLink>
|
||||
<NavLink class="nav-link col-md-6 col-lg-4 col-12" href="clubcards">
|
||||
<Animation Effect="@Effect.FadeIn" Speed="@Speed.Fast" Delay="@TimeSpan.FromMilliseconds(250)">
|
||||
<div class="card m-3 my-card text-white">
|
||||
<img class="card-img" src="_content/TIAMSharedUI/images/m_restaurant.jpg" alt="Card image">
|
||||
<div class="card-img-overlay">
|
||||
<h3 class="card-title">@localizer.GetString("Index.Clubcards")</h3>
|
||||
<p class="card-text">@localizer.GetString("Index.Clubcards.Desc")</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</Animation>
|
||||
</NavLink>
|
||||
<section class="py-5 bg-light">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<!-- Text Content -->
|
||||
<div class="col-md-6">
|
||||
<h2>@localizer.GetString("Index.WhoWeAre")</h2>
|
||||
<p>We’re not here to take over your trip. We’re here to help shape it — with smooth rides, flexible options, and curated experiences you can actually enjoy.</p>
|
||||
<p>Think of us as your private ride — whether you’re heading to the airport, a spa town, a winery, or somewhere off the map.</p>
|
||||
<p>You can follow one of our signature routes, or create your own. We adapt. You decide. We take care of the road.</p>
|
||||
<p>Our role changes with every guest. Sometimes we’re your calm in the chaos. Sometimes we’re your quiet local expert. Sometimes we’re just there — when you need someone who knows the way.</p>
|
||||
</div>
|
||||
<!-- Image Placeholder -->
|
||||
<div class="col-md-6 text-center">
|
||||
<img src="https://images.unsplash.com/photo-1563461660947-507ef49e9c47?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="Who we are" class="img-fluid rounded shadow">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<section class="py-5 bg-gradient">
|
||||
<ToursComponent />
|
||||
</section>
|
||||
<section class="py-5">
|
||||
<BlogComponent />
|
||||
</section>
|
||||
|
||||
<CallToActionComponent></CallToActionComponent>
|
||||
|
||||
<script>
|
||||
$(".custom-carousel").owlCarousel({
|
||||
autoWidth: true,
|
||||
loop: true
|
||||
});
|
||||
$(document).ready(function () {
|
||||
$(".custom-carousel .item").click(function () {
|
||||
$(".custom-carousel .item").not($(this)).removeClass("active");
|
||||
$(this).toggleClass("active");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@code {
|
||||
|
||||
|
|
@ -102,18 +102,27 @@
|
|||
{
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Welcome to TIAM",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1551867633-194f125bddfa?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1551867633-194f125bddfa?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Explore Our Programs",
|
||||
ButtonUrl= "/signature-ride-packages"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Welcome to TIAM",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1549877452-9c387954fbc2?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1549877452-9c387954fbc2?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Book now",
|
||||
ButtonUrl= "/transfer"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Welcome to TIAM",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1507622560124-621e26755fb8?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1507622560124-621e26755fb8?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "",
|
||||
ButtonUrl= ""
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -122,8 +131,8 @@
|
|||
public List<string> TransferIgnorList = new List<string>
|
||||
{
|
||||
"Id",
|
||||
"UserId",
|
||||
"ProductId",
|
||||
"UserId",
|
||||
"ProductId",
|
||||
"PaymentId",
|
||||
"FirstName",
|
||||
"LastName",
|
||||
|
|
@ -134,24 +143,37 @@
|
|||
"Revenue"
|
||||
};
|
||||
|
||||
/*protected override void OnAfterRender(bool isFirst)
|
||||
{
|
||||
message = " Target destination is " + slider.SliderElementId.ToString();
|
||||
}*/
|
||||
/*protected override void OnAfterRender(bool isFirst)
|
||||
{
|
||||
message = " Target destination is " + slider.SliderElementId.ToString();
|
||||
}*/
|
||||
|
||||
public async Task SubmitForm(object Result)
|
||||
{
|
||||
TransferWizardModel transferWizardModel = Result as TransferWizardModel;
|
||||
if(sessionService.IsAuthenticated)
|
||||
{
|
||||
transferWizardModel.UserId = sessionService.User.UserModelDto.Id;
|
||||
transferWizardModel.ProductId = sessionService.User.UserModelDto.Products.FirstOrDefault().Id;
|
||||
}
|
||||
transferWizardModel.Price = null;
|
||||
var transfer = await WizardProcessor.ProcessWizardAsync<TransferWizardModel>(transferWizardModel.GetType(), transferWizardModel);
|
||||
BrowserConsoleLogWriter.Info($"Submitted nested form: {Result.GetType().FullName}");
|
||||
navManager.NavigateTo("/transfer2/" + transfer.Id);
|
||||
TransferWizardModel transferWizardModel = Result as TransferWizardModel;
|
||||
if (sessionService.IsAuthenticated)
|
||||
{
|
||||
transferWizardModel.UserId = sessionService.User.UserModelDto.Id;
|
||||
transferWizardModel.ProductId = sessionService.User.UserModelDto.Products.FirstOrDefault().Id;
|
||||
}
|
||||
transferWizardModel.Price = null;
|
||||
var transfer = await WizardProcessor.ProcessWizardAsync<TransferWizardModel>(transferWizardModel.GetType(), transferWizardModel);
|
||||
BrowserConsoleLogWriter.Info($"Submitted nested form: {Result.GetType().FullName}");
|
||||
navManager.NavigateTo("/transfer2/" + transfer.Id);
|
||||
}
|
||||
|
||||
public List<Event> MockEvents = new List<Event>
|
||||
{
|
||||
new Event("HONEYBEAST", "A Honeybeast egyedisége abban rejlik, hogy a popzenét rengeteg különféle irányból közelítik meg - hol vidám, hol szomorú, hol pörgős, hogy lassú, koncertjükön az érzelmek széles skáláját élhetjük át együtt.", "2024.05.03", "Budapest Park", "https://tixa.hu/kepek/0027/320/27870-1_20231214141702.jpg"),
|
||||
new Event("VAD FRUTTIK", "A kék a tenger és az ég színe, a nyílt terekkel, valamint a képzelettel és tágassággal társul - végtelen kékség május 3-án a Budapest Parkban!", "2024.04.26", "Budapest Park", "https://tixa.hu/kepek/0027/768/27692-1_20231129185842.jpg"),
|
||||
new Event("DEBORAH DE LUCA", "DEBORAH DE LUCA 2024-ben Magyarországon csak a Budapest Park színpadán", "2024.08.19", "Budapest Park", "https://tixa.hu/kepek/0028/768/28535-1_20240118172423.jpg"),
|
||||
new Event("TOTAL DANCE CABRIO", "Ha nyár, akkor Total Dance Cabrio! Ülj be mellénk a cabrioba, és hagyd, hogy visszarepítsünk a 90-es és 2000-es évekbe! ", "2024.04.26", "Budapest Park", "https://tixa.hu/kepek/0027/768/27829-1_20231206193253.jpg"),
|
||||
new Event("HONEYBEAST", "A Honeybeast egyedisége abban rejlik, hogy a popzenét rengeteg különféle irányból közelítik meg - hol vidám, hol szomorú, hol pörgős, hogy lassú, koncertjükön az érzelmek széles skáláját élhetjük át együtt.", "2024.05.03", "Budapest Park", "https://tixa.hu/kepek/0027/320/27870-1_20231214141702.jpg"),
|
||||
new Event("VAD FRUTTIK", "A kék a tenger és az ég színe, a nyílt terekkel, valamint a képzelettel és tágassággal társul - végtelen kékség május 3-án a Budapest Parkban!", "2024.04.26", "Budapest Park", "https://tixa.hu/kepek/0027/768/27692-1_20231129185842.jpg"),
|
||||
new Event("DEBORAH DE LUCA", "DEBORAH DE LUCA 2024-ben Magyarországon csak a Budapest Park színpadán", "2024.08.19", "Budapest Park", "https://tixa.hu/kepek/0028/768/28535-1_20240118172423.jpg"),
|
||||
new Event("TOTAL DANCE CABRIO", "Ha nyár, akkor Total Dance Cabrio! Ülj be mellénk a cabrioba, és hagyd, hogy visszarepítsünk a 90-es és 2000-es évekbe! ", "2024.04.26", "Budapest Park", "https://tixa.hu/kepek/0027/768/27829-1_20231206193253.jpg"),
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
@page "/backgroundtest"
|
||||
@inject IStringLocalizer<TIAMResources> localizer;
|
||||
@using TIAM.Resources
|
||||
@using TIAMSharedUI.Shared
|
||||
@using TIAMWebApp.Shared.Application.Models.ClientSide.UI
|
||||
|
||||
<div class="hero">
|
||||
|
||||
<div class="parallax-layer layer-6"></div>
|
||||
<div class="parallax-layer layer-5"></div>
|
||||
<div class="parallax-layer layer-4"></div>
|
||||
<div class="parallax-layer bike-1"></div>
|
||||
<div class="parallax-layer bike-2"></div>
|
||||
<div class="parallax-layer layer-3"></div>
|
||||
<div class="parallax-layer layer-2"></div>
|
||||
<div class="parallax-layer layer-1"></div>
|
||||
<div class="logo">
|
||||
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/24650/logo.svg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<HeroSlider SliderItems="@sliders" PBottom="50px" Height="70vh"></HeroSlider>
|
||||
|
||||
<section class="py-5 bg-light">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<!-- Text Content -->
|
||||
<div class="col-md-6">
|
||||
<h2>@localizer.GetString("Index.WhoWeAre")</h2>
|
||||
<p>We’re not here to take over your trip. We’re here to help shape it — with smooth rides, flexible options, and curated experiences you can actually enjoy.</p>
|
||||
<p>Think of us as your private ride — whether you’re heading to the airport, a spa town, a winery, or somewhere off the map.</p>
|
||||
<p>You can follow one of our signature routes, or create your own. We adapt. You decide. We take care of the road.</p>
|
||||
<p>Our role changes with every guest. Sometimes we’re your calm in the chaos. Sometimes we’re your quiet local expert. Sometimes we’re just there — when you need someone who knows the way.</p>
|
||||
</div>
|
||||
<!-- Image Placeholder -->
|
||||
<div class="col-md-6 text-center">
|
||||
<img src="https://via.placeholder.com/500x350" alt="Who we are" class="img-fluid rounded shadow">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@code {
|
||||
public List<HeroSliderItem> sliders = new List<HeroSliderItem>
|
||||
{
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1551867633-194f125bddfa?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Explore Our Programs",
|
||||
ButtonUrl= "/signature-ride-packages"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1549877452-9c387954fbc2?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Book now",
|
||||
ButtonUrl= "/transfer"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1507622560124-621e26755fb8?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "",
|
||||
ButtonUrl= ""
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Lato", sans-serif;
|
||||
}
|
||||
|
||||
.hero {
|
||||
width: 100%;
|
||||
min-height: 450px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #d9edfd;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.layer-1 {
|
||||
-webkit-animation: parallax_fg linear 20s infinite both;
|
||||
animation: parallax_fg linear 20s infinite both;
|
||||
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/24650/1.png) 0 100% repeat-x;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: auto 136px;
|
||||
}
|
||||
|
||||
.layer-2 {
|
||||
-webkit-animation: parallax_fg linear 30s infinite both;
|
||||
animation: parallax_fg linear 30s infinite both;
|
||||
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/24650/2.png) 0 100% repeat-x;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: auto 145px;
|
||||
}
|
||||
|
||||
.layer-3 {
|
||||
-webkit-animation: parallax_fg linear 55s infinite both;
|
||||
animation: parallax_fg linear 55s infinite both;
|
||||
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/24650/3.png) 0 100% repeat-x;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: auto 158px;
|
||||
}
|
||||
|
||||
.layer-4 {
|
||||
-webkit-animation: parallax_fg linear 75s infinite both;
|
||||
animation: parallax_fg linear 75s infinite both;
|
||||
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/24650/4.png) 0 100% repeat-x;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: auto 468px;
|
||||
}
|
||||
|
||||
.layer-5 {
|
||||
-webkit-animation: parallax_fg linear 95s infinite both;
|
||||
animation: parallax_fg linear 95s infinite both;
|
||||
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/24650/5.png) 0 100% repeat-x;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: auto 311px;
|
||||
}
|
||||
|
||||
.layer-6 {
|
||||
-webkit-animation: parallax_fg linear 120s infinite both;
|
||||
animation: parallax_fg linear 120s infinite both;
|
||||
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/24650/6.png) 0 100% repeat-x;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: auto 222px;
|
||||
}
|
||||
|
||||
.bike-1,
|
||||
.bike-2 {
|
||||
background: url(images/car.png) 0 100% no-repeat;
|
||||
z-index: 1;
|
||||
position: fixed;
|
||||
bottom: 100px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: auto 75px;
|
||||
}
|
||||
|
||||
.bike-1 {
|
||||
-webkit-animation: parallax_bike linear 10s infinite both;
|
||||
animation: parallax_bike linear 10s infinite both;
|
||||
}
|
||||
|
||||
.bike-2 {
|
||||
-webkit-animation: parallax_bike linear 15s infinite both;
|
||||
animation: parallax_bike linear 15s infinite both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes parallax_fg {
|
||||
0% {
|
||||
background-position: 2765px 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 550px 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes parallax_fg {
|
||||
0% {
|
||||
background-position: 2765px 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 550px 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes parallax_bike {
|
||||
0% {
|
||||
background-position: -300px 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 2000px 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes parallax_bike {
|
||||
0% {
|
||||
background-position: -300px 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 2000px 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 70px auto;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.logo img {
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
@page "/tours"
|
||||
@using TIAMSharedUI.Shared
|
||||
|
||||
<HeroSlider></HeroSlider>
|
||||
|
||||
<div class="container">
|
||||
<section class="game-section" style="max-width: 100%; overflow-x:hidden">
|
||||
<div class="text-center">
|
||||
|
||||
<h1>Tours</h1>
|
||||
<h2>Please select!</h2>
|
||||
</div>
|
||||
<div class="owl-carousel custom-carousel owl-theme">
|
||||
<a href="login">
|
||||
<div class="item active" style="background-image: url(_content/TIAMSharedUI/images/slider1.jpg);">
|
||||
|
||||
<div class="item-desc">
|
||||
<h3>Parliament of Hungary</h3>
|
||||
<p>The Parliament of Hungary, located in Budapest, is a magnificent neo-Gothic building along the Danube River, housing the country's legislature and representing a symbol of Hungary's political and historical significance.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
<div class="item" style="background-image: url(_content/TIAMSharedUI/images/slider2.jpg);">
|
||||
<div class="item-desc">
|
||||
<h3>Buda Castle</h3>
|
||||
<p>Buda Castle, located in Budapest, Hungary, is a historic palace complex that has served as the royal residence and administrative center for Hungarian kings throughout history. </p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" style="background-image: url(_content/TIAMSharedUI/images/slider3.jpg);">
|
||||
<div class="item-desc">
|
||||
<h3>Aggtelek National Park</h3>
|
||||
<p>Aggtelek Cave, in northeastern Hungary, is a UNESCO World Heritage site renowned for its intricate karst formations, underground river, and breathtaking natural beauty, making it a must-visit destination for cave enthusiasts and nature lovers.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" style="background-image: url(_content/TIAMSharedUI/images/slider4.jpg);">
|
||||
<div class="item-desc">
|
||||
<h3>Margaret Island</h3>
|
||||
<p>Margaret Island, also in Budapest, is a serene green oasis situated in the middle of the Danube River, offering recreational opportunities, gardens, and a relaxing escape from the city.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" style="background-image: url(_content/TIAMSharedUI/images/slider5.jpg);">
|
||||
<div class="item-desc">
|
||||
<h3>Heroes square</h3>
|
||||
<p>Heroes' Square in Budapest, Hungary, is a grand public square featuring iconic statues and monuments honoring the nation's historic leaders and heroes.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" style="background-image: url(_content/TIAMSharedUI/images/slider6.jpg);">
|
||||
<div class="item-desc">
|
||||
<h3>Vajdahunyad Castle</h3>
|
||||
<p>Vajdahunyad Castle, also in Budapest, is a fairytale-like complex showcasing architectural styles from various historical periods, making it a unique and picturesque cultural destination.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<EventsComponent ItemType="0"></EventsComponent>
|
||||
<FeaturedItems ItemType="0"></FeaturedItems>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(".custom-carousel").owlCarousel({
|
||||
autoWidth: true,
|
||||
loop: true
|
||||
});
|
||||
$(document).ready(function () {
|
||||
$(".custom-carousel .item").click(function () {
|
||||
$(".custom-carousel .item").not($(this)).removeClass("active");
|
||||
$(this).toggleClass("active");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
@using TIAM.Entities.Transfers
|
||||
@using TIAMWebApp.Shared.Application.Models
|
||||
|
||||
|
||||
<EditForm Model="@Tour" OnValidSubmit="@HandleValidSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Title</label>
|
||||
<InputText class="form-control" @bind-Value="Tour.Title" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Fancy Description</label>
|
||||
<InputTextArea class="form-control" @bind-Value="Tour.FancyDescription" rows="4" />
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-success">Save</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="HandleCancel">Cancel</button>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
@code {
|
||||
[Parameter] public TourInfo Tour { get; set; } = new();
|
||||
[Parameter] public EventCallback<TourInfo> OnSave { get; set; }
|
||||
[Parameter] public EventCallback OnCancel { get; set; }
|
||||
|
||||
private async Task HandleValidSubmit()
|
||||
{
|
||||
await OnSave.InvokeAsync(Tour);
|
||||
}
|
||||
|
||||
private async Task HandleCancel()
|
||||
{
|
||||
await OnCancel.InvokeAsync(Tour);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
@page "/signature-ride-packages"
|
||||
@inject TourService TourService
|
||||
@using TIAMSharedUI.Shared
|
||||
@using TIAMSharedUI.Shared.Components
|
||||
@using TIAMWebApp.Shared.Application.Models
|
||||
@using TIAMWebApp.Shared.Application.Models.ClientSide.UI
|
||||
@using TIAMWebApp.Shared.Application.Services
|
||||
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
|
||||
<HeroSlider SliderItems="@sliders" PBottom="50px" Height="30vh"></HeroSlider>
|
||||
|
||||
|
||||
|
||||
<ToursComponent />
|
||||
@* <FeaturedItems ItemType="0" /> *@
|
||||
|
||||
|
||||
@code {
|
||||
public List<HeroSliderItem> sliders = new()
|
||||
{
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1551867633-194f125bddfa?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Explore Our Programs",
|
||||
ButtonUrl= "/signature-ride-packages"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1549877452-9c387954fbc2?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Book now",
|
||||
ButtonUrl= "/transfer"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1507622560124-621e26755fb8?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "",
|
||||
ButtonUrl= ""
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,419 @@
|
|||
@page "/book-a-tour/{TransferDestinationId}/{PriceCategory:int}"
|
||||
@using AyCode.Core.Consts
|
||||
@using AyCode.Core.Helpers
|
||||
@using TIAM.Models.Dtos.Users
|
||||
@using TIAM.Services
|
||||
@using TIAMSharedUI.Pages.Components
|
||||
@using TIAMSharedUI.Pages.Components.EditComponents
|
||||
@using TIAMSharedUI.Shared
|
||||
@using TIAMSharedUI.Shared.Components.BaseComponents
|
||||
@using TIAMWebApp.Shared.Application.Interfaces
|
||||
@using TIAMWebApp.Shared.Application.Models
|
||||
@using TIAMWebApp.Shared.Application.Models.ClientSide.UI
|
||||
@using TIAMWebApp.Shared.Application.Models.ClientSide.UI.WizardModels
|
||||
@using AyCode.Services.Loggers
|
||||
@using TIAMSharedUI.Pages.User.Hotels
|
||||
@using TIAMWebApp.Shared.Application.Models.PageModels
|
||||
@using TIAMWebApp.Shared.Application.Services
|
||||
@using TIAMWebApp.Shared.Application.Utility
|
||||
@inherits BasePageComponent
|
||||
@inject NavigationManager navManager
|
||||
@inject IEnumerable<IAcLogWriterClientBase> LogWriters
|
||||
@inject IWizardProcessor WizardProcessor
|
||||
@inject IUserDataService UserDataService
|
||||
@inject AdminSignalRClient _adminSignalRClient
|
||||
@inject ITransferDataService TransferDataService
|
||||
@inject TourService TourService
|
||||
|
||||
<PageTitle>Transfer</PageTitle>
|
||||
|
||||
<HeroSlider SliderItems="sliders" Height="30vh"></HeroSlider>
|
||||
<div class="container-fluid" style="position: relative; z-index: 2;">
|
||||
<div class="row d-flex justify-content-center">
|
||||
|
||||
@{
|
||||
if (_displayHelp)
|
||||
{
|
||||
<div class="col-md-6 col-12 px-5">
|
||||
<!-- Step 1 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 1: Choose Your Transfer Direction
|
||||
</p>
|
||||
<p class="text-muted">At the top of the form, select whether you need a transfer <strong>"To the Airport"</strong> or <strong>"From the Airport"</strong> by clicking on the corresponding tab.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
|
||||
Step 2: Enter the Destination (or Pickup) Address
|
||||
|
||||
</p>
|
||||
<p class="text-muted">In the <strong>PickupAddress</strong> section, select your address:</p>
|
||||
<ul>
|
||||
<li><strong>Preset addresses:</strong> Click on the dropdown menu under “Preset addresses” and choose an address from the list. For example, you may see options like "Széchenyi fürdő."</li>
|
||||
<li><strong>Custom address:</strong> If your address is not listed, click on the "Custom address" tab to manually enter your address.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 3: Confirm the Destination
|
||||
</p>
|
||||
<p class="text-muted">Once you select or enter the address, the form will display it as the "Selected address." Make sure this is correct before proceeding.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 4: Enter Flight Information (Optional)
|
||||
</p>
|
||||
<p class="text-muted">If applicable, fill in your <strong>FlightNumber</strong>. This is optional, so you can leave it blank if you don't have or don’t want to provide it.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 5 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 5: Specify the Number of Passengers
|
||||
</p>
|
||||
<p>In the <strong>NumberOfPassengers</strong> field, enter the number of people who will be traveling. The default is set to 1, but you can change it as needed.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 6 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 6: Indicate the Amount of Luggage
|
||||
</p>
|
||||
<p class="text-muted">In the <strong>NumberOfLuggage</strong> field, enter the total number of luggage items you will be bringing.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 7 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 7: Fill in Your Personal Information
|
||||
</p>
|
||||
<p class="text-muted">Under <strong>FullName</strong>, enter your first and last name in the respective fields. Provide your <strong>PhoneNumber</strong> in the designated field. The phone number appears to be pre-filled with a sample number ("+11234567890"), so be sure to update it with your actual contact number. Enter your <strong>EmailAddress</strong> to receive confirmation and any further communication regarding your transfer.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 8 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 8: Add Additional Comments (Optional)
|
||||
</p>
|
||||
<p class="text-muted">If you have any special requests or additional information you’d like to provide, use the <strong>Comment</strong> section to do so.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 9 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 9: Submit Your Request
|
||||
</p>
|
||||
<p>Once all fields are filled in correctly, click the <strong>Submit</strong> button at the bottom of the form to place your transfer order.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 10 -->
|
||||
<div class="card mb-3 shadow-sm">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<p>
|
||||
Step 10: Confirmation
|
||||
</p>
|
||||
|
||||
<p>After submitting, you should receive a confirmation email or message. Ensure all the details are correct, and you’re all set!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<DxToolbar ItemRenderStyleMode="ToolbarRenderStyleMode.Contained">
|
||||
<Items>
|
||||
<DxToolbarItem Alignment="ToolbarItemAlignment.Right" Text="Help" RenderStyle="ButtonRenderStyle.Secondary" IconCssClass="grid-icon-column-chooser" Click="ShowHelp_Click" />
|
||||
</Items>
|
||||
</DxToolbar>
|
||||
<div style="max-height: 300px; overflow-y: hidden">
|
||||
<img class="img-fluid" src="@tourInfo.CoverImageUrl" alt="@tourInfo.Title"/>
|
||||
</div>
|
||||
<DynamicEditForm Data="_myModel" TitleString="@($"To: {tourInfo.Title}, Price: {_myModel.Price} EUR")" isEditing="true" IgnoreReflection="_transferIgnorList1" OnSubmit="SubmitForm"></DynamicEditForm>
|
||||
|
||||
<InputWizard Data=@_myModel
|
||||
OnSubmit="SubmitForm"
|
||||
IgnoreReflection="@_transferIgnorList1"
|
||||
SubmitButtonText="ButtonSend"
|
||||
TitleResourceString="TransferTitle"
|
||||
SubtitleResourceString="TransferSubtitle"></InputWizard>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-12 col-sm-6 p-5">
|
||||
<h1>Book an Airport Transfer</h1>
|
||||
<p>Welcome to Tour I Am! Book your airport transfer with us for a smooth and stress-free experience. Our professional drivers are ready to take you to and from the airport in comfort and style. We offer competitive rates and reliable service, ensuring you get to your destination on time.</p>
|
||||
|
||||
<h2>Why Choose Tour I Am?</h2>
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Reliable Service:</strong> Punctual pickups and drop-offs.</li>
|
||||
<li><strong>Professional Drivers:</strong> Experienced and courteous drivers.</li>
|
||||
<li><strong>Affordable Rates:</strong> Competitive pricing with no hidden fees.</li>
|
||||
<li><strong>Comfortable Vehicles:</strong> Clean, modern, and well-maintained cars.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 p-5">
|
||||
<img class="img-fluid" src="_content/TIAMSharedUI/images/about1.jpg" />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 p-5">
|
||||
<img class="img-fluid" src="_content/TIAMSharedUI/images/about2.jpg" />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 p-5">
|
||||
|
||||
<h2>How to Book</h2>
|
||||
<p>Booking your airport transfer is easy! Simply visit our <a href="/transfer">booking page</a>, enter your details, and confirm your reservation. You can also contact us at <a href="mailto:info@touriam.com">info@touriam.com</a> or call us at (123) 456-7890 for assistance.</p>
|
||||
|
||||
<p>Experience the convenience and reliability of Tour I Am. Book your airport transfer today and travel with peace of mind!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@code {
|
||||
[Parameter] public string TransferDestinationId { get; set; }
|
||||
[Parameter] public int PriceCategory { get; set; } = 1;
|
||||
private LoggerClient<TransferPage> _logger;
|
||||
|
||||
private readonly TransferWizardModel _myModel = new TransferWizardModel();
|
||||
private TourInfo? tourInfo = new();
|
||||
|
||||
private bool _displayHelp = false;
|
||||
|
||||
public List<HeroSliderItem> sliders = new List<HeroSliderItem>
|
||||
{
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1551867633-194f125bddfa?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Explore Our Programs",
|
||||
ButtonUrl= "/signature-ride-packages"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "Discover the freedom of personalized travel with expert private transfers and curated inspiration.",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1549877452-9c387954fbc2?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "Book now",
|
||||
ButtonUrl= "/transfer"
|
||||
},
|
||||
new HeroSliderItem
|
||||
{
|
||||
Title = "Experience Hungary on Your Terms",
|
||||
Subtitle = "",
|
||||
ImageUrl = "https://images.unsplash.com/photo-1507622560124-621e26755fb8?auto=format&fit=crop&q=80&w=2070&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||
ButtonText = "",
|
||||
ButtonUrl= ""
|
||||
}
|
||||
};
|
||||
|
||||
private readonly List<string> _transferIgnorList1 =
|
||||
[
|
||||
"Id",
|
||||
"Destination",
|
||||
"UserId",
|
||||
"ProductId",
|
||||
"PaymentId",
|
||||
"FirstName",
|
||||
"LastName",
|
||||
"UserProductMappingId",
|
||||
"UserProductToCarId",
|
||||
"ReferralId",
|
||||
"Price"
|
||||
];
|
||||
|
||||
|
||||
public async Task SubmitForm(object result)
|
||||
{
|
||||
var resModel = (TransferWizardModel)result;
|
||||
|
||||
//let's check if user exists with this email
|
||||
var user = await UserDataService.GetUserByEmailAsync(resModel.EmailAddress!);
|
||||
if (user != null && user.Id != Guid.Empty)
|
||||
{
|
||||
resModel.UserId = user.Id;
|
||||
//user exists already
|
||||
|
||||
if (_sessionService.User != null)
|
||||
{
|
||||
if (_sessionService.User.UserId == user.Id)
|
||||
{
|
||||
|
||||
//I have ordered for myself
|
||||
resModel.ReferralId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if I am logged in and different user I become referrer (if no referrer already)
|
||||
var transferUserDetail = await UserDataService.GetUserDetailByIdAsync(user.Id);
|
||||
var userDetail = await UserDataService.GetUserDetailByIdAsync(_sessionService.User.UserId);
|
||||
|
||||
if (transferUserDetail.UserDto.RefferalId != null)
|
||||
{
|
||||
//user has aready a referrer so we use that
|
||||
resModel.ReferralId = transferUserDetail.UserDto.RefferalId;
|
||||
}
|
||||
else
|
||||
{
|
||||
//user has no referrer so I am the referrer
|
||||
resModel.ReferralId = userDetail.UserDto.RefferalId;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//create a guest user and set referralId
|
||||
var registration = new RegistrationModel();
|
||||
|
||||
var password = AcCharsGenerator.NewPassword(AcConst.MinPasswordLength, 16);
|
||||
|
||||
registration.Email = resModel.EmailAddress;
|
||||
registration.PhoneNumber = resModel.PhoneNumber;
|
||||
registration.Password = password;
|
||||
//get list with one member!
|
||||
|
||||
var createResult = await UserDataService.CreateGuestUser(registration);
|
||||
if (createResult.isSuccess)
|
||||
{
|
||||
if (createResult.user != null)
|
||||
{
|
||||
if (_sessionService.User != null)
|
||||
{
|
||||
//if I am logged in user I become referrer
|
||||
var userDetail = await UserDataService.GetUserDetailByIdAsync(_sessionService.User.UserId);
|
||||
var createdUserDetail = await UserDataService.GetUserDetailByIdAsync(createResult.user.Id);
|
||||
if (createdUserDetail != null)
|
||||
{
|
||||
createdUserDetail.UserDto.RefferalId = userDetail.UserDto.RefferalId;
|
||||
|
||||
var updatedNewUser = await _adminSignalRClient.UpdateUserModelDtoDetail(userDetail);
|
||||
if (updatedNewUser != null)
|
||||
{
|
||||
//referral set
|
||||
}
|
||||
else
|
||||
{
|
||||
//something wrong
|
||||
}
|
||||
}
|
||||
|
||||
resModel.ReferralId = userDetail.UserDto.RefferalId;
|
||||
}
|
||||
|
||||
resModel.UserId = createResult.user.Id;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//some error handling
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var transfer = await WizardProcessor.ProcessWizardAsync<TransferWizardModel>(result.GetType(), result);
|
||||
_logger.Info($"Submitted nested form: {result.GetType().FullName}");
|
||||
navManager.NavigateTo($"/mytransfers/{resModel.Id}");
|
||||
}
|
||||
|
||||
protected override Task OnInitializedAsync()
|
||||
{
|
||||
_logger = new LoggerClient<TransferPage>(LogWriters.ToArray());
|
||||
|
||||
|
||||
return base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
//get transferDestination
|
||||
var destination = await TransferDataService.GetTransferDestinationbyIdAsync(Guid.Parse(TransferDestinationId));
|
||||
_myModel.Destination = destination.AddressString;
|
||||
switch (PriceCategory)
|
||||
{
|
||||
case 1:
|
||||
_myModel.Price = destination.Price;
|
||||
break;
|
||||
case 2:
|
||||
_myModel.Price = destination.Price2;
|
||||
break;
|
||||
case 3:
|
||||
_myModel.Price = destination.Price3;
|
||||
break;
|
||||
}
|
||||
|
||||
tourInfo = await TourService.GetAsync(destination.Id);
|
||||
|
||||
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
private void ShowHelp_Click()
|
||||
{
|
||||
_displayHelp = !_displayHelp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
.container {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
width: 750px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.container {
|
||||
width: 970px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.container {
|
||||
width: 1170px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
#featureContainer .carousel-inner .carousel-item > div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#featureContainer .carousel-inner .carousel-item > div:first-child {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#featureContainer .carousel-inner .carousel-item.active,
|
||||
#featureContainer .carousel-inner .carousel-item-next,
|
||||
#featureContainer .carousel-inner .carousel-item-prev {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
||||
#featureContainer .carousel-inner .carousel-item-end.active,
|
||||
#featureContainer .carousel-inner .carousel-item-next {
|
||||
transform: translateX(25%);
|
||||
}
|
||||
|
||||
#featureContainer .carousel-inner .carousel-item-start.active,
|
||||
#featureContainer .carousel-inner .carousel-item-prev {
|
||||
transform: translateX(-25%);
|
||||
}
|
||||
|
||||
#featureContainer .card img {
|
||||
width: 90%;
|
||||
height: 40vh;
|
||||
}
|
||||
|
||||
#featureContainer .carousel-item {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
#featureContainer .card img {
|
||||
width: 100%;
|
||||
height: 75vh;
|
||||
}
|
||||
}
|
||||
|
||||
#featureContainer .carousel-inner .carousel-item-end,
|
||||
#featureContainer .carousel-inner .carousel-item-start {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
#featureContainer .card {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#featureContainer .card {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#featureContainer .card .card-img-overlays {
|
||||
position: absolute;
|
||||
bottom: 15%;
|
||||
left: 10%;
|
||||
}
|
||||
|
||||
#featureContainer a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#featureContainer .indicator {
|
||||
border: 1px solid rgb(202, 202, 202);
|
||||
padding: 3px 6px 3px 6px;
|
||||
}
|
||||
|
||||
#featureContainer .indicator:hover {
|
||||
background-color: blue;
|
||||
border: 1px solid blue;
|
||||
transition: 200ms;
|
||||
}
|
||||
|
||||
#featureContainer .indicator:hover {
|
||||
color: white;
|
||||
transition: 200ms;
|
||||
}
|
||||
|
||||
#featureContainer .indicator {
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
#featureContainer .float-end {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Reseting */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
max-width: 800px;
|
||||
min-height: 500px;
|
||||
margin: 10px auto;
|
||||
padding: 30px 30px 30px 30px;
|
||||
/*background-color: #ecf0f3;*/
|
||||
border-radius: 15px;
|
||||
/*box-shadow: 13px 13px 20px #cbced1, -13px -13px 20px #fff;*/
|
||||
}
|
||||
|
||||
|
||||
.wrapper .name {
|
||||
font-weight: 600;
|
||||
font-size: 1.4rem;
|
||||
letter-spacing: 1.3px;
|
||||
padding-left: 10px;
|
||||
/*color: #555*/;
|
||||
}
|
||||
|
||||
.wrapper .form-field input {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
font-size: 1.2rem;
|
||||
color: #666;
|
||||
padding: 10px 15px 10px 10px;
|
||||
/* border: 1px solid red; */
|
||||
}
|
||||
|
||||
.wrapper .form-field {
|
||||
padding-left: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid;
|
||||
/*border-radius: 20px;*/
|
||||
//*ox-shadow: inset 3px 3px 3px #cbced1, inset -3px -3px 3px #fff;*/
|
||||
}
|
||||
|
||||
.wrapper .form-field .fas {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.wrapper .btn {
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
/*background-color: #03A9F4;*/
|
||||
color: #fff;
|
||||
border-radius: 15px;
|
||||
/*box-shadow: 3px 3px 3px #b1b1b1, -3px -3px 3px #fff;*/
|
||||
letter-spacing: 1.3px;
|
||||
}
|
||||
|
||||
.wrapper .btn:hover {
|
||||
background-color: #039BE5;
|
||||
}
|
||||
|
||||
.wrapper a {
|
||||
text-decoration: none;
|
||||
font-size: 0.8rem;
|
||||
color: #03A9F4;
|
||||
}
|
||||
|
||||
.wrapper a:hover {
|
||||
color: #039BE5;
|
||||
}
|
||||
|
||||
@media(max-width: 380px) {
|
||||
.wrapper {
|
||||
margin: 30px 20px;
|
||||
padding: 40px 15px 15px 15px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
@page "/sysadmin/blogs"
|
||||
@using TIAMWebApp.Shared.Application.Models
|
||||
@using TIAMWebApp.Shared.Application.Services
|
||||
@using TIAMSharedUI.Shared
|
||||
@layout AdminLayout
|
||||
@inject BlogService BlogService
|
||||
|
||||
<div clss="container">
|
||||
<h3 class="mb-4">Manage Blog Posts</h3>
|
||||
|
||||
<EditForm Model="@Post" OnValidSubmit="@SavePost">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Title</label>
|
||||
<InputText class="form-control" @bind-Value="Post.Title" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Lead</label>
|
||||
<InputText class="form-control" @bind-Value="Post.Lead" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Google Drive Link</label>
|
||||
<InputText class="form-control" @bind-Value="Post.DriveLink" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Tags (comma-separated)</label>
|
||||
<InputText class="form-control" @bind-Value="TagsCsv" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Cover Image (optional)</label>
|
||||
<InputFile OnChange="@HandleFileSelected" />
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" type="submit" disabled="@IsSaving">
|
||||
@if (IsSaving)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm"></span> @(IsEditMode ? "Updating..." : "Saving...")
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@(IsEditMode ? "Update Post" : "Save Post")</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
@if (IsEditMode)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary ms-2" @onclick="CancelEdit">Cancel</button>
|
||||
}
|
||||
</EditForm>
|
||||
|
||||
@if (SaveSuccess)
|
||||
{
|
||||
<div class="alert alert-success mt-3">Post saved successfully!</div>
|
||||
}
|
||||
|
||||
<h4 class="mt-5">Existing Posts</h4>
|
||||
|
||||
@if (Posts.Count == 0)
|
||||
{
|
||||
<p>No blog posts found.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-bordered mt-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Tags</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Posts)
|
||||
{
|
||||
<tr>
|
||||
<td><img src="@item.CoverImage" style="max-height:150px" /></td>
|
||||
<td>@item.Title</td>
|
||||
<td>@string.Join(", ", item.Tags)</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary me-2" @onclick="() => EditPost(item)">Edit</button>
|
||||
<button class="btn btn-sm btn-danger" @onclick="() => DeletePost(item.Id)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private BlogPostMetadata Post = new();
|
||||
private List<BlogPostMetadata> Posts = new();
|
||||
private IBrowserFile? CoverImage;
|
||||
private string TagsCsv = "";
|
||||
private bool IsSaving = false;
|
||||
private bool SaveSuccess = false;
|
||||
private bool IsEditMode = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadPosts();
|
||||
}
|
||||
|
||||
private async Task LoadPosts()
|
||||
{
|
||||
Posts = await BlogService.GetAllPostsAsync();
|
||||
}
|
||||
|
||||
private async Task HandleFileSelected(InputFileChangeEventArgs e)
|
||||
{
|
||||
CoverImage = e.File;
|
||||
}
|
||||
|
||||
private async Task SavePost()
|
||||
{
|
||||
IsSaving = true;
|
||||
SaveSuccess = false;
|
||||
|
||||
Post.Tags = TagsCsv.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
|
||||
|
||||
if (IsEditMode)
|
||||
{
|
||||
await BlogService.UpdatePostAsync(Post, CoverImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
await BlogService.CreatePostAsync(Post, CoverImage);
|
||||
}
|
||||
|
||||
IsSaving = false;
|
||||
SaveSuccess = true;
|
||||
|
||||
await LoadPosts();
|
||||
ResetForm();
|
||||
}
|
||||
|
||||
private void EditPost(BlogPostMetadata post)
|
||||
{
|
||||
Post = new BlogPostMetadata
|
||||
{
|
||||
Id = post.Id,
|
||||
Title = post.Title,
|
||||
Lead = post.Lead,
|
||||
DriveLink = post.DriveLink,
|
||||
Tags = new List<string>(post.Tags),
|
||||
CoverImage = post.CoverImage
|
||||
};
|
||||
TagsCsv = string.Join(", ", post.Tags);
|
||||
IsEditMode = true;
|
||||
SaveSuccess = false;
|
||||
}
|
||||
|
||||
private async Task DeletePost(string id)
|
||||
{
|
||||
if (await ConfirmDeleteAsync())
|
||||
{
|
||||
await BlogService.DeletePostAsync(id);
|
||||
await LoadPosts();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> ConfirmDeleteAsync()
|
||||
{
|
||||
return await Task.FromResult(true); // Replace with real confirmation popup later
|
||||
}
|
||||
|
||||
private void CancelEdit()
|
||||
{
|
||||
ResetForm();
|
||||
}
|
||||
|
||||
private void ResetForm()
|
||||
{
|
||||
Post = new BlogPostMetadata();
|
||||
TagsCsv = "";
|
||||
CoverImage = null;
|
||||
IsEditMode = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -560,7 +560,7 @@
|
|||
{
|
||||
if (x?.ResponseData == null) return Task.CompletedTask;
|
||||
|
||||
transfer.TransferToDrivers.UpdateCollection(x.ResponseData, false);
|
||||
transfer.TransferToDrivers.UpdateCollection<TransferToDriver>(x.ResponseData, false);
|
||||
callback?.Invoke();
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,240 @@
|
|||
@page "/sysadmin/manage-tours"
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using TIAM.Entities.Transfers
|
||||
@using TIAMWebApp.Shared.Application.Interfaces
|
||||
@using TIAMWebApp.Shared.Application.Models
|
||||
@using TIAMWebApp.Shared.Application.Services
|
||||
@using TIAMSharedUI.Shared
|
||||
@inject ITransferDataService TransferDataService
|
||||
@inject TourService TourService
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
@layout AdminLayout
|
||||
|
||||
<PageTitle>Manage Tours</PageTitle>
|
||||
|
||||
<h1 class="text-center m-4">Manage Tours</h1>
|
||||
|
||||
<div class="container">
|
||||
<div class="mb-4">
|
||||
<h4>Create New Tour</h4>
|
||||
|
||||
<EditForm Model="@newTour" OnValidSubmit="HandleSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Select Transfer Destination</label>
|
||||
<InputSelect @bind-Value="newTour.TransferDestinationId" class="form-control">
|
||||
<option value="">-- Select --</option>
|
||||
@foreach (var dest in TransferDestinations)
|
||||
{
|
||||
<option value="@dest.Id">@dest.Name (@dest.AddressString)</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Tour name</label>
|
||||
<InputTextArea @bind-Value="newTour.Name" class="form-control" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Description</label>
|
||||
<InputTextArea @bind-Value="newTour.Description" class="form-control" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Cover Photo</label>
|
||||
<InputFile OnChange="HandleFileChange" />
|
||||
@if (!string.IsNullOrEmpty(UploadStatus))
|
||||
{
|
||||
<div class="text-muted mt-1">@UploadStatus</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary me-2" type="submit">
|
||||
@(IsEditing ? "Update Tour" : "Save Tour")
|
||||
</button>
|
||||
@if (IsEditing)
|
||||
{
|
||||
<button class="btn btn-secondary" type="button" @onclick="CancelEdit">Cancel</button>
|
||||
}
|
||||
</EditForm>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<h4>Existing Tours</h4>
|
||||
@if (Tours.Any())
|
||||
{
|
||||
<ul class="list-group">
|
||||
@foreach (var tour in Tours)
|
||||
{
|
||||
var dest = TransferDestinations.FirstOrDefault(d => d.Id == tour.TransferDestinationId);
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<img src="@tour.CoverImageUrl" style="max-height: 100px;"/>
|
||||
<span>
|
||||
<strong>@tour?.Title</strong> — @tour.FancyDescription
|
||||
</span>
|
||||
<button class="btn btn-sm btn-secondary me-2" @onclick="() => EditTour(tour)">Edit</button>
|
||||
<button class="btn btn-sm btn-danger" @onclick="() => DeleteTourAsync(tour.Id)">Delete</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>No tours available.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private List<TransferDestination> TransferDestinations = [];
|
||||
private List<TourInfo> Tours = [];
|
||||
|
||||
private TourFormModel newTour = new();
|
||||
private IBrowserFile? uploadedFile;
|
||||
private string UploadStatus = string.Empty;
|
||||
|
||||
private bool IsEditing = false;
|
||||
private Guid? EditingTourId = null;
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
TransferDestinations = (await TransferDataService.GetDestinationsAsync()).ToList();
|
||||
Tours = (await TourService.GetAllAsync()).ToList();
|
||||
}
|
||||
|
||||
private void EditTour(TourInfo tour)
|
||||
{
|
||||
newTour = new TourFormModel
|
||||
{
|
||||
TransferDestinationId = tour.TransferDestinationId,
|
||||
Name = tour.Title,
|
||||
Description = tour.FancyDescription,
|
||||
ImageBytes = null,
|
||||
ImageFileName = null
|
||||
};
|
||||
|
||||
IsEditing = true;
|
||||
EditingTourId = tour.Id;
|
||||
UploadStatus = string.Empty;
|
||||
}
|
||||
|
||||
private void CancelEdit()
|
||||
{
|
||||
newTour = new TourFormModel();
|
||||
uploadedFile = null;
|
||||
IsEditing = false;
|
||||
EditingTourId = null;
|
||||
UploadStatus = string.Empty;
|
||||
}
|
||||
|
||||
// private async Task SaveTourAsync()
|
||||
// {
|
||||
// if (uploadedFile is not null)
|
||||
// {
|
||||
// using var stream = uploadedFile.OpenReadStream(maxAllowedSize: 5 * 1024 * 1024);
|
||||
// using var ms = new MemoryStream();
|
||||
// await stream.CopyToAsync(ms);
|
||||
// newTour.ImageBytes = ms.ToArray();
|
||||
// newTour.ImageFileName = uploadedFile.Name;
|
||||
// }
|
||||
|
||||
// TourInfo tourToCreate = new TourInfo();
|
||||
// tourToCreate.TransferDestinationId = newTour.TransferDestinationId;
|
||||
// tourToCreate.FancyDescription = newTour.Description;
|
||||
// tourToCreate.Title = newTour.Name;
|
||||
// tourToCreate.CoverImageUrl = "";
|
||||
// if (tourToCreate.TransferDestinationId == Guid.Empty)
|
||||
// {
|
||||
// throw new InvalidOperationException("Transfer Destination must be selected.");
|
||||
// }
|
||||
|
||||
// await TourService.CreateAsync(tourToCreate, uploadedFile);
|
||||
// newTour = new();
|
||||
// uploadedFile = null;
|
||||
// UploadStatus = string.Empty;
|
||||
// Tours = (await TourService.GetAllAsync()).ToList();
|
||||
// }
|
||||
|
||||
private async Task HandleSubmit()
|
||||
{
|
||||
if (uploadedFile is not null)
|
||||
{
|
||||
using var stream = uploadedFile.OpenReadStream(maxAllowedSize: 5 * 1024 * 1024);
|
||||
using var ms = new MemoryStream();
|
||||
await stream.CopyToAsync(ms);
|
||||
newTour.ImageBytes = ms.ToArray();
|
||||
newTour.ImageFileName = uploadedFile.Name;
|
||||
}
|
||||
|
||||
if (IsEditing && EditingTourId.HasValue)
|
||||
{
|
||||
var updatedTour = new TourInfo
|
||||
{
|
||||
Id = EditingTourId.Value,
|
||||
TransferDestinationId = newTour.TransferDestinationId,
|
||||
Title = newTour.Name,
|
||||
FancyDescription = newTour.Description,
|
||||
CoverImageUrl = "" // assume your service handles image URL logic
|
||||
};
|
||||
|
||||
await TourService.UpdateAsync(updatedTour, uploadedFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
var tourToCreate = new TourInfo
|
||||
{
|
||||
TransferDestinationId = newTour.TransferDestinationId,
|
||||
Title = newTour.Name,
|
||||
FancyDescription = newTour.Description,
|
||||
CoverImageUrl = newTour.ImageFileName
|
||||
};
|
||||
|
||||
await TourService.CreateAsync(tourToCreate, uploadedFile);
|
||||
}
|
||||
|
||||
// Reset form
|
||||
newTour = new TourFormModel();
|
||||
uploadedFile = null;
|
||||
IsEditing = false;
|
||||
EditingTourId = null;
|
||||
UploadStatus = string.Empty;
|
||||
|
||||
Tours = (await TourService.GetAllAsync()).ToList();
|
||||
}
|
||||
|
||||
private void HandleFileChange(InputFileChangeEventArgs e)
|
||||
{
|
||||
uploadedFile = e.File;
|
||||
UploadStatus = $"Selected: {uploadedFile.Name}";
|
||||
}
|
||||
|
||||
private async Task DeleteTourAsync(Guid id)
|
||||
{
|
||||
await TourService.DeleteAsync(id);
|
||||
Tours = (await TourService.GetAllAsync()).ToList();
|
||||
}
|
||||
|
||||
public class TourFormModel
|
||||
{
|
||||
[Required]
|
||||
public Guid TransferDestinationId { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "Tour name cannot exceed 100 characters.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public byte[]? ImageBytes { get; set; }
|
||||
public string? ImageFileName { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
@using TIAMWebApp.Shared.Application.Models
|
||||
@using TIAMWebApp.Shared.Application.Services
|
||||
@inject BlogService BlogService
|
||||
|
||||
<div class="container text-center pb-5">
|
||||
<h1 class="mb-4">Blog Posts</h1>
|
||||
<p class="lead">
|
||||
Discover the hidden gems and breathtaking landscapes of Hungary! Our blog features inspiring destinations, scenic routes, and must-see spots that invite you to explore the natural beauty of this enchanting country. Whether you're planning your next adventure or simply dreaming of a getaway, let these stories guide your journey.
|
||||
</p>
|
||||
|
||||
@if (posts == null)
|
||||
{
|
||||
<p>Loading blog posts...</p>
|
||||
}
|
||||
else if (posts.Count == 0)
|
||||
{
|
||||
<p>No blog posts found.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* <section class="game-section" style="max-width: 100%; overflow-x:hidden"> *@
|
||||
<div class="row">
|
||||
@foreach (var post in posts)
|
||||
{
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card rounded h-100 shadow">
|
||||
@if (!string.IsNullOrEmpty(post.CoverImage))
|
||||
{
|
||||
<img src="@post.CoverImage" class="card-img-top" alt="@post.Title">
|
||||
}
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">@post.Title</h5>
|
||||
@if (!string.IsNullOrEmpty(post.Lead))
|
||||
{
|
||||
|
||||
<p class="card-title">@post.Lead</p>
|
||||
}
|
||||
@if (post.Tags?.Count > 0)
|
||||
{
|
||||
<p class="card-text text-muted small">
|
||||
@string.Join(", ", post.Tags)
|
||||
</p>
|
||||
}
|
||||
<a href="@($"/blogpost/{post.Id}")" class="btn btn-primary mt-auto">Read More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@* <a href="@($"/blogpost/{post.Id}")">
|
||||
<div class="item" style="background-image: url(@post.CoverImage);">
|
||||
<div class="item-desc" style="height: 100%; background-color: rgba(0,0,0,0.3);">
|
||||
<h3 class="card-title" style="margin-top: 80px;">@post.Title</h3>
|
||||
<p>@post.Tags</p>
|
||||
<p>@post.Lead</p>
|
||||
<p>@post.Id</p>
|
||||
<div class="glass" style="position: absolute; top:0px; left:0px; padding:10px; width:100%">
|
||||
<span>by</span>
|
||||
<svg width="30%" viewBox="0 0 112 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Logó"><path d="M99.636,13.719c-0.569,1.804 -1.048,3.323 -1.542,4.889l3.215,0c-0.549,-1.606 -1.062,-3.104 -1.673,-4.889m11.754,11.595c-2.575,0 -4.95,0.027 -7.323,-0.036c-0.267,-0.007 -0.637,-0.476 -0.76,-0.805c-0.592,-1.582 -0.567,-1.591 -2.263,-1.592c-1.452,0 -2.903,0 -4.316,0c-0.913,2.437 -0.913,2.437 -3.473,2.437l-3.674,0c2.395,-6.454 4.723,-12.727 7.07,-19.049c2.38,0 4.725,-0.037 7.065,0.044c0.312,0.01 0.736,0.565 0.884,0.953c2.099,5.504 4.159,11.022 6.228,16.537c0.16,0.426 0.318,0.854 0.562,1.511" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><path d="M89.146,6.271c-2.087,2.974 -4.038,5.753 -6.039,8.604c1.968,3.411 3.942,6.833 6.02,10.434c-2.908,0 -5.596,0.014 -8.284,-0.025c-0.228,-0.003 -0.526,-0.317 -0.663,-0.559c-0.559,-0.986 -1.068,-2.001 -1.698,-3.202c-0.649,1.062 -1.269,1.982 -1.786,2.957c-0.348,0.658 -0.797,0.87 -1.523,0.853c-1.934,-0.045 -3.869,-0.015 -6.019,-0.015c1.733,-2.622 3.245,-5.103 4.963,-7.431c0.923,-1.251 0.94,-2.138 0.063,-3.432c-1.751,-2.579 -3.285,-5.306 -4.969,-8.072c0.297,-0.045 0.516,-0.107 0.735,-0.108c2.344,-0.007 4.689,0.018 7.032,-0.019c0.622,-0.009 0.972,0.195 1.247,0.741c0.475,0.94 1.019,1.846 1.607,2.898c0.707,-1.095 1.311,-2.088 1.982,-3.033c0.192,-0.269 0.577,-0.558 0.879,-0.565c2.058,-0.05 4.117,-0.026 6.453,-0.026" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><path d="M59.654,6.335l0,5.691l-5.73,0l0,13.193l-7.515,0l0,-13.147l-5.731,0l0,-5.737l18.976,0Z" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><rect x="61.187" y="6.342" width="6.698" height="18.911" style="fill:#fff;fill-opacity:0.8;"></rect><path d="M28.354,21.366c0.306,4.69 -5.098,5.2 -6.831,2.855c-2.169,-2.934 -5.05,-2.797 -6.619,-1.438c-1.569,-1.359 -4.449,-1.496 -6.618,1.438c-1.733,2.345 -7.137,1.835 -6.831,-2.855c0,0 -2.763,4.258 0.611,8.055c3.263,3.671 8.689,3.784 12.032,0.918c0.305,-0.262 0.568,-0.53 0.806,-0.802c0.238,0.272 0.501,0.54 0.807,0.802c3.343,2.866 8.768,2.753 12.031,-0.918c3.375,3.796 0.612,-8.055 0.612,-8.055Z" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><path d="M28.354,11.37c0.306,-4.69 -5.098,-5.2 -6.831,-2.855c-2.169,2.934 -5.05,2.797 -6.619,1.438c-1.569,1.359 -4.449,1.496 -6.618,-1.438c-1.733,-2.345 -7.137,-1.835 -6.831,2.855c0,0 -2.763,-4.258 0.611,-8.055c3.263,-3.671 8.689,-3.784 12.032,-0.918c0.305,0.262 0.568,0.53 0.806,0.802c0.238,-0.272 0.501,-0.54 0.807,-0.802c3.343,-2.866 8.768,-2.753 12.031,0.918c3.375,3.796 0.612,8.055 0.612,8.055Z" style="fill:#fff;fill-opacity:0.8;fill-rule:nonzero;"></path><circle cx="12.241" cy="16.368" r="1.332" style="fill:#fff;fill-opacity:0.8;"></circle><circle cx="16.902" cy="16.368" r="1.332" style="fill:#fff;fill-opacity:0.8;"></circle></g></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a> *@
|
||||
}
|
||||
@* </section> *@
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public int PreviewNumber { get; set; } = 4;
|
||||
|
||||
private List<BlogPostMetadata>? posts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
posts = (await BlogService.GetAllPostsAsync()).Take(PreviewNumber).ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
<!-- footer -->
|
||||
<footer class="pt-lg-10 pt-5 footer bg-white">
|
||||
<footer class="pt-lg-10 pt-5 footer bg-light">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-md-6 col-12">
|
||||
<!-- about company -->
|
||||
<div class="mb-4">
|
||||
<img src="../assets/images/brand/logo/logo.svg" alt="" class="logo-inverse ">
|
||||
<img src="_content/TIAMSharedUI/images/logo_wide.png" alt="" class="logo-inverse ">
|
||||
<div class="mt-4">
|
||||
<p>
|
||||
Geek is feature-rich components and beautifully Bootstrap UIKit for developers, built with bootstrap
|
||||
|
|
|
|||
|
|
@ -23,9 +23,38 @@
|
|||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="explore-the-region">
|
||||
@localizer.GetString("NavMenu.ExploreTheRegion")
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="signature-ride-packages">
|
||||
@localizer.GetString("NavMenu.SignatureRidePackages")
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="how-it-works">
|
||||
@localizer.GetString("NavMenu.HowItWorks")
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="about">
|
||||
@localizer.GetString("NavMenu.About")
|
||||
</NavLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="contact">
|
||||
@localizer.GetString("NavMenu.Contact")
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="transfer">
|
||||
@localizer.GetString("NavMenu.Transfer")
|
||||
@localizer.GetString("NavMenu.BookNow")
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
|
|
@ -50,17 +79,12 @@
|
|||
|
||||
|
||||
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="about">
|
||||
@localizer.GetString("NavMenu.About")
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
|
||||
@* <li class="nav-item">
|
||||
<NavLink class="nav-link" href="roadmap">
|
||||
@localizer.GetString("NavMenu.Roadmap")
|
||||
</NavLink>
|
||||
</li>
|
||||
</li> *@
|
||||
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
.navbar {
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
backdrop-filter: blur(8px);
|
||||
background-color: rgba(255, 255, 255, 0.7)
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
@using TIAMWebApp.Shared.Application.Models
|
||||
@using TIAMWebApp.Shared.Application.Services
|
||||
@inject TourService TourService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<div class="container">
|
||||
<section class="game-section" style="max-width: 100%; overflow-x:hidden">
|
||||
<div class="text-center">
|
||||
<h1>Tours</h1>
|
||||
<h2>Please select!</h2>
|
||||
</div>
|
||||
<div class="owl-carousel custom-carousel owl-theme">
|
||||
@if (Tours != null && Tours.Any()) // Only render the loop if Tours has data
|
||||
{
|
||||
foreach (var item in Tours)
|
||||
{
|
||||
<div class="item" style="background-image: url(@item.CoverImageUrl);">
|
||||
<div class="item-desc" style="height: 100%; background-color: rgba(0,0,0,0.3);">
|
||||
<p style="padding-top:70px">@item.Created</p>
|
||||
<p>@item.FancyDescription</p>
|
||||
@* <p>@item.TransferDestinationId</p> *@
|
||||
<div>
|
||||
<button class="btn btn-primary mt-auto" @onclick="@( ()=> Book(item.TransferDestinationId, 1))">1-3 passengers</button>
|
||||
<button class="btn btn-primary mt-auto" @onclick="@( ()=> Book(item.TransferDestinationId, 2))">4-6 passengers</button>
|
||||
<button class="btn btn-primary mt-auto" @onclick="@( ()=> Book(item.TransferDestinationId, 3))">7-8 passengers</button>
|
||||
</div>
|
||||
<div class="glass" style="position: absolute; top:0px; left:0px; padding:10px; width:100%">
|
||||
<h4>@item.Title</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>Loading tours...</p>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function initializeOwlCarousel() {
|
||||
console.log("Initializing Owl Carousel..."); // For debugging
|
||||
$(".custom-carousel").owlCarousel({
|
||||
autoWidth: true,
|
||||
loop: true
|
||||
});
|
||||
// Ensure this runs only once per initialization
|
||||
$(document).ready(function () { // This ready might not be needed if called after DOM update
|
||||
$(".custom-carousel .item").off("click").on("click", function () { // Use .off().on() to prevent multiple handlers
|
||||
$(".custom-carousel .item").not($(this)).removeClass("active");
|
||||
$(this).toggleClass("active");
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@code {
|
||||
public List<TourInfo> Tours = new List<TourInfo>();
|
||||
private bool _owlCarouselInitialized = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Tours = (await TourService.GetAllAsync()).ToList();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
// Only initialize Owl Carousel once, after data is loaded and rendered
|
||||
if (Tours != null && Tours.Any() && !_owlCarouselInitialized)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("initializeOwlCarousel");
|
||||
_owlCarouselInitialized = true; // Set flag to true after successful initialization
|
||||
}
|
||||
}
|
||||
|
||||
private void Book(Guid id, int category)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/book-a-tour/{id.ToString()}/{category}");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
|
||||
|
||||
/******* Common Element CSS Start ******/
|
||||
|
||||
|
||||
body {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
transition: all 0.4s ease-in-out;
|
||||
-webkit-transition: all 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:active,
|
||||
a:visited,
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #e73700;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 48px;
|
||||
padding-bottom: 16px;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
font-weight: 700;
|
||||
position: relative;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
button {
|
||||
outline: none !important;
|
||||
}
|
||||
/******* Common Element CSS End *********/
|
||||
|
||||
/* -------- title style ------- */
|
||||
.line-title {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.line-title::before,
|
||||
.line-title::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.line-title::before {
|
||||
width: 100%;
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
.line-title::after {
|
||||
width: 32px;
|
||||
background: #e73700;
|
||||
}
|
||||
|
||||
/******* Middle section CSS Start ******/
|
||||
/* -------- Landing page ------- */
|
||||
.game-section {
|
||||
/*padding: 5px 50px;*/
|
||||
}
|
||||
|
||||
.game-section .owl-stage {
|
||||
margin: 15px 0;
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
}
|
||||
|
||||
.game-section .item {
|
||||
margin: 0 15px 60px;
|
||||
width: 320px;
|
||||
height: 400px;
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
align-items: flex-end;
|
||||
-webkit-align-items: flex-end;
|
||||
background: #343434 no-repeat center center / cover;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: all 0.4s ease-in-out;
|
||||
-webkit-transition: all 0.4s ease-in-out;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.57 );
|
||||
}
|
||||
|
||||
.game-section .item.active {
|
||||
width: 500px;
|
||||
box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.57 );
|
||||
-webkit-box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.57 );
|
||||
}
|
||||
|
||||
.game-section .item:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
.game-section .item-desc {
|
||||
padding: 0 24px 12px;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
transform: translateY(calc(100% - 54px));
|
||||
-webkit-transform: translateY(calc(100% - 54px));
|
||||
transition: all 0.4s ease-in-out;
|
||||
-webkit-transition: all 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
.game-section .item.active .item-desc {
|
||||
transform: none;
|
||||
-webkit-transform: none;
|
||||
}
|
||||
|
||||
.game-section .item-desc p {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(32px);
|
||||
transform: translateY(32px);
|
||||
transition: all 0.4s ease-in-out 0.2s;
|
||||
-webkit-transition: all 0.4s ease-in-out 0.2s;
|
||||
}
|
||||
|
||||
.game-section .item.active .item-desc p {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.game-section .owl-theme.custom-carousel .owl-dots {
|
||||
margin-top: -20px;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
}
|
||||
/******** Middle section CSS End *******/
|
||||
|
||||
/***** responsive css Start ******/
|
||||
|
||||
@media (min-width: 992px) and (max-width: 1199px) {
|
||||
h2 {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
/* -------- Landing page ------- */
|
||||
.game-section {
|
||||
/*padding: 50px 5px;*/
|
||||
}
|
||||
|
||||
.game-section .item {
|
||||
margin: 0 12px 60px;
|
||||
width: 260px;
|
||||
height: 360px;
|
||||
}
|
||||
|
||||
.game-section .item.active {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.game-section .item-desc {
|
||||
transform: translateY(calc(100% - 46px));
|
||||
-webkit-transform: translateY(calc(100% - 46px));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
h2 {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.line-title {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
/* -------- Landing page ------- */
|
||||
.game-section {
|
||||
/*padding: 50px 5px 40px;*/
|
||||
}
|
||||
|
||||
.game-section .item {
|
||||
margin: 0 12px 60px;
|
||||
width: 240px;
|
||||
height: 330px;
|
||||
}
|
||||
|
||||
.game-section .item.active {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.game-section .item-desc {
|
||||
transform: translateY(calc(100% - 42px));
|
||||
-webkit-transform: translateY(calc(100% - 42px));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.line-title {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
/* -------- Landing page ------- */
|
||||
.game-section {
|
||||
padding: 30px 15px 20px;
|
||||
}
|
||||
|
||||
.game-section .item {
|
||||
margin: 0 10px 40px;
|
||||
width: 200px;
|
||||
height: 280px;
|
||||
}
|
||||
|
||||
.game-section .item.active {
|
||||
width: 270px;
|
||||
box-shadow: 6px 10px 10px rgba(0, 0, 0, 0.25);
|
||||
-webkit-box-shadow: 6px 10px 10px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.game-section .item-desc {
|
||||
padding: 0 14px 5px;
|
||||
transform: translateY(calc(100% - 42px));
|
||||
-webkit-transform: translateY(calc(100% - 42px));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,24 @@
|
|||
@using BlazorAnimation
|
||||
@using TIAMWebApp.Shared.Application.Models.ClientSide.UI
|
||||
<header style="padding-bottom: @PBottom;">
|
||||
<header style="padding-bottom: @PBottom;" class="bg-light">
|
||||
|
||||
<div id="owl-demo" class="owl-carousel owl-theme" style="/* position:absolute; */ z-index: 1;">
|
||||
|
||||
@{
|
||||
foreach (var slider in SliderItems)
|
||||
{
|
||||
<div class="item d-flex align-items-center" style="height: @Height;">
|
||||
<img src="@slider.ImageUrl" class="my-auto" alt="@slider.Title">
|
||||
<div class="item d-flex align-items-center" style="height: @Height; background-image : url('@slider.ImageUrl'); background-size:cover; background-position:center; background-color: rgba(0, 0, 0, 0.6); background-blend-mode: overlay;">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-6 col-sm-12 text-white">
|
||||
<h1>@slider.Title</h1>
|
||||
<p>@slider.Subtitle</p>
|
||||
<a href="@slider.ButtonUrl" class="btn btn-primary">@slider.ButtonText</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<NavMenu />
|
||||
</div-->
|
||||
|
||||
@* <main class="bg-gradient"> *@
|
||||
<main>
|
||||
<article class="content">
|
||||
<CascadingValue Value=PopupMessageBox>
|
||||
|
|
|
|||
|
|
@ -98,6 +98,8 @@
|
|||
|
||||
<DxMenuItem NavigateUrl="sysadmin/userproductmappings" Text="Permissions" />
|
||||
<DxMenuItem NavigateUrl="sysadmin/users" Text="Users" />
|
||||
<DxMenuItem NavigateUrl="sysadmin/blogs" Text="Blog" />
|
||||
<DxMenuItem NavigateUrl="sysadmin/manage-tours" Text="Blog" />
|
||||
</Items>
|
||||
</DxMenuItem>
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@
|
|||
<Content Update="Pages\User\SysAdmins\AddressDetailGridComponent.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
<Content Update="wwwroot\images\car.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -6,14 +6,12 @@
|
|||
body {
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
/*background-color: #e7e6f7;
|
||||
background: url('../images/bg.jpg');*/
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
height: 100vh;
|
||||
color: #58457b;
|
||||
--bs-border-radius: 20px;
|
||||
}
|
||||
|
||||
|
||||
.content {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
|
|
@ -25,8 +23,8 @@ body {
|
|||
}
|
||||
|
||||
.container-fluid {
|
||||
padding-left:40px;
|
||||
padding-right: 40px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
|
|
@ -134,11 +132,12 @@ select {
|
|||
}
|
||||
|
||||
.bg-gradient {
|
||||
|
||||
color: #fff !important;
|
||||
background: linear-gradient(111deg,deepskyblue,darkviolet,#502caf) !important;
|
||||
background-size: 180% 180% !important;
|
||||
animation: gradient-animation 3s ease infinite !important;
|
||||
background-image: linear-gradient(21deg, deepskyblue, #6f42c1, #af2c53) !important;
|
||||
/* background-size: 180% 180% !important; */
|
||||
animation: gradient-animation 5s ease infinite !important;
|
||||
background-attachment: fixed;
|
||||
background-blend-mode: color;
|
||||
}
|
||||
|
||||
@keyframes gradient-animation {
|
||||
|
|
@ -578,6 +577,213 @@ select:focus-visible {
|
|||
|
||||
/*End Hero Carousel*/
|
||||
|
||||
|
||||
|
||||
/* -------- Landing page ------- */
|
||||
.game-section {
|
||||
/*padding: 5px 50px;*/
|
||||
}
|
||||
|
||||
.game-section .owl-stage {
|
||||
margin: 15px 0;
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
}
|
||||
|
||||
.game-section .item {
|
||||
margin: 0 15px 60px;
|
||||
width: 320px;
|
||||
height: 400px;
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
align-items: flex-end;
|
||||
-webkit-align-items: flex-end;
|
||||
background: #343434 no-repeat center center / cover;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: all 0.4s ease-in-out;
|
||||
-webkit-transition: all 0.4s ease-in-out;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.57 );
|
||||
}
|
||||
|
||||
.game-section .item.active {
|
||||
width: 500px;
|
||||
box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.57 );
|
||||
-webkit-box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.57 );
|
||||
}
|
||||
|
||||
.game-section .item:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
.game-section .item-desc {
|
||||
padding: 0 24px 12px;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
transform: translateY(calc(100% - 54px));
|
||||
-webkit-transform: translateY(calc(100% - 54px));
|
||||
transition: all 0.4s ease-in-out;
|
||||
-webkit-transition: all 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
.game-section .item.active .item-desc {
|
||||
transform: none;
|
||||
-webkit-transform: none;
|
||||
}
|
||||
|
||||
.game-section .item-desc p {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(32px);
|
||||
transform: translateY(32px);
|
||||
transition: all 0.4s ease-in-out 0.2s;
|
||||
-webkit-transition: all 0.4s ease-in-out 0.2s;
|
||||
}
|
||||
|
||||
.game-section .item.active .item-desc p {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.game-section .owl-theme.custom-carousel .owl-dots {
|
||||
margin-top: -20px;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
}
|
||||
/******** Middle section CSS End *******/
|
||||
|
||||
/***** responsive css Start ******/
|
||||
|
||||
@media (min-width: 992px) and (max-width: 1199px) {
|
||||
h2 {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
/* -------- Landing page ------- */
|
||||
.game-section {
|
||||
/*padding: 50px 5px;*/
|
||||
}
|
||||
|
||||
.game-section .item {
|
||||
margin: 0 12px 60px;
|
||||
width: 260px;
|
||||
height: 360px;
|
||||
}
|
||||
|
||||
.game-section .item.active {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.game-section .item-desc {
|
||||
transform: translateY(calc(100% - 46px));
|
||||
-webkit-transform: translateY(calc(100% - 46px));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
h2 {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.line-title {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
/* -------- Landing page ------- */
|
||||
.game-section {
|
||||
/*padding: 50px 5px 40px;*/
|
||||
}
|
||||
|
||||
.game-section .item {
|
||||
margin: 0 12px 60px;
|
||||
width: 240px;
|
||||
height: 330px;
|
||||
}
|
||||
|
||||
.game-section .item.active {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.game-section .item-desc {
|
||||
transform: translateY(calc(100% - 42px));
|
||||
-webkit-transform: translateY(calc(100% - 42px));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.line-title {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
/* -------- Landing page ------- */
|
||||
.game-section {
|
||||
padding: 30px 15px 20px;
|
||||
}
|
||||
|
||||
.game-section .item {
|
||||
margin: 0 10px 40px;
|
||||
width: 200px;
|
||||
height: 280px;
|
||||
}
|
||||
|
||||
.game-section .item.active {
|
||||
width: 270px;
|
||||
box-shadow: 6px 10px 10px rgba(0, 0, 0, 0.25);
|
||||
-webkit-box-shadow: 6px 10px 10px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.game-section .item-desc {
|
||||
padding: 0 14px 5px;
|
||||
transform: translateY(calc(100% - 42px));
|
||||
-webkit-transform: translateY(calc(100% - 42px));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*Admin*/
|
||||
|
||||
.gradient-custom-2 {
|
||||
|
|
@ -684,4 +890,6 @@ select:focus-visible {
|
|||
min-height: 65px;
|
||||
}
|
||||
|
||||
/*Admin end*/
|
||||
/*Admin end*/
|
||||
|
||||
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -56,6 +56,8 @@ builder.Services.AddScoped<ExchangeRateService>();
|
|||
builder.Services.AddScoped<SmartyStreetsService>();
|
||||
builder.Services.AddScoped<GooglePlacesService>();
|
||||
builder.Services.AddSingleton<PageHistoryState>();
|
||||
builder.Services.AddScoped<BlogService>();
|
||||
builder.Services.AddScoped<TourService>();
|
||||
//builder.Services.AddScoped<BrowserConsoleLogWriter>();
|
||||
|
||||
//WebSpecific end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,213 @@
|
|||
using GoogleApi.Entities.Search.Video.Common;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using TIAMWebApp.Shared.Application.Models;
|
||||
|
||||
|
||||
namespace TIAMWebApp.Server.Controllers
|
||||
{
|
||||
|
||||
[ApiController]
|
||||
[Route("api/blog")]
|
||||
public class BlogAPIController : ControllerBase
|
||||
{
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly string _dataPath;
|
||||
private readonly string _coverPath;
|
||||
|
||||
public BlogAPIController(IWebHostEnvironment env)
|
||||
{
|
||||
_env = env;
|
||||
_dataPath = Path.Combine(_env.WebRootPath, "data");
|
||||
_coverPath = Path.Combine(_env.WebRootPath, "uploads", "covers");
|
||||
Directory.CreateDirectory(_dataPath);
|
||||
Directory.CreateDirectory(_coverPath);
|
||||
}
|
||||
|
||||
// ✅ Get All Posts
|
||||
[HttpGet]
|
||||
public IActionResult GetAll()
|
||||
{
|
||||
var files = Directory.GetFiles(_dataPath, "*.json");
|
||||
var posts = files.Select(file =>
|
||||
{
|
||||
var json = System.IO.File.ReadAllText(file);
|
||||
return JsonSerializer.Deserialize<BlogPostMetadata>(json);
|
||||
}).ToList();
|
||||
|
||||
return Ok(posts);
|
||||
}
|
||||
|
||||
// ✅ Get Single Post
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult GetById(string id)
|
||||
{
|
||||
var file = Path.Combine(_dataPath, $"{id}.json");
|
||||
if (!System.IO.File.Exists(file))
|
||||
return NotFound();
|
||||
|
||||
var json = System.IO.File.ReadAllText(file);
|
||||
var post = JsonSerializer.Deserialize<BlogPostMetadata>(json);
|
||||
return Ok(post);
|
||||
}
|
||||
|
||||
// ✅ Create Post (with optional cover upload)
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromForm] BlogPostForm form)
|
||||
{
|
||||
var post = new BlogPostMetadata
|
||||
{
|
||||
Title = form.Title,
|
||||
Lead = form.Lead,
|
||||
DriveLink = form.DriveLink,
|
||||
Tags = form.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList()
|
||||
};
|
||||
|
||||
if (form.CoverImage != null)
|
||||
{
|
||||
string fileName = $"{post.Id}{Path.GetExtension(form.CoverImage.FileName)}";
|
||||
string filePath = Path.Combine(_coverPath, fileName);
|
||||
await using var fs = System.IO.File.Create(filePath);
|
||||
await form.CoverImage.CopyToAsync(fs);
|
||||
post.CoverImage = $"/uploads/covers/{fileName}";
|
||||
}
|
||||
|
||||
string jsonPath = Path.Combine(_dataPath, $"{post.Id}.json");
|
||||
await System.IO.File.WriteAllTextAsync(jsonPath, JsonSerializer.Serialize(post));
|
||||
|
||||
return Ok(post);
|
||||
}
|
||||
|
||||
//// ✅ Update Post
|
||||
//[HttpPut("{id}")]
|
||||
//public async Task<IActionResult> Update(string id, [FromBody] BlogPostMetadata post)
|
||||
//{
|
||||
// string jsonPath = Path.Combine(_dataPath, $"{id}.json");
|
||||
// if (!System.IO.File.Exists(jsonPath))
|
||||
// return NotFound();
|
||||
|
||||
// post.Id = id; // Ensure correct ID
|
||||
// await System.IO.File.WriteAllTextAsync(jsonPath, JsonSerializer.Serialize(post));
|
||||
// return Ok(post);
|
||||
//}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(string id)
|
||||
{
|
||||
var form = await Request.ReadFormAsync();
|
||||
|
||||
// Parse metadata fields
|
||||
var title = form["Title"].ToString();
|
||||
var lead = form["Lead"].ToString();
|
||||
var tags = form["Tags"].ToString().Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList();
|
||||
var driveLink = form["DriveLink"].ToString();
|
||||
|
||||
string coverImagePath = null;
|
||||
var file = form.Files.GetFile("CoverImage");
|
||||
|
||||
if (file != null)
|
||||
{
|
||||
var ext = Path.GetExtension(file.FileName);
|
||||
var fileName = $"{id}{ext}";
|
||||
var filePath = Path.Combine(_coverPath, fileName);
|
||||
|
||||
using var stream = System.IO.File.Create(filePath);
|
||||
await file.CopyToAsync(stream);
|
||||
|
||||
// Save relative path
|
||||
coverImagePath = $"/uploads/covers/{fileName}";
|
||||
}
|
||||
|
||||
// Load existing metadata
|
||||
string jsonPath = Path.Combine(_dataPath, $"{id}.json");
|
||||
if (!System.IO.File.Exists(jsonPath))
|
||||
return NotFound();
|
||||
|
||||
var existingJson = await System.IO.File.ReadAllTextAsync(jsonPath);
|
||||
var existing = JsonSerializer.Deserialize<BlogPostMetadata>(existingJson);
|
||||
|
||||
// Update values
|
||||
existing.Title = title;
|
||||
existing.Lead = lead;
|
||||
existing.Tags = tags;
|
||||
existing.DriveLink = driveLink;
|
||||
existing.CoverImage = coverImagePath;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(coverImagePath))
|
||||
existing.CoverImage = coverImagePath;
|
||||
|
||||
await System.IO.File.WriteAllTextAsync(jsonPath, JsonSerializer.Serialize(existing));
|
||||
return Ok(existing);
|
||||
}
|
||||
|
||||
|
||||
// ✅ Delete Post
|
||||
[HttpDelete("{id}")]
|
||||
public IActionResult Delete(string id)
|
||||
{
|
||||
string jsonPath = Path.Combine(_dataPath, $"{id}.json");
|
||||
if (!System.IO.File.Exists(jsonPath))
|
||||
return NotFound();
|
||||
|
||||
var post = JsonSerializer.Deserialize<BlogPostMetadata>(System.IO.File.ReadAllText(jsonPath));
|
||||
if (!string.IsNullOrEmpty(post?.CoverImage))
|
||||
{
|
||||
var coverPath = Path.Combine(_env.WebRootPath, post.CoverImage.TrimStart('/').Replace("/", Path.DirectorySeparatorChar.ToString()));
|
||||
if (System.IO.File.Exists(coverPath))
|
||||
System.IO.File.Delete(coverPath);
|
||||
}
|
||||
|
||||
System.IO.File.Delete(jsonPath);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpGet("postcontent/{postId}")]
|
||||
public async Task<IActionResult> GetPostContent(string postId)
|
||||
{
|
||||
// Load metadata (adjust this to your actual metadata loading logic)
|
||||
var metadataPath = Path.Combine(_dataPath, $"{postId}.json");
|
||||
if (!System.IO.File.Exists(metadataPath))
|
||||
{
|
||||
return NotFound("Post metadata not found.");
|
||||
}
|
||||
|
||||
var metadataJson = await System.IO.File.ReadAllTextAsync(metadataPath);
|
||||
var metadata = System.Text.Json.JsonSerializer.Deserialize<BlogPostMetadata>(metadataJson);
|
||||
if (metadata == null || string.IsNullOrEmpty(metadata.DriveLink))
|
||||
{
|
||||
return BadRequest("Invalid post metadata.");
|
||||
}
|
||||
|
||||
// Extract file ID from Google Drive link
|
||||
string? fileId = ExtractGoogleDriveFileId(metadata.DriveLink);
|
||||
if (string.IsNullOrEmpty(fileId))
|
||||
{
|
||||
return BadRequest("Invalid Drive link.");
|
||||
}
|
||||
|
||||
// Download blog content from Drive
|
||||
string downloadUrl = $"https://drive.google.com/uc?export=download&id={fileId}";
|
||||
using var httpClient = new HttpClient();
|
||||
var content = await httpClient.GetStringAsync(downloadUrl);
|
||||
|
||||
return Content(content, "text/html");
|
||||
}
|
||||
|
||||
private string? ExtractGoogleDriveFileId(string driveLink)
|
||||
{
|
||||
var match = Regex.Match(driveLink, @"\/d\/([^\/]+)");
|
||||
return match.Success ? match.Groups[1].Value : null;
|
||||
}
|
||||
|
||||
public class BlogPostForm
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Lead { get; set; }
|
||||
public string DriveLink { get; set; }
|
||||
public string Tags { get; set; }
|
||||
public IFormFile CoverImage { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
using GoogleApi.Entities.Maps.Routes.Common;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json;
|
||||
using TIAM.Entities.Transfers;
|
||||
using TIAMWebApp.Shared.Application.Models;
|
||||
|
||||
namespace TIAMWebApp.Server.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/tours")]
|
||||
public class TourAPIController : ControllerBase
|
||||
{
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly string _toursPath;
|
||||
private readonly string _coverPath;
|
||||
|
||||
public TourAPIController(IWebHostEnvironment env)
|
||||
{
|
||||
_env = env;
|
||||
_toursPath = Path.Combine(_env.WebRootPath, "data", "tours");
|
||||
_coverPath = Path.Combine(_env.WebRootPath, "uploads", "tourcovers");
|
||||
Directory.CreateDirectory(_toursPath);
|
||||
Directory.CreateDirectory(_coverPath);
|
||||
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult<IEnumerable<TourInfo>> GetAll()
|
||||
{
|
||||
var files = Directory.GetFiles(_toursPath, "*.json");
|
||||
var tours = files.Select(file =>
|
||||
{
|
||||
var json = System.IO.File.ReadAllText(file);
|
||||
return JsonSerializer.Deserialize<TourInfo>(json);
|
||||
}).Where(t => t != null).ToList();
|
||||
|
||||
return Ok(tours);
|
||||
}
|
||||
|
||||
[HttpGet("{transferDestinationId}")]
|
||||
public ActionResult<TourInfo> Get(Guid transferDestinationId)
|
||||
{
|
||||
var path = GetPath(transferDestinationId);
|
||||
if (!System.IO.File.Exists(path))
|
||||
return NotFound();
|
||||
|
||||
var json = System.IO.File.ReadAllText(path);
|
||||
var tour = JsonSerializer.Deserialize<TourInfo>(json);
|
||||
return Ok(tour);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromForm] TourForm form)
|
||||
{
|
||||
var tour = new TourInfo
|
||||
{
|
||||
TransferDestinationId = Guid.Parse(form.TransferDestinationId),
|
||||
Title = form.Title,
|
||||
FancyDescription = form.Description,
|
||||
Created = DateTime.UtcNow,
|
||||
Modified = DateTime.UtcNow
|
||||
};
|
||||
|
||||
// Save cover image if exists
|
||||
if (form.CoverImage != null)
|
||||
{
|
||||
string fileName = $"{tour.TransferDestinationId}{Path.GetExtension(form.CoverImage.FileName)}";
|
||||
string savePath = Path.Combine(_coverPath, fileName);
|
||||
|
||||
Directory.CreateDirectory(_coverPath); // Ensure folder exists
|
||||
|
||||
await using var fs = System.IO.File.Create(savePath);
|
||||
await form.CoverImage.CopyToAsync(fs);
|
||||
|
||||
tour.CoverImageUrl = $"/uploads/tourcovers/{fileName}";
|
||||
}
|
||||
|
||||
// Save metadata as JSON
|
||||
var jsonPath = GetPath(tour.TransferDestinationId);
|
||||
var json = JsonSerializer.Serialize(tour, new JsonSerializerOptions { WriteIndented = true });
|
||||
|
||||
await System.IO.File.WriteAllTextAsync(jsonPath, json);
|
||||
|
||||
return Ok(tour);
|
||||
}
|
||||
|
||||
[HttpPut("{transferDestinationId}")]
|
||||
public async Task<IActionResult> Update(Guid transferDestinationId, [FromBody] TourInfo updated)
|
||||
{
|
||||
var path = GetPath(transferDestinationId);
|
||||
if (!System.IO.File.Exists(path))
|
||||
return NotFound();
|
||||
|
||||
updated.TransferDestinationId = transferDestinationId;
|
||||
updated.Modified = DateTime.UtcNow;
|
||||
|
||||
var json = JsonSerializer.Serialize(updated, new JsonSerializerOptions { WriteIndented = true });
|
||||
await System.IO.File.WriteAllTextAsync(path, json);
|
||||
|
||||
return Ok(updated);
|
||||
}
|
||||
|
||||
[HttpDelete("{transferDestinationId}")]
|
||||
public IActionResult Delete(Guid transferDestinationId)
|
||||
{
|
||||
var path = GetPath(transferDestinationId);
|
||||
if (!System.IO.File.Exists(path))
|
||||
return NotFound();
|
||||
|
||||
System.IO.File.Delete(path);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
//[HttpPost("{id}/image")]
|
||||
//public async Task<IActionResult> UploadTourImage(Guid id, IFormFile file)
|
||||
//{
|
||||
// if (file == null || file.Length == 0)
|
||||
// return BadRequest("No file uploaded.");
|
||||
|
||||
// var ext = Path.GetExtension(file.FileName);
|
||||
// var fileName = $"{id}{ext}";
|
||||
// var filePath = Path.Combine(_coverPath, fileName);
|
||||
// Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
|
||||
|
||||
// using var stream = System.IO.File.Create(filePath);
|
||||
// await file.CopyToAsync(stream);
|
||||
|
||||
// // Optionally: update metadata to store file path (e.g., in a JSON file or DB)
|
||||
|
||||
// return Ok();
|
||||
//}
|
||||
|
||||
[HttpPost("{id}/image")]
|
||||
public async Task<IActionResult> UploadTourImage(Guid id, IFormFile file)
|
||||
{
|
||||
if (file == null || file.Length == 0)
|
||||
return BadRequest("No file uploaded.");
|
||||
|
||||
var ext = Path.GetExtension(file.FileName);
|
||||
var fileName = $"{id}{ext}";
|
||||
//var folderPath = Path.Combine("wwwroot", "uploads", "tours", id.ToString());
|
||||
//Directory.CreateDirectory(folderPath);
|
||||
|
||||
var filePath = Path.Combine(_coverPath, fileName);
|
||||
|
||||
using var stream = System.IO.File.Create(filePath);
|
||||
await file.CopyToAsync(stream);
|
||||
|
||||
// ✅ Update TourInfo JSON
|
||||
var jsonPath = GetPath(id); // Same as in your PUT method
|
||||
if (!System.IO.File.Exists(jsonPath))
|
||||
return NotFound("TourInfo JSON not found.");
|
||||
|
||||
var json = await System.IO.File.ReadAllTextAsync(jsonPath);
|
||||
var tour = JsonSerializer.Deserialize<TourInfo>(json);
|
||||
|
||||
// Relative path to serve the image (adjust if needed)
|
||||
var relativeImagePath = Path.Combine("uploads", "tourcovers", fileName).Replace("\\", "/");
|
||||
|
||||
tour!.CoverImageUrl = "/" + relativeImagePath;
|
||||
tour.Modified = DateTime.UtcNow;
|
||||
|
||||
var updatedJson = JsonSerializer.Serialize(tour, new JsonSerializerOptions { WriteIndented = true });
|
||||
await System.IO.File.WriteAllTextAsync(jsonPath, updatedJson);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
private string GetPath(Guid id) =>
|
||||
Path.Combine(_toursPath, $"{id}.json");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -90,11 +90,12 @@ namespace TIAMWebApp.Server.Controllers
|
|||
}
|
||||
|
||||
//[Authorize]
|
||||
//[HttpGet]
|
||||
//[Route(APIUrls.GetTransferDriversByTransferIdRouteName)]
|
||||
[NonAction]
|
||||
//[NonAction]
|
||||
[AllowAnonymous]
|
||||
[HttpPost]
|
||||
[Route(APIUrls.GetTransferDestinationByIdRouteName)]
|
||||
[SignalR(SignalRTags.GetTransferDestinationById)]
|
||||
public async Task<TransferDestination?> GetTransferDestinationById(Guid transferDestinationId)
|
||||
public async Task<TransferDestination?> GetTransferDestinationById([FromBody] Guid transferDestinationId)
|
||||
{
|
||||
var transferDestination = await _adminDal.GetTransferDestinationByIdAsync(transferDestinationId);
|
||||
return transferDestination;
|
||||
|
|
@ -108,6 +109,15 @@ namespace TIAMWebApp.Server.Controllers
|
|||
return null;// await _transferDestinationDal.Context.TransferDestinations.FirstOrDefaultAsync(x => x.Latitude == latitude && x.Longitude == longitude);
|
||||
}
|
||||
|
||||
//[AllowAnonymous]
|
||||
//[HttpGet]
|
||||
//[Route(APIUrls.GetTransferDestinationById)]
|
||||
//public async Task<TransferDestination?> GetTransferDestinationById(Guid )
|
||||
//{
|
||||
// var transferDestination = await _adminDal.GetTransferDestinationByIdAsync(transferDestinationId);
|
||||
// return transferDestination;
|
||||
//}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet]
|
||||
[Route("GetTransferDestinationByAddress")]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
<PropertyGroup>
|
||||
<ActiveDebugProfile>https</ActiveDebugProfile>
|
||||
<NameOfLastUsedPublishProfile>D:\REPOS\AYCODE\source\TourIAm\TIAMWebApp\Server\Properties\PublishProfiles\ReleaseDeployProfile.pubxml</NameOfLastUsedPublishProfile>
|
||||
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
|
||||
<Controller_SelectedScaffolderCategoryPath>root/Common/MVC/Controller</Controller_SelectedScaffolderCategoryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
//"DeveloperDbConnection": "Data Source=185.51.190.197;Initial Catalog=TIAM_DEV;Trusted_Connection=false;Encrypt=false;TrustServerCertificate=True;Connect Timeout=200;User ID=Anata_Development_Team;Password=v6f_?xNfg9N1;MultipleActiveResultSets=true"
|
||||
|
||||
//"DeveloperDbConnection": "Data Source=194.164.235.47;Initial Catalog=TIAM_DEV;Trusted_Connection=false;Encrypt=false;TrustServerCertificate=True;Connect Timeout=200;User ID=Anata_Development_Team;Password=v6f_?xNfg9N1;MultipleActiveResultSets=true"
|
||||
"DeveloperDbConnection": "Data Source=194.164.235.47;Initial Catalog=TIAM_DEVRELEASE;Trusted_Connection=false;Encrypt=false;TrustServerCertificate=True;Connect Timeout=200;User ID=Anata_Development_Team;Password=v6f_?xNfg9N1;MultipleActiveResultSets=true"
|
||||
"DeveloperDbConnection": "Data Source=195.26.231.218;Initial Catalog=TIAM_DEV;Trusted_Connection=false;Encrypt=false;TrustServerCertificate=True;Connect Timeout=200;User ID=sa;Password=v6f_?xNfg9N1;MultipleActiveResultSets=true"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
{"Id":"38705566-2e31-4897-8677-ca776523673f","Title":"Time Slows in Stone and Vine: Adventures Through the K\u00E1li Basin","Lead":"Tucked away in the northwestern corner of Lake Balaton, the K\u00E1li Basin offers an escape into rolling hills, small lakes, and peaceful countryside. \u003Cstrong\u003EThis region is known for its quiet villages, ancient ruins, and scenic bike routes that invite travelers to slow down and explore at their own pace.\u003C/strong\u003E Nature thrives here, with fields of wildflowers and stone churches dotting the landscape.","DriveLink":"https://drive.google.com/file/d/1leHZ0f1uLYOCFhEoDY-dD0KXiSNqMcFJ/view?usp=drive_link","CoverImage":"/uploads/covers/38705566-2e31-4897-8677-ca776523673f.jpg","Tags":["\u00E9szak magyarorsz\u00E1g","csal\u00E1di","t\u00FAr\u00E1z\u00E1s","term\u00E9szet"]}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"Id":"bce48760-9272-492b-8c88-db99cd4f55f0","Title":"Teszt poszt","Lead":"Tucked away in the northwestern corner of Lake Balaton, the K\u00E1li Basin offers an escape into rolling hills, small lakes, and peaceful countryside. \u003Cstrong\u003EThis region is known for its quiet villages, ancient ruins, and scenic bike routes that invite travelers to slow down and explore at their own pace.\u003C/strong\u003E Nature thrives here, with fields of wildflowers and stone churches dotting the landscape.","DriveLink":"https://drive.google.com/file/d/1d1RwVrZ92dgPLPX98Vr5HUXbtB1Bwj8x/view?usp=drive_link","CoverImage":"/uploads/covers/bce48760-9272-492b-8c88-db99cd4f55f0.png","Tags":["balaton","csal\u00E1di","d\u00E9l-magyarorsz\u00E1g","v\u00EDzpart"]}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Id": "00000000-0000-0000-0000-000000000000",
|
||||
"TransferDestinationId": "171fcca4-ffd5-4380-9402-f27447af1e08",
|
||||
"Title": "Downtown",
|
||||
"FancyDescription": "fdshjklhsjfkslh dfjkhsjklfhd jkkslhfdsjkfh dskfh dsklhf dsjkl fdhjkl fdshjk fkjld hlkdskl dsjk fdjlfh dskh fdsjkfdshl khf dk hldsk hldskh dsjk hdslkf dhjkh dsfk fdslsk",
|
||||
"CoverImageUrl": "/uploads/tourcovers/171fcca4-ffd5-4380-9402-f27447af1e08.png",
|
||||
"Created": "2025-07-28T06:29:35.6626494Z",
|
||||
"Modified": "2025-07-28T06:29:35.6626499Z"
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Id": "00000000-0000-0000-0000-000000000000",
|
||||
"TransferDestinationId": "72a29052-0d99-49ec-a103-3b5ca86b5b4c",
|
||||
"Title": "Rept\u00E9ri t\u00FAra",
|
||||
"FancyDescription": "jhkl hjk hjfdsklh sjklfh sjkfh sjklfhdsjlfh djkf hdjklf hjk fldshjkf hdsjkf hdjk fhdsjkf hdsjklfhdjklf dhjkf hdsjklfdhjkldsfh jklsdfhjkfslhfjkslh fdsklh fjklhfdsjklhdsjkfdshjkfdhjksdlhjksdh skl",
|
||||
"CoverImageUrl": "/uploads/tourcovers/72a29052-0d99-49ec-a103-3b5ca86b5b4c.jpeg",
|
||||
"Created": "2025-07-27T18:05:27.5184619Z",
|
||||
"Modified": "2025-07-27T18:05:27.5184622Z"
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Id": "00000000-0000-0000-0000-000000000000",
|
||||
"TransferDestinationId": "ac30e313-6752-4358-b686-65a57be7a030",
|
||||
"Title": "T\u00FAra 2",
|
||||
"FancyDescription": "jkhk lhjdh fksldh fjk hsfjklh jdskhfsjk fhdsjk fhsjklfhsjlhf dsjkh fdsjklfh sjklhf sjdkhf sjklhfsjkfh dsjklhf dsjk hfdsjklhfdsklhfjdkslhfdsjklhf dsjklhf dsjkl hfdslh fdsjkh fsjkl hfdsjklfh sjkh fdskh fdkslfhjkshfdsl hfdskl",
|
||||
"CoverImageUrl": "/uploads/tourcovers/ac30e313-6752-4358-b686-65a57be7a030.jpeg",
|
||||
"Created": "2025-07-27T16:24:56.1635945Z",
|
||||
"Modified": "2025-07-27T16:24:56.1635947Z"
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Id": "00000000-0000-0000-0000-000000000000",
|
||||
"TransferDestinationId": "ae7c3d59-e9fb-4393-a89c-3124ed5eba04",
|
||||
"Title": "Valami",
|
||||
"FancyDescription": "jkhjdkhfjkdhfjsklhf jskh fsjdklfh sjkh fsjdk hfdsjkh fdsk hfdskl hdfsjkh fjdsklfh jdkl hfjkdslhfdjskh fdjsk hfsjklfh sdjh fdsjklfh sjk hfjklh fsjklhdls",
|
||||
"CoverImageUrl": "/uploads/tourcovers/ae7c3d59-e9fb-4393-a89c-3124ed5eba04.jpeg",
|
||||
"Created": "0001-01-01T00:00:00",
|
||||
"Modified": "2025-07-28T06:26:09.0304045Z"
|
||||
}
|
||||
|
After Width: | Height: | Size: 8.6 MiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
|
@ -8,6 +8,7 @@ namespace TIAMWebApp.Shared.Application.Interfaces
|
|||
Task<List<TransferDestination>> GetDestinationsAsync();
|
||||
Task<TransferDestination?> GetTransferDestinationbyCoordinatesAsync(string destinationId);
|
||||
Task<TransferDestination?> GetTransferDestinationbyAddressAsync(string destinationId);
|
||||
Task<TransferDestination?> GetTransferDestinationbyIdAsync(Guid destinationId);
|
||||
Task<TransferDestination?> CreateTransferDestination(TransferDestination model);
|
||||
|
||||
Task<TransferDestination?> UpdateTransferDestination(TransferDestination model);
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ namespace TIAMWebApp.Shared.Application.Models
|
|||
public const string GetTransferDestinationByAddressRouteName = "GetTransferDestinationByAddress";
|
||||
public const string GetTransferDestinationByAddress = TransferDataAPI + GetTransferDestinationByAddressRouteName;
|
||||
|
||||
public const string GetTransferDestinationByIdRouteName = "GetTransferDestinationById";
|
||||
public const string GetTransferDestinationById = TransferDataAPI + GetTransferDestinationByIdRouteName;
|
||||
|
||||
public const string CreateTransferDestinationRouteName = "CreateTransferDestination";
|
||||
public const string CreateTransferDestination = TransferDataAPI + CreateTransferDestinationRouteName;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TIAMWebApp.Shared.Application.Models
|
||||
{
|
||||
public class BlogPostMetadata
|
||||
{
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
public string Title { get; set; }
|
||||
public string Lead { get; set; }
|
||||
public string DriveLink { get; set; }
|
||||
public string CoverImage { get; set; } // Relative URL
|
||||
public List<string> Tags { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TIAMWebApp.Shared.Application.Models
|
||||
{
|
||||
public class TourForm
|
||||
{
|
||||
public string TransferDestinationId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public IFormFile? CoverImage { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TIAMWebApp.Shared.Application.Models
|
||||
{
|
||||
public class TourInfo
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid TransferDestinationId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string? FancyDescription { get; set; }
|
||||
public string? CoverImageUrl { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime Modified { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TIAMWebApp.Shared.Application.Models;
|
||||
|
||||
namespace TIAMWebApp.Shared.Application.Services
|
||||
{
|
||||
|
||||
public class BlogService
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public BlogService(HttpClient http)
|
||||
{
|
||||
_http = http;
|
||||
}
|
||||
|
||||
public async Task<List<BlogPostMetadata>> GetAllPostsAsync()
|
||||
{
|
||||
return await _http.GetFromJsonAsync<List<BlogPostMetadata>>("/api/blog") ?? new();
|
||||
}
|
||||
|
||||
public async Task<BlogPostMetadata?> GetPostByIdAsync(string id)
|
||||
{
|
||||
return await _http.GetFromJsonAsync<BlogPostMetadata>($"/api/blog/{id}");
|
||||
}
|
||||
|
||||
public async Task<BlogPostMetadata?> CreatePostAsync(BlogPostMetadata metadata, IBrowserFile? coverImage)
|
||||
{
|
||||
using var content = new MultipartFormDataContent();
|
||||
content.Add(new StringContent(metadata.Title ?? ""), "Title");
|
||||
content.Add(new StringContent(metadata.Lead ?? ""), "Lead");
|
||||
content.Add(new StringContent(metadata.DriveLink ?? ""), "DriveLink");
|
||||
content.Add(new StringContent(string.Join(",", metadata.Tags ?? new())), "Tags");
|
||||
|
||||
if (coverImage != null)
|
||||
{
|
||||
var fileContent = new StreamContent(coverImage.OpenReadStream(long.MaxValue));
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(coverImage.ContentType);
|
||||
content.Add(fileContent, "CoverImage", coverImage.Name);
|
||||
}
|
||||
|
||||
var response = await _http.PostAsync("/api/blog", content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<BlogPostMetadata>();
|
||||
}
|
||||
|
||||
public async Task UpdatePostAsync(BlogPostMetadata metadata, IBrowserFile? newImageFile = null)
|
||||
{
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
content.Add(new StringContent(metadata.Title ?? ""), "Title");
|
||||
content.Add(new StringContent(metadata.Lead ?? ""), "Lead");
|
||||
content.Add(new StringContent(metadata.DriveLink ?? ""), "DriveLink");
|
||||
content.Add(new StringContent(string.Join(",", metadata.Tags ?? new List<string>())), "Tags");
|
||||
|
||||
if (newImageFile != null)
|
||||
{
|
||||
var stream = newImageFile.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024); // 10 MB max
|
||||
var fileContent = new StreamContent(stream);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(newImageFile.ContentType);
|
||||
content.Add(fileContent, "CoverImage", newImageFile.Name);
|
||||
}
|
||||
|
||||
var response = await _http.PutAsync($"/api/blog/{metadata.Id}", content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
|
||||
public async Task DeletePostAsync(string id)
|
||||
{
|
||||
var response = await _http.DeleteAsync($"/api/blog/{id}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TIAMWebApp.Shared.Application.Models;
|
||||
|
||||
namespace TIAMWebApp.Shared.Application.Services
|
||||
{
|
||||
public class TourService
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public TourService(HttpClient http)
|
||||
{
|
||||
_http = http;
|
||||
}
|
||||
|
||||
public async Task<List<TourInfo>> GetAllAsync() =>
|
||||
await _http.GetFromJsonAsync<List<TourInfo>>("/api/tours") ?? new();
|
||||
|
||||
public async Task<TourInfo?> GetAsync(Guid id) =>
|
||||
await _http.GetFromJsonAsync<TourInfo>($"/api/tours/{id}");
|
||||
|
||||
public async Task CreateAsync(TourInfo tour, IBrowserFile? coverImage)
|
||||
{
|
||||
var content = new MultipartFormDataContent
|
||||
{
|
||||
{ new StringContent(tour.TransferDestinationId.ToString()), nameof(TourForm.TransferDestinationId) },
|
||||
{ new StringContent(tour.Title ?? ""), nameof(TourForm.Title) },
|
||||
{ new StringContent(tour.FancyDescription ?? ""), nameof(TourForm.Description) }
|
||||
};
|
||||
|
||||
if (coverImage != null)
|
||||
{
|
||||
var stream = coverImage.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024); // adjust limit if needed
|
||||
var fileContent = new StreamContent(stream);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(coverImage.ContentType);
|
||||
|
||||
content.Add(fileContent, nameof(TourForm.CoverImage), coverImage.Name);
|
||||
}
|
||||
|
||||
var response = await _http.PostAsync("/api/tours", content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
|
||||
public async Task UpdateAsync(TourInfo tour, IBrowserFile? file)
|
||||
{
|
||||
// Step 1: Update metadata
|
||||
var response = await _http.PutAsJsonAsync($"/api/tours/{tour.TransferDestinationId}", tour);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// Step 2: Upload new image if provided
|
||||
if (file is not null)
|
||||
{
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new StreamContent(file.OpenReadStream(maxAllowedSize: 5 * 1024 * 1024));
|
||||
fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType);
|
||||
content.Add(content: fileContent, name: "file", fileName: file.Name);
|
||||
|
||||
var uploadResponse = await _http.PostAsync($"/api/tours/{tour.TransferDestinationId}/image", content);
|
||||
uploadResponse.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(Guid id)
|
||||
{
|
||||
var response = await _http.DeleteAsync($"/api/tours/{id}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -47,6 +47,18 @@ namespace TIAMWebApp.Shared.Application.Services
|
|||
return await _http.GetFromJsonAsync<TransferDestination>(url);
|
||||
}
|
||||
|
||||
public async Task<TransferDestination?> GetTransferDestinationbyIdAsync(Guid destinationId)
|
||||
{
|
||||
var url = $"{Setting.ApiBaseUrl}/{APIUrls.GetTransferDestinationById}";
|
||||
var response = await _http.PostAsJsonAsync(url, destinationId);
|
||||
//return await _http.GetFromJsonAsync<TransferDestination>(url);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return null;
|
||||
|
||||
var result = (TransferDestination)(await response.Content.ReadFromJsonAsync(typeof(TransferDestination)))!;
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<TransferDestination?> CreateTransferDestination(TransferDestination model)
|
||||
{
|
||||
var url = $"{Setting.ApiBaseUrl}/{APIUrls.CreateTransferDestination}";
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
<PackageReference Include="MessagePack" Version="2.5.187" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.JSInterop" Version="8.0.10" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.8" />
|
||||
|
|
|
|||