diff --git a/ShoppingAssistantWebClient.Web/Models/Enums/SearchEventType.cs b/ShoppingAssistantWebClient.Web/Models/Enums/SearchEventType.cs new file mode 100644 index 0000000..af14ebd --- /dev/null +++ b/ShoppingAssistantWebClient.Web/Models/Enums/SearchEventType.cs @@ -0,0 +1,9 @@ +namespace ShoppingAssistantWebClient.Web.Models.Enums; + +public enum SearchEventType +{ + Wishlist = 0, + Message = 1, + Suggestion = 2, + Product = 3 +} \ No newline at end of file diff --git a/ShoppingAssistantWebClient.Web/Models/Input/MessageCreateDto.cs b/ShoppingAssistantWebClient.Web/Models/Input/MessageCreateDto.cs new file mode 100644 index 0000000..5349051 --- /dev/null +++ b/ShoppingAssistantWebClient.Web/Models/Input/MessageCreateDto.cs @@ -0,0 +1,6 @@ +namespace ShoppingAssistantWebClient.Web.Models.Input; + +public class MessageCreateDto +{ + public required string Text { get; set; } +} diff --git a/ShoppingAssistantWebClient.Web/Models/Messages.cs b/ShoppingAssistantWebClient.Web/Models/Messages.cs new file mode 100644 index 0000000..0c7ecf9 --- /dev/null +++ b/ShoppingAssistantWebClient.Web/Models/Messages.cs @@ -0,0 +1,18 @@ +namespace ShoppingAssistantWebClient.Web.Models +{ + public class Messages + { + + public string Id { get; set; } + + public string Text { get; set; } + + public string Role { get; set; } + + public string CreatedById { get; set; } + + + } + + +} diff --git a/ShoppingAssistantWebClient.Web/Models/Product.cs b/ShoppingAssistantWebClient.Web/Models/Product.cs index 05e075a..210c462 100644 --- a/ShoppingAssistantWebClient.Web/Models/Product.cs +++ b/ShoppingAssistantWebClient.Web/Models/Product.cs @@ -1,22 +1,22 @@ -namespace ShoppingAssistantWebClient.Web.Models; - -public class Product -{ - public required string Id {get; set;} - - public required string Url {get; set;} - - public required string Name {get; set;} - - public required string Description {get; set;} - - public required double Rating {get; set;} - - public required double Price { get; set; } - - public required string[] ImagesUrls {get; set;} - - public required bool WasOpened {get; set;} - - public required string WishlistId {get; set;} +namespace ShoppingAssistantWebClient.Web.Models; + +public class Product +{ + public required string Id {get; set;} + + public required string Url {get; set;} + + public required string Name {get; set;} + + public required string Description {get; set;} + + public required double Rating {get; set;} + + public required double Price { get; set; } + + public required string[] ImagesUrls {get; set;} + + public required bool WasOpened {get; set;} + + public required string WishlistId {get; set;} } \ No newline at end of file diff --git a/ShoppingAssistantWebClient.Web/Models/ProductSearch/ServerSentEvent.cs b/ShoppingAssistantWebClient.Web/Models/ProductSearch/ServerSentEvent.cs new file mode 100644 index 0000000..20f0df1 --- /dev/null +++ b/ShoppingAssistantWebClient.Web/Models/ProductSearch/ServerSentEvent.cs @@ -0,0 +1,10 @@ +using ShoppingAssistantWebClient.Web.Models.Enums; + +namespace ShoppingAssistantWebClient.Web.Models.ProductSearch; + +public class ServerSentEvent +{ + public SearchEventType Event { get; set; } + + public string Data { get; set; } +} diff --git a/ShoppingAssistantWebClient.Web/Network/ApiClient.cs b/ShoppingAssistantWebClient.Web/Network/ApiClient.cs index f7b6532..a4a6f24 100644 --- a/ShoppingAssistantWebClient.Web/Network/ApiClient.cs +++ b/ShoppingAssistantWebClient.Web/Network/ApiClient.cs @@ -3,6 +3,9 @@ using GraphQL; using Newtonsoft.Json; using System.Net.Http.Headers; using ShoppingAssistantWebClient.Web.Models.GlobalInstances; +using ShoppingAssistantWebClient.Web.Models.ProductSearch; +using System.Text; +using ShoppingAssistantWebClient.Web.Models.Enums; namespace ShoppingAssistantWebClient.Web.Network; @@ -93,6 +96,55 @@ public class ApiClient return model; } + public async IAsyncEnumerable GetServerSentEventStreamed(string url, Object obj, CancellationToken cancellationToken) + { + await SetAuthenticationAsync(); + var count = 0; // + var requestUrl = $"{_httpClient.BaseAddress}{url}"; + var response = await _httpClient.PostAsJsonAsync(requestUrl, obj); + using var responseStream = await response.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(responseStream, Encoding.UTF8); + + SearchEventType eventType = SearchEventType.Message; + while (!cancellationToken.IsCancellationRequested) + { + var jsonChunk = await reader.ReadLineAsync(cancellationToken); + count += 1; // + if (count >=5 ){ // + break; // + }; // + if (jsonChunk == null) continue; + if (jsonChunk.StartsWith("event: ")) + { + count = 0; // + var type = jsonChunk.Substring("event: ".Length); + switch(type) + { + case "Message": + eventType = SearchEventType.Message; + break; + case "Suggestion": + eventType = SearchEventType.Suggestion; + break; + case "Product": + eventType = SearchEventType.Product; + break; + case "Wishlist": + eventType = SearchEventType.Wishlist; + break; + } + } + if (jsonChunk.StartsWith("data: ")) + { + yield return new ServerSentEvent + { + Event = eventType, + Data = jsonChunk.Substring("data: ".Length), + }; + } + } + } + private MultipartFormDataContent MapIFormCollectionToForm(IFormCollection formCollection) { var formDataContent = new MultipartFormDataContent(); diff --git a/ShoppingAssistantWebClient.Web/Pages/Chat.razor b/ShoppingAssistantWebClient.Web/Pages/Chat.razor index b79f8ce..88b9c82 100644 --- a/ShoppingAssistantWebClient.Web/Pages/Chat.razor +++ b/ShoppingAssistantWebClient.Web/Pages/Chat.razor @@ -1,133 +1,139 @@ -@page "/chat/1" - -
- -
- - - - - - - -
- -
- - - - -
- -
- -
Gift for Jessica
- -
    - -
  • -

    Give me product recommendation. Ask me questions if you need more directions. I am looking for: hub for my macbook to connect external monitors

    -
  • -
  • -

    Sure! I can help you with that. I will ask you some leading questions. This is the first: -
    - How many external monitors do you want to connect to your MacBook?

    -
  • -
  • -

    7

    -
  • -
  • -

    Thank you. Here is the next question: -
    - What type of external monitors do you have? (e.g., HDMI, DisplayPort, VGA)

    -
  • - -
- - - - -
- - - - - Send message - - - -
- - -
-
- -
-
- +@page "/chat/{chatId}" + +@inject IHttpClientFactory ClientFactory +@inject IJSRuntime JSRuntime + +@name + +
+ + + + +
+ +
@name
+ +
+ +
    + + @if (!isLoading && Messages != null) + { + + @foreach (var item in Messages) + { + + if (item.Role != "User") + { + +
  • +

    @item.Text

    +
  • + + } + else + { + +
  • +

    @item.Text

    +
  • + + } + } + + } + +
+ + +
+ + +
+ + @if (Suggestion.Count != 0) + { + +
Several possible options
+ +
+ + + + @foreach (var item in Suggestion) + { + +
+ @item +
+ } + + + +
+ } + +
+ + +
+ + Send message +
+ +
+ +
+ + + +@code { + + [Parameter] public string chatId { get; set; } + + protected override async Task OnParametersSetAsync() + { + await LoadMessages(); + } + + public void Enter(KeyboardEventArgs e) + { + if (e.Code == "Enter" || e.Code == "NumpadEnter") + { + AddNewMessage(); + } + } + + private ElementReference chatMessageRef; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await JSRuntime.InvokeVoidAsync("scrollToBottom", chatMessageRef); + } + + + private void ClickOption(string item) + { + inputValue = item; + } + +} \ No newline at end of file diff --git a/ShoppingAssistantWebClient.Web/Pages/Chat.razor.cs b/ShoppingAssistantWebClient.Web/Pages/Chat.razor.cs index 80b5ddc..d361622 100644 --- a/ShoppingAssistantWebClient.Web/Pages/Chat.razor.cs +++ b/ShoppingAssistantWebClient.Web/Pages/Chat.razor.cs @@ -1,14 +1,169 @@ -using Microsoft.AspNetCore.Components; - -namespace ShoppingAssistantWebClient.Web.Pages; - -public partial class Chat : ComponentBase -{ - [Inject] - public ILogger Logger { get; set; } - - protected override async Task OnInitializedAsync() - { - // Get data from Back-end - } -} +using Microsoft.AspNetCore.Components; +using ShoppingAssistantWebClient.Web.Models; +using GraphQL; +using Newtonsoft.Json; +using ShoppingAssistantWebClient.Web.Network; +using ShoppingAssistantWebClient.Web.Models.Input; +using ShoppingAssistantWebClient.Web.Models.Enums; +using System.Text.RegularExpressions; +namespace ShoppingAssistantWebClient.Web.Pages; + +public partial class Chat : ComponentBase +{ + + [Inject] + private ApiClient _apiClient { get; set; } + [Inject] + private NavigationManager Navigation { get; set; } + + public List Messages { get; set; } + public List Suggestion { get; set; } = new List(); + + public Messages Message { get; set; } + + private CancellationTokenSource cancelTokenSource; + + private MessageCreateDto messageCreateDto; + public bool isLoading = true; + private string inputValue = ""; + private string name = ""; + protected override async Task OnInitializedAsync() + { + await LoadMessages(); + } + + + private async Task LoadMessages() + { + try{ + string wishlistId = chatId; + + var request = new GraphQLRequest + { + Query = @"query PersonalWishlist( $wishlistId: String!) { + personalWishlist(wishlistId: $wishlistId) { + name + } + }", + + Variables = new + { + wishlistId, + } + }; + + var response = await _apiClient.QueryAsync(request); + var responseData = response.Data; + name = responseData.personalWishlist.name; + + + isLoading = true; + int pageNumber = 1; + request = new GraphQLRequest + { + Query = @"query MessagesPageFromPersonalWishlist($wishlistId: String!, $pageNumber: Int!, $pageSize: Int!) { + messagesPageFromPersonalWishlist( wishlistId: $wishlistId, pageNumber: $pageNumber, pageSize: $pageSize) + { + items { + id + text + role + createdById + } + } + }", + + Variables = new + { + wishlistId, + pageNumber, + pageSize = 200 + } + }; + + + + response = await _apiClient.QueryAsync(request); + responseData = response.Data; + var jsonCategoriesResponse = JsonConvert.SerializeObject(responseData.messagesPageFromPersonalWishlist.items); + this.Messages = JsonConvert.DeserializeObject>(jsonCategoriesResponse); + Messages.Reverse(); + isLoading = false; + + }catch(Exception ex){ + Console.WriteLine($"Error : {ex.Message}"); + } + } + private async Task AddNewMessage() + { + try{ + messageCreateDto = new MessageCreateDto { Text = inputValue };; + Message = new Messages(); + Message.Text = inputValue; + Message.Role = "User"; + Message.Id = ""; + Message.CreatedById = ""; + inputValue = ""; + Suggestion = new List(); + Messages.Add(Message); + StateHasChanged(); + + cancelTokenSource = new CancellationTokenSource(); + var cancellationToken = cancelTokenSource.Token; + + var serverSentEvent = _apiClient.GetServerSentEventStreamed($"ProductsSearch/search/{chatId}", messageCreateDto, cancellationToken); + bool first = true; + + await foreach (var sseEvent in serverSentEvent.WithCancellation(cancellationToken)) + { + Console.WriteLine($"Received SSE Event: {sseEvent.Event}, Data: {sseEvent.Data}"); + + + if(sseEvent.Event == SearchEventType.Message){ + + string input = sseEvent.Data; + Regex regex = new Regex("\"(.*?)\""); + Match match = regex.Match(input); + string result = match.Groups[1].Value; + + + + Message = new Messages(); + Message.Text = result; + Message.Role = "bot"; + Message.Id = ""; + Message.CreatedById = ""; + + if (first) + { + Messages.Add(Message); + first = false; + } + else + { + var lengt = Messages.Count(); + Messages[lengt-1].Text += Message.Text; + } + + StateHasChanged(); + + }else if(sseEvent.Event == SearchEventType.Product){ + + var url = $"/chat/{chatId}/product"; + Navigation.NavigateTo(url); + + }else if(sseEvent.Event == SearchEventType.Suggestion){ + + Suggestion.Add(sseEvent.Data); + } + + } + + }catch(Exception ex){ + Console.WriteLine($"Error : {ex.Message}"); + } + + + } + +} diff --git a/ShoppingAssistantWebClient.Web/Pages/Chat.razor.css b/ShoppingAssistantWebClient.Web/Pages/Chat.razor.css index a2e08ed..ef44028 100644 --- a/ShoppingAssistantWebClient.Web/Pages/Chat.razor.css +++ b/ShoppingAssistantWebClient.Web/Pages/Chat.razor.css @@ -1,330 +1,139 @@ -html { - font-family: 'Nunito'; - padding-top: 20px; - padding-bottom: 20px; - } - .menu { - position: relative; - width: 100%; - height: 100%; - border: 1.5px solid; - border-color: #0052CC; - border-radius: 10px; - padding-top: 16px; - padding-bottom: 16px; - } +.right_frame { + position: relative; + border: 0.09em solid; + border-color: #0052CC; + border-radius: 0.6em; + height: 100%; + width: 100%; +} + - .logo { - display: flex; - align-items: center; - padding-bottom: 15PX; - } - - .logo_name { - padding-top: 10px; - padding-left: 3px; - font-size: 17px; - justify-content: center; - /* Горизонтальное центрирование */ - align-items: center; - letter-spacing: 0.5px; - font-weight: 600; - - } - - .logo img { - float: left; - width: 52px; - height: 52px; - } - - .left_frame { - padding-left: 20px; - position: absolute; - height: calc(100% - 105px); - left: 0; - width: 20%; - transition: 1s; - } - - - .wishlist_name { - font-size: 15px; - margin-top: 7px; - margin-bottom: 7px; - color: black; - cursor: pointer; - } - - .elements { - padding-left: 12px; - padding-right: 12px; - - } - .elements_wishlisht{ - overflow-y: scroll; - padding-left: 12px; - padding-right: 12px; - height: 90%; - - } - .elements_wishlisht::-webkit-scrollbar { - visibility: hidden; - } - - - .close_menu { - position: relative; - background-color: #EAEAEA; - border-radius: 10px; - color: #4E4E4E; - font-size: 16px; - width: 90%; - height: 40px; - padding: 7px 9px; - } - - .add_chat { - display: flex; - align-items: center; - justify-content: center; - margin-top: 15px; - margin-bottom: 5px; - background-color: #EAEAEA; - border-radius: 10px; - font-size: 16px; - width: 100px; - height: 40px; - - cursor: pointer; - } - .add_chat a{ - border-radius: 10px; - padding: 7px 9px; - text-decoration: none; - color: #4E4E4E; - } - - - .info_user { - background-color: white; - position:absolute; - bottom: 0; - padding-bottom: 10px; - padding-top: 10px; - } - - .user_name { - font-size: 16px; - padding-left: 7px; - } - - .info_user img { - height: 40px; - width: 40px; - border-radius: 50%; - } - - .line { - position: absolute; - bottom: 60px; - width: 100%; - border-bottom: 1px solid #4b7bc4; - } - .button_close_menu{ - width: 23px; - height: 23px; - position: absolute; - right: 20px; - cursor: pointer; - - } - - .button_close_menu span { - width: 20px; - height: 1.5px; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: #4E4E4E; - } - - .button_close_menu span:nth-of-type(2) { - top: calc(50% - 5px); - } - .button_close_menu span:nth-of-type(3) { - top: calc(50% + 5px); - } - - .right_frame { - position: absolute; - border: 1.5px solid; - border-color: #0052CC; - border-radius: 10px; - left: 20px; - height: calc(100% - 40px); - width: calc(100% - 40px); - transition: 1s; - - } - - .button_open_menu{ - z-index: 2; - width: 23px; - height: 23px; - position: absolute; - top: 25px; - left: 25px; - cursor: pointer; - } - .button_open_menu span { - width: 20px; - height: 1.5px; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: #4E4E4E; - } - - .button_open_menu span:nth-of-type(2) { - top: calc(50% - 5px); - } - .button_open_menu span:nth-of-type(3) { - top: calc(50% + 5px); - } - .title_one_frame{ - padding-top: 20px ; - color: #0052CC; - font-size: 17px; - text-align: center; - } - .title_two_frame{ - padding-top: 60px ; - padding-bottom: 40px ; - color: black; - font-size: 40px; - text-align: center; - font-weight: 600; - } - .title_three_frame{ - padding-top: 60px ; - padding-bottom: 40px ; - color: #202124; - font-size: 20px; - text-align: center; - font-weight: 400; - } - .topic { - font-size: 18px; - text-align: center; - line-height: 35px; - color: #009FFF; - width: 250px; - margin: 0 auto; - } - .topic div{ - margin-top: 20px; - border: 1.5px solid; +.button_open_menu { + z-index: 2; + width: 1.43em; + height: 1.23em; + position: absolute; + top: 1.56em; + left: 1.56em; + cursor: pointer; + visibility: hidden; +} + +.button_open_menu span { + width: 20px; + height: 1.5px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #4E4E4E; +} + +.button_open_menu span:nth-of-type(2) { + top: calc(50% - 5px); +} + +.button_open_menu span:nth-of-type(3) { + top: calc(50% + 5px); +} + +.title_one_frame { + padding-top: 1.25em; + color: #0052CC; + font-size: 1.0625em; + text-align: center; +} + +.chat_input { + background-color: #EAEAEA; + position: absolute; + display: flex; + align-items: center; + bottom: 2em; + margin-left: 25%; + width: 50%; + border-radius: 0.6em; +} + +.possible_options { + position: absolute; + bottom: 5.5em; + margin-left: 25%; + width: 50%; + border-radius: 0.6em; +} +.tite_options{ + font-size: 0.9em; + color: #ADADAD; + margin-bottom: 0.5em; + +} +.options{ + justify-content: space-between; + align-items: center; + font-size: 1em; +} +.topic_options +{ + display: inline-block; + padding: 0.5em; + border: 0.09em solid; border-color: #009FFF; - border-radius: 10px; - justify-content: center; - align-items: center; - cursor: pointer; - } - .topic div a{ - color: #009FFF; - text-decoration:none; - } - .switch{ - height: 45px; - width: 350px; - margin: 0 auto; - border: 1.5px solid; - border-color: #EAEAEA; - border-radius: 10px; + border-radius: 0.6em; + margin: 0em 0.6em; + flex: 1; text-align: center; - position: relative; - - } - - .switch_product{ - position: absolute; - background-color: #0052CC; - border-radius: 10px; - margin: 5px; - width: calc(50% - 10px); - height: calc(100% - 10px); - display: flex; - align-items: center; - justify-content: center; cursor: pointer; - transition: 0.8s; - color: white; - } - - .switch_gift{ - right: 0; - position: absolute; - border-radius: 10px; - margin: 5px; - width: calc(50% - 10px); - height: calc(100% - 10px); - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: 0.8s; - color: #202124; - } - - .chat_input{ - position: absolute; - margin-left: 25%; - margin-bottom: 20px; - width: 50%; - border-radius: 10px; - bottom: 0; - - } - .chat_input input{ - font-size: 17px; - width: 100%; - position: relative; - color: #4E4E4E; - background-color: #EAEAEA; - border-radius: 10px; - border: none; - padding: 10px 20px; - outline:none; - } - .button_sende{ - margin-top: 14px; - margin-right: 20px; - cursor: pointer; - height: calc(100% - 28px); - width: 2.8%; - position: absolute; - right: 0; - } - .button_sende img{ - height: 100%; - width: 100%; - position: absolute; - - } - - .new_chat{ - position: absolute; - overflow-y: scroll; - height: 100%; - width: 100%; - } - .new_chat::-webkit-scrollbar { - visibility: hidden; - } - +} + +.input_messages { + width: 100%; + height: 2.5em; + font-size: 1.0625em; + background-color: #EAEAEA; + color: #4E4E4E; + border-radius: 0.6em; + border: none; + padding: 0.625em 1.25em; + outline: none; +} + +.button_sende { + float: right; + cursor: pointer; + line-height: 2.5em; + margin-right: 0.8em; + width: 1.5em; + height: 1.4em; +} + +.new_chat { + padding-top: 0.5em; + position: relative; + height: 100%; + width: 100%; +} + +.chat_message { + position: relative; + overflow-y: auto; + height: calc(100% - 8em); + width: 100%; +} +.chat_message::-webkit-scrollbar { + border-radius: 20px; + width: 0.2em; +} + +.chat_message::-webkit-scrollbar-thumb { + background-color: #0052CC; + /* Колір позиції покажчика */ + border-radius: 10px; + /* Закруглення країв позиції покажчика */ + width: 0.2em; +} + + .chat_box{ border-radius: 10px; position: absolute; diff --git a/ShoppingAssistantWebClient.Web/Pages/Index.razor b/ShoppingAssistantWebClient.Web/Pages/Index.razor index e95f25b..a3db7bb 100644 --- a/ShoppingAssistantWebClient.Web/Pages/Index.razor +++ b/ShoppingAssistantWebClient.Web/Pages/Index.razor @@ -1,9 +1,136 @@ @page "/" +New Chat -CartaId +
-

Hello, world!

+ -Welcome to your new app. +@if(isLoading == false){ - +
+
+
New chat
+
What you're looking for
+ +
+
+ Product +
+
+ Gift +
+
+ + +
What you're looking for, we will help you solve your problem and find it +
+ + + +
+
+ + Send message +
+ +
+ +}else{ + +
+ + Loading chat + +
+ +} + + +
+ + +@code{ + private string selectedChoice = "Product"; + + private void Сhoose_product() { + selectedChoice = "Product"; + } + private void Сhoose_gift() { + selectedChoice = "Gift"; + } + + private async Task UpdateSideMenu(string wishlistId) + { + + await JSRuntime.InvokeVoidAsync("myJavaScriptFunction", wishlistId); + + } + +} diff --git a/ShoppingAssistantWebClient.Web/Pages/Index.razor.cs b/ShoppingAssistantWebClient.Web/Pages/Index.razor.cs new file mode 100644 index 0000000..2cc2915 --- /dev/null +++ b/ShoppingAssistantWebClient.Web/Pages/Index.razor.cs @@ -0,0 +1,120 @@ +using Microsoft.AspNetCore.Components; +using ShoppingAssistantWebClient.Web.Models; +using ShoppingAssistantWebClient.Web.Models.ProductSearch; +using ShoppingAssistantWebClient.Web.Models.Input; +using GraphQL; +using Newtonsoft.Json; +using ShoppingAssistantWebClient.Web.Network; +using System; +using Microsoft.JSInterop; + +namespace ShoppingAssistantWebClient.Web.Pages +{ + public partial class Index : ComponentBase + { + + [Inject] + private ApiClient _apiClient { get; set; } + [Inject] + + private NavigationManager Navigation { get; set; } + [Inject] + protected IJSRuntime JSRuntime { get; set; } + + private MessageCreateDto messageCreateDto; + + private CancellationTokenSource cancelTokenSource; + + private string inputValue = ""; + public bool isLoading; + + + private async Task CreateNewChat() { + + try + { + if (string.IsNullOrWhiteSpace(inputValue)) + { + return; + } + + isLoading = true; + StateHasChanged(); + messageCreateDto = new MessageCreateDto { Text = inputValue }; + var type = selectedChoice; + var firstMessageText = $"What are you looking for?"; + + var request = new GraphQLRequest + { + Query = @" + mutation StartPersonalWishlist($type: String!, $firstMessageText: String!) { + startPersonalWishlist(dto: { type: $type, firstMessageText: $firstMessageText }) { + id + } + }", + Variables = new + { + type, + firstMessageText + } + }; + + var response = await _apiClient.QueryAsync(request); + var responseData = response.Data; + var chatId = responseData?.startPersonalWishlist?.id; + string wishlistId1 = chatId; + + var text = inputValue; + + cancelTokenSource = new CancellationTokenSource(); + var cancellationToken = cancelTokenSource.Token; + + var serverSentEvent = _apiClient.GetServerSentEventStreamed($"ProductsSearch/search/{chatId}", messageCreateDto, cancellationToken); + + await foreach (var sseEvent in serverSentEvent.WithCancellation(cancellationToken)) + { + // Handle each ServerSentEvent as needed + Console.WriteLine($"Received SSE Event: {sseEvent.Event}, Data: {sseEvent.Data}"); + } + + string wishlistId = chatId; + + request = new GraphQLRequest + { + Query = @"mutation GenerateNameForPersonalWishlist($wishlistId: String!) { + generateNameForPersonalWishlist(wishlistId: $wishlistId) { + id + name + } + }", + Variables = new + { + wishlistId + + } + }; + + response = await _apiClient.QueryAsync(request); + + isLoading = false; + StateHasChanged(); + + await UpdateSideMenu(wishlistId1); + var url = $"/chat/{chatId}"; + Navigation.NavigateTo(url); + + } + catch (Exception ex) + { + // Handle exceptions appropriately + Console.WriteLine($"Error in CreateNewChat: {ex.Message}"); + } + finally + { + isLoading = false; + cancelTokenSource?.Dispose(); + } + } + + } +} diff --git a/ShoppingAssistantWebClient.Web/Pages/Index.razor.css b/ShoppingAssistantWebClient.Web/Pages/Index.razor.css new file mode 100644 index 0000000..531d66c --- /dev/null +++ b/ShoppingAssistantWebClient.Web/Pages/Index.razor.css @@ -0,0 +1,196 @@ +.right_frame { + position: relative; + border: 0.09em solid; + border-color: #0052CC; + border-radius: 0.6em; + height: 100%; + width: 100%; +} + +.button_open_menu { + z-index: 2; + width: 1.43em; + height: 1.23em; + position: absolute; + top: 1.56em; + left: 1.56em; + cursor: pointer; + visibility: hidden; +} +.loading { + margin: 0 auto; + margin-top: 25%; + height: 10em; + width: 10em; + display: flex; + justify-content: center; + align-items: center; + text-align: center; +} +.button_open_menu span { + width: 20px; + height: 1.5px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #4E4E4E; +} + +.button_open_menu span:nth-of-type(2) { + top: calc(50% - 5px); +} + +.button_open_menu span:nth-of-type(3) { + top: calc(50% + 5px); +} + +.title_one_frame { + padding-top: 1.25em; + color: #0052CC; + font-size: 1.0625em; + text-align: center; +} + +.title_two_frame { + padding-top: 2.25em; + padding-bottom: 1.5em; + font-size: 2.5em; + text-align: center; + font-weight: 600; +} + +.title_three_frame { + padding-top: 3.75em; + padding-bottom: 2.5em; + color: #202124; + font-size: 1.25em; + text-align: center; + font-weight: 400; +} + +.topic { + font-size: 1.125em; + text-align: center; + line-height: 2.1875em; + color: #009FFF; + width: 15.625em; + margin: 0 auto; +} + +.topic div { + margin-top: 1.25em; + border: 0.09em solid; + border-color: #009FFF; + border-radius: 0.6em; + justify-content: center; + align-items: center; + cursor: pointer; +} + +.topic div a { + color: #009FFF; + text-decoration: none; +} + +.switch { + height: 2.8125em; + width: 21.875em; + margin: 0 auto; + border: 0.09em solid; + border-color: #EAEAEA; + border-radius: 0.6em; + text-align: center; + position: relative; +} + +.switch_product { + position: absolute; + background-color: #0052CC; + border-radius: 0.6em; + margin: 0.3125em; + width: calc(50% - 0.625em); + height: calc(100% - 0.625em); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: 0.8s; + color: white; +} + +.switch_gift { + right: 0; + position: absolute; + border-radius: 0.6em; + margin: 0.3125em; + width: calc(50% - 0.625em); + height: calc(100% - 0.625em); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: 0.8s; + color: #202124; +} + +.chat_message { + position: relative; + overflow-y: auto; + height: calc(100% - 5.5em); + width: 100%; +} + +.chat_input { + background-color: #EAEAEA; + position: absolute; + display: flex; + align-items: center; + bottom: 2em; + margin-left: 25%; + width: 50%; + border-radius: 0.6em; +} + +.input_messages { + width: 100%; + height: 2.5em; + font-size: 1.0625em; + background-color: #EAEAEA; + color: #4E4E4E; + border-radius: 0.6em; + border: none; + padding: 0.625em 1.25em; + outline: none; +} + +.button_sende { + float: right; + cursor: pointer; + line-height: 2.5em; + margin-right: 0.8em; + width: 1.8em; + height: 1.7em; +} + + +.new_chat { + padding-top: 0.5em; + position: relative; + height: 100%; + width: 100%; +} + + +.chat_message::-webkit-scrollbar { + border-radius: 20px; + width: 0.2em; +} + +.chat_message::-webkit-scrollbar-thumb { + background-color: #0052CC; + /* Колір позиції покажчика */ + border-radius: 10px; + /* Закруглення країв позиції покажчика */ + width: 0.2em; +} \ No newline at end of file diff --git a/ShoppingAssistantWebClient.Web/Program.cs b/ShoppingAssistantWebClient.Web/Program.cs index 8c07b81..d8ea416 100644 --- a/ShoppingAssistantWebClient.Web/Program.cs +++ b/ShoppingAssistantWebClient.Web/Program.cs @@ -1,5 +1,7 @@ +using GraphQL.Client.Http; using ShoppingAssistantWebClient.Web.Configurations; using ShoppingAssistantWebClient.Web.Data; +using ShoppingAssistantWebClient.Web.Network; var builder = WebApplication.CreateBuilder(args); diff --git a/ShoppingAssistantWebClient.Web/Shared/MainLayout.razor b/ShoppingAssistantWebClient.Web/Shared/MainLayout.razor index fd92cab..64166c4 100644 --- a/ShoppingAssistantWebClient.Web/Shared/MainLayout.razor +++ b/ShoppingAssistantWebClient.Web/Shared/MainLayout.razor @@ -1,6 +1,6 @@ @inherits LayoutComponentBase @using ShoppingAssistantWebClient.Web.Pages -ShoppingAssistantWebClient.Web +CARTAID diff --git a/ShoppingAssistantWebClient.Web/Shared/MainLayout.razor.css b/ShoppingAssistantWebClient.Web/Shared/MainLayout.razor.css new file mode 100644 index 0000000..0f1cc15 --- /dev/null +++ b/ShoppingAssistantWebClient.Web/Shared/MainLayout.razor.css @@ -0,0 +1,24 @@ +.page { + position: relative; + width: 100%; + height: 100vh; + padding: 1.25em; +} + +.sidebar-menu { + position: absolute; + width: 20em; + top: 1.25em; + bottom: 1.25em; + margin-right: 1.5em; + transition: 1s; +} + +.right-frame { + position: absolute; + right: 1.25em; + left: 23.25em; + top: 1.25em; + bottom: 1.25em; + transition: 1s; +} \ No newline at end of file diff --git a/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor b/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor index 295ee43..222f245 100644 --- a/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor +++ b/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor @@ -1,7 +1,11 @@ -@using Models.GlobalInstances +@using Models.GlobalInstances +@using System.Linq +@using Microsoft.AspNetCore.Components +@using Microsoft.JSInterop @inject NavigationManager Navigation -
+@inject IJSRuntime JSRuntime; +