content management rewrite, groups, chunking, multidimensional collections
|
|
@ -7,6 +7,13 @@
|
|||
<AssemblyName>BLAIzor</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="SeemGen.Tests\**" />
|
||||
<Content Remove="SeemGen.Tests\**" />
|
||||
<EmbeddedResource Remove="SeemGen.Tests\**" />
|
||||
<None Remove="SeemGen.Tests\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Components\Pages\Home.razorOLD" />
|
||||
</ItemGroup>
|
||||
|
|
@ -30,9 +37,11 @@
|
|||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="PdfPig" Version="0.1.10" />
|
||||
<PackageReference Include="Qdrant.Client" Version="1.13.0" />
|
||||
<PackageReference Include="Radzen.Blazor" Version="6.4.0" />
|
||||
<PackageReference Include="Radzen.Blazor" Version="7.1.1" />
|
||||
<PackageReference Include="Sidio.Sitemap.Blazor" Version="1.1.2" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="9.0.3" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.9.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -57,6 +66,9 @@
|
|||
<Content Update="wwwroot\bootstrap\js\bootstrap.js">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\admin.css">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\main.css">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ VisualStudioVersion = 17.10.35004.147
|
|||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BLAIzor", "BLAIzor.csproj", "{A7A021E4-C303-4DF0-B55B-CE3233137085}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeemGen.Tests", "SeemGen.Tests\SeemGen.Tests.csproj", "{9B3FF465-BD0D-F50F-F19B-E2D1338E7FE6}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -15,6 +17,10 @@ Global
|
|||
{A7A021E4-C303-4DF0-B55B-CE3233137085}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7A021E4-C303-4DF0-B55B-CE3233137085}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7A021E4-C303-4DF0-B55B-CE3233137085}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9B3FF465-BD0D-F50F-F19B-E2D1338E7FE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B3FF465-BD0D-F50F-F19B-E2D1338E7FE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B3FF465-BD0D-F50F-F19B-E2D1338E7FE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B3FF465-BD0D-F50F-F19B-E2D1338E7FE6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="main.css" />
|
||||
<link rel="stylesheet" href="admin.css" />
|
||||
<link rel="stylesheet" href="animate.css" />
|
||||
<link rel="stylesheet" href="loader.css" />
|
||||
<link rel="stylesheet" href="BLAIzor.styles.css" />
|
||||
|
|
@ -23,6 +24,7 @@
|
|||
<script src="https://assets.calendly.com/assets/external/widget.js" type="text/javascript"></script>
|
||||
<script type="text/javascript" src="scripts/background.js"> </script>
|
||||
<script type="text/javascript" src="scripts/SeemGenCss.js"> </script>
|
||||
|
||||
@* <script>
|
||||
window.applyDynamicCss = (cssContent) => {
|
||||
let styleTag = document.getElementById('seemgen-style');
|
||||
|
|
@ -49,6 +51,7 @@
|
|||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
|
||||
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"
|
||||
crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="scripts/finisher-header.es5.min.js"> </script>
|
||||
<script src="_content/Radzen.Blazor/Radzen.Blazor.js?v=@(typeof(Radzen.Colors).Assembly.GetName().Version)"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@using BLAIzor.Services
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<RadzenComponents @rendermode="InteractiveServer" />
|
||||
@Body
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
@using BLAIzor.Components.Layout
|
||||
@attribute [Authorize]
|
||||
@layout AdminLayout
|
||||
@inject ContentEditorService ContentEditorService
|
||||
@inject ContentEditorAIService ContentEditorAIService
|
||||
|
||||
<h3>Create a new from from pdf or word</h3>
|
||||
|
||||
|
|
@ -68,7 +68,7 @@
|
|||
|
||||
private async Task<List<FormFieldGroup>> CallAiForFieldExtraction(string plainText)
|
||||
{
|
||||
var jsonResult = await ContentEditorService.AnalyzeGroupedFormFieldsFromText(plainText);
|
||||
var jsonResult = await ContentEditorAIService.AnalyzeGroupedFormFieldsFromText(plainText);
|
||||
Console.WriteLine($"📄 PDF form AI response: {jsonResult}");
|
||||
|
||||
try
|
||||
|
|
|
|||
|
|
@ -21,44 +21,52 @@
|
|||
|
||||
<ErrorBoundary>
|
||||
<ChildContent>
|
||||
<div class="page" style="z-index: 1">
|
||||
<NavMenu MenuString="@Menu" OnMenuClicked=@MenuClick></NavMenu>
|
||||
<main>
|
||||
<div class="page" style="z-index: 1">
|
||||
<NewNavMenu Menu="@MenuItems" BrandName="@SelectedBrandName" OnMenuClicked=@MenuClick></NewNavMenu>
|
||||
<main>
|
||||
|
||||
<article class="content text-center" style="position: relative; z-index: 4;">
|
||||
<PageTitle>Home</PageTitle>
|
||||
<VideoComponent SelectedBrandName="@SelectedBrandName" />
|
||||
@* <HeadContent>
|
||||
<article class="content text-center" style="position: relative; z-index: 4;">
|
||||
<PageTitle>Home</PageTitle>
|
||||
@{
|
||||
if(SiteInfo!= null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SiteInfo.BackgroundVideo))
|
||||
{
|
||||
<VideoComponent site="SiteInfo" />
|
||||
}
|
||||
}
|
||||
}
|
||||
@* <HeadContent>
|
||||
|
||||
<style id="seemgen-style">@dynamicallyLoadedCss</style>
|
||||
|
||||
</HeadContent> *@
|
||||
|
||||
<div id="maincontrol" >
|
||||
<div id="maincontrol">
|
||||
|
||||
|
||||
@* <div class="hoverslide"> *@
|
||||
@* <div class="hoverslide"> *@
|
||||
|
||||
<div class="displaysearch">
|
||||
<div class="searchBox">
|
||||
<div class="displaysearch">
|
||||
<div class="searchBox">
|
||||
|
||||
<input @oninput="(e) => UserInput = e.Value.ToString()"
|
||||
@onkeydown="@Enter" class="searchInput" type="text" name="" value="@UserInput" placeholder="Ask any question">
|
||||
<button data-hint="ask anything" class="searchButton border-0" @onclick="SendUserQuery" href="#">
|
||||
<i class="fa-solid fa-hexagon-nodes-bolt" style="font-size:20px"></i>
|
||||
</button>
|
||||
</div>
|
||||
<input @oninput="(e) => UserInput = e.Value.ToString()"
|
||||
@onkeydown="@Enter" class="searchInput" type="text" name="" value="@UserInput" placeholder="Ask any question">
|
||||
<button data-hint="ask anything" class="searchButton border-0" @onclick="SendUserQuery" href="#">
|
||||
<i class="fa-solid fa-hexagon-nodes-bolt" style="font-size:20px"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@{
|
||||
@if(VoiceEnabled)
|
||||
@{
|
||||
@if (VoiceEnabled)
|
||||
{
|
||||
if(STTEnabled)
|
||||
if (STTEnabled)
|
||||
{
|
||||
<button id="recButton" class="btn btn-primary voicebutton" onclick="startRecording()"><i class="fa-solid fa-microphone"></i></button>
|
||||
<button id="stopButton" class="btn btn-primary voicebutton" onclick="stopRecording()" hidden><i class="fa-solid fa-microphone-slash"></i></button>
|
||||
}
|
||||
|
||||
if(TTSEnabled)
|
||||
if (TTSEnabled)
|
||||
{
|
||||
if (!AiVoicePermitted)
|
||||
{
|
||||
|
|
@ -92,7 +100,7 @@
|
|||
|
||||
if (isEmailFormVisible)
|
||||
{
|
||||
<div class="conteiner-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="pt-5 @FirstColumnClass">
|
||||
@((MarkupString)HtmlContent.ToString())
|
||||
|
|
@ -135,6 +143,7 @@
|
|||
|
||||
</article>
|
||||
</main>
|
||||
<FooterComponent MenuString="@Menu" OnMenuClicked=@MenuClick></FooterComponent>
|
||||
</div>
|
||||
</ChildContent>
|
||||
<ErrorContent Context="ex">
|
||||
|
|
@ -190,19 +199,6 @@
|
|||
private string ChatGptResponse = string.Empty;
|
||||
private bool isRecording = false;
|
||||
|
||||
// private string FirstColumnClass = "";
|
||||
// private bool isEmailFormVisible = false;
|
||||
|
||||
// private ContactFormModel ContactFormModel = new();
|
||||
// // private string? SuccessMessage;
|
||||
// // private string? ErrorMessage;
|
||||
// private string? DocumentEmailAddress = "";
|
||||
|
||||
private string dynamicallyLoadedCss = string.Empty;
|
||||
|
||||
private string Menu;
|
||||
|
||||
|
||||
private void AllowAIVoice()
|
||||
{
|
||||
AiVoicePermitted = true;
|
||||
|
|
@ -234,7 +230,7 @@
|
|||
SelectedBrandName = "default";
|
||||
}
|
||||
Subdomain = HttpContextAccessor.HttpContext?.Items["Subdomain"]?.ToString();
|
||||
SiteInfo = await _scopedContentService.GetSiteInfoByNameAsync(Subdomain);
|
||||
SiteInfo = await _contentEditorService.GetSiteInfoByNameAsync(Subdomain);
|
||||
if (SiteInfo != null && SiteInfo.IsPublished)
|
||||
{
|
||||
|
||||
|
|
@ -253,6 +249,8 @@
|
|||
STTEnabled = false;
|
||||
}
|
||||
_scopedContentService.SelectedSiteId = SiteId;
|
||||
ContentCollectionName = SiteInfo.VectorCollectionName ?? "default_content_collection";
|
||||
|
||||
|
||||
Console.Write("------------------------");
|
||||
// Load the CSS template for the selected brand from the database
|
||||
|
|
@ -273,16 +271,19 @@
|
|||
Console.Write($"------------------------ {SiteInfo.MenuItems}, {SiteId}, {SiteInfo.TemplateId}, {SiteInfo.SiteName}");
|
||||
|
||||
Menu = await GetMenuList(SiteId);
|
||||
MenuItems = await GetMenuItems(SiteId);
|
||||
if (string.IsNullOrEmpty(HtmlContent.ToString()))
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(topic))
|
||||
if (!string.IsNullOrWhiteSpace(topic))
|
||||
{
|
||||
UserInput = topic;
|
||||
await ChatGptService.ProcessContentRequest(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, Menu, true);
|
||||
await ChatGptService.ProcessContentRequest(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, ContentCollectionName, Menu, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ChatGptService.GetChatGptWelcomeMessage(SessionId, SiteId, TemplateCollectionName, Menu);
|
||||
SiteModel = await ChatGptService.InitSite(SessionId, SiteId, TemplateCollectionName, Menu);
|
||||
//await ChatGptService.ProcessContentRequest(SessionId, MenuItems.FirstOrDefault(), SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, Menu, true);
|
||||
}
|
||||
// HtmlContent = await ChatGptService.GetChatGptWelcomeMessage();
|
||||
// UserInput = "Sumerize for me, what is this website about, and what can I do on this website?";
|
||||
|
|
@ -340,7 +341,7 @@
|
|||
VoiceEnabled = configuration?.GetSection("AiSettings")?.GetValue<bool>("VoiceActivated") ?? false;
|
||||
}
|
||||
|
||||
private async void UpdateContent(string receivedSessionId, string content)
|
||||
private async void UpdateContent(string receivedSessionId, string content, MenuItem? menuItem)
|
||||
{
|
||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
using BLAIzor.Models;
|
||||
using BLAIzor.Services;
|
||||
using Google.Api;
|
||||
using Google.Cloud.Speech.V1;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Collections;
|
||||
using Radzen;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using UglyToad.PdfPig.DocumentLayoutAnalysis;
|
||||
|
||||
|
||||
namespace BLAIzor.Components.Pages
|
||||
|
|
@ -21,6 +19,7 @@ namespace BLAIzor.Components.Pages
|
|||
[Inject] protected HttpClient Http { get; set; }
|
||||
[Inject] protected IJSRuntime jsRuntime { get; set; }
|
||||
[Inject] protected AIService ChatGptService { get; set; }
|
||||
[Inject] NotificationService NotificationService { get; set; }
|
||||
|
||||
public static readonly Dictionary<string, MainPageBase> _instances = new();
|
||||
|
||||
|
|
@ -34,6 +33,7 @@ namespace BLAIzor.Components.Pages
|
|||
public string StatusContent = "";
|
||||
public string UserInput = string.Empty;
|
||||
public string TemplateCollectionName = "html_snippets";
|
||||
public string ContentCollectionName = "";
|
||||
|
||||
public bool VoiceEnabled;
|
||||
public bool TTSEnabled;
|
||||
|
|
@ -49,6 +49,13 @@ namespace BLAIzor.Components.Pages
|
|||
// private string? ErrorMessage;
|
||||
public string? DocumentEmailAddress = "";
|
||||
|
||||
public string Menu;
|
||||
public List<MenuItem> MenuItems = new();
|
||||
public string dynamicallyLoadedCss = string.Empty;
|
||||
public MenuItem currentMenuItem = new();
|
||||
|
||||
public WebsiteContentModel SiteModel = new();
|
||||
|
||||
public void DoSharedWork()
|
||||
{
|
||||
// Logic here
|
||||
|
|
@ -91,7 +98,7 @@ namespace BLAIzor.Components.Pages
|
|||
|
||||
public async Task<List<MenuItem>> GetMenuItems(int siteId)
|
||||
{
|
||||
List<MenuItem> menuItems = (await _contentEditorService.GetMenuItemsBySiteIdAsync(siteId)).Where(m => m.ShowInMainMenu == true).OrderBy(m => m.SortOrder).ToList();
|
||||
List<MenuItem> menuItems = (await _contentEditorService.GetMenuItemsBySiteIdWithChildrenAsync(siteId)).Where(m => m.ShowInMainMenu == true).OrderBy(m => m.SortOrder).ToList();
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
|
|
@ -211,14 +218,26 @@ namespace BLAIzor.Components.Pages
|
|||
{
|
||||
HtmlContent.Clear();
|
||||
var menu = await GetMenuList(SiteId);
|
||||
var menuItem = (await GetMenuItems(SiteId)).Where(m => m.Name == input).FirstOrDefault();
|
||||
var menuList = await GetMenuItems(SiteId);
|
||||
|
||||
var menuItem = CompareMenuItemNames(input, menuList);
|
||||
if (menuItem == null)
|
||||
{
|
||||
await ChatGptService.ProcessContentRequest(SessionId, input, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu, forceUnmodified);
|
||||
await ChatGptService.ProcessContentRequest(SessionId, input, SiteId, (int)SiteInfo.TemplateId!, ContentCollectionName, menu, forceUnmodified);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ChatGptService.ProcessContentRequest(SessionId, menuItem, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu, forceUnmodified);
|
||||
currentMenuItem = menuItem;
|
||||
if (!string.IsNullOrEmpty(menuItem.StoredHtml))
|
||||
{
|
||||
|
||||
HtmlContent.Clear();
|
||||
HtmlContent.Append(menuItem.StoredHtml);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ChatGptService.ProcessContentRequest(SessionId, menuItem, SiteId, (int)SiteInfo.TemplateId!, ContentCollectionName, menu, forceUnmodified);
|
||||
}
|
||||
}
|
||||
UserInput = string.Empty;
|
||||
}
|
||||
|
|
@ -296,16 +315,40 @@ namespace BLAIzor.Components.Pages
|
|||
Console.Write("openEmail with: " + emailAddress);
|
||||
}
|
||||
|
||||
|
||||
public async Task SendMessage()
|
||||
{
|
||||
Console.WriteLine("Button clicked!");
|
||||
var menu = await GetMenuList(SiteId);
|
||||
HtmlContent.Clear();
|
||||
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu);
|
||||
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, ContentCollectionName, menu);
|
||||
UserInput = string.Empty;
|
||||
}
|
||||
|
||||
public MenuItem CompareMenuItemNames(string input, List<MenuItem> menuList)
|
||||
{
|
||||
var parent = menuList.FirstOrDefault(ml => ml.Name == input);
|
||||
if (parent != null)
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
foreach (var item in menuList)
|
||||
{
|
||||
var child = item.Children.FirstOrDefault(ch => ch.Name == input);
|
||||
if (child != null)
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null; // or throw, or return a default
|
||||
}
|
||||
|
||||
public void ShowNotification(NotificationMessage message)
|
||||
{
|
||||
NotificationService.Notify(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
@page "/site/{SiteInfoId:int}/content-groups"
|
||||
@using BLAIzor.Components.Layout
|
||||
@using BLAIzor.Models
|
||||
@using BLAIzor.Services
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@layout AdminLayout
|
||||
@inject ContentEditorService contentEditorService
|
||||
@inject NavigationManager Navigation
|
||||
@attribute [Authorize]
|
||||
|
||||
@if (isLoading)
|
||||
{
|
||||
<p>Loading content groups...</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var group in groups)
|
||||
{
|
||||
if (selectedGroupId == group.Id)
|
||||
{
|
||||
<EditForm Model="@group" OnValidSubmit="() => SaveGroup(group)">
|
||||
<div class="border rounded p-3 mb-3">
|
||||
<label>
|
||||
Name
|
||||
<InputText class="form-control my-1" @bind-Value="group.Name" />
|
||||
</label>
|
||||
<label>
|
||||
Type
|
||||
<InputText class="form-control my-1" @bind-Value="group.Type" />
|
||||
</label>
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<button class="btn btn-success" type="submit">Save</button>
|
||||
<button class="btn btn-danger" type="button" @onclick="() => DeleteGroup(group)">Delete</button>
|
||||
<button class="btn btn-secondary" type="button" @onclick="() => DeselectGroup()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="border rounded p-3 mb-3 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<strong>@group.Name</strong><br />
|
||||
<small class="text-muted">@group.Type</small>
|
||||
</div>
|
||||
<button class="btn btn-outline-primary" @onclick="() => SelectGroup(group.Id)">Edit</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<button class="btn btn-primary mt-3" @onclick="AddNewGroup">Add New Group</button>
|
||||
}
|
||||
|
||||
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public int SiteInfoId { get; set; }
|
||||
|
||||
private List<ContentGroup> groups = new();
|
||||
private bool isLoading = true;
|
||||
|
||||
private int? selectedGroupId = null;
|
||||
|
||||
private void SelectGroup(int groupId)
|
||||
{
|
||||
selectedGroupId = groupId;
|
||||
}
|
||||
|
||||
private void DeselectGroup()
|
||||
{
|
||||
selectedGroupId = null;
|
||||
}
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
isLoading = true;
|
||||
groups = (await contentEditorService.GetContentGroupsBySiteInfoIdAsync(SiteInfoId))
|
||||
.Where(g => g.SiteInfoId == SiteInfoId)
|
||||
.OrderByDescending(g => g.LastUpdated)
|
||||
.ToList();
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
private void EditGroup(ContentGroup group)
|
||||
{
|
||||
Navigation.NavigateTo($"/content-group/{group.Id}/items");
|
||||
}
|
||||
|
||||
private async Task SaveGroup(ContentGroup group)
|
||||
{
|
||||
group.LastUpdated = DateTime.UtcNow;
|
||||
group.Version = group.Version + 1;
|
||||
var result = await contentEditorService.UpdateContentGroupByIdAsync(group);
|
||||
if(result != null) ;
|
||||
}
|
||||
|
||||
private async Task DeleteGroup(ContentGroup group)
|
||||
{
|
||||
|
||||
var result = await contentEditorService.DeleteContentGroupByIdAsync(group.Id); ;
|
||||
groups.Remove(group);
|
||||
}
|
||||
|
||||
private async Task AddNewGroup()
|
||||
{
|
||||
var newGroup = new ContentGroup
|
||||
{
|
||||
SiteInfoId = SiteInfoId,
|
||||
Name = "New Group",
|
||||
Slug = $"group-{DateTime.UtcNow.Ticks}",
|
||||
Type = "manual",
|
||||
VectorSize = 384,
|
||||
EmbeddingModel = "default",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
LastUpdated = DateTime.UtcNow,
|
||||
Version = 1
|
||||
};
|
||||
var result = await contentEditorService.CreateContentGroupAsync(newGroup);
|
||||
if(result!=null)
|
||||
{
|
||||
groups.Insert(0, newGroup);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5,33 +5,35 @@
|
|||
@using BLAIzor.Services
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@layout AdminLayout
|
||||
@inject ScopedContentService SiteInfoService
|
||||
@inject ContentEditorService _contentEditorService
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject CustomAuthenticationStateProvider CustomAuthProvider
|
||||
|
||||
<h3>Site Information</h3>
|
||||
|
||||
<EditForm Model="@siteInfo" OnValidSubmit="SaveSiteInfo">
|
||||
<label>
|
||||
Brand name:
|
||||
Brand name
|
||||
<InputText class="form-control my-3" id="siteName" @bind-Value="siteInfo.SiteName" />
|
||||
</label>
|
||||
<label>
|
||||
Site description:
|
||||
<InputText class="form-control my-3" id="siteDescription" @bind-Value="siteInfo.SiteDescription" />
|
||||
Site description
|
||||
<InputTextArea class="form-control my-3" style="height: 100px;" id="siteDescription" @bind-Value="siteInfo.SiteDescription" rows="3"/>
|
||||
</label>
|
||||
<label>
|
||||
Logo url:
|
||||
Logo url
|
||||
<InputText class="form-control my-3" id="brandLogoUrl" @bind-Value="siteInfo.BrandLogoUrl" />
|
||||
</label>
|
||||
<label>
|
||||
Default color
|
||||
Default color (Do not bother with this)
|
||||
<InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.DefaultColor" />
|
||||
</label>
|
||||
<label>
|
||||
Entity
|
||||
The Entity behind this website (eg. "company", "professional sportsman", "worldwide brand", etc);
|
||||
<InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.Entity" />
|
||||
</label>
|
||||
<label>
|
||||
Your agent Persona
|
||||
<InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.Persona" />
|
||||
</label>
|
||||
<label>
|
||||
Your domain url
|
||||
<InputText class="form-control my-3" id="domainUrl" @bind-Value="siteInfo.DomainUrl" />
|
||||
|
|
@ -91,12 +93,12 @@ else
|
|||
userId = CustomAuthProvider.GetUserId();
|
||||
userName = CustomAuthProvider.GetUserName();
|
||||
}
|
||||
siteInfo = await SiteInfoService.GetSiteInfoWithFormsByIdAsync(SiteId) ?? new SiteInfo();
|
||||
siteInfo = await _contentEditorService.GetSiteInfoWithFormsByIdAsync(SiteId) ?? new SiteInfo();
|
||||
}
|
||||
|
||||
private async Task SaveSiteInfo()
|
||||
{
|
||||
await SiteInfoService.UpdateSiteInfoAsync(siteInfo);
|
||||
await _contentEditorService.UpdateSiteInfoAsync(siteInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
@using BLAIzor.Models.Editor
|
||||
@using BLAIzor.Services
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using SixLabors.ImageSharp
|
||||
@using SixLabors.ImageSharp.Processing
|
||||
@layout AdminLayout
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IHttpContextAccessor HttpContextAccessor
|
||||
|
|
@ -26,19 +28,28 @@ else
|
|||
{
|
||||
<div>
|
||||
<h4>Uploaded Images</h4>
|
||||
<div class="row">
|
||||
|
||||
|
||||
<RadzenRow class="rz-text-align-center" Gap="1rem">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@foreach (var image in files.Images)
|
||||
{
|
||||
<div class="col-xs-12 col-sm-6 col-md-3 col-lg-2">
|
||||
|
||||
<RadzenColumn Size="6" SizeXS="12" SizeSM="6" SizeMD="4" SizeLG="3" SizeXL="2" SizeXX="2" class="rz-color-on-info-lighter rz-p-5">
|
||||
<div class="card">
|
||||
<div class="upload-image-container" @onclick="() => CopyToClipboard(image)">
|
||||
<img class="img-fluid square-thumbnail" src="@image" alt="Uploaded Image" />
|
||||
<div class="upload-image-container" @onclick="() => CopyToClipboard(image.OriginalUrl)">
|
||||
<img class="img-fluid square-thumbnail" src="@image.ThumbnailUrl" alt="Uploaded Image" loading="lazy" />
|
||||
</div>
|
||||
<button class="btn btn-danger" @onclick="@(() => DeleteFile(image, "Images"))">Delete</button>
|
||||
<button class="btn btn-danger" @onclick="@(() => DeleteFile(image.OriginalUrl, "Images"))">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</RadzenColumn>
|
||||
|
||||
}
|
||||
</div>
|
||||
</RadzenRow>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Uploaded Videos</h4>
|
||||
|
|
@ -106,25 +117,81 @@ else
|
|||
{
|
||||
var basePath = Path.Combine("wwwroot", "uploads", userId);
|
||||
|
||||
files.Images = Directory.Exists(Path.Combine(basePath, "images"))
|
||||
? Directory.GetFiles(Path.Combine(basePath, "images"))
|
||||
.Select(f => $"/uploads/{userId}/images/{Path.GetFileName(f)}")
|
||||
.ToList()
|
||||
: new List<string>();
|
||||
// Load IMAGES
|
||||
var imagesPath = Path.Combine(basePath, "images");
|
||||
var thumbnailsPath = Path.Combine(imagesPath, "thumbnails");
|
||||
|
||||
files.Videos = Directory.Exists(Path.Combine(basePath, "videos"))
|
||||
? Directory.GetFiles(Path.Combine(basePath, "videos"))
|
||||
if (!Directory.Exists(thumbnailsPath))
|
||||
{
|
||||
Directory.CreateDirectory(thumbnailsPath);
|
||||
}
|
||||
|
||||
if (Directory.Exists(imagesPath))
|
||||
{
|
||||
var imageFiles = Directory.GetFiles(imagesPath)
|
||||
.Where(f => !Path.GetFileName(f).Equals("thumbnails", StringComparison.OrdinalIgnoreCase)) // skip folder
|
||||
.Where(f => !Directory.Exists(f)) // skip directories
|
||||
.Select(f =>
|
||||
{
|
||||
var fileName = Path.GetFileName(f);
|
||||
var originalUrl = $"/uploads/{userId}/images/{fileName}";
|
||||
var thumbnailPath = Path.Combine(thumbnailsPath, fileName);
|
||||
var thumbnailUrl = $"/uploads/{userId}/images/thumbnails/{fileName}";
|
||||
|
||||
// Generate thumbnail if missing
|
||||
if (!File.Exists(thumbnailPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var image = Image.Load(f);
|
||||
image.Mutate(x => x.Resize(new ResizeOptions
|
||||
{
|
||||
Mode = ResizeMode.Max,
|
||||
Size = new Size(300, 300)
|
||||
}));
|
||||
image.Save(thumbnailPath); // Will auto-detect format
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"❌ Error generating thumbnail for {fileName}: {ex.Message}");
|
||||
thumbnailUrl = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
return new UploadedImage
|
||||
{
|
||||
OriginalUrl = originalUrl,
|
||||
ThumbnailUrl = thumbnailUrl
|
||||
};
|
||||
})
|
||||
.ToList();
|
||||
|
||||
files.Images = imageFiles;
|
||||
}
|
||||
else
|
||||
{
|
||||
files.Images = new List<UploadedImage>();
|
||||
}
|
||||
|
||||
// Load VIDEOS
|
||||
var videosPath = Path.Combine(basePath, "videos");
|
||||
files.Videos = Directory.Exists(videosPath)
|
||||
? Directory.GetFiles(videosPath)
|
||||
.Select(f => $"/uploads/{userId}/videos/{Path.GetFileName(f)}")
|
||||
.ToList()
|
||||
: new List<string>();
|
||||
|
||||
files.Audio = Directory.Exists(Path.Combine(basePath, "audio"))
|
||||
? Directory.GetFiles(Path.Combine(basePath, "audio"))
|
||||
// Load AUDIO
|
||||
var audioPath = Path.Combine(basePath, "audio");
|
||||
files.Audio = Directory.Exists(audioPath)
|
||||
? Directory.GetFiles(audioPath)
|
||||
.Select(f => $"/uploads/{userId}/audio/{Path.GetFileName(f)}")
|
||||
.ToList()
|
||||
: new List<string>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task DeleteFile(string filePath, string fileType)
|
||||
{
|
||||
|
||||
|
|
@ -152,27 +219,44 @@ else
|
|||
var folder = GetFolderForFile(file.ContentType);
|
||||
var folderPath = Path.Combine(uploadPath, folder);
|
||||
|
||||
// Create directories if they don't exist
|
||||
if (!Directory.Exists(folderPath))
|
||||
{
|
||||
Directory.CreateDirectory(folderPath);
|
||||
}
|
||||
// Create target directory
|
||||
Directory.CreateDirectory(folderPath);
|
||||
|
||||
// Save file
|
||||
var filePath = Path.Combine(folderPath, file.Name);
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
await using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.OpenReadStream(50 * 1024 * 1024).CopyToAsync(stream);
|
||||
}
|
||||
|
||||
// Add relative path to content
|
||||
var relativePath = $"/uploads/{userId}/{folder}/{file.Name}";
|
||||
AppendFilePathToContent(file.ContentType, relativePath);
|
||||
|
||||
// Generate thumbnail if it's an image
|
||||
string? thumbnailRelativePath = null;
|
||||
|
||||
if (file.ContentType.StartsWith("image/"))
|
||||
{
|
||||
var thumbnailFolder = Path.Combine(folderPath, "thumbnails");
|
||||
Directory.CreateDirectory(thumbnailFolder);
|
||||
|
||||
var thumbnailPath = Path.Combine(thumbnailFolder, file.Name);
|
||||
using var image = await Image.LoadAsync(file.OpenReadStream());
|
||||
image.Mutate(x => x.Resize(new ResizeOptions
|
||||
{
|
||||
Size = new Size(300, 0),
|
||||
Mode = ResizeMode.Max
|
||||
}));
|
||||
await image.SaveAsync(thumbnailPath);
|
||||
|
||||
thumbnailRelativePath = $"/uploads/{userId}/{folder}/thumbnails/{file.Name}";
|
||||
}
|
||||
|
||||
AppendFilePathToContent(file.ContentType, relativePath, thumbnailRelativePath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Write($"Error uploading files: {ex.Message}");
|
||||
Console.WriteLine($"Error uploading files: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -191,11 +275,15 @@ else
|
|||
};
|
||||
}
|
||||
|
||||
private void AppendFilePathToContent(string contentType, string relativePath)
|
||||
private void AppendFilePathToContent(string contentType, string relativePath, string? thumbnailPath = null)
|
||||
{
|
||||
if (contentType.StartsWith("image/"))
|
||||
{
|
||||
files.Images.Add(relativePath);
|
||||
files.Images.Add(new UploadedImage
|
||||
{
|
||||
OriginalUrl = relativePath,
|
||||
ThumbnailUrl = thumbnailPath ?? string.Empty
|
||||
});
|
||||
}
|
||||
else if (contentType.StartsWith("video/"))
|
||||
{
|
||||
|
|
@ -205,6 +293,7 @@ else
|
|||
{
|
||||
files.Audio.Add(relativePath);
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using System.Text
|
||||
@using UglyToad.PdfPig
|
||||
@inject ContentEditorService ContentEditorService
|
||||
@inject ContentEditorAIService ContentEditorAIService
|
||||
|
||||
<h3>PDF Form Extractor</h3>
|
||||
|
||||
|
|
@ -89,7 +89,7 @@
|
|||
|
||||
private async Task<List<FormFieldGroup>> CallAiForFieldExtraction(string plainText)
|
||||
{
|
||||
var jsonResult = await ContentEditorService.AnalyzeGroupedFormFieldsFromText(plainText);
|
||||
var jsonResult = await ContentEditorAIService.AnalyzeGroupedFormFieldsFromText(plainText);
|
||||
Console.WriteLine($"📄 PDF form AI response: {jsonResult}");
|
||||
|
||||
try
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
@using System.Text
|
||||
@using System.Text.Json
|
||||
@using System.Net
|
||||
@using Radzen.Blazor.Rendering
|
||||
|
||||
@rendermode InteractiveServer
|
||||
@inject ContentService _contentService
|
||||
|
|
@ -18,14 +19,84 @@
|
|||
@inject DesignTemplateService DesignTemplateService
|
||||
@inject CssTemplateService CssTemplateService
|
||||
@inject CssInjectorService CssService
|
||||
@inject DialogService DialogService
|
||||
|
||||
|
||||
|
||||
<div class="page" style="z-index: 1">
|
||||
<NavMenu MenuString="@Menu" OnMenuClicked=@MenuClick></NavMenu>
|
||||
<RadzenButton Click="@(args => ToggleSettings())" style="position: fixed;
|
||||
z-index: 10005;
|
||||
top: 100px;
|
||||
left: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-right-radius: 20px;
|
||||
border-top-right-radius: 20px;">
|
||||
<i class="fa-solid fa-wrench"></i>
|
||||
</RadzenButton>
|
||||
|
||||
<RadzenStack class="editor-window animate__animated animate__slideInLeft" Orientation="Orientation.Vertical" style="" Visible=@displaySettingsPanel>
|
||||
<RadzenStack Orientation="Orientation.Vertical" style="">
|
||||
<div class="rz-p-2 rz-text-align-center">
|
||||
<h3>Basic information</h3>
|
||||
<p>@SiteInfo.SiteName</p>
|
||||
<RadzenButton class="btn" Text="Edit basic info" Click="@EditSite" />
|
||||
</div>
|
||||
</RadzenStack>
|
||||
<RadzenStack Orientation="Orientation.Vertical" style="">
|
||||
<div class="rz-p-2 rz-text-align-center">
|
||||
<h5>Manage content</h5>
|
||||
<RadzenButton class="btn" Text="Manage content" Click="@OpenManageContentGroups" />
|
||||
</div>
|
||||
|
||||
|
||||
</RadzenStack>
|
||||
<RadzenStack Orientation="Orientation.Vertical" style="">
|
||||
<div class="rz-p-2 rz-text-align-center">
|
||||
<h5>Manage design</h5>
|
||||
<p>Template: @SiteInfo.TemplateId</p>
|
||||
</div>
|
||||
|
||||
|
||||
</RadzenStack>
|
||||
|
||||
<RadzenStack Orientation="Orientation.Vertical" Visible=@displayOptions style="">
|
||||
<div class="rz-p-2 rz-text-align-center">
|
||||
<h5>Page</h5>
|
||||
<p>@currentMenuItem.Name</p>
|
||||
</div>
|
||||
<div class="rz-p-2 rz-text-align-center">
|
||||
<p>Happy with this page?</p>
|
||||
<RadzenButton class="btn" Text="@(isContentSaved ? "Update layout" : "Save layout")" Click="@(args => SaveCurrentLayout(currentMenuItem))" />
|
||||
</div>
|
||||
<div class="rz-p-2 rz-text-align-center">
|
||||
<RadzenButton class="btn" @ref=button Text="Regenerate" Click="@(args => MenuClick(currentMenuItem.Name))" />
|
||||
</div>
|
||||
</RadzenStack>
|
||||
<RadzenStack Orientation="Orientation.Vertical" style="">
|
||||
<div class="rz-p-2 rz-text-align-center">
|
||||
<h5>Media</h5>
|
||||
<RadzenButton class="btn" Text="Open library" Click="@OpenManageUploads" />
|
||||
</div>
|
||||
|
||||
</RadzenStack>
|
||||
</RadzenStack>
|
||||
|
||||
|
||||
<NewNavMenu Menu="@MenuItems" BrandName="@SelectedBrandName" OnMenuClicked=@MenuClick></NewNavMenu>
|
||||
<main>
|
||||
|
||||
<article class="content text-center" style="position: relative; z-index: 4;">
|
||||
<PageTitle>Home</PageTitle>
|
||||
<VideoComponent SelectedBrandName="@SelectedBrandName" />
|
||||
@{
|
||||
if (SiteInfo != null)
|
||||
{
|
||||
if (SiteInfo.BackgroundVideo != null)
|
||||
{
|
||||
<VideoComponent site="SiteInfo" />
|
||||
}
|
||||
}
|
||||
}
|
||||
@* <HeadContent>
|
||||
|
||||
@if (!string.IsNullOrEmpty(dynamicallyLoadedCss))
|
||||
|
|
@ -59,11 +130,12 @@
|
|||
<button id="stopButton" class="btn btn-primary voicebutton" onclick="stopRecording()" hidden><i class="fa-solid fa-microphone-slash"></i></button>
|
||||
}
|
||||
|
||||
if(TTSEnabled)
|
||||
if (TTSEnabled)
|
||||
{
|
||||
if(!AiVoicePermitted)
|
||||
if (!AiVoicePermitted)
|
||||
{
|
||||
<button data-hint="listen" class="btn btn-primary voicebutton" @onclick="AllowAIVoice"><i class="fa-solid fa-volume-xmark"></i>
|
||||
<button data-hint="listen" class="btn btn-primary voicebutton" @onclick="AllowAIVoice">
|
||||
<i class="fa-solid fa-volume-xmark"></i>
|
||||
|
||||
</button>
|
||||
}
|
||||
|
|
@ -134,6 +206,7 @@
|
|||
<button class="btn btn-primary" @onclick="HomeClick"><i class="fa-solid fa-rotate"></i></button>
|
||||
</article>
|
||||
</main>
|
||||
<FooterComponent MenuString="@Menu" OnMenuClicked=@MenuClick></FooterComponent>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
|
@ -186,9 +259,33 @@
|
|||
private string ChatGptResponse = string.Empty;
|
||||
private bool isRecording = false;
|
||||
|
||||
private string dynamicallyLoadedCss = string.Empty;
|
||||
// private string dynamicallyLoadedCss = string.Empty;
|
||||
private bool isContentSaved = false;
|
||||
private bool displaySettingsPanel = false;
|
||||
private bool displayOptions = false;
|
||||
private bool forceRegenerate = false;
|
||||
|
||||
private string Menu;
|
||||
private RadzenButton button;
|
||||
|
||||
public string DialogKey { get; set; } = "DefaultDialog";
|
||||
private Dictionary<string, EditSiteInfoDialogSettings> _dialogSettings = new();
|
||||
private readonly Dictionary<string, bool> _hasInitialized = new();
|
||||
|
||||
private void ToggleSettings()
|
||||
{
|
||||
displaySettingsPanel = !displaySettingsPanel;
|
||||
}
|
||||
|
||||
async Task SaveCurrentLayout(MenuItem menuItem)
|
||||
{
|
||||
//Save current layout called
|
||||
menuItem.StoredHtml = HtmlContent.ToString();
|
||||
Console.WriteLine($"Content length: {HtmlContent.Length}");
|
||||
Console.WriteLine(menuItem.StoredHtml);
|
||||
var result = await _contentEditorService.UpdateMenuItemAsync(menuItem);
|
||||
Console.WriteLine($"menuitem updated: {result.Id}, {result.Name}");
|
||||
|
||||
}
|
||||
|
||||
private void AllowAIVoice()
|
||||
{
|
||||
|
|
@ -199,7 +296,6 @@
|
|||
AiVoicePermitted = false;
|
||||
}
|
||||
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
|
|
@ -235,25 +331,6 @@
|
|||
_navigationManager.Refresh(true);
|
||||
}
|
||||
|
||||
// private async Task HandleValidSubmit()
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // Simulate sending an email
|
||||
// await ((EmailService)_emailService).SendEmailAsync(ContactFormModel, DocumentEmailAddress);
|
||||
// SuccessMessage = "Thank you for contacting us! Your message has been sent.";
|
||||
// ErrorMessage = null;
|
||||
|
||||
// // Clear the form
|
||||
// ContactFormModel = new();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// ErrorMessage = "An error occurred while sending your message. Please try again later.";
|
||||
// SuccessMessage = null;
|
||||
// }
|
||||
// }
|
||||
|
||||
private void CancelEmail()
|
||||
{
|
||||
FirstColumnClass = "";
|
||||
|
|
@ -266,26 +343,6 @@
|
|||
myHome = this; // Set the static reference to the current instance
|
||||
}
|
||||
|
||||
// [JSInvokable("OpenEmailForm3")]
|
||||
// public static async void OpenEmailForm3(string emailAddress)
|
||||
// {
|
||||
// if (myHome != null)
|
||||
// {
|
||||
// await myHome.DisplayEmailForm(emailAddress);
|
||||
// }
|
||||
// Console.Write("openEmail with: " + emailAddress);
|
||||
// }
|
||||
|
||||
// public async Task DisplayEmailForm(string emailAddress)
|
||||
// {
|
||||
// FirstColumnClass = "col-12 col-md-6";
|
||||
// isEmailFormVisible = true;
|
||||
// DocumentEmailAddress = emailAddress;
|
||||
// StateHasChanged();
|
||||
// var result = await jsRuntime.InvokeAsync<object>("getDivContent", "currentContent");
|
||||
// _scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
|
||||
// // Console.Write($"{_scopedContentService.CurrentDOM}");
|
||||
// }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
|
|
@ -295,7 +352,7 @@
|
|||
_scopedContentService.OnBrandNameChanged += HandleBrandNameChanged;
|
||||
SelectedBrandName = _scopedContentService.SelectedBrandName;
|
||||
// Subdomain = HttpContextAccessor.HttpContext?.Items["Subdomain"]?.ToString();
|
||||
SiteInfo = await _scopedContentService.GetSiteInfoByIdAsync(SiteId);
|
||||
SiteInfo = await _contentEditorService.GetSiteInfoByIdAsync(SiteId);
|
||||
if (SiteInfo != null)
|
||||
{
|
||||
|
||||
|
|
@ -316,6 +373,7 @@
|
|||
|
||||
}
|
||||
_scopedContentService.SelectedSiteId = SiteId;
|
||||
ContentCollectionName = SiteInfo.VectorCollectionName;
|
||||
|
||||
Console.Write("------------------------");
|
||||
// Load the CSS template for the selected brand from the database
|
||||
|
|
@ -340,16 +398,19 @@
|
|||
AIService.OnTextContentAvailable += UpdateTextContentForVoice;
|
||||
|
||||
Menu = await GetMenuList(SiteId);
|
||||
MenuItems = await GetMenuItems(SiteId);
|
||||
if (string.IsNullOrEmpty(HtmlContent.ToString()))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(topic))
|
||||
{
|
||||
UserInput = topic;
|
||||
await ChatGptService.ProcessContentRequest(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, Menu, true);
|
||||
await ChatGptService.ProcessContentRequest(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, ContentCollectionName, Menu, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ChatGptService.GetChatGptWelcomeMessage(SessionId, SiteId, TemplateCollectionName, Menu);
|
||||
SiteModel = await ChatGptService.InitSite(SessionId, SiteId, TemplateCollectionName, Menu);
|
||||
//await ChatGptService.ProcessContentRequest(SessionId, MenuItems.FirstOrDefault(), SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, Menu, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -357,17 +418,25 @@
|
|||
_initVoicePending = true;
|
||||
}
|
||||
|
||||
private async void UpdateContent(string receivedSessionId, string content)
|
||||
private async void UpdateContent(string receivedSessionId, string content, MenuItem menuItem)
|
||||
{
|
||||
if (receivedSessionId == SessionId) // Only accept messages meant for this tab
|
||||
{
|
||||
HtmlContent.Clear();
|
||||
HtmlContent.Append(content);
|
||||
//TODO SAVE TO DB
|
||||
if (menuItem != null)
|
||||
{
|
||||
currentMenuItem = menuItem;
|
||||
displayOptions = true;
|
||||
}
|
||||
//InvokeAsync(StateHasChanged); // Ensures UI updates dynamically
|
||||
await InvokeAsync(() =>
|
||||
{
|
||||
StateHasChanged();
|
||||
});
|
||||
var result = await jsRuntime.InvokeAsync<object>("getDivContent", "currentContent");
|
||||
_scopedContentService.CurrentDOM = JsonSerializer.Serialize(result);
|
||||
//_scopedContentService.CurrentDOM = await jsRuntime.InvokeAsync<string>("getDivContent", "currentContent");
|
||||
}
|
||||
}
|
||||
|
|
@ -420,34 +489,11 @@
|
|||
{
|
||||
HtmlContent.Clear();
|
||||
var menu = await GetMenuList(SiteId);
|
||||
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, TemplateCollectionName, menu);
|
||||
await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, ContentCollectionName, menu);
|
||||
UserInput = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
// public async Task HandleVoiceCommand(string input)
|
||||
// {
|
||||
// // HtmlContent = string.Empty;
|
||||
// UserInput = input;
|
||||
// await InvokeAsync(StateHasChanged);
|
||||
|
||||
// await SendUserQuery();
|
||||
// //UserInput = string.Empty;
|
||||
// }
|
||||
|
||||
// private async Task SendUserQuery()
|
||||
// {
|
||||
// welcomeStage = false;
|
||||
// if (!string.IsNullOrEmpty(UserInput))
|
||||
// {
|
||||
// HtmlContent.Clear();
|
||||
// var menu = await GetMenuList(SiteId);
|
||||
// Console.Write($"\n\n Input: {UserInput} \n\n");
|
||||
// await ChatGptService.ProcessUserIntent(SessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, CollectionName, menu);
|
||||
// UserInput = string.Empty;
|
||||
// }
|
||||
// }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
dynamicallyLoadedCss = "";
|
||||
|
|
@ -461,4 +507,355 @@
|
|||
{
|
||||
await CssTemplateService.DeleteSessionCssFile(SessionId);
|
||||
}
|
||||
|
||||
public async Task EditSite()
|
||||
{
|
||||
string dialogKey = $"EditSiteInfoDialogSettings_{siteid}"; // can be anything unique
|
||||
|
||||
var settings = await LoadStateAsync(dialogKey)
|
||||
?? new EditSiteInfoDialogSettings
|
||||
{
|
||||
Width = "500px",
|
||||
Height = "512px",
|
||||
Left = "10%",
|
||||
Top = "10%"
|
||||
};
|
||||
|
||||
await DialogService.OpenAsync<ManageSiteInfoPartial>($"Edit information of {SiteInfo.SiteName}",
|
||||
new Dictionary<string, object>() {
|
||||
{ "SiteId", siteid },
|
||||
{ "OnSiteNameChanged", new Func<string, Task>(OnSiteNameChanged) },
|
||||
},
|
||||
new DialogOptions()
|
||||
{
|
||||
Resizable = true,
|
||||
Draggable = true,
|
||||
Resize = GetResizeHandler(dialogKey),
|
||||
Drag = GetDragHandler(dialogKey),
|
||||
Width = settings.Width,
|
||||
Height = settings.Height,
|
||||
Left = settings.Left,
|
||||
Top = settings.Top,
|
||||
CssClass = "draggable-popup-dialog",
|
||||
WrapperCssClass = "draggable-popup-dialog-wrapper"
|
||||
});
|
||||
|
||||
await SaveStateAsync(dialogKey, _editSiteinfoSettings);
|
||||
}
|
||||
|
||||
private async Task OnSiteNameChanged(string newName)
|
||||
{
|
||||
Console.WriteLine("Sitename updated!!!!!!");
|
||||
SelectedBrandName = newName;
|
||||
}
|
||||
|
||||
public async Task EditContentItem(int Id)
|
||||
{
|
||||
string dialogKey = $"EditContentItemDialogSettings_{Id}"; // can be anything unique
|
||||
|
||||
var settings = await LoadStateAsync(dialogKey)
|
||||
?? new EditSiteInfoDialogSettings
|
||||
{
|
||||
Width = "70vw",
|
||||
Height = "80vh",
|
||||
Left = "10%",
|
||||
Top = "10%"
|
||||
};
|
||||
|
||||
await DialogService.OpenAsync<EditContentItem>($"Edit content {Id}",
|
||||
new Dictionary<string, object>() {
|
||||
{ "ContentItemId", Id },
|
||||
{ "OnContentUpdated", new Func<ContentItem, Task>(OnContentItemUpdated) },
|
||||
{ "OnSaved", new Func<ContentItem, Task>(OnContentItemSaved) },
|
||||
{ "OnCancelled", new Func<Task>(OnEditContentItemCancelClicked) },
|
||||
},
|
||||
new DialogOptions()
|
||||
{
|
||||
Resizable = true,
|
||||
Draggable = true,
|
||||
Resize = GetResizeHandler(dialogKey),
|
||||
Drag = GetDragHandler(dialogKey),
|
||||
Width = settings.Width,
|
||||
Height = settings.Height,
|
||||
Left = settings.Left,
|
||||
Top = settings.Top,
|
||||
CssClass = "draggable-popup-dialog",
|
||||
WrapperCssClass = "draggable-popup-dialog-wrapper"
|
||||
});
|
||||
|
||||
await SaveStateAsync(dialogKey, _editSiteinfoSettings);
|
||||
}
|
||||
|
||||
private async Task OnContentItemUpdated(ContentItem contentGroup)
|
||||
{
|
||||
Console.WriteLine("ContentGroup updated!!!!!!");
|
||||
|
||||
}
|
||||
|
||||
private async Task OnContentItemSaved(ContentItem contentGroup)
|
||||
{
|
||||
Console.WriteLine("ContentGroup edit started!!!!!!");
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async Task OnEditContentItemCancelClicked()
|
||||
{
|
||||
Console.WriteLine("ContentItem Edit clicked");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task OpenManageContentGroups()
|
||||
{
|
||||
string dialogKey = $"ManageContentGroupsDialogSettings_{siteid}"; // can be anything unique
|
||||
var settings = await LoadStateAsync(dialogKey);
|
||||
if (settings != null)
|
||||
{
|
||||
Console.WriteLine($"Settings: {settings.Top}, {settings.Left}, {settings.Height}, {settings.Width}");
|
||||
}
|
||||
else
|
||||
{
|
||||
settings = new EditSiteInfoDialogSettings
|
||||
{
|
||||
Width = "300px",
|
||||
Height = "612px",
|
||||
Left = "10%",
|
||||
Top = "10%"
|
||||
};
|
||||
}
|
||||
|
||||
await DialogService.OpenAsync<ManageContentGroupsPartial>($"Manage content of {SiteInfo.SiteName}",
|
||||
new Dictionary<string, object>() {
|
||||
{ "SiteInfoId", siteid },
|
||||
{ "OnManageContentItemClicked", new Func<string, int, Task>(OnManageContentItemClicked) }
|
||||
},
|
||||
|
||||
new DialogOptions()
|
||||
{
|
||||
Resizable = true,
|
||||
Draggable = true,
|
||||
Resize = GetResizeHandler(dialogKey),
|
||||
Drag = GetDragHandler(dialogKey),
|
||||
Width = settings.Width,
|
||||
Height = settings.Height,
|
||||
Left = settings.Left,
|
||||
Top = settings.Top,
|
||||
CssClass = "draggable-popup-dialog",
|
||||
WrapperCssClass = "draggable-popup-dialog-wrapper"
|
||||
});
|
||||
|
||||
await SaveStateAsync(dialogKey, _editSiteinfoSettings);
|
||||
}
|
||||
|
||||
private async Task OnContentGroupUpdated(ContentGroup contentGroup)
|
||||
{
|
||||
Console.WriteLine("ContentGroup updated!!!!!!");
|
||||
|
||||
}
|
||||
|
||||
private async Task OnContentGroupEditStarted(ContentGroup contentGroup)
|
||||
{
|
||||
Console.WriteLine("ContentGroup edit started!!!!!!");
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async Task OnManageContentItemClicked(string methodName, int Id)
|
||||
{
|
||||
Console.WriteLine("ContentItem Edit clicked");
|
||||
|
||||
await EditContentItem(Id);
|
||||
|
||||
}
|
||||
|
||||
public async Task OpenManageUploads()
|
||||
{
|
||||
string dialogKey = $"ManageUploadsDialogSettings_{siteid}"; // can be anything unique
|
||||
|
||||
var settings = await LoadStateAsync(dialogKey)
|
||||
?? new EditSiteInfoDialogSettings
|
||||
{
|
||||
Width = "600px",
|
||||
Height = "600px",
|
||||
Left = "10%",
|
||||
Top = "10%"
|
||||
};
|
||||
|
||||
await DialogService.OpenAsync<ManageUploads>($"Media library",
|
||||
null,
|
||||
new DialogOptions()
|
||||
{
|
||||
Resizable = true,
|
||||
Draggable = true,
|
||||
Resize = GetResizeHandler(dialogKey),
|
||||
Drag = GetDragHandler(dialogKey),
|
||||
Width = settings.Width,
|
||||
Height = settings.Height,
|
||||
Left = settings.Left,
|
||||
Top = settings.Top,
|
||||
CssClass = "draggable-popup-dialog",
|
||||
WrapperCssClass = "draggable-popup-dialog-wrapper"
|
||||
});
|
||||
|
||||
await SaveStateAsync(dialogKey, _editSiteinfoSettings);
|
||||
}
|
||||
|
||||
|
||||
private Action<System.Drawing.Point> GetDragHandler(string key)
|
||||
{
|
||||
return (point) =>
|
||||
{
|
||||
if (!_hasInitialized.ContainsKey(key))
|
||||
{
|
||||
_hasInitialized[key] = true;
|
||||
Console.WriteLine($"🚫 Skipping initial drag for {key}");
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = GetSettingsForKey(key);
|
||||
Console.WriteLine($"Got settings: {settings.Top}, {settings.Left}, {settings.Height}, {settings.Width}");
|
||||
settings.Left = $"{point.X}px";
|
||||
settings.Top = $"{point.Y}px";
|
||||
Console.WriteLine($"NEW Settings: {settings.Top}, {settings.Left}, {settings.Height}, {settings.Width}");
|
||||
_dialogSettings[key] = settings;
|
||||
InvokeAsync(() => SaveStateAsync(key, settings));
|
||||
};
|
||||
}
|
||||
|
||||
private Action<System.Drawing.Size> GetResizeHandler(string key)
|
||||
{
|
||||
return (size) =>
|
||||
{
|
||||
if (!_hasInitialized.ContainsKey(key))
|
||||
{
|
||||
_hasInitialized[key] = true;
|
||||
Console.WriteLine($"🚫 Skipping initial resize for {key}");
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = GetSettingsForKey(key);
|
||||
Console.WriteLine($"Got settings: {settings.Top}, {settings.Left}, {settings.Height}, {settings.Width}");
|
||||
settings.Width = $"{size.Width}px";
|
||||
settings.Height = $"{size.Height}px";
|
||||
Console.WriteLine($"NEW Settings: {settings.Top}, {settings.Left}, {settings.Height}, {settings.Width}");
|
||||
_dialogSettings[key] = settings;
|
||||
InvokeAsync(() => SaveStateAsync(key, settings));
|
||||
};
|
||||
}
|
||||
|
||||
private EditSiteInfoDialogSettings GetSettingsForKey(string key)
|
||||
{
|
||||
|
||||
Console.WriteLine($"Getting settings for {key}");
|
||||
if (_dialogSettings.TryGetValue(key, out var value))
|
||||
return value;
|
||||
|
||||
var newSettings = new EditSiteInfoDialogSettings();
|
||||
_dialogSettings[key] = newSettings;
|
||||
return newSettings;
|
||||
}
|
||||
|
||||
// void OnDrag(System.Drawing.Point point)
|
||||
// {
|
||||
// jsRuntime.InvokeVoidAsync("eval", $"console.log('Dialog drag. Left:{point.X}, Top:{point.Y}')");
|
||||
|
||||
// if (EditSiteInfoSettings == null)
|
||||
// {
|
||||
// EditSiteInfoSettings = new EditSiteInfoDialogSettings();
|
||||
// }
|
||||
|
||||
// EditSiteInfoSettings.Left = $"{point.X}px";
|
||||
// EditSiteInfoSettings.Top = $"{point.Y}px";
|
||||
|
||||
// InvokeAsync(() => SaveStateAsync(DialogKey, EditSiteInfoSettings));
|
||||
// }
|
||||
|
||||
// void OnResize(System.Drawing.Size size)
|
||||
// {
|
||||
// jsRuntime.InvokeVoidAsync("eval", $"console.log('Dialog resize. Width:{size.Width}, Height:{size.Height}')");
|
||||
|
||||
// if (EditSiteInfoSettings == null)
|
||||
// {
|
||||
// EditSiteInfoSettings = new EditSiteInfoDialogSettings();
|
||||
// }
|
||||
|
||||
// EditSiteInfoSettings.Width = $"{size.Width}px";
|
||||
// EditSiteInfoSettings.Height = $"{size.Height}px";
|
||||
|
||||
// InvokeAsync(() => SaveStateAsync(DialogKey, EditSiteInfoSettings));
|
||||
// }
|
||||
|
||||
private EditSiteInfoDialogSettings _editSiteinfoSettings = new();
|
||||
|
||||
public EditSiteInfoDialogSettings EditSiteInfoSettings
|
||||
{
|
||||
get => _editSiteinfoSettings;
|
||||
set
|
||||
{
|
||||
if (_editSiteinfoSettings != value)
|
||||
{
|
||||
_editSiteinfoSettings = value;
|
||||
InvokeAsync(() => SaveStateAsync(DialogKey, _editSiteinfoSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private async Task LoadStateAsync()
|
||||
// {
|
||||
// await Task.CompletedTask;
|
||||
|
||||
// var result = await jsRuntime.InvokeAsync<string>("window.localStorage.getItem", "EditSiteInfoDialogSettings");
|
||||
// if (!string.IsNullOrEmpty(result))
|
||||
// {
|
||||
// _editSiteinfoSettings = JsonSerializer.Deserialize<EditSiteInfoDialogSettings>(result);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async Task SaveStateAsync()
|
||||
// {
|
||||
// await Task.CompletedTask;
|
||||
|
||||
// await jsRuntime.InvokeVoidAsync("window.localStorage.setItem", "EditSiteInfoDialogSettings", JsonSerializer.Serialize<EditSiteInfoDialogSettings>(EditSiteInfoSettings));
|
||||
// }
|
||||
|
||||
private async Task<EditSiteInfoDialogSettings?> LoadStateAsync(string dialogKey)
|
||||
{
|
||||
Console.WriteLine($"Loading setting for {dialogKey}");
|
||||
var result = await jsRuntime.InvokeAsync<string>("window.localStorage.getItem", dialogKey);
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
Console.WriteLine($"Found setting for {dialogKey}");
|
||||
return JsonSerializer.Deserialize<EditSiteInfoDialogSettings>(result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task SaveStateAsync(string dialogKey, EditSiteInfoDialogSettings settings)
|
||||
{
|
||||
if (string.IsNullOrEmpty(settings?.Width) ||
|
||||
string.IsNullOrEmpty(settings?.Height) ||
|
||||
string.IsNullOrEmpty(settings?.Top) ||
|
||||
string.IsNullOrEmpty(settings?.Left))
|
||||
{
|
||||
Console.WriteLine($"❌ Skipping save for {dialogKey} due to invalid values");
|
||||
return; // Do NOT save garbage data
|
||||
}
|
||||
Console.WriteLine($"Saving setting for {dialogKey}");
|
||||
var json = JsonSerializer.Serialize(settings);
|
||||
Console.WriteLine($"Saving ettings: {settings.Top}, {settings.Left}, {settings.Height}, {settings.Width}, {json}");
|
||||
await jsRuntime.InvokeVoidAsync("window.localStorage.setItem", dialogKey, json);
|
||||
}
|
||||
|
||||
public class EditSiteInfoDialogSettings
|
||||
{
|
||||
public string Left { get; set; }
|
||||
public string Top { get; set; }
|
||||
public string Width { get; set; }
|
||||
public string Height { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ else
|
|||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await base.OnParametersSetAsync();
|
||||
var siteInfo = await scopedContentService.GetSiteInfoByIdAsync(siteId);
|
||||
var siteInfo = await contentEditorService.GetSiteInfoByIdAsync(siteId);
|
||||
// var menuItems1 = await contentEditorService.GetMenuItemsBySiteIdAsync(1);
|
||||
|
||||
menuItems = new ObservableCollection<MenuItem>(await contentEditorService.GetMenuItemsBySiteIdAsync(siteId));
|
||||
|
|
@ -129,17 +129,17 @@ else
|
|||
foreach (var menuItem in menuItems)
|
||||
{
|
||||
|
||||
string content;
|
||||
List<WebPageContent> content;
|
||||
MenuItemModel model = new MenuItemModel("");
|
||||
//try to get content from qDrant
|
||||
if (menuItem.QdrantPointId != null)
|
||||
if (menuItem.ContentGroupId != null && menuItem.ContentItemId != null)
|
||||
{
|
||||
content = await qDrantService.GetContentAsync(siteId, menuItem.SortOrder);
|
||||
var selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(content)!;
|
||||
if (selectedPoint != null)
|
||||
var contentItem = await contentEditorService.GetContentItemByIdAsync((int)menuItem.ContentItemId);
|
||||
|
||||
if (contentItem != null)
|
||||
{
|
||||
model.Content = selectedPoint.result.payload.content;
|
||||
Console.Write($"Found point: {selectedPoint.result.payload.content}");
|
||||
model.Content = contentItem.Content;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@layout AdminLayout
|
||||
@inject ScopedContentService SiteInfoService
|
||||
@inject ContentService ContentService
|
||||
@inject ContentEditorService _contentEditorService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject CustomAuthenticationStateProvider CustomAuthProvider
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
{
|
||||
|
||||
<RadzenPanel AllowCollapse="true" class="rz-my-5 rz-mx-auto" Style="width: 100%"
|
||||
Expand=@(() => Change("Panel expanded")) Collapse=@(() => Change("Panel collapsed"))>
|
||||
Expand=@(() => Change("Panel expanded")) Collapse=@(() => Change("Panel collapsed"))>
|
||||
<HeaderTemplate>
|
||||
<RadzenText TextStyle="TextStyle.H6" class="rz-display-flex rz-align-items-center rz-m-0">
|
||||
<RadzenIcon Icon="account_box" class="rz-me-1" /><b>Sites created</b>
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
<ChildContent>
|
||||
<RadzenCard class="rz-mt-4">
|
||||
<RadzenDataList PageSize="3" WrapItems="true" AllowPaging="true"
|
||||
Data="@siteInfoList" TItem="SiteInfo">
|
||||
Data="@siteInfoList" TItem="SiteInfo">
|
||||
<Template Context="site">
|
||||
<RadzenCard Style="width: 250px; background-color: darkgrey">
|
||||
<RadzenRow JustifyContent="@JustifyContent.SpaceBetween">
|
||||
|
|
@ -95,9 +95,9 @@
|
|||
</RadzenStack>
|
||||
</RadzenStack>
|
||||
<RadzenStack Orientation="@Orientation.Horizontal" Gap="10px" Reverse="false" JustifyContent="@JustifyContent.Center" AlignItems="@AlignItems.Center" Wrap="@FlexWrap.Wrap" Style="height: fit-content">
|
||||
|
||||
<a style="font-size: 14px;" href="/site-info/@site.Id" class="btn btn-secondary">Edit</a>
|
||||
<a style="font-size: 14px;" href="/generate-content/@site.Id" class="btn btn-secondary">Manage content</a>
|
||||
<InputText @bind-Value="collectionName" class="form-control" style="width: 100%;" placeholder="Site name" />
|
||||
<a style="font-size: 14px;" @onclick="()=>Migrate(site.Id, collectionName)" class="btn btn-secondary">Migrate</a>
|
||||
@* <a style="font-size: 14px;" href="/generate-content/@site.Id" class="btn btn-secondary">Manage content</a> *@
|
||||
<a style="font-size: 14px;" @onclick="()=>Preview(site)" class="btn btn-secondary"> Preview</a>
|
||||
|
||||
</RadzenStack>
|
||||
|
|
@ -157,6 +157,8 @@
|
|||
private string? userName;
|
||||
private AuthenticationState? authState;
|
||||
int position = 1;
|
||||
//TEMPORARY
|
||||
private string collectionName = "seemgen-collection";
|
||||
|
||||
void Change(string text)
|
||||
{
|
||||
|
|
@ -170,6 +172,12 @@
|
|||
NavigationManager.NavigateTo(site.DefaultUrl, true);
|
||||
}
|
||||
|
||||
private async Task Migrate(int siteId, string collectionName)
|
||||
{
|
||||
var result = await _contentEditorService.MigrateQdrantToContentItemsAsync(siteId, collectionName);
|
||||
Console.WriteLine($"Migration result: {result}");
|
||||
}
|
||||
|
||||
private async Task Preview(SiteInfo site)
|
||||
{
|
||||
string myUrl = $"/preview/{site.Id}";
|
||||
|
|
@ -192,7 +200,7 @@
|
|||
userId = CustomAuthProvider.GetUserId();
|
||||
userName = CustomAuthProvider.GetUserName();
|
||||
}
|
||||
siteInfoList = await SiteInfoService.GetUserSitesAsync(userId!);
|
||||
siteInfoList = await _contentEditorService.GetUserSitesAsync(userId!);
|
||||
}
|
||||
|
||||
private async Task HandleValidSubmit()
|
||||
|
|
@ -200,8 +208,9 @@
|
|||
newSite.UserId = userId;
|
||||
newSite.TemplateId = 1;
|
||||
newSite.DefaultUrl = await GenerateSubdomainAsync(newSite.SiteName);
|
||||
var result = await SiteInfoService.AddSiteInfoAsync(newSite);
|
||||
siteInfoList = await SiteInfoService.GetUserSitesAsync(userId!);
|
||||
newSite.VectorCollectionName = _contentEditorService.GetGeneratedVectorCollectionName(newSite);
|
||||
var result = await _contentEditorService.AddSiteInfoAsync(newSite);
|
||||
siteInfoList = await _contentEditorService.GetUserSitesAsync(userId!);
|
||||
newSite = new(); // Reset the form
|
||||
}
|
||||
public async Task<string> GenerateSubdomainAsync(string siteName)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
@using BLAIzor.Models
|
||||
@using BLAIzor.Services
|
||||
@inject ContentEditorService contentEditorService
|
||||
@inject DialogService dialogService
|
||||
@inject NotificationService notificationService
|
||||
|
||||
|
||||
|
||||
<a style="width:100%" @onclick="ToggleVisibility">
|
||||
<div class="mb-2 p-3 reference-button bg-panel">
|
||||
<div class="text-content">
|
||||
<strong>Content count: @contentItems.Count</strong><br />
|
||||
<small class="text-muted">See all</small>
|
||||
</div>
|
||||
<div class="icon-buttons">
|
||||
|
||||
<div class="icon-circle"><i class="fa-solid fa-arrow-down text-white"></i></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@if (isVisible)
|
||||
{
|
||||
|
||||
<ul class="list-group content-item-list mb-3">
|
||||
<li class="list-group-item">
|
||||
<button class="pointer bg-transparent border-0" style="width:100%; text-align: left; " @onclick="CreateNewItem">
|
||||
<div class="px-3 py-2 reference-button bg-panel-gradient-highlight pointer">
|
||||
<div class="text-content">
|
||||
<strong>Add new</strong>
|
||||
<br />
|
||||
<small class="text-muted">Add a new content to this group</small>
|
||||
</div>
|
||||
<div class="icon-buttons">
|
||||
|
||||
<div class="icon-circle"><i class="fa-solid fa-plus text-white"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
@* <li><button class="btn btn-sm btn-primary ms-2" @onclick="CreateNewItem">Add New Item</button></li> *@
|
||||
@foreach (var item in contentItems)
|
||||
{
|
||||
<li class="list-group-item">
|
||||
|
||||
<div class="p-2 reference-button">
|
||||
<div class="text-content">
|
||||
<strong>@item.Title</strong>
|
||||
<br />
|
||||
<small class="text-muted">@item.Language - @item.Tags</small>
|
||||
</div>
|
||||
<div class="icon-buttons">
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditItem(item.Id)">Edit</button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => DeleteItem(item.Id)">Delete</button>
|
||||
@* <div class="icon-circle">V</div> *@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public int ContentGroupId { get; set; }
|
||||
[Parameter] public Func<string, int, Task> OnManageContentItemClicked { get; set; }
|
||||
|
||||
private List<ContentItem> contentItems = new();
|
||||
private bool isVisible = false;
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
contentItems = await contentEditorService.GetContentItemsByGroupIdAsync(ContentGroupId);
|
||||
}
|
||||
|
||||
private void ToggleVisibility()
|
||||
{
|
||||
isVisible = !isVisible;
|
||||
}
|
||||
|
||||
private async Task CreateNewItem()
|
||||
{
|
||||
var newItem = new ContentItem
|
||||
{
|
||||
ContentGroupId = ContentGroupId,
|
||||
Title = "New Item",
|
||||
Description = "",
|
||||
Content = "",
|
||||
Language = "en",
|
||||
Tags = "",
|
||||
IsPublished = false,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
LastUpdated = DateTime.UtcNow,
|
||||
Version = 1
|
||||
};
|
||||
|
||||
var result = await contentEditorService.CreateContentItemAsync(newItem);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
contentItems.Insert(0, result);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EditItem(int itemId)
|
||||
{
|
||||
if (OnManageContentItemClicked != null)
|
||||
await OnManageContentItemClicked.Invoke("EditContentItem", itemId);
|
||||
}
|
||||
|
||||
private async Task DeleteItem(int itemId)
|
||||
{
|
||||
var confirmationResult = await dialogService.Confirm("Are you sure?", "Delete content", new ConfirmOptions() { OkButtonText = "Delete", CancelButtonText = "Oops, no" });
|
||||
if (confirmationResult == true)
|
||||
{
|
||||
await ReallyDeleteItem(itemId);
|
||||
}
|
||||
else
|
||||
{
|
||||
//do nothing?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task ReallyDeleteItem(int itemId)
|
||||
{
|
||||
var result = await contentEditorService.DeleteContentItemByIdAsync(itemId);
|
||||
if (result)
|
||||
{
|
||||
var toRemove = contentItems.FirstOrDefault(c => c.Id == itemId);
|
||||
if (toRemove != null)
|
||||
{
|
||||
contentItems.Remove(toRemove);
|
||||
}
|
||||
var message = NotificationHelper.CreateNotificationMessage("Group deleted", 2, "Success", "ContentGroup deleted");
|
||||
ShowNotification(message);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowNotification(NotificationMessage message)
|
||||
{
|
||||
notificationService.Notify(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
@page "/dialogcard/{OrderID}"
|
||||
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
|
||||
@inject Radzen.DialogService dialogService
|
||||
|
||||
<RadzenStack Gap="1rem" Orientation="Orientation.Vertical" JustifyContent="JustifyContent.SpaceBetween" Style="height: 100%;">
|
||||
<RadzenStack>
|
||||
|
||||
</RadzenStack>
|
||||
</RadzenStack>
|
||||
@code {
|
||||
[Parameter] public int OrderID { get; set; }
|
||||
[Parameter] public bool ShowClose { get; set; } = true;
|
||||
|
||||
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await base.OnParametersSetAsync();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
@using BLAIzor.Models
|
||||
|
||||
|
||||
@if (!editCurrentGroup)
|
||||
{
|
||||
@* <div class="reference-button">
|
||||
<div class="text-content">
|
||||
<h4>Reference</h4>
|
||||
<p>Create songs inspired by a<br>reference track</p>
|
||||
</div>
|
||||
<div class="icon-buttons">
|
||||
<div class="icon-circle">+</div>
|
||||
<div class="icon-circle">★</div>
|
||||
</div>
|
||||
</div> *@
|
||||
|
||||
<div class="bg-panel rounded p-3 mb-3">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div @key="Group.Id">
|
||||
<strong>
|
||||
@Group.Name
|
||||
</strong>
|
||||
<small class="text-muted">
|
||||
Group type: @Group.Type
|
||||
</small>
|
||||
<small class="text-muted">
|
||||
Group slug: @Group.Slug
|
||||
</small>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="d-flex flex-row-reverse">
|
||||
@* <RadzenButton Text="Edit" Click="() => ToggleGroupEdit()"></RadzenButton>
|
||||
<RadzenButton ButtonStyle=@ButtonStyle.Warning Text="Edit" Click="() => OnForceReChunkClicked(Group)"></RadzenButton> *@
|
||||
<button class="btn" type="button" @onclick="() => ToggleGroupEdit()">Edit</button>
|
||||
<button class="btn btn-warning" type="button" @onclick="() => OnForceReChunkClicked(Group)">ReChunk</button>
|
||||
<button class="btn btn-danger" type="button" @onclick="() => DeleteGroup(Group)">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<ContentItemList ContentGroupId="@Group.Id" OnManageContentItemClicked="ContentItemManagedCallback"></ContentItemList>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<EditForm Model="@Group" OnValidSubmit="() => SaveGroup(Group)">
|
||||
<div class="rounded bg-panel p-3 mb-3">
|
||||
<label>
|
||||
Name
|
||||
<InputText class="form-control my-1" @bind-Value="Group.Name" />
|
||||
</label>
|
||||
<label>
|
||||
Type
|
||||
<InputText class="form-control my-1" @bind-Value="Group.Type" />
|
||||
</label>
|
||||
<div class="d-flex flex-row-reverse">
|
||||
<button class="btn btn-success" type="submit">Save</button>
|
||||
|
||||
<button class="btn btn-secondary" type="button" @onclick="() => ToggleGroupEdit()">Cancel</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@code {
|
||||
[Parameter] public ContentGroup Group { get; set; }
|
||||
// [Parameter] public Action<ContentGroup> OnContentGroupUpdated { get; set; }
|
||||
[Parameter] public Func<ContentGroup, Task> OnContentGroupSaveClicked { get; set; }
|
||||
[Parameter] public Func<ContentGroup, Task> OnContentGroupDeleteClicked { get; set; }
|
||||
[Parameter] public Func<ContentGroup, Task> OnForceReChunkClicked { get; set; }
|
||||
// [Parameter] public Func<Task> OnContentGroupDeselectClicked { get; set; }
|
||||
[Parameter] public Func<string, int, Task> OnManageContentItemClicked { get; set; }
|
||||
|
||||
private bool editCurrentGroup = false;
|
||||
|
||||
private async Task ForceReChunk(int contenGroupId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void ToggleGroupEdit()
|
||||
{
|
||||
Console.WriteLine($"dklsajdlaéjdksléajdéal djkéa sdjklaésj dkléajdklaéjdkléa");
|
||||
editCurrentGroup = !editCurrentGroup;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task SaveGroup(ContentGroup group)
|
||||
{
|
||||
if (OnContentGroupSaveClicked != null)
|
||||
await OnContentGroupSaveClicked.Invoke(group);
|
||||
}
|
||||
|
||||
private async Task DeleteGroup(ContentGroup group)
|
||||
{
|
||||
if (OnContentGroupDeleteClicked != null)
|
||||
await OnContentGroupDeleteClicked.Invoke(group);
|
||||
}
|
||||
|
||||
private async Task ContentItemManagedCallback(string method, int itemId)
|
||||
{
|
||||
if (OnManageContentItemClicked != null)
|
||||
await OnManageContentItemClicked.Invoke(method, itemId);
|
||||
}
|
||||
|
||||
private async Task ForceContentReChunkCallback(ContentGroup group)
|
||||
{
|
||||
if (OnForceReChunkClicked != null)
|
||||
await OnForceReChunkClicked.Invoke(group);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
@using BLAIzor.Models
|
||||
@using BLAIzor.Services
|
||||
@inject ContentEditorService contentEditorService
|
||||
|
||||
@if (isLoading)
|
||||
{
|
||||
<p>Loading content item...</p>
|
||||
}
|
||||
else if (contentItem == null)
|
||||
{
|
||||
<p class="text-danger">Content item not found.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<EditForm Model="@contentItem" OnValidSubmit="HandleSave">
|
||||
<div class="rounded p-3 bg-panel">
|
||||
<label>
|
||||
Title
|
||||
<InputText class="form-control my-2" @bind-Value="contentItem.Title" />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Description
|
||||
<InputTextArea class="form-control my-2" style="height: 60px;" @bind-Value="contentItem.Description" Rows="3" />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Content
|
||||
<InputTextArea class="form-control my-2" style="height: 200px;" @bind-Value="contentItem.Content" Rows="10" />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Language
|
||||
<InputText class="form-control my-2" @bind-Value="contentItem.Language" />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Tags (comma-separated)
|
||||
<InputText class="form-control my-2" @bind-Value="contentItem.Tags" />
|
||||
</label>
|
||||
|
||||
<div class="form-check my-2">
|
||||
<InputCheckbox class="form-check-input" id="isPublished" @bind-Value="contentItem.IsPublished" />
|
||||
<label class="form-check-label" for="isPublished">Published</label>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-row-reverse">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<button class="btn btn-secondary" type="button" @onclick="CancelEdit">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public int ContentItemId { get; set; }
|
||||
[Parameter] public Func<ContentItem, Task> OnContentUpdated { get; set; }
|
||||
[Parameter] public Func<ContentItem, Task> OnSaved { get; set; }
|
||||
[Parameter] public Func<Task> OnCancelled { get; set; }
|
||||
|
||||
private ContentItem? contentItem;
|
||||
private bool isLoading = true;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
isLoading = true;
|
||||
contentItem = await contentEditorService.GetContentItemByIdAsync(ContentItemId);
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
private async Task HandleSave()
|
||||
{
|
||||
contentItem!.LastUpdated = DateTime.UtcNow;
|
||||
|
||||
await contentEditorService.SaveAndSyncContentItemAsync(contentItem, contentItem.ContentGroup.SiteInfo.VectorCollectionName, false);
|
||||
|
||||
if (OnSaved != null)
|
||||
await OnSaved.Invoke(contentItem);
|
||||
}
|
||||
|
||||
private async Task CancelEdit()
|
||||
{
|
||||
if (OnCancelled != null)
|
||||
await OnCancelled.Invoke();
|
||||
}
|
||||
|
||||
private async Task OnUpdated()
|
||||
{
|
||||
if (OnContentUpdated != null)
|
||||
await OnContentUpdated.Invoke(contentItem);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using BLAIzor.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace BLAIzor.Components.Partials
|
||||
{
|
||||
public class EditorUIComponentBase<T> : ComponentBase
|
||||
{
|
||||
[Parameter] public Func<T, Task> OnContentUpdated { get; set; }
|
||||
[Parameter] public Func<T, Task> OnContentEditStarted { get; set; }
|
||||
|
||||
protected async Task NotifyContentUpdatedAsync(T updatedObject)
|
||||
{
|
||||
if (OnContentUpdated != null)
|
||||
{
|
||||
await OnContentUpdated.Invoke(updatedObject);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task NotifyContentEditStartedAsync(T updatedObject)
|
||||
{
|
||||
if (OnContentEditStarted != null)
|
||||
{
|
||||
await OnContentEditStarted.Invoke(updatedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
@using BLAIzor.Models
|
||||
@using BLAIzor.Services
|
||||
@inject ScopedContentService _scopedContentService;
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject ContentEditorService _contentEditorService
|
||||
@inject IHttpContextAccessor HttpContextAccessor
|
||||
@inject IJSRuntime JS
|
||||
|
||||
|
||||
<div class="container-fluid footer" style="position: relative; z-index: 10000; margin-top: 100px;">
|
||||
<div class="container">
|
||||
<footer class="footer-inner pt-5">
|
||||
<div class="row">
|
||||
@{
|
||||
|
||||
|
||||
var brandFileName = _scopedContentService.SelectedDocument;
|
||||
var brandName = _scopedContentService.SelectedBrandName;
|
||||
|
||||
if (MenuList != null)
|
||||
{
|
||||
int columnCount = 3;
|
||||
int itemsPerColumn = (int)Math.Ceiling(MenuList.Length / (double)columnCount);
|
||||
var columns = new List<List<string>>();
|
||||
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
{
|
||||
columns.Add(MenuList.Skip(i * itemsPerColumn).Take(itemsPerColumn).ToList());
|
||||
}
|
||||
|
||||
@foreach (var column in columns)
|
||||
{
|
||||
<div class="col-6 col-md-2 mb-3">
|
||||
<h5>@brandName</h5>
|
||||
<ul class="nav flex-column">
|
||||
@foreach (var item in column)
|
||||
{
|
||||
<li class="nav-item mb-2" style="text-align: start;"><a class="nav-link p-0 text-body-secondary" @onclick="()=>MenuClickedAsync(item)">@item</a></li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
<div class="col-md-5 offset-md-1 mb-3">
|
||||
<form>
|
||||
<h5>Subscribe to our newsletter</h5> <p>Monthly digest of what's new and exciting from us.</p>
|
||||
<div class="d-flex flex-column flex-sm-row w-100 gap-2">
|
||||
<label for="newsletter1" class="visually-hidden">Email address</label>
|
||||
<input id="newsletter1" type="email" class="form-control" placeholder="Email address">
|
||||
<button class="btn btn-primary" type="button">Subscribe</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-sm-row justify-content-between pt-4 mt-4 border-top">
|
||||
|
||||
<p>© 2025 <NavLink @onclick="HomeClick">@brandName</NavLink>, Inc. All rights reserved.</p>
|
||||
<ul class="list-unstyled d-flex">
|
||||
<li class="ms-3"><a href="#" aria-label="Instagram"><i class="fa-brands fa-square-instagram"></i></a></li>
|
||||
<li class="ms-3"><a href="#" aria-label="Facebook"><i class="fa-brands fa-square-facebook"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@code {
|
||||
public int SiteId;
|
||||
[Parameter]
|
||||
public string? MenuString { get; set; }
|
||||
public string[] MenuList;
|
||||
public string videoUrl = "";
|
||||
public string SelectedLanguage;
|
||||
private int RenderCounter = 0;
|
||||
|
||||
[Parameter] public Action<string>? OnMenuClicked { get; set; }
|
||||
|
||||
public async Task MenuClickedAsync(string menuName)
|
||||
{
|
||||
OnMenuClicked?.Invoke(menuName);
|
||||
await JS.InvokeVoidAsync("collapseNavbar");
|
||||
}
|
||||
public void HomeClick()
|
||||
{
|
||||
_navigationManager.Refresh(true);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var lang = await JS.InvokeAsync<string>("getUserLanguage");
|
||||
|
||||
// Normalize and match to one of your supported languages
|
||||
if (lang.StartsWith("hu", StringComparison.OrdinalIgnoreCase))
|
||||
SelectedLanguage = "Hungarian";
|
||||
else if (lang.StartsWith("de", StringComparison.OrdinalIgnoreCase))
|
||||
SelectedLanguage = "German";
|
||||
else
|
||||
SelectedLanguage = "English";
|
||||
|
||||
_scopedContentService.SelectedLanguage = SelectedLanguage;
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(MenuString))
|
||||
{
|
||||
MenuList = MenuString.Split(",");
|
||||
}
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
public void OnLanguageSelected(ChangeEventArgs e)
|
||||
{
|
||||
SelectedLanguage = e.Value?.ToString();
|
||||
|
||||
if (!string.IsNullOrEmpty(SelectedLanguage))
|
||||
{
|
||||
Console.WriteLine($"Language selected: {SelectedLanguage}");
|
||||
_scopedContentService.SelectedLanguage = SelectedLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
@using Newtonsoft.Json
|
||||
@using System.Collections.ObjectModel
|
||||
@inject ContentEditorService ContentEditorService
|
||||
@inject ContentEditorAIService ContentEditorAIService
|
||||
@inject HtmlSnippetProcessor HtmlSnippetProcessor
|
||||
@inject QDrantService QDrantService
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
|
@ -153,18 +154,23 @@ else if (!string.IsNullOrEmpty(ErrorMessage))
|
|||
foreach (var menuItem in menuItems)
|
||||
{
|
||||
|
||||
string content;
|
||||
List<WebPageContent> content;
|
||||
MenuItemModel model = new MenuItemModel("");
|
||||
//try to get content from qDrant
|
||||
if (menuItem.QdrantPointId != null)
|
||||
if (menuItem.ContentGroupId != null && menuItem.ContentItemId != null) ////FIXXXXXXX
|
||||
{
|
||||
content = await QDrantService.GetContentAsync(SiteId, menuItem.PointId);
|
||||
var selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(content)!;
|
||||
if (selectedPoint != null)
|
||||
ContentItem contentItem = await ContentEditorService.GetContentItemByIdAsync((int)menuItem.ContentItemId);
|
||||
|
||||
/* content = await QDrantService.GetPointFromQdrantAsyncByPointId(SiteId, contentItem.Chunks); */ //FIXXXXXXX WONT WORKNOW
|
||||
//var selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(content)!;
|
||||
if (contentItem != null)
|
||||
{
|
||||
model.Content = selectedPoint.result.payload.content;
|
||||
model.ContentDescription = selectedPoint.result.payload.description;
|
||||
Console.Write($"Found point: {selectedPoint.result.payload.content}");
|
||||
foreach (var vector in contentItem.Chunks)
|
||||
{
|
||||
//get vectors to compare
|
||||
}
|
||||
model.Content = contentItem.Content;
|
||||
model.ContentDescription = contentItem.Description;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -201,14 +207,14 @@ else if (!string.IsNullOrEmpty(ErrorMessage))
|
|||
|
||||
//get website subject
|
||||
var prompt1 = $"Analyze the following text and make an assumption about the characteristics of the text type (website, document, book, etc). Do not attach any explanation, make your answer like `A medical clinic of mammography` or `A webdesign and software development company` or `A book about XY` :\n\n{document}";
|
||||
var response1 = await ContentEditorService.GetGeneratedContentAsync(SessionId, prompt1);
|
||||
var response1 = await ContentEditorAIService.GetGeneratedContentAsync(SessionId, prompt1);
|
||||
Console.Write(response1);
|
||||
subject = response1;
|
||||
|
||||
if (!MenuItemsSaved)
|
||||
{
|
||||
var prompt2 = $"Analyze the following text and based on the sections suggest a list of menu items for a website. Do not attach any explanation. Text:\n\n`{document}`.";
|
||||
var response2 = await ContentEditorService.GetMenuSuggestionsAsync(SessionId, prompt2);
|
||||
var response2 = await ContentEditorAIService.GetMenuSuggestionsAsync(SessionId, prompt2);
|
||||
ExtractedMenuItems = response2.Select(name => new MenuItemModel(name)).ToList();
|
||||
}
|
||||
// Send the content to ChatGPT
|
||||
|
|
@ -255,7 +261,7 @@ else if (!string.IsNullOrEmpty(ErrorMessage))
|
|||
private async Task SaveMenuItems(bool updateVectorDatabase)
|
||||
{
|
||||
//bool valami = updateVectorDatabase;
|
||||
var result = await ContentEditorService.ProcessMenuItems(SiteId, hasCollection, ExtractedMenuItems, subject, MenuItemsSaved, updateVectorDatabase);
|
||||
var result = await ContentEditorAIService.ProcessMenuItems(SiteId, hasCollection, ExtractedMenuItems, subject, MenuItemsSaved, updateVectorDatabase);
|
||||
if (result == "OK")
|
||||
{
|
||||
MenuItemsSaved = true;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
@using BLAIzor.Components.Partials
|
||||
@using System.Collections.ObjectModel
|
||||
@inject ContentEditorService ContentEditorService
|
||||
@inject ContentEditorAIService ContentEditorAIService
|
||||
@inject HtmlSnippetProcessor HtmlSnippetProcessor
|
||||
@inject QDrantService QDrantService
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
|
@ -122,24 +123,27 @@ else if (!string.IsNullOrEmpty(errorMessage))
|
|||
foreach (var menuItem in menuItems)
|
||||
{
|
||||
|
||||
string content;
|
||||
List<WebPageContent> content;
|
||||
MenuItemModel model = new MenuItemModel("");
|
||||
//try to get content from qDrant
|
||||
if (menuItem.QdrantPointId != null)
|
||||
if (menuItem.ContentGroupId != null && menuItem.ContentItemId != null) ////FIXXXXXXX
|
||||
{
|
||||
content = await QDrantService.GetContentAsync(SiteId, menuItem.PointId);
|
||||
// content = await QDrantService.GetContentAsync(SiteId, menuItem.QdrantPointId); TODO
|
||||
var selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(content)!;
|
||||
if (selectedPoint != null)
|
||||
ContentItem contentItem = await ContentEditorService.GetContentItemByIdAsync((int)menuItem.ContentItemId);
|
||||
|
||||
/* content = await QDrantService.GetPointFromQdrantAsyncByPointId(SiteId, contentItem.Chunks); */
|
||||
//var selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(content)!;
|
||||
if (contentItem != null)
|
||||
{
|
||||
model.Content = selectedPoint.result.payload.content;
|
||||
model.ContentDescription = selectedPoint.result.payload.description;
|
||||
Console.Write($"Found point: {selectedPoint.result.payload.content}");
|
||||
foreach (var vector in contentItem.Chunks)
|
||||
{
|
||||
//get vectors to compare
|
||||
}
|
||||
model.Content = contentItem.Content;
|
||||
model.ContentDescription = contentItem.Description;
|
||||
}
|
||||
|
||||
}
|
||||
model.MenuItem = menuItem;
|
||||
|
||||
extractedMenuItems.Add(model);
|
||||
// UpdateMenuItem(model);
|
||||
}
|
||||
|
|
@ -162,7 +166,7 @@ else if (!string.IsNullOrEmpty(errorMessage))
|
|||
try
|
||||
{
|
||||
var prompt = $"Suggest a list of menu items for a website about: {subject}. Please do not attach any explanation.";
|
||||
var response = await ContentEditorService.GetMenuSuggestionsAsync(SessionId, prompt);
|
||||
var response = await ContentEditorAIService.GetMenuSuggestionsAsync(SessionId, prompt);
|
||||
extractedMenuItems = response.Select(name => new MenuItemModel(name)).ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -208,7 +212,7 @@ else if (!string.IsNullOrEmpty(errorMessage))
|
|||
|
||||
private async Task SaveMenuItems(bool updateVectorDatabase)
|
||||
{
|
||||
var result = await ContentEditorService.ProcessMenuItems(SiteId, hasCollection, extractedMenuItems, subject, MenuItemsSaved, updateVectorDatabase);
|
||||
var result = await ContentEditorAIService.ProcessMenuItems(SiteId, hasCollection, extractedMenuItems, subject, MenuItemsSaved, updateVectorDatabase);
|
||||
if (result == "OK")
|
||||
{
|
||||
MenuItemsSaved = true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,208 @@
|
|||
@using BLAIzor.Models
|
||||
@using BLAIzor.Services
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@inherits EditorUIComponentBase<ContentGroup>
|
||||
@inject ContentEditorService contentEditorService
|
||||
@inject NavigationManager navigation
|
||||
@inject NotificationService notificationService
|
||||
@inject DialogService dialogService
|
||||
@attribute [Authorize]
|
||||
|
||||
@if (isLoading)
|
||||
{
|
||||
<p>Loading content groups...</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var group in groups)
|
||||
{
|
||||
if (selectedGroupId == group.Id)
|
||||
{
|
||||
|
||||
<EditContentGroup Group="group" OnContentGroupSaveClicked="SaveGroup" OnContentGroupDeleteClicked="DeleteGroup" OnManageContentItemClicked="ContentItemManagedCallback" OnForceReChunkClicked="Rechunk"></EditContentGroup>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="rounded bg-panel p-3 mb-3 d-flex justify-content-between align-items-center">
|
||||
<a style="width:100%" @onclick="() => SelectGroup(group.Id)">
|
||||
<div style="width:100%; cursor:pointer;">
|
||||
<strong>@group.Name</strong><br />
|
||||
<small class="text-muted">@group.Type</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<button class="pointer bg-transparent border-0 p-0" style="width:100%; text-align: left;" @onclick="AddNewGroup">
|
||||
<div class="mb-2 p-3 reference-button bg-panel-gradient-highlight pointer">
|
||||
<div class="text-content">
|
||||
<strong>Add new</strong>
|
||||
<br />
|
||||
<small class="text-muted">Add a new content group like "Blog", News, "Manuals" etc...</small>
|
||||
</div>
|
||||
<div class="icon-buttons">
|
||||
|
||||
<div class="icon-circle"><i class="fa-solid fa-plus text-white"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public int SiteInfoId { get; set; }
|
||||
[Parameter] public Func<string, int, Task> OnManageContentItemClicked { get; set; }
|
||||
|
||||
private List<ContentGroup> groups = new();
|
||||
private bool isLoading = true;
|
||||
|
||||
private int? selectedGroupId = null;
|
||||
|
||||
private async Task SelectGroup(int groupId)
|
||||
{
|
||||
selectedGroupId = groupId;
|
||||
var group = groups.FirstOrDefault(g => g.Id == groupId);
|
||||
if (group != null)
|
||||
{
|
||||
await NotifyContentEditStartedAsync(group);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeselectGroup()
|
||||
{
|
||||
selectedGroupId = null;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
|
||||
isLoading = true;
|
||||
groups = (await contentEditorService.GetContentGroupsBySiteInfoIdAsync(SiteInfoId))
|
||||
.Where(g => g.SiteInfoId == SiteInfoId)
|
||||
.OrderByDescending(g => g.LastUpdated)
|
||||
.ToList();
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
private void EditGroup(ContentGroup group)
|
||||
{
|
||||
navigation.NavigateTo($"/content-group/{group.Id}/items");
|
||||
}
|
||||
|
||||
private async Task Rechunk(ContentGroup group)
|
||||
{
|
||||
var confirmationResult = await dialogService.Confirm("Are you sure?", "Rechunk group", new ConfirmOptions() { OkButtonText = "Rechunk", CancelButtonText = "Oops, no" });
|
||||
if (confirmationResult == true)
|
||||
{
|
||||
await ReallyRechunk(group);
|
||||
}
|
||||
else
|
||||
{
|
||||
//do nothing?
|
||||
}
|
||||
|
||||
}
|
||||
private async Task ReallyRechunk(ContentGroup group)
|
||||
{
|
||||
group.LastUpdated = DateTime.UtcNow;
|
||||
var siteInfo = await contentEditorService.GetSiteInfoByIdAsync(group.SiteInfoId);
|
||||
|
||||
var result = await contentEditorService.ForceRechunkContentGroupAsync(group.Id, siteInfo.VectorCollectionName);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
await NotifyContentUpdatedAsync(group); // This triggers the base class logic
|
||||
await DeselectGroup();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveGroup(ContentGroup group)
|
||||
{
|
||||
group.LastUpdated = DateTime.UtcNow;
|
||||
group.Version = group.Version + 1;
|
||||
var result = await contentEditorService.UpdateContentGroupByIdAsync(group);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
await NotifyContentUpdatedAsync(group); // This triggers the base class logic
|
||||
await DeselectGroup();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteGroup(ContentGroup group)
|
||||
{
|
||||
var confirmationResult = await dialogService.Confirm("Are you sure?", "Delete group", new ConfirmOptions() { OkButtonText = "Delete", CancelButtonText = "Oops, no" });
|
||||
if (confirmationResult == true)
|
||||
{
|
||||
await ReallyDeleteGroup(group);
|
||||
}
|
||||
else
|
||||
{
|
||||
//do nothing?
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReallyDeleteGroup(ContentGroup group)
|
||||
{
|
||||
|
||||
var result = await contentEditorService.DeleteContentGroupByIdAsync(group.Id); ;
|
||||
groups.Remove(group);
|
||||
var message = NotificationHelper.CreateNotificationMessage("Group deleted", 2, "Success", "ContentGroup deleted");
|
||||
ShowNotification(message);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task AddNewGroup()
|
||||
{
|
||||
var newGroup = new ContentGroup
|
||||
{
|
||||
SiteInfoId = SiteInfoId,
|
||||
Name = "New Group",
|
||||
Slug = $"group-{DateTime.UtcNow.Ticks}",
|
||||
Type = "manual",
|
||||
VectorSize = 384,
|
||||
EmbeddingModel = "default",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
LastUpdated = DateTime.UtcNow,
|
||||
Version = 1
|
||||
};
|
||||
var result = await contentEditorService.CreateContentGroupAsync(newGroup);
|
||||
if (result != null)
|
||||
{
|
||||
var message = NotificationHelper.CreateNotificationMessage("Group created", 2, "Success", "ContentGroup created");
|
||||
ShowNotification(message);
|
||||
groups.Insert(0, newGroup);
|
||||
selectedGroupId = newGroup.Id;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ContentItemManagedCallback(string method, int itemId)
|
||||
{
|
||||
if (OnManageContentItemClicked != null)
|
||||
await OnManageContentItemClicked.Invoke(method, itemId);
|
||||
}
|
||||
|
||||
public void ShowNotification(NotificationMessage message)
|
||||
{
|
||||
notificationService.Notify(message);
|
||||
}
|
||||
|
||||
RenderFragment GetMessage()
|
||||
{
|
||||
return __builder =>
|
||||
{
|
||||
<text>
|
||||
Are <b>you</b> sure?
|
||||
</text>
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
|
||||
@attribute [Authorize]
|
||||
@using BLAIzor.Components.Layout
|
||||
@using BLAIzor.Models
|
||||
@using BLAIzor.Services
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@layout AdminLayout
|
||||
@inject ContentEditorService _contentEditorService
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject CustomAuthenticationStateProvider CustomAuthProvider
|
||||
@inject NotificationService notificationService
|
||||
@inject DialogService dialogService
|
||||
|
||||
|
||||
<EditForm OnValidSubmit="SaveSiteInfo" EditContext="@EditContext">
|
||||
<label>
|
||||
Brand name
|
||||
<InputText class="form-control my-3" id="siteName" @bind-Value="siteInfo.SiteName" />
|
||||
</label>
|
||||
<label>
|
||||
Site description
|
||||
<InputTextArea class="form-control my-3" style="height: 100px;" id="siteDescription" @bind-Value="siteInfo.SiteDescription" rows="3"/>
|
||||
</label>
|
||||
<label>
|
||||
Logo url
|
||||
<InputText class="form-control my-3" id="brandLogoUrl" @bind-Value="siteInfo.BrandLogoUrl" />
|
||||
</label>
|
||||
<label>
|
||||
Default color (Do not bother with this)
|
||||
<InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.DefaultColor" />
|
||||
</label>
|
||||
<label>
|
||||
The Entity behind this website (eg. "company", "professional sportsman", "worldwide brand", etc);
|
||||
<InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.Entity" />
|
||||
</label>
|
||||
<label>
|
||||
Your agent Persona
|
||||
<InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.Persona" />
|
||||
</label>
|
||||
<label>
|
||||
Your domain url
|
||||
<InputText class="form-control my-3" id="domainUrl" @bind-Value="siteInfo.DomainUrl" />
|
||||
</label>
|
||||
<label>
|
||||
Chosen template
|
||||
<InputNumber class="form-control my-3" id="templateId" @bind-Value="siteInfo.TemplateId" />
|
||||
</label>
|
||||
<label>
|
||||
Published
|
||||
<InputCheckbox class="my-3" id="ispublished" @bind-Value="siteInfo.IsPublished" />
|
||||
</label>
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
</EditForm>
|
||||
<button class="btn btn-warning" type="button" @onclick="() => Rechunk(siteInfo)">ReChunk Site</button>
|
||||
<hr />
|
||||
<h4 class="mt-4">Forms linked to this site</h4>
|
||||
|
||||
@if (siteInfo.FormDefinitions?.Count > 0)
|
||||
{
|
||||
<ul class="list-group mb-3">
|
||||
@foreach (var form in siteInfo.FormDefinitions)
|
||||
{
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<strong>@form.Title</strong><br />
|
||||
<small class="text-muted">@form.CreatedAt.ToString("yyyy-MM-dd HH:mm")</small>
|
||||
</span>
|
||||
<a class="btn btn-sm btn-outline-secondary" href="/form-editor/@form.Id">Edit</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted">No forms added yet.</p>
|
||||
}
|
||||
|
||||
<a class="btn btn-success" href="/createform/@SiteId">
|
||||
<i class="fas fa-plus-circle"></i> Add New Form
|
||||
</a>
|
||||
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public int SiteId { get; set; }
|
||||
|
||||
[Parameter] public Func<ContentGroup, Task> OnSiteInfoSaveClicked { get; set; }
|
||||
[Parameter] public Func<string, Task> OnSiteNameChanged { get; set; }
|
||||
[Parameter] public Func<string, int, Task> OnCancelItemClicked { get; set; }
|
||||
|
||||
private EditContext EditContext;
|
||||
private SiteInfo siteInfo = new();
|
||||
private string? userId;
|
||||
private string? userName;
|
||||
private AuthenticationState? authState;
|
||||
|
||||
|
||||
protected override Task OnInitializedAsync()
|
||||
{
|
||||
EditContext = new EditContext(siteInfo);
|
||||
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
|
||||
return base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
if (authState.User.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
userId = CustomAuthProvider.GetUserId();
|
||||
userName = CustomAuthProvider.GetUserName();
|
||||
}
|
||||
siteInfo = await _contentEditorService.GetSiteInfoWithFormsByIdAsync(SiteId) ?? new SiteInfo();
|
||||
}
|
||||
|
||||
private async Task SaveSiteInfo()
|
||||
{
|
||||
await _contentEditorService.UpdateSiteInfoAsync(siteInfo);
|
||||
}
|
||||
|
||||
private async void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
|
||||
{
|
||||
if (e.FieldIdentifier.FieldName == "SiteName")
|
||||
{
|
||||
//invoke SiteNameChanged
|
||||
if (OnSiteNameChanged != null)
|
||||
await OnSiteNameChanged.Invoke(siteInfo.SiteName);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Rechunk(SiteInfo site)
|
||||
{
|
||||
var confirmationResult = await dialogService.Confirm("Are you sure?", "Rechunk site", new ConfirmOptions() { OkButtonText = "Rechunk", CancelButtonText = "Oops, no" });
|
||||
if (confirmationResult == true)
|
||||
{
|
||||
await ReallyRechunk(site);
|
||||
}
|
||||
else
|
||||
{
|
||||
//do nothing?
|
||||
}
|
||||
|
||||
}
|
||||
private async Task ReallyRechunk(SiteInfo site)
|
||||
{
|
||||
await _contentEditorService.ForceRecreateQdrantCollectionAsync(site.Id);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
@using BLAIzor.Services
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@inject ContentEditorService ContentEditorService
|
||||
@inject ContentEditorAIService ContentEditorAIService
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject CustomAuthenticationStateProvider CustomAuthProvider
|
||||
|
||||
|
|
@ -72,7 +73,7 @@
|
|||
{
|
||||
Console.Write($"Subject: {Subject}");
|
||||
var prompt = $"Generate website content for a page titled '{MenuItem.MenuItem.Name}' of a '{Subject}' website. Please do not attach any explanation.";
|
||||
GeneratedContent = await ContentEditorService.GetGeneratedContentAsync(SessionId, prompt);
|
||||
GeneratedContent = await ContentEditorAIService.GetGeneratedContentAsync(SessionId, prompt);
|
||||
MenuItem.Content = GeneratedContent;
|
||||
await OnContentUpdated.InvokeAsync(MenuItem);
|
||||
}
|
||||
|
|
@ -93,7 +94,7 @@
|
|||
{
|
||||
Console.Write($"Subject: {Subject}");
|
||||
var prompt = $"This is the provided text: {WordFile}. Look for the relevant content for a page titled '{MenuItem.MenuItem.Name}' of a '{Subject}' website. Please do not attach any explanation.";
|
||||
GeneratedContent = await ContentEditorService.GetGeneratedContentAsync(SessionId, prompt);
|
||||
GeneratedContent = await ContentEditorAIService.GetGeneratedContentAsync(SessionId, prompt);
|
||||
MenuItem.Content = GeneratedContent;
|
||||
await OnContentUpdated.InvokeAsync(MenuItem);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
|
||||
@* <nav class="navbar navbar-expand-lg bg-body-tertiary" style="z-index: 5"> *@
|
||||
<nav class="navbar fixed-top" style="z-index: 5">
|
||||
<nav class="navbar fixed-top" style="z-index: 10005">
|
||||
@* <div class="container-fluid"> *@
|
||||
<div style="--bs-gutter-x: 1.5rem; --bs-gutter-y: 0; width: 100%; padding-right: calc(var(--bs-gutter-x) * .5); padding-left: calc(var(--bs-gutter-x) * .5); margin-right: auto; margin-left: auto;">
|
||||
@{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
@using BLAIzor.Models
|
||||
@using BLAIzor.Services
|
||||
@inject ScopedContentService _scopedContentService;
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject ContentEditorService _contentEditorService
|
||||
@inject IHttpContextAccessor HttpContextAccessor
|
||||
@inject IJSRuntime JS
|
||||
|
||||
<nav class="navbar fixed-top navbar-expand-lg" style="z-index: 10005">
|
||||
<div class="container-fluid">
|
||||
|
||||
<NavLink class="navbar-brand" @onclick="HomeClick">@BrandName</NavLink>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<i class="fa-solid fa-bars"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
@{
|
||||
if (Menu != null)
|
||||
{
|
||||
// foreach (var menuItem in Menu)
|
||||
// {
|
||||
|
||||
// <li class="nav-item">
|
||||
// <a class="nav-link" aria-current="page" @onclick="()=>MenuClickedAsync(menuItem.Name)">@menuItem.Name</a>
|
||||
// </li>
|
||||
// }
|
||||
|
||||
foreach (var menuItem in Menu.Where(m => m.ParentId == null).OrderBy(m => m.SortOrder))
|
||||
{
|
||||
if (menuItem.Children != null && menuItem.Children.Any())
|
||||
{
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" role="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
@menuItem.Name
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var child in menuItem.Children.OrderBy(c => c.SortOrder))
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="() => MenuClickedAsync(child.Name)">
|
||||
@child.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" @onclick="() => MenuClickedAsync(menuItem.Name)">
|
||||
@menuItem.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@* <li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Dropdown
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<li><a class="dropdown-item" href="#">Action</a></li>
|
||||
<li><a class="dropdown-item" href="#">Another action</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="#">Something else here</a></li>
|
||||
</ul>
|
||||
</li> *@
|
||||
|
||||
</ul>
|
||||
<select @onchange="OnLanguageSelected" class="form-select" style="width:100px">
|
||||
<option value="">Lang</option>
|
||||
@foreach (var Language in Languages)
|
||||
{
|
||||
<option value="@Language">@Language</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
@code {
|
||||
public int SiteId;
|
||||
[Parameter]
|
||||
public List<MenuItem> Menu { get; set; }
|
||||
[Parameter] public string? BrandName { get; set; } = "BLAIzor";
|
||||
//public string[] MenuList;
|
||||
public string videoUrl = "";
|
||||
public string SelectedLanguage;
|
||||
private int RenderCounter = 0;
|
||||
|
||||
//public static event Action<string>? OnMenuClicked;
|
||||
[Parameter] public Action<string>? OnMenuClicked { get; set; }
|
||||
|
||||
public List<string> Languages = new List<string>
|
||||
{
|
||||
"Hungarian", "English", "German"
|
||||
};
|
||||
|
||||
public async Task MenuClickedAsync(string menuName)
|
||||
{
|
||||
OnMenuClicked?.Invoke(menuName);
|
||||
await JS.InvokeVoidAsync("collapseNavbar");
|
||||
}
|
||||
|
||||
public void HomeClick()
|
||||
{
|
||||
_navigationManager.Refresh(true);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var lang = await JS.InvokeAsync<string>("getUserLanguage");
|
||||
|
||||
// Normalize and match to one of your supported languages
|
||||
if (lang.StartsWith("hu", StringComparison.OrdinalIgnoreCase))
|
||||
SelectedLanguage = "Hungarian";
|
||||
else if (lang.StartsWith("de", StringComparison.OrdinalIgnoreCase))
|
||||
SelectedLanguage = "German";
|
||||
else
|
||||
SelectedLanguage = "English";
|
||||
|
||||
_scopedContentService.SelectedLanguage = SelectedLanguage;
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (Menu != null)
|
||||
{
|
||||
//
|
||||
}
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
public void OnLanguageSelected(ChangeEventArgs e)
|
||||
{
|
||||
SelectedLanguage = e.Value?.ToString();
|
||||
|
||||
if (!string.IsNullOrEmpty(SelectedLanguage))
|
||||
{
|
||||
Console.WriteLine($"Language selected: {SelectedLanguage}");
|
||||
_scopedContentService.SelectedLanguage = SelectedLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<style>
|
||||
@using BLAIzor.Models
|
||||
<style>
|
||||
.parent-element-to-video {
|
||||
overflow: hidden;
|
||||
width: 120vw;
|
||||
|
|
@ -16,8 +17,8 @@
|
|||
</style>
|
||||
|
||||
<div class="parent-element-to-video">
|
||||
<video style="opacity:0.3; z-index: -1" autoplay muted loop id="myVideo" @key="SelectedBrandName">
|
||||
<source src="@("/video/" + SelectedBrandName + ".mp4")" type="video/mp4">
|
||||
<video style="opacity:0.3; z-index: -1" autoplay muted loop id="myVideo" @key="site.BackgroundVideo">
|
||||
<source src="@(site.BackgroundVideo)" type="video/mp4">
|
||||
</video>
|
||||
</div>
|
||||
|
||||
|
|
@ -34,5 +35,5 @@
|
|||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string SelectedBrandName { get; set; } = "default";
|
||||
public SiteInfo site { get; set; } = new SiteInfo();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,14 @@ namespace BLAIzor.Data
|
|||
public DbSet<SiteInfo> SiteInfos { get; set; }
|
||||
public DbSet<MenuItem> MenuItems { get; set; } // Add MenuItem DbSet
|
||||
public DbSet<DesignTemplate> DesignTemplates { get; set; }
|
||||
|
||||
public DbSet<CssTemplate> CssTemplates { get; set; }
|
||||
|
||||
public DbSet<ContentGroup> ContentGroups { get; set; }
|
||||
public DbSet<ContentItem> ContentItems { get; set; }
|
||||
public DbSet<ContentChunk> ContentChunks { get; set; }
|
||||
public DbSet<FormDefinition> FormDefinitions { get; set; }
|
||||
public DbSet<AppLog> Logs { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
|
|
@ -90,6 +94,36 @@ namespace BLAIzor.Data
|
|||
.HasForeignKey(m => m.SiteInfoId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<MenuItem>()
|
||||
.HasOne(m => m.ContentGroup)
|
||||
.WithMany()
|
||||
.HasForeignKey(m => m.ContentGroupId)
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
modelBuilder.Entity<MenuItem>()
|
||||
.HasOne(m => m.ContentItem)
|
||||
.WithMany()
|
||||
.HasForeignKey(m => m.ContentItemId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
modelBuilder.Entity<MenuItem>()
|
||||
.HasOne(m => m.Parent)
|
||||
.WithMany(p => p.Children)
|
||||
.HasForeignKey(m => m.ParentId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
modelBuilder.Entity<ContentItem>()
|
||||
.HasOne(ci => ci.ContentGroup)
|
||||
.WithMany(cg => cg.Items)
|
||||
.HasForeignKey(ci => ci.ContentGroupId)
|
||||
.OnDelete(DeleteBehavior.Cascade); // When a ContentGroup is deleted, delete its ContentItems
|
||||
|
||||
modelBuilder.Entity<ContentChunk>()
|
||||
.HasOne(cc => cc.ContentItem)
|
||||
.WithMany(ci => ci.Chunks)
|
||||
.HasForeignKey(cc => cc.ContentItemId)
|
||||
.OnDelete(DeleteBehavior.Cascade); // When a ContentItem is deleted, delete its Chunks
|
||||
|
||||
modelBuilder.Entity<DesignTemplate>()
|
||||
.HasOne(s => s.User)
|
||||
.WithMany()
|
||||
|
|
@ -103,10 +137,10 @@ namespace BLAIzor.Data
|
|||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<FormDefinition>()
|
||||
.HasOne(fd => fd.SiteInfo)
|
||||
.WithMany(s => s.FormDefinitions)
|
||||
.HasForeignKey(fd => fd.SiteInfoId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.HasOne(fd => fd.SiteInfo)
|
||||
.WithMany(s => s.FormDefinitions)
|
||||
.HasForeignKey(fd => fd.SiteInfoId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<FormDefinition>()
|
||||
.HasIndex(fd => new { fd.SiteInfoId, fd.Slug })
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
using BLAIzor.Models;
|
||||
using System.Text;
|
||||
|
||||
namespace BLAIzor.Helpers
|
||||
{
|
||||
public static class ChunkingHelper
|
||||
{
|
||||
private static readonly string[] SectionDelimiters =
|
||||
{
|
||||
"\n##", "\n###", "\nSection:", "\nTopic:", "\nTitle:", "\n---", "[[CHUNK_BREAK]]", "### Logical Break ###"
|
||||
};
|
||||
|
||||
public static List<string> SplitStructuredText(string content, int maxChunkCharLength = 3000)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
return new List<string>();
|
||||
|
||||
// Normalize newlines
|
||||
content = content.Replace("\r\n", "\n").Trim();
|
||||
|
||||
// Step 1: Split by known logical boundaries
|
||||
var logicalParts = SplitByDelimiters(content, SectionDelimiters);
|
||||
|
||||
// Step 2: Recombine smaller logical parts into full-size chunks
|
||||
var chunks = new List<string>();
|
||||
var currentChunk = new StringBuilder();
|
||||
|
||||
foreach (var part in logicalParts)
|
||||
{
|
||||
if (currentChunk.Length + part.Length + 2 <= maxChunkCharLength)
|
||||
{
|
||||
currentChunk.AppendLine(part);
|
||||
currentChunk.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentChunk.Length > 0)
|
||||
{
|
||||
chunks.Add(currentChunk.ToString().Trim());
|
||||
currentChunk.Clear();
|
||||
}
|
||||
|
||||
if (part.Length > maxChunkCharLength)
|
||||
{
|
||||
chunks.AddRange(SplitLongParagraph(part, maxChunkCharLength));
|
||||
}
|
||||
else
|
||||
{
|
||||
currentChunk.AppendLine(part);
|
||||
currentChunk.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentChunk.Length > 0)
|
||||
chunks.Add(currentChunk.ToString().Trim());
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
private static List<string> SplitByDelimiters(string content, string[] delimiters)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
var remaining = content;
|
||||
|
||||
foreach (var delimiter in delimiters)
|
||||
{
|
||||
remaining = remaining.Replace(delimiter, "\n[[SECTION_BREAK]]");
|
||||
}
|
||||
|
||||
return remaining.Split(new[] { "[[SECTION_BREAK]]" }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => s.Trim())
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static List<string> SplitLongParagraph(string text, int maxLength)
|
||||
{
|
||||
var chunks = new List<string>();
|
||||
int index = 0;
|
||||
while (index < text.Length)
|
||||
{
|
||||
int len = Math.Min(maxLength, text.Length - index);
|
||||
chunks.Add(text.Substring(index, len));
|
||||
index += len;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Qdrant-based chunks match the original ContentItem text.
|
||||
/// </summary>
|
||||
/// <param name="item">The ContentItem to check.</param>
|
||||
/// <param name="chunks">The SQL chunks that reference Qdrant IDs.</param>
|
||||
/// <param name="vectorData">All related WebPageContent objects pulled from Qdrant.</param>
|
||||
public static bool IsChunkingConsistent(
|
||||
ContentItem item,
|
||||
List<ContentChunk> chunks,
|
||||
List<WebPageContent> vectorData)
|
||||
{
|
||||
if (item == null || string.IsNullOrWhiteSpace(item.Content) || chunks == null || vectorData == null)
|
||||
return false;
|
||||
|
||||
var orderedChunks = chunks.OrderBy(c => c.ChunkIndex).ToList();
|
||||
|
||||
var combinedText = string.Join("",
|
||||
orderedChunks.Select(chunk =>
|
||||
vectorData.FirstOrDefault(v => v.UId == chunk.QdrantPointId)?.Content?.Trim() ?? ""
|
||||
));
|
||||
|
||||
var original = NormalizeText(item.Content);
|
||||
var reassembled = NormalizeText(combinedText);
|
||||
|
||||
return original == reassembled;
|
||||
}
|
||||
|
||||
private static string NormalizeText(string text)
|
||||
{
|
||||
return text.Replace("\r", "")
|
||||
.Replace("\n", "")
|
||||
.Replace(" ", "")
|
||||
.Trim()
|
||||
.ToLowerInvariant();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using Radzen;
|
||||
|
||||
public static class NotificationHelper
|
||||
{
|
||||
// Special character replacement map
|
||||
public static NotificationMessage CreateNotificationMessage(string messageText, int severity, string summary, string detail, int duration = 4000, bool showProgress = true, Action<Radzen.NotificationMessage> callBack = null)
|
||||
{
|
||||
NotificationSeverity messageSeverity;
|
||||
switch (severity)
|
||||
{
|
||||
case 0:
|
||||
messageSeverity = NotificationSeverity.Error;
|
||||
break;
|
||||
case 1:
|
||||
messageSeverity = NotificationSeverity.Info;
|
||||
break;
|
||||
case 2:
|
||||
messageSeverity = NotificationSeverity.Success;
|
||||
break;
|
||||
case 3:
|
||||
messageSeverity = NotificationSeverity.Warning;
|
||||
break;
|
||||
default:
|
||||
messageSeverity = NotificationSeverity.Info;
|
||||
break;
|
||||
}
|
||||
|
||||
var message = new NotificationMessage
|
||||
{
|
||||
//error 0, info 1, success 2, warning 3
|
||||
|
||||
Severity = messageSeverity,
|
||||
Summary = summary,
|
||||
Detail = detail,
|
||||
Duration = duration,
|
||||
ShowProgress = showProgress,
|
||||
Click = callBack,
|
||||
CloseOnClick = true,
|
||||
Payload = DateTime.Now
|
||||
};
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -150,6 +150,10 @@ public static class TextHelper
|
|||
{
|
||||
aiResponse = aiResponse.Substring(4);
|
||||
}
|
||||
if (aiResponse.StartsWith("html"))
|
||||
{
|
||||
aiResponse = aiResponse.Substring(4);
|
||||
}
|
||||
aiResponse = aiResponse.Substring(0, aiResponse.Length - 3);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace BLAIzor.Helpers
|
||||
{
|
||||
|
||||
public static class VectorHashHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes a SHA-256 hash for a float vector and returns it as a Base64 string.
|
||||
/// </summary>
|
||||
public static string ComputeVectorHash(float[] vector)
|
||||
{
|
||||
if (vector == null || vector.Length == 0)
|
||||
throw new ArgumentException("Vector cannot be null or empty.");
|
||||
|
||||
// Convert float array to byte array
|
||||
byte[] byteArray = new byte[vector.Length * sizeof(float)];
|
||||
Buffer.BlockCopy(vector, 0, byteArray, 0, byteArray.Length);
|
||||
|
||||
using (var sha = SHA256.Create())
|
||||
{
|
||||
byte[] hashBytes = sha.ComputeHash(byteArray);
|
||||
return Convert.ToBase64String(hashBytes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two float vectors by computing and comparing their SHA-256 hashes.
|
||||
/// </summary>
|
||||
public static bool AreVectorsEqual(float[] vector1, float[] vector2)
|
||||
{
|
||||
if (vector1 == null || vector2 == null)
|
||||
return false;
|
||||
|
||||
if (vector1.Length != vector2.Length)
|
||||
return false;
|
||||
|
||||
string hash1 = ComputeVectorHash(vector1);
|
||||
string hash2 = ComputeVectorHash(vector2);
|
||||
|
||||
return hash1 == hash2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,841 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using BLAIzor.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20250617221435_CollectionAdministrationUpdate")]
|
||||
partial class CollectionAdministrationUpdate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ChunkIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("QdrantPointId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VectorHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.ToTable("ContentChunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("EmbeddingModel")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("VectorSize")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("ContentGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.ToTable("ContentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("CssContent")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("DesignTemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DesignTemplateId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("CssTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsDeprecated")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPrivate")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("QDrandCollectionName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplateName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplatePhotoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("DesignTemplates");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
CreatedAt = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
|
||||
Description = "The default template",
|
||||
IsDeprecated = false,
|
||||
IsPrivate = false,
|
||||
IsPublished = false,
|
||||
QDrandCollectionName = "html_snippets",
|
||||
Status = "Draft",
|
||||
Tags = "system",
|
||||
TemplateName = "Default Site",
|
||||
TemplatePhotoUrl = "/images/default-logo.png",
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
Version = 1
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("JsonDefinition")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId", "Slug")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("FormDefinitions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int?>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("ParentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("ShowInMainMenu")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("StoredHtml")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("MenuItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("BackgroundVideo")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("BrandLogoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultColor")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultLanguage")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DomainUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("EmbeddingService")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Persona")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("STTActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SiteDescription")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SiteName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TTSActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int?>("TemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("VoiceId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TemplateId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("SiteInfos");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
BrandLogoUrl = "/images/default-logo.png",
|
||||
DefaultColor = "#FFFFFF",
|
||||
DefaultUrl = "https://ai.poppixel.cloud",
|
||||
DomainUrl = "poppixel.cloud",
|
||||
IsPublished = false,
|
||||
STTActive = false,
|
||||
SiteName = "Default Site",
|
||||
TTSActive = false,
|
||||
TemplateId = 1,
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
AccessFailedCount = 0,
|
||||
ConcurrencyStamp = "a2836246-0303-4370-b283-e53a9a3f2813",
|
||||
Email = "adam.g@aycode.com",
|
||||
EmailConfirmed = true,
|
||||
LockoutEnabled = false,
|
||||
NormalizedEmail = "ADAM.G@AYCODE.COM",
|
||||
NormalizedUserName = "ADAM.G@AYCODE.COM",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAEChxKCu+ReGvcZFR/6kPASbpnQdMp1MJuepeRyR4bfHTkUk8SfNAqmckGXvuw+GaGA==",
|
||||
PhoneNumberConfirmed = false,
|
||||
SecurityStamp = "7ecf121a-b0e7-4e30-a1f1-299eeaf0a9cc",
|
||||
TwoFactorEnabled = false,
|
||||
UserName = "adam.g@aycode.com"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany("Chunks")
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany()
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
|
||||
.WithOne("CssTemplate")
|
||||
.HasForeignKey("BLAIzor.Models.CssTemplate", "DesignTemplateId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DesignTemplate");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("FormDefinitions")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
b.HasOne("BLAIzor.Models.MenuItem", "Parent")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("ParentId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("MenuItems")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
|
||||
b.Navigation("Parent");
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "Template")
|
||||
.WithMany("Sites")
|
||||
.HasForeignKey("TemplateId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Template");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Navigation("Chunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Navigation("CssTemplate")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Sites");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Navigation("FormDefinitions");
|
||||
|
||||
b.Navigation("MenuItems");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CollectionAdministrationUpdate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PointId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "QdrantPointId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "ContentGroupId",
|
||||
table: "MenuItems",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "ParentId",
|
||||
table: "MenuItems",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Slug",
|
||||
table: "MenuItems",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ContentGroups",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
SiteInfoId = table.Column<int>(type: "int", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Slug = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Type = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
VectorSize = table.Column<int>(type: "int", nullable: false),
|
||||
EmbeddingModel = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Version = table.Column<int>(type: "int", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
LastUpdated = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ContentGroups", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ContentGroups_SiteInfos_SiteInfoId",
|
||||
column: x => x.SiteInfoId,
|
||||
principalTable: "SiteInfos",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ContentItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
ContentGroupId = table.Column<int>(type: "int", nullable: false),
|
||||
Title = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Description = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Content = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Language = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Tags = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
IsPublished = table.Column<bool>(type: "bit", nullable: false),
|
||||
Version = table.Column<int>(type: "int", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
LastUpdated = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ContentItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ContentItems_ContentGroups_ContentGroupId",
|
||||
column: x => x.ContentGroupId,
|
||||
principalTable: "ContentGroups",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ContentChunks",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
ContentItemId = table.Column<int>(type: "int", nullable: false),
|
||||
QdrantPointId = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
ChunkIndex = table.Column<int>(type: "int", nullable: false),
|
||||
VectorHash = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ContentChunks", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ContentChunks_ContentItems_ContentItemId",
|
||||
column: x => x.ContentItemId,
|
||||
principalTable: "ContentItems",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MenuItems_ContentGroupId",
|
||||
table: "MenuItems",
|
||||
column: "ContentGroupId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MenuItems_ParentId",
|
||||
table: "MenuItems",
|
||||
column: "ParentId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ContentChunks_ContentItemId",
|
||||
table: "ContentChunks",
|
||||
column: "ContentItemId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ContentGroups_SiteInfoId",
|
||||
table: "ContentGroups",
|
||||
column: "SiteInfoId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ContentItems_ContentGroupId",
|
||||
table: "ContentItems",
|
||||
column: "ContentGroupId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MenuItems_ContentGroups_ContentGroupId",
|
||||
table: "MenuItems",
|
||||
column: "ContentGroupId",
|
||||
principalTable: "ContentGroups",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.NoAction);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MenuItems_MenuItems_ParentId",
|
||||
table: "MenuItems",
|
||||
column: "ParentId",
|
||||
principalTable: "MenuItems",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MenuItems_ContentGroups_ContentGroupId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MenuItems_MenuItems_ParentId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ContentChunks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ContentItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ContentGroups");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MenuItems_ContentGroupId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MenuItems_ParentId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ContentGroupId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ParentId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Slug",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PointId",
|
||||
table: "MenuItems",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "QdrantPointId",
|
||||
table: "MenuItems",
|
||||
type: "uniqueidentifier",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,841 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using BLAIzor.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20250617224307_CollectionAdministrationUpdateFIX")]
|
||||
partial class CollectionAdministrationUpdateFIX
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ChunkIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("QdrantPointId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VectorHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.ToTable("ContentChunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("EmbeddingModel")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("VectorSize")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("ContentGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.ToTable("ContentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("CssContent")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("DesignTemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DesignTemplateId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("CssTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsDeprecated")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPrivate")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("QDrandCollectionName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplateName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplatePhotoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("DesignTemplates");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
CreatedAt = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
|
||||
Description = "The default template",
|
||||
IsDeprecated = false,
|
||||
IsPrivate = false,
|
||||
IsPublished = false,
|
||||
QDrandCollectionName = "html_snippets",
|
||||
Status = "Draft",
|
||||
Tags = "system",
|
||||
TemplateName = "Default Site",
|
||||
TemplatePhotoUrl = "/images/default-logo.png",
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
Version = 1
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("JsonDefinition")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId", "Slug")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("FormDefinitions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int?>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("ParentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("ShowInMainMenu")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("StoredHtml")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("MenuItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("BackgroundVideo")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("BrandLogoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultColor")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultLanguage")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DomainUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("EmbeddingService")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Persona")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("STTActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SiteDescription")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SiteName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TTSActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int?>("TemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("VoiceId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TemplateId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("SiteInfos");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
BrandLogoUrl = "/images/default-logo.png",
|
||||
DefaultColor = "#FFFFFF",
|
||||
DefaultUrl = "https://ai.poppixel.cloud",
|
||||
DomainUrl = "poppixel.cloud",
|
||||
IsPublished = false,
|
||||
STTActive = false,
|
||||
SiteName = "Default Site",
|
||||
TTSActive = false,
|
||||
TemplateId = 1,
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
AccessFailedCount = 0,
|
||||
ConcurrencyStamp = "a2836246-0303-4370-b283-e53a9a3f2813",
|
||||
Email = "adam.g@aycode.com",
|
||||
EmailConfirmed = true,
|
||||
LockoutEnabled = false,
|
||||
NormalizedEmail = "ADAM.G@AYCODE.COM",
|
||||
NormalizedUserName = "ADAM.G@AYCODE.COM",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAEChxKCu+ReGvcZFR/6kPASbpnQdMp1MJuepeRyR4bfHTkUk8SfNAqmckGXvuw+GaGA==",
|
||||
PhoneNumberConfirmed = false,
|
||||
SecurityStamp = "7ecf121a-b0e7-4e30-a1f1-299eeaf0a9cc",
|
||||
TwoFactorEnabled = false,
|
||||
UserName = "adam.g@aycode.com"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany("Chunks")
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany()
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
|
||||
.WithOne("CssTemplate")
|
||||
.HasForeignKey("BLAIzor.Models.CssTemplate", "DesignTemplateId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DesignTemplate");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("FormDefinitions")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
b.HasOne("BLAIzor.Models.MenuItem", "Parent")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("ParentId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("MenuItems")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
|
||||
b.Navigation("Parent");
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "Template")
|
||||
.WithMany("Sites")
|
||||
.HasForeignKey("TemplateId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Template");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Navigation("Chunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Navigation("CssTemplate")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Sites");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Navigation("FormDefinitions");
|
||||
|
||||
b.Navigation("MenuItems");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CollectionAdministrationUpdateFIX : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MenuItems_ContentGroups_ContentGroupId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MenuItems_ContentGroups_ContentGroupId",
|
||||
table: "MenuItems",
|
||||
column: "ContentGroupId",
|
||||
principalTable: "ContentGroups",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MenuItems_ContentGroups_ContentGroupId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MenuItems_ContentGroups_ContentGroupId",
|
||||
table: "MenuItems",
|
||||
column: "ContentGroupId",
|
||||
principalTable: "ContentGroups",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.NoAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,855 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using BLAIzor.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20250618105221_CollectionAdministrationUpdate2")]
|
||||
partial class CollectionAdministrationUpdate2
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ChunkIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("QdrantPointId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VectorHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.ToTable("ContentChunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("EmbeddingModel")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("VectorSize")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("ContentGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.ToTable("ContentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("CssContent")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("DesignTemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DesignTemplateId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("CssTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsDeprecated")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPrivate")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("QDrandCollectionName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplateName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplatePhotoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("DesignTemplates");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
CreatedAt = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
|
||||
Description = "The default template",
|
||||
IsDeprecated = false,
|
||||
IsPrivate = false,
|
||||
IsPublished = false,
|
||||
QDrandCollectionName = "html_snippets",
|
||||
Status = "Draft",
|
||||
Tags = "system",
|
||||
TemplateName = "Default Site",
|
||||
TemplatePhotoUrl = "/images/default-logo.png",
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
Version = 1
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("JsonDefinition")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId", "Slug")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("FormDefinitions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int?>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("ParentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("ShowInMainMenu")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("StoredHtml")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("MenuItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("BackgroundVideo")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("BrandLogoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultColor")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultLanguage")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DomainUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("EmbeddingService")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Persona")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("STTActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SiteDescription")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SiteName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TTSActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int?>("TemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("VoiceId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TemplateId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("SiteInfos");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
BrandLogoUrl = "/images/default-logo.png",
|
||||
DefaultColor = "#FFFFFF",
|
||||
DefaultUrl = "https://ai.poppixel.cloud",
|
||||
DomainUrl = "poppixel.cloud",
|
||||
IsPublished = false,
|
||||
STTActive = false,
|
||||
SiteName = "Default Site",
|
||||
TTSActive = false,
|
||||
TemplateId = 1,
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
AccessFailedCount = 0,
|
||||
ConcurrencyStamp = "a2836246-0303-4370-b283-e53a9a3f2813",
|
||||
Email = "adam.g@aycode.com",
|
||||
EmailConfirmed = true,
|
||||
LockoutEnabled = false,
|
||||
NormalizedEmail = "ADAM.G@AYCODE.COM",
|
||||
NormalizedUserName = "ADAM.G@AYCODE.COM",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAEChxKCu+ReGvcZFR/6kPASbpnQdMp1MJuepeRyR4bfHTkUk8SfNAqmckGXvuw+GaGA==",
|
||||
PhoneNumberConfirmed = false,
|
||||
SecurityStamp = "7ecf121a-b0e7-4e30-a1f1-299eeaf0a9cc",
|
||||
TwoFactorEnabled = false,
|
||||
UserName = "adam.g@aycode.com"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany("Chunks")
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("ContentGroups")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
|
||||
.WithOne("CssTemplate")
|
||||
.HasForeignKey("BLAIzor.Models.CssTemplate", "DesignTemplateId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DesignTemplate");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("FormDefinitions")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.MenuItem", "Parent")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("ParentId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("MenuItems")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
|
||||
b.Navigation("Parent");
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "Template")
|
||||
.WithMany("Sites")
|
||||
.HasForeignKey("TemplateId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Template");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Navigation("Chunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Navigation("CssTemplate")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Sites");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Navigation("ContentGroups");
|
||||
|
||||
b.Navigation("FormDefinitions");
|
||||
|
||||
b.Navigation("MenuItems");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CollectionAdministrationUpdate2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "ContentItemId",
|
||||
table: "MenuItems",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MenuItems_ContentItemId",
|
||||
table: "MenuItems",
|
||||
column: "ContentItemId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MenuItems_ContentItems_ContentItemId",
|
||||
table: "MenuItems",
|
||||
column: "ContentItemId",
|
||||
principalTable: "ContentItems",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MenuItems_ContentItems_ContentItemId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MenuItems_ContentItemId",
|
||||
table: "MenuItems");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ContentItemId",
|
||||
table: "MenuItems");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,858 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using BLAIzor.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20250625185236_StoredCollectionName")]
|
||||
partial class StoredCollectionName
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ChunkIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("QdrantPointId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VectorHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.ToTable("ContentChunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("EmbeddingModel")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("VectorSize")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("ContentGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.ToTable("ContentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("CssContent")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("DesignTemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DesignTemplateId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("CssTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsDeprecated")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPrivate")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("QDrandCollectionName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplateName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplatePhotoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("DesignTemplates");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
CreatedAt = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
|
||||
Description = "The default template",
|
||||
IsDeprecated = false,
|
||||
IsPrivate = false,
|
||||
IsPublished = false,
|
||||
QDrandCollectionName = "html_snippets",
|
||||
Status = "Draft",
|
||||
Tags = "system",
|
||||
TemplateName = "Default Site",
|
||||
TemplatePhotoUrl = "/images/default-logo.png",
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
Version = 1
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("JsonDefinition")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId", "Slug")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("FormDefinitions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int?>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("ParentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("ShowInMainMenu")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("StoredHtml")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("MenuItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("BackgroundVideo")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("BrandLogoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultColor")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultLanguage")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DomainUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("EmbeddingService")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Persona")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("STTActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SiteDescription")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SiteName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TTSActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int?>("TemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("VectorCollectionName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VoiceId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TemplateId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("SiteInfos");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
BrandLogoUrl = "/images/default-logo.png",
|
||||
DefaultColor = "#FFFFFF",
|
||||
DefaultUrl = "https://ai.poppixel.cloud",
|
||||
DomainUrl = "poppixel.cloud",
|
||||
IsPublished = false,
|
||||
STTActive = false,
|
||||
SiteName = "Default Site",
|
||||
TTSActive = false,
|
||||
TemplateId = 1,
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
AccessFailedCount = 0,
|
||||
ConcurrencyStamp = "a2836246-0303-4370-b283-e53a9a3f2813",
|
||||
Email = "adam.g@aycode.com",
|
||||
EmailConfirmed = true,
|
||||
LockoutEnabled = false,
|
||||
NormalizedEmail = "ADAM.G@AYCODE.COM",
|
||||
NormalizedUserName = "ADAM.G@AYCODE.COM",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAEChxKCu+ReGvcZFR/6kPASbpnQdMp1MJuepeRyR4bfHTkUk8SfNAqmckGXvuw+GaGA==",
|
||||
PhoneNumberConfirmed = false,
|
||||
SecurityStamp = "7ecf121a-b0e7-4e30-a1f1-299eeaf0a9cc",
|
||||
TwoFactorEnabled = false,
|
||||
UserName = "adam.g@aycode.com"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany("Chunks")
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("ContentGroups")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
|
||||
.WithOne("CssTemplate")
|
||||
.HasForeignKey("BLAIzor.Models.CssTemplate", "DesignTemplateId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DesignTemplate");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("FormDefinitions")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.MenuItem", "Parent")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("ParentId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("MenuItems")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
|
||||
b.Navigation("Parent");
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "Template")
|
||||
.WithMany("Sites")
|
||||
.HasForeignKey("TemplateId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Template");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Navigation("Chunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Navigation("CssTemplate")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Sites");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Navigation("ContentGroups");
|
||||
|
||||
b.Navigation("FormDefinitions");
|
||||
|
||||
b.Navigation("MenuItems");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class StoredCollectionName : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "VectorCollectionName",
|
||||
table: "SiteInfos",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "SiteInfos",
|
||||
keyColumn: "Id",
|
||||
keyValue: 1,
|
||||
column: "VectorCollectionName",
|
||||
value: null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "VectorCollectionName",
|
||||
table: "SiteInfos");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,885 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using BLAIzor.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20250628191216_logger")]
|
||||
partial class logger
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.AppLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Details")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Severity")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Logs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ChunkIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("QdrantPointId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VectorHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.ToTable("ContentChunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("EmbeddingModel")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("VectorSize")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("ContentGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.ToTable("ContentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("CssContent")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("DesignTemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DesignTemplateId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("CssTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsDeprecated")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPrivate")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("QDrandCollectionName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplateName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("TemplatePhotoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("DesignTemplates");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
CreatedAt = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
|
||||
Description = "The default template",
|
||||
IsDeprecated = false,
|
||||
IsPrivate = false,
|
||||
IsPublished = false,
|
||||
QDrandCollectionName = "html_snippets",
|
||||
Status = "Draft",
|
||||
Tags = "system",
|
||||
TemplateName = "Default Site",
|
||||
TemplatePhotoUrl = "/images/default-logo.png",
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
Version = 1
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("JsonDefinition")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId", "Slug")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("FormDefinitions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int?>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("ParentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("ShowInMainMenu")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("StoredHtml")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("MenuItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("BackgroundVideo")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("BrandLogoUrl")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultColor")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultLanguage")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DefaultUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("DomainUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("EmbeddingService")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Entity")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Persona")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("STTActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SiteDescription")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SiteName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TTSActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int?>("TemplateId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("VectorCollectionName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VoiceId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TemplateId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("SiteInfos");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
BrandLogoUrl = "/images/default-logo.png",
|
||||
DefaultColor = "#FFFFFF",
|
||||
DefaultUrl = "https://ai.poppixel.cloud",
|
||||
DomainUrl = "poppixel.cloud",
|
||||
IsPublished = false,
|
||||
STTActive = false,
|
||||
SiteName = "Default Site",
|
||||
TTSActive = false,
|
||||
TemplateId = 1,
|
||||
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
|
||||
AccessFailedCount = 0,
|
||||
ConcurrencyStamp = "a2836246-0303-4370-b283-e53a9a3f2813",
|
||||
Email = "adam.g@aycode.com",
|
||||
EmailConfirmed = true,
|
||||
LockoutEnabled = false,
|
||||
NormalizedEmail = "ADAM.G@AYCODE.COM",
|
||||
NormalizedUserName = "ADAM.G@AYCODE.COM",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAEChxKCu+ReGvcZFR/6kPASbpnQdMp1MJuepeRyR4bfHTkUk8SfNAqmckGXvuw+GaGA==",
|
||||
PhoneNumberConfirmed = false,
|
||||
SecurityStamp = "7ecf121a-b0e7-4e30-a1f1-299eeaf0a9cc",
|
||||
TwoFactorEnabled = false,
|
||||
UserName = "adam.g@aycode.com"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany("Chunks")
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("ContentGroups")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
|
||||
.WithOne("CssTemplate")
|
||||
.HasForeignKey("BLAIzor.Models.CssTemplate", "DesignTemplateId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("DesignTemplate");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("FormDefinitions")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.MenuItem", "Parent")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("ParentId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("MenuItems")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
|
||||
b.Navigation("Parent");
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "Template")
|
||||
.WithMany("Sites")
|
||||
.HasForeignKey("TemplateId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Template");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Navigation("Chunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Navigation("CssTemplate")
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Sites");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Navigation("ContentGroups");
|
||||
|
||||
b.Navigation("FormDefinitions");
|
||||
|
||||
b.Navigation("MenuItems");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BLAIzor.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class logger : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Logs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Timestamp = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
Severity = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Message = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Details = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Logs", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,160 @@ namespace BLAIzor.Migrations
|
|||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.AppLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Details")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Severity")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Logs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ChunkIndex")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("QdrantPointId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VectorHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.ToTable("ContentChunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("EmbeddingModel")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("VectorSize")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("ContentGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsPublished")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Version")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.ToTable("ContentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -173,22 +327,28 @@ namespace BLAIzor.Migrations
|
|||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int?>("ContentGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("ContentItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("PointId")
|
||||
b.Property<int?>("ParentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid?>("QdrantPointId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("ShowInMainMenu")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("SiteInfoId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
|
@ -197,6 +357,12 @@ namespace BLAIzor.Migrations
|
|||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ContentGroupId");
|
||||
|
||||
b.HasIndex("ContentItemId");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.HasIndex("SiteInfoId");
|
||||
|
||||
b.ToTable("MenuItems");
|
||||
|
|
@ -261,6 +427,9 @@ namespace BLAIzor.Migrations
|
|||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("VectorCollectionName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("VoiceId")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
|
|
@ -509,6 +678,39 @@ namespace BLAIzor.Migrations
|
|||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentChunk", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany("Chunks")
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("ContentGroups")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
|
||||
|
|
@ -544,12 +746,33 @@ namespace BLAIzor.Migrations
|
|||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.HasOne("BLAIzor.Models.ContentGroup", "ContentGroup")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentGroupId")
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
b.HasOne("BLAIzor.Models.ContentItem", "ContentItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentItemId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.MenuItem", "Parent")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("ParentId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
|
||||
.WithMany("MenuItems")
|
||||
.HasForeignKey("SiteInfoId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ContentGroup");
|
||||
|
||||
b.Navigation("ContentItem");
|
||||
|
||||
b.Navigation("Parent");
|
||||
|
||||
b.Navigation("SiteInfo");
|
||||
});
|
||||
|
||||
|
|
@ -622,6 +845,16 @@ namespace BLAIzor.Migrations
|
|||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentGroup", b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.ContentItem", b =>
|
||||
{
|
||||
b.Navigation("Chunks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
|
||||
{
|
||||
b.Navigation("CssTemplate")
|
||||
|
|
@ -630,8 +863,15 @@ namespace BLAIzor.Migrations
|
|||
b.Navigation("Sites");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
|
||||
{
|
||||
b.Navigation("ContentGroups");
|
||||
|
||||
b.Navigation("FormDefinitions");
|
||||
|
||||
b.Navigation("MenuItems");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,424 @@
|
|||
using BLAIzor.Services;
|
||||
using System.Text;
|
||||
|
||||
namespace BLAIzor.Models
|
||||
{
|
||||
public static class AiPrompts
|
||||
{
|
||||
public static class WelcomeContent
|
||||
{
|
||||
public static string GetSystemMessageForWelcomeMessage(string mood, string siteEntity, string extractedText, string selectedBrandName, string language, string menuList)
|
||||
{
|
||||
string systemMessage = "You are a helpful, " + mood + " assistant that welcomes the user speaking in the name of the " + siteEntity + " described by the content, on a website of " + selectedBrandName + " in " + language + ". Use the following content: `" +
|
||||
extractedText + "` " +
|
||||
//"and generate a short" +Mood+ "but kind marketing-oriented welcome message and introduction of the brand for the user, constructed as simple Bootstrap HTML codeblock with a <h1> tagged title and a paragraph." +
|
||||
"and generate a" + mood + " marketing-oriented welcome message and a summary of the content and introduction " +
|
||||
"of the brand for the user in the name of " + siteEntity + " , aiming to explain clearly, what does the company/person offer, constructed " +
|
||||
"as simple Bootstrap HTML <div clss=\"container\"> codeblock with a <h1> tagged title and a paragraph." +
|
||||
"If there is any logo, or not logo but main brand image in the document use that url, " +
|
||||
"and add that in a new container, as a bootstrap responsive ('img-fluid py-3') image, with the maximum height of 30vh." +
|
||||
"If there is anything marked important in the text, make sure to add that in your answer." +
|
||||
"If there are links to be displayed, make sure to display them as clickable links." +
|
||||
"After the welcome message, always add in a new div with container class: 'This website has a live, " +
|
||||
"AI interface: if you have any questions, you can simply ask either by typing in the message " +
|
||||
"box, or by clicking the microphone icon on the top of the page. '" +
|
||||
"Here is a list of topics " + menuList +
|
||||
", make a new bootstrap clearfix and after that make a clickable bootstrap " +
|
||||
"styled (btn btn-primary) button from each of the determined topics, " +
|
||||
"that calls the javascript function 'callAI({the name of the topic})' on click. " +
|
||||
"Do not include anything else than the html title and text elements, no css, no scripts, no head or other tags." +
|
||||
"Do not mark your answer with ```html or any other mark.";
|
||||
return systemMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UserIntention
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static class ContentProcessing
|
||||
{
|
||||
|
||||
public static string GetSystemMessageForJsonResultDecision(string language, string extractedText, string currentDom)
|
||||
{
|
||||
string systemMessage = $"You are a helpful assistant built in a website, trying to figure out what the User wants to do or know about.\r\n" +
|
||||
"Your job is to classify the user's request into one of the following categories:\r\n" +
|
||||
"1. **Ask about or search infromation in the website’s content** (Return a 'Text result')\r\n" +
|
||||
"2. **Analyze the currently displayed HTML content** (Return an 'Examination result')\r\n" +
|
||||
"3. **Initiate an action** (Return a 'Method result')\r\n" +
|
||||
"If none of the above applies, return an 'Error result'.\r\n\r\n" +
|
||||
|
||||
"**Response format:**\r\n" +
|
||||
"Strictly respond in " + language + " as a JSON object, using one of the following formats:\r\n" +
|
||||
|
||||
"1. **chatGPTMethodResult** (for initiating actions):\r\n" +
|
||||
" - `type`: \"methodresult\"\r\n" +
|
||||
" - `text`: A short explanation of what the user wants to do.\r\n" +
|
||||
" - `methodToCall`: One of: [openContactForm, openCalendar, openApplicationForm]\r\n" +
|
||||
" - `parameter`: [email address for openContactForm, calendlyUserName for openCalendar, empty string for openApplicationForm]\r\n\r\n" +
|
||||
|
||||
"2. **chatGPTTextResult** (for general website content searches):\r\n" +
|
||||
" - `type`: \"textresult\"\r\n" +
|
||||
" - `text`: The user’s unmodified query.\r\n\r\n" +
|
||||
|
||||
"3. **chatGPTExaminationResult** (for analyzing the currently displayed page only):\r\n" +
|
||||
" - `type`: \"examinationresult\"\r\n" +
|
||||
" - `text`: The user’s unmodified query.\r\n\r\n" +
|
||||
|
||||
"4. **chatGPTErrorResult** (for errors):\r\n" +
|
||||
" - `type`: \"errorresult\"\r\n" +
|
||||
" - `text`: A description of the issue encountered.\r\n\r\n" +
|
||||
|
||||
"**Decision Rules:**\r\n" +
|
||||
"- If the user is **searching for website content** beyond what is currently displayed (e.g., 'Find information about our services'), return a `textresult`.\r\n" +
|
||||
"- If the user is **asking about the currently visible content** (e.g., 'What is shown on the page?'), return an `examinationresult`.\r\n" +
|
||||
"- If the user wants to **perform an action**, return a `methodresult`.\r\n" +
|
||||
"- If the required parameter is missing, return an `errorresult`.\r\n\r\n" +
|
||||
|
||||
"**Examples:**\r\n" +
|
||||
"- User asks: 'Show me information about pricing' → `textresult`\r\n" +
|
||||
"- User asks: 'What is displayed right now?' → `examinationresult`\r\n" +
|
||||
"- User asks: 'Open the contact form' → `methodresult`\r\n" +
|
||||
"- User asks: 'Contact support' but no email is found → `errorresult`\r\n\r\n" +
|
||||
|
||||
"**Context:**\r\n" +
|
||||
"- Base responses on this initial document: {" + extractedText + "}\r\n" +
|
||||
"- Current displayed HTML: {" + currentDom + "}\r\n" +
|
||||
|
||||
"**IMPORTANT:**\r\n" +
|
||||
"- If the request is about general content, **DO NOT use 'examinationresult'**.\r\n" +
|
||||
"- If the request is about the currently displayed page, **DO NOT use 'textresult'**.\r\n" +
|
||||
"- Do NOT format the response with markdown, code blocks, or `json` tags, do not add any title, or explanation besides the plain json object";
|
||||
|
||||
return systemMessage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class HtmlGeneration
|
||||
{
|
||||
public const string BootstrapCard = "Generate a Bootstrap 5 card layout using the following content:";
|
||||
public const string SectionLayout = "Create a responsive HTML5 layout section for this text:";
|
||||
}
|
||||
public static class LayoutPlanning
|
||||
{
|
||||
public static string GetLayoutPlanningSystemPrompt(List<HtmlSnippet> htmlToUse, Dictionary<string, string>? photos, string[]? topics)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
// 📌 STRICT FORMAT INSTRUCTION
|
||||
sb.AppendLine("You are a helpful assistant whose ONLY task is to break down THE PROVIDED CONTENT into a structured JSON object for Bootstrap 5 pages, using \n" +
|
||||
"**layoutplan**:\r\n" +
|
||||
"- `title`: \"The title of the page\"\r\n" +
|
||||
"- `blocks`: \"an array of layoutblocks\"\r\n" +
|
||||
".");
|
||||
|
||||
if ((htmlToUse != null))
|
||||
{
|
||||
sb.AppendLine("\n### You have these html snippets provided:");
|
||||
foreach (var snippet in htmlToUse)
|
||||
{
|
||||
//no need to paste the html code
|
||||
//sb.AppendLine($"Snippet id: {snippet.Id}, Snippet name: {snippet.Name}, Snippet description: {snippet.Description}, Snippet type: {snippet.Type}, Snippet template code: {snippet.Html}");
|
||||
sb.AppendLine($"Snippet id: {snippet.Id}, Snippet name: {snippet.Name}, Snippet description: {snippet.Description}, Snippet type: {snippet.Type}");
|
||||
}
|
||||
sb.AppendLine("- Use them ONLY if relevant.");
|
||||
}
|
||||
|
||||
if (photos != null && photos.Any())
|
||||
{
|
||||
sb.AppendLine("\n### Photos to be used in the blocks:");
|
||||
sb.AppendLine(string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}")));
|
||||
}
|
||||
|
||||
if (topics != null && topics.Any())
|
||||
{
|
||||
sb.AppendLine("\n### Topics:");
|
||||
sb.AppendLine(string.Join(", ", topics));
|
||||
}
|
||||
|
||||
sb.AppendLine("### ⛔️ ABSOLUTELY DO NOT:");
|
||||
sb.AppendLine("- ❌ Return HTML structure trees like `<section><div>`...");
|
||||
sb.AppendLine("- ❌ Include keys like `src`, `alt`, `url`, `items`, or nested object lists.");
|
||||
sb.AppendLine("- ❌ Invent block types not on the list.");
|
||||
sb.AppendLine("- ❌ Return any explanation, markdown, prose, comments, or fallback formats.");
|
||||
sb.AppendLine("- ❌ Use any of these block types: `image`, `quote`, `list`, `layout`, `header`, `footer`, `sidebar`, etc.");
|
||||
sb.AppendLine("- ❌ Remove messages for AI marked with []");
|
||||
|
||||
sb.AppendLine("\n### ✅ REQUIRED OUTPUT FORMAT:");
|
||||
sb.AppendLine("{");
|
||||
sb.AppendLine(" \"title\": \"string\",");
|
||||
sb.AppendLine(" \"blocks\": [");
|
||||
sb.AppendLine(" { \"type\": \"string\", \"rawcontent\": \"string (text)\", \"preferredsnippetid\" : an integer id of matching html snippet if one found., \"order\": an incremented integer to set the blocks in order }");
|
||||
sb.AppendLine(" ]");
|
||||
sb.AppendLine("}");
|
||||
|
||||
sb.AppendLine("\n### ✅ Allowed `type` values (only use these):");
|
||||
sb.AppendLine("- hero");
|
||||
sb.AppendLine("- text");
|
||||
sb.AppendLine("- text-image");
|
||||
sb.AppendLine("- features");
|
||||
sb.AppendLine("- cta");
|
||||
sb.AppendLine("- video");
|
||||
sb.AppendLine("- icon-list");
|
||||
sb.AppendLine("- event-list");
|
||||
sb.AppendLine("- audio-player");
|
||||
sb.AppendLine("- topic-buttons");
|
||||
|
||||
sb.AppendLine("\n### ❗ GOOD EXAMPLE:");
|
||||
sb.AppendLine("{");
|
||||
sb.AppendLine(" \"title\": \"Welcome to Our Product\",");
|
||||
sb.AppendLine(" \"blocks\": [");
|
||||
sb.AppendLine(" { \"type\": \"hero\", \"rawcontent\": \"Example text\", \"preferredsnippetid\": 1, \"order\": 0 },");
|
||||
sb.AppendLine(" { \"type\": \"features\", \"rawcontent\": \"Example features\", \"order\": 1 },");
|
||||
sb.AppendLine(" { \"type\": \"cta\", \"rawcontent\": \"call to action content\", \"order\": 2 }");
|
||||
sb.AppendLine(" ]");
|
||||
sb.AppendLine("}");
|
||||
|
||||
sb.AppendLine("\n### ⛔ BAD EXAMPLES (DO NOT DO THIS):");
|
||||
sb.AppendLine("- { \"type\": \"image\", \"src\": \"...\", \"alt\": \"...\" }");
|
||||
sb.AppendLine("- { \"type\": \"list\", \"items\": [\"...\", \"...\"] }");
|
||||
sb.AppendLine("- { \"layout\": { \"header\": ..., \"footer\": ... } }");
|
||||
|
||||
sb.AppendLine("\n### FINAL TASK:");
|
||||
sb.AppendLine("Based on the following content, generate a JSON object following the exact format above. Output only the JSON. No markdown. No other marks. No explanation. ONLY the pure JSON.");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GetLayoutPlanningUserPrompt(string interMediateResult, string pageTitle, Dictionary<string, string>? photos)
|
||||
{
|
||||
var userMessage = "Based on the following content:\n\n*** " + interMediateResult + " ***\n\n" +
|
||||
"and the available photos:";
|
||||
|
||||
if (photos != null && photos.Any())
|
||||
{
|
||||
userMessage += "\n### Photo urls to be used WITHOUT ANY MODIFICATION in the blocks:";
|
||||
userMessage += string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}"));
|
||||
}
|
||||
|
||||
userMessage += ", return ONLY a JSON object representing a structured content layout for a webpage with the title '" + pageTitle + "', with this exact shape:\n" +
|
||||
"{ \"title\": string, \"blocks\": [ { \"type\": \"string\", \"rawcontent\": \"string (text)\", \"preferredsnippetid\" : an integer id of matching html snippet if one found., \"order\": an incremented integer to set the blocks in order } ] }\n\n" +
|
||||
"⚠️ DO NOT use layout structures like 'header', 'sidebar', 'mainContent', or 'footer'.\n" +
|
||||
"⚠️ DO NOT explain anything. Just return the pure JSON. No markdown, no ``` markers.\n\n" +
|
||||
"🎯 OBJECTIVE:\n" +
|
||||
"1. Determine 1 to 5 major content blocks from the input, grouping full paragraphs or sections together.\n" +
|
||||
"Prefer **1 to 5 blocks max**, unless the content is extremely long. Each block should represent a semantically complete section.\r\n" +
|
||||
"Group related ideas into one block, even if they are multiple paragraphs. Do NOT break up content just because it is a new sentence. \r\n" +
|
||||
"2. Use meaningful, semantically grouped layout block types like: 'hero', 'features list', 'text with image', 'text', 'product list', 'call to action', 'videoplayer', 'audioplayer', etc.\n" +
|
||||
"3. Each block should contain **rich content** and related photo ['photo url': 'the url of the photo'], not just one or two sentences. Group related ideas into a single `rawcontent` field.\n\n" +
|
||||
"DO NOT REMOVE ANY URLS (photo, wen link, etc) from the section or paragraph that it follows. \n\n" +
|
||||
"📌 Examples of good block grouping:\n" +
|
||||
"- All introductory marketing text together in one 'hero' or 'text' block\n" +
|
||||
"- A group of benefits or features into a single 'features' block\n" +
|
||||
"- A pitch paragraph into a 'call to action' block\n\n" +
|
||||
"🎨 Naming:\n" +
|
||||
"- Try to find related type values in the available snippets list. If you find a relevant snippet for that block, use the name of that snippet for type, and add the id if the snippet as preferredsnippetid." +
|
||||
"- If there is no relevant snippet, use such layout `type` values: hero, text, features, text with image, call to action, product list, team members, testimonial, event list, blogpost, article, video player, audio player, etc\n" +
|
||||
"X Restrictions:" +
|
||||
"- DO **NOT** modify the photo urls in any way." +
|
||||
"- Do **NOT** generate or assume new photo URLs.\n" +
|
||||
"- Do **NOT** modify photo URLs in any way.\n" +
|
||||
"- Do **NOT** skip or deny ANY part of the provided text. All of the provided text should be displayed as html \n" +
|
||||
"- Do **NOT** assume ANY content, like missing prices, missing links, etc. \n" +
|
||||
"✅ Final output: JSON only, well grouped, no extra explanation or formatting, no markdown, no ``` markers.\n";
|
||||
|
||||
return userMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public static class HtmlRendering
|
||||
{
|
||||
public static string GetHtmlRenderingSystemPromptForTextAndErrorResult(string language, string pageTitle, List<HtmlSnippet> htmlToUse, Dictionary<string, string>? photos, string[]? topics)
|
||||
{
|
||||
//var sb = new StringBuilder("You are a helpful assistant generating HTML content in " + language + " using Bootstrap.\n\n" +
|
||||
// "### Rules to Follow: \n" +
|
||||
// "- Please generate clean and structured HTML that goes between the menu and the footer of a Bootstrap5 webpage.\n" +
|
||||
// "- DO NOT include `<head>`, `<body>` tags — only content that goes inside the Bootstrap container.\n" +
|
||||
// "- Use `<h1 class='p-3'>` for the title: " + pageTitle + ".\n" +
|
||||
// "- Structure content using **separate `row` elements** for different sections.\n" +
|
||||
// "- Use Bootstrap classes to ensure proper spacing and alignment.\n" +
|
||||
// " - Use 'row justify-content-center' for layout, 'col-xx-x' classes for columns (ONLY IF multiple columns are needed), and always use 'img-fluid' class for images.\r\n" +
|
||||
// " - Do NOT use 'col' class if there is only one column to display in the row.\n" +
|
||||
// "- Do NOT use additional class for paragraphs!\n" +
|
||||
// "- Do NOT nest images inside paragraphs!\n" +
|
||||
// "- Ensure clear **separation of content into multiple sections if multiple snippets are provided**.\n");
|
||||
|
||||
//if (htmlToUse != null && htmlToUse.Any())
|
||||
//{
|
||||
// sb.AppendLine("### Using Provided Snippets:\n" +
|
||||
// "- You have been given **multiple HTML snippets**:\n");
|
||||
// foreach (var snippet in htmlToUse)
|
||||
// {
|
||||
// sb.AppendLine($"{snippet.Id}: {snippet.Name}: {snippet.Html}.\n");
|
||||
// sb.AppendLine($"Type: {snippet.Type}, Tags: {snippet.Tags}, Variant: {snippet.Variant}.\n");
|
||||
// }
|
||||
// sb.AppendLine("**DO NOT merge them into one**.\n" +
|
||||
// "- Use each snippet **as a separate section** inside its own div.\n" +
|
||||
// "- Using all the snippets is NOT mandatory, use them only if you identified content that fits in a specific snippet.\n" +
|
||||
// "- Validate if there are multiple variants of a snippet, and choose the best option, for example if the block has text and photo url, choose the variant that has image in it.\n" +
|
||||
// "- Do NOT push all text of a block in the title <h> tags, always pick a short title, followed by the rest of the content as a paragraph.\n" +
|
||||
// "- If a snippet contains a button, ensure it is placed inside a `div.text-center` for proper alignment.\n");
|
||||
//}
|
||||
|
||||
//if (photos != null && photos.Any())
|
||||
//{
|
||||
// sb.AppendLine("### Handling Photos:\n" +
|
||||
// "- ONLY use the provided photo URLs **as they are**, without modification.\n" +
|
||||
// "- Do **NOT** generate or assume image URLs.\n" +
|
||||
// "- Use these photos where appropriate:\n" +
|
||||
// " " + string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}")) + "\n\n" +
|
||||
// "- Example usage:\n" +
|
||||
// " <img src='" + photos.First().Value + "' class='img-fluid' alt='" + photos.First().Key + "' />\n" +
|
||||
// "DO NOT modifiy the photo urls in any way.");
|
||||
//}
|
||||
|
||||
//if (topics != null && topics.Any())
|
||||
//{
|
||||
// sb.AppendLine("### Generating Topic Buttons:\n" +
|
||||
// "Start this section with a title `Related`\n" +
|
||||
// "- Create a **separate button** for each topic.\n" +
|
||||
// $"- Make sure the topics are in {language}, if not, translate the name of them." +
|
||||
// "- Each button should use `btn btn-primary` and call `callAI('{original_non_translated_topicName}')` on click.\n" +
|
||||
// "- List of topics:\n" +
|
||||
// " " + string.Join(", ", topics) + "\n\n" +
|
||||
// "- Example:\n" +
|
||||
// " <button class='btn btn-primary' onclick='callAI(\"" + topics.FirstOrDefault() + "\")'>" + topics.FirstOrDefault() + "</button>\n" +
|
||||
// "Put this section always as last, on the bottom of the page.\n");
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// sb.AppendLine("- **No topics provided** → **Do NOT generate topic buttons.**");
|
||||
//}
|
||||
|
||||
//sb.AppendLine("- DO **NOT** merge different content sections.\n" +
|
||||
// "- DO **NOT** wrap the entire content in a single `div`— use separate section tags.\n" +
|
||||
// "- DO **NOT** modify the photo urls in any way.\n" +
|
||||
// "- Do **NOT** generate or assume new photo URLs.\n" +
|
||||
// "- Do **NOT** skip or deny ANY part of the provided text. All of the provided text should be displayed as html\n" +
|
||||
// "- Do **NOT** add ANY extra content, like assumed prices, or assumed links.\n" +
|
||||
// "- If the snippet contains an image, but there is no photo url available, SKIP adding the image tag.\n" +
|
||||
// "- **Never** add explanations or start with ```html syntax markers.\n");
|
||||
|
||||
var sb = new StringBuilder($"You are a helpful assistant generating HTML in {language} using Bootstrap 5.\n\n" +
|
||||
"### General Instructions:\n" +
|
||||
"- Output only the **HTML content** between the menu and footer.\n" +
|
||||
"- DO NOT include `<head>`, `<body>`, or markdown formatting.\n" +
|
||||
"- Use `<h1 class='p-3'>` for the title: " + pageTitle + ".\n" +
|
||||
"- Structure sections inside separate `<section>` or `<div class='row'>` blocks.\n" +
|
||||
"- Use Bootstrap spacing and layout classes:\n" +
|
||||
" - Use `row justify-content-center` for rows.\n" +
|
||||
" - Use `col-xx-x` only for multiple-column layouts.\n" +
|
||||
" - Always use `img-fluid` for images.\n" +
|
||||
"- Avoid: unnecessary paragraph classes, nesting `<img>` inside `<p>`, or using `col` for single-column content.\n\n");
|
||||
|
||||
if (htmlToUse != null && htmlToUse.Any())
|
||||
{
|
||||
sb.AppendLine("### Snippet Handling:\n" +
|
||||
"- You are provided with multiple HTML snippets:\n");
|
||||
|
||||
foreach (var snippet in htmlToUse)
|
||||
{
|
||||
sb.AppendLine($"{snippet.Id}: {snippet.Name}: {snippet.Html}\n");
|
||||
sb.AppendLine($"Type: {snippet.Type}, Tags: {snippet.Tags}, Variant: {snippet.Variant}\n");
|
||||
}
|
||||
|
||||
sb.AppendLine(
|
||||
"- Use each snippet as a separate `<section>`.\n" +
|
||||
"- DO NOT merge snippets into one block.\n" +
|
||||
"- Use snippets only if they match the content block.\n" +
|
||||
"- Prefer variants with images if image content is present.\n" +
|
||||
"- Always use a short title in `<h>` and place body text in a `<p>`.\n" +
|
||||
"- If snippet includes a button, wrap it in `<div class='text-center'>`.\n");
|
||||
}
|
||||
|
||||
if (photos != null && photos.Any())
|
||||
{
|
||||
sb.AppendLine("### Photo Usage:\n" +
|
||||
"- Use ONLY the following image URLs as-is (no changes):\n" +
|
||||
string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}")) + "\n" +
|
||||
"- Example:\n" +
|
||||
$" <img src='{photos.First().Value}' class='img-fluid' alt='{photos.First().Key}' />\n" +
|
||||
"- DO NOT generate or modify photo URLs.\n");
|
||||
}
|
||||
|
||||
if (topics != null && topics.Any())
|
||||
{
|
||||
sb.AppendLine("### Topic Buttons:\n" +
|
||||
"- Place this section last, titled `Related`.\n" +
|
||||
"- Generate a `btn btn-primary` for each topic, calling `callAI('{original_non_translated_topicName}')`.\n" +
|
||||
"- Translate topic names to " + language + " if needed.\n" +
|
||||
"- Example:\n" +
|
||||
$" <button class='btn btn-primary' onclick='callAI(\"{topics.FirstOrDefault()}\")'>{topics.FirstOrDefault()}</button>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine("- No topics provided → DO NOT generate topic buttons.");
|
||||
}
|
||||
|
||||
sb.AppendLine("\n### DO NOT:\n" +
|
||||
"- Merge different content blocks.\n" +
|
||||
"- Remove javascript or <script> tags"+
|
||||
"- Wrap the full output in a single `div`.\n" +
|
||||
"- Skip any provided content.\n" +
|
||||
"- Add assumed text (e.g. prices, links).\n" +
|
||||
"- Add `<img>` if no image URL exists.\n" +
|
||||
"- Include explanation, markdown, or ` ```html` markers.\n");
|
||||
|
||||
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string HtmlRenderingUserPromptForTextAndErrorResult = "Create a perfect, production ready, structured, responsive Bootstrap 5 webpage " +
|
||||
"content from the provided layout and available html snippets. \r \n" +
|
||||
"Read the layout plan, analyze the blocks, and identify if there is any matching html snippets for each block folowing these rules: \r \n" +
|
||||
"- If there is no matching html snippet, generate the bootstrap 5 based layout for the given block. Else parse the block content into the available code snippet." +
|
||||
"- For each block, add am opening and closing comment with the name of the block like: <!-- Hero start --> \r \n" +
|
||||
"- Do not remove script tags from the provided snippets. \r \n" +
|
||||
"If the block's rawcontent contains photo url, display that photo within that section, not elsewhere.";
|
||||
|
||||
public static string GetHtmlRenderingAssistantMessageForTextAndErrorResult(LayoutPlan layoutPlan)
|
||||
{
|
||||
string assistantMessage = "`Provided layout plan, that contains the text to be displayed as HTML`:";
|
||||
|
||||
foreach (var block in layoutPlan.Blocks)
|
||||
{
|
||||
assistantMessage += $"Block type: {block.Type}, block content: {block.RawContent}";
|
||||
}
|
||||
return assistantMessage;
|
||||
}
|
||||
|
||||
public static string GetHtmlRenderingSystemPromptForMethodResult(string language, string methodToCall, string methodParameter)
|
||||
{
|
||||
var sb = new StringBuilder("You are a helpful assistant generating HTML content in " + language + " using Bootstrap. \n\n" +
|
||||
"### Rules to Follow: \n" +
|
||||
"- Please generate clean and structured HTML that goes inside a Bootstrap container.\n" +
|
||||
"- DO NOT include `<head>`, `<body>` tags — only content inside the Bootstrap container.\n" +
|
||||
"- Use `<h1 class='p-3'>` for a short title followed by the content, based on the user's query.\n" +
|
||||
"- Structure content using **separate `row` elements** for different sections.\n" +
|
||||
"- Use Bootstrap classes to ensure proper spacing and alignment.\n" +
|
||||
" - Use 'row justify-content-center' for layout, 'col-xx-x' classes for columns (ONLY IF multiple columns are needed), and always use 'img-fluid' class for images.\r\n" +
|
||||
" - Do NOT use 'col' class if there is only one column to display in the row. \n" +
|
||||
"- Do NOT use additional class for paragraphs! \n" +
|
||||
"- Do NOT nest images inside paragraphs! \n" +
|
||||
"- Ensure clear **separation of content into multiple sections if multiple snippets are provided**.\n");
|
||||
|
||||
if (!string.IsNullOrEmpty(methodToCall))
|
||||
{
|
||||
sb.AppendLine("- At the END of the content, include a **single** Bootstrap button (`btn btn-primary`) that calls `" + methodToCall + "` with `" + methodParameter + "` on click.\n");
|
||||
}
|
||||
|
||||
sb.AppendLine("- DO **NOT** merge different content sections.\n" +
|
||||
"- DO **NOT** wrap the entire content in a single `div`— use separate `row` divs.\n" +
|
||||
"- Do **NOT** skip or deny ANY part of the provided text. All of the provided text should be displayed as html\n" +
|
||||
"- Do **NOT** assume ANY content, like missing prices, missing links. \n" +
|
||||
"- If the snippet contains an image, but there is no photo url available, SKIP adding the image tag." +
|
||||
"- **Never** add explanations or start with ```html syntax markers.\n");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
namespace BLAIzor.Models
|
||||
{
|
||||
public class AppLog
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
public string Severity { get; set; } = "Info"; // "Info", "Warning", "Error"
|
||||
public string Message { get; set; }
|
||||
public string? Details { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
namespace BLAIzor.Models
|
||||
{
|
||||
public class ContentChunk
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ContentItemId { get; set; } // FK to ContentItem
|
||||
public string QdrantPointId { get; set; }
|
||||
public int ChunkIndex { get; set; }
|
||||
public string? VectorHash { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
public ContentItem ContentItem { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using NuGet.ContentModel;
|
||||
|
||||
namespace BLAIzor.Models
|
||||
{
|
||||
public class ContentGroup
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int SiteInfoId { get; set; } // FK to SiteInfo
|
||||
public string Name { get; set; } // e.g. "Blog", "Manuals"
|
||||
public string Slug { get; set; } // e.g. "blog", "manuals"
|
||||
public string Type { get; set; } // "blogpost", "manual", etc.
|
||||
public int VectorSize { get; set; }
|
||||
public string EmbeddingModel { get; set; }
|
||||
public int Version { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime LastUpdated { get; set; }
|
||||
|
||||
public SiteInfo SiteInfo { get; set; }
|
||||
public ICollection<ContentItem> Items { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace BLAIzor.Models
|
||||
{
|
||||
public class ContentGroupModel
|
||||
{
|
||||
public int SiteInfoId { get; set; }
|
||||
public ContentGroup ContentGroup { get; set; }
|
||||
public List<ContentItemModel> ContentItems { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
namespace BLAIzor.Models
|
||||
{
|
||||
public class ContentItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ContentGroupId { get; set; } // FK to ContentGroup
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string Language { get; set; }
|
||||
public string Tags { get; set; } // comma-separated tags
|
||||
public bool IsPublished { get; set; }
|
||||
public int Version { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime LastUpdated { get; set; }
|
||||
|
||||
public ContentGroup ContentGroup { get; set; }
|
||||
public ICollection<ContentChunk> Chunks { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
namespace BLAIzor.Models
|
||||
{
|
||||
public class ContentItemModel
|
||||
{
|
||||
public ContentItem ContentItem { get; set; }
|
||||
public List<ContentChunk> Chunks { get; set; } = new();
|
||||
|
||||
// Optional: enriched vector data pulled from Qdrant
|
||||
public List<WebPageContent> VectorPoints { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,14 @@
|
|||
{
|
||||
public class UploadedFilesModel
|
||||
{
|
||||
public List<string> Images { get; set; } = new();
|
||||
public List<UploadedImage> Images { get; set; } = new();
|
||||
public List<string> Videos { get; set; } = new();
|
||||
public List<string> Audio { get; set; } = new();
|
||||
}
|
||||
|
||||
public class UploadedImage
|
||||
{
|
||||
public string OriginalUrl { get; set; } = string.Empty;
|
||||
public string ThumbnailUrl { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,18 +12,32 @@ namespace BLAIzor.Models
|
|||
[Required]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public int PointId { get; set; }
|
||||
public string? Slug { get; set; }
|
||||
|
||||
//public int PointId { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public bool ShowInMainMenu { get; set; } = false;
|
||||
|
||||
public string? StoredHtml { get; set; }
|
||||
|
||||
public Guid? QdrantPointId { get; set; }
|
||||
//public Guid? QdrantPointId { get; set; }
|
||||
|
||||
[ForeignKey("ContentGroup")]
|
||||
public int? ContentGroupId { get; set; } // optional link to a ContentGroup
|
||||
public ContentGroup? ContentGroup { get; set; }
|
||||
|
||||
public int? ContentItemId { get; set; } // optional link to a specific ContentItem
|
||||
public ContentItem? ContentItem { get; set; }
|
||||
|
||||
[ForeignKey("SiteInfo")]
|
||||
public int SiteInfoId { get; set; }
|
||||
public SiteInfo SiteInfo { get; set; } = null!;
|
||||
|
||||
[ForeignKey("MenuItem")]
|
||||
public int? ParentId { get; set; }
|
||||
public MenuItem? Parent { get; set; }
|
||||
public ICollection<MenuItem> Children { get; set; } = new List<MenuItem>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ namespace BLAIzor.Models
|
|||
public string? Entity { get; set; }
|
||||
public string? DefaultLanguage { get; set; }
|
||||
public string? BackgroundVideo { get; set; }
|
||||
|
||||
public string? VectorCollectionName { get; set; }
|
||||
public string? EmbeddingService { get; set; }
|
||||
public List<ContentGroup> ContentGroups { get; set; } = new();
|
||||
|
||||
// Navigation property for IdentityUser
|
||||
public IdentityUser User { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
using DateTime = System.DateTime;
|
||||
using Qdrant.Client.Grpc;
|
||||
using DateTime = System.DateTime;
|
||||
|
||||
namespace BLAIzor.Models
|
||||
{
|
||||
public class WebPageContent
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public PointId Id { get; set; }
|
||||
public string UId { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int SiteId { get; set; }
|
||||
|
|
@ -13,6 +14,24 @@ namespace BLAIzor.Models
|
|||
public string Content { get; set; }
|
||||
public float[] Vectors { get; set; }
|
||||
public DateTime LastUpdated { get; set; }
|
||||
|
||||
public WebPageContent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WebPageContent(PointId id, string uId, string type, int siteId, string name, string description, string content, float[] vectors, DateTime lastUpdated)
|
||||
{
|
||||
Id = id;
|
||||
UId = uId;
|
||||
Type = type;
|
||||
SiteId = siteId;
|
||||
Name = name;
|
||||
Description = description;
|
||||
Content = content;
|
||||
Vectors = vectors;
|
||||
LastUpdated = lastUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
namespace BLAIzor.Models
|
||||
{
|
||||
public class WebsiteContentModel
|
||||
{
|
||||
public int SiteInfoId { get; set; }
|
||||
public List<ContentGroup> ContentGroups { get; set; }
|
||||
public List<ContentItemModel> ContentItems { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,9 @@ builder.Services.AddScoped<OpenAiRealtimeService>();
|
|||
builder.Services.AddScoped<CerebrasAPIService>();
|
||||
builder.Services.AddScoped<CssInjectorService>();
|
||||
builder.Services.AddScoped<LocalVectorSearchService>();
|
||||
builder.Services.AddScoped<WebsiteContentLoaderService>();
|
||||
builder.Services.AddHostedService<TempFileCleanupService>();
|
||||
builder.Services.AddScoped<ISimpleLogger, SimpleLogger>();
|
||||
builder.Services.AddServerSideBlazor().AddCircuitOptions(options => options.DetailedErrors = true).AddHubOptions(options =>
|
||||
{
|
||||
options.MaximumReceiveMessageSize = 1024000; // e.g. 100 KB
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BLAIzor.Data;
|
||||
using BLAIzor.Models;
|
||||
using BLAIzor.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace SeemGen.Tests;
|
||||
public class ContentEditorServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SaveAndSyncContentItemAsync_UpdatesFieldsAndRechunks_WhenContentChanged()
|
||||
{
|
||||
// Arrange
|
||||
var contentItemId = 1;
|
||||
var oldContent = "Old content";
|
||||
var newContent = "New content for chunking";
|
||||
var collectionName = "test_collection";
|
||||
|
||||
var contentGroup = new ContentGroup { Id = 1, Name = "Group", SiteInfoId = 1, Items = new List<ContentItem>() };
|
||||
var existingChunks = new List<ContentChunk>
|
||||
{
|
||||
new ContentChunk { Id = 1, ContentItemId = contentItemId, QdrantPointId = Guid.NewGuid().ToString(), ChunkIndex = 0, CreatedAt = DateTime.UtcNow }
|
||||
};
|
||||
var existingItem = new ContentItem
|
||||
{
|
||||
Id = contentItemId,
|
||||
Title = "Old Title",
|
||||
Description = "Old Desc",
|
||||
Content = oldContent,
|
||||
Language = "en",
|
||||
Tags = "tag1",
|
||||
IsPublished = true,
|
||||
LastUpdated = DateTime.UtcNow,
|
||||
ContentGroup = contentGroup,
|
||||
Chunks = existingChunks
|
||||
};
|
||||
contentGroup.Items.Add(existingItem);
|
||||
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
var dbContext = new ApplicationDbContext(options);
|
||||
dbContext.ContentItems.Add(existingItem);
|
||||
dbContext.ContentChunks.AddRange(existingChunks);
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var serviceScopeFactoryMock = new Mock<IServiceScopeFactory>();
|
||||
var scopeMock = new Mock<IServiceScope>();
|
||||
var providerMock = new Mock<IServiceProvider>();
|
||||
providerMock.Setup(x => x.GetService(typeof(ApplicationDbContext))).Returns(dbContext);
|
||||
scopeMock.Setup(x => x.ServiceProvider).Returns(providerMock.Object);
|
||||
serviceScopeFactoryMock.Setup(x => x.CreateScope()).Returns(scopeMock.Object);
|
||||
|
||||
var qdrantServiceMock = new Mock<QDrantService>(null);
|
||||
qdrantServiceMock.Setup(x => x.DeletePointsAsync(It.IsAny<Guid[]>(), collectionName)).Returns(Task.CompletedTask);
|
||||
qdrantServiceMock.Setup(x => x.QDrantInsertManyAsync(It.IsAny<List<WebPageContent>>(), collectionName)).Returns(Task.CompletedTask);
|
||||
|
||||
var openAIEmbeddingServiceMock = new Mock<OpenAIEmbeddingService>();
|
||||
openAIEmbeddingServiceMock.Setup(x => x.GenerateEmbeddingAsync(It.IsAny<string>())).ReturnsAsync(new float[] { 0.1f, 0.2f });
|
||||
|
||||
var localEmbeddingServiceMock = new Mock<LocalEmbeddingService>();
|
||||
localEmbeddingServiceMock.Setup(x => x.GenerateEmbeddingAsync(It.IsAny<string>())).ReturnsAsync(new float[] { 0.1f, 0.2f });
|
||||
|
||||
var openAIApiServiceMock = new Mock<OpenAIApiService>(MockBehavior.Loose, null, null);
|
||||
var htmlSnippetProcessorMock = new Mock<HtmlSnippetProcessor>(qdrantServiceMock.Object, openAIEmbeddingServiceMock.Object, localEmbeddingServiceMock.Object, null);
|
||||
var scopedContentServiceMock = new Mock<ScopedContentService>(dbContext, serviceScopeFactoryMock.Object);
|
||||
|
||||
var configMock = new Mock<IConfiguration>();
|
||||
configMock.Setup(x => x.GetSection("AiSettings").GetValue<string>("EmbeddingService")).Returns("openai");
|
||||
|
||||
var service = new ContentEditorService(
|
||||
openAIApiServiceMock.Object,
|
||||
qdrantServiceMock.Object,
|
||||
openAIEmbeddingServiceMock.Object,
|
||||
localEmbeddingServiceMock.Object,
|
||||
htmlSnippetProcessorMock.Object,
|
||||
serviceScopeFactoryMock.Object,
|
||||
scopedContentServiceMock.Object,
|
||||
configMock.Object
|
||||
);
|
||||
|
||||
var dto = new ContentItem
|
||||
{
|
||||
Id = contentItemId,
|
||||
Title = "New Title",
|
||||
Description = "New Desc",
|
||||
Content = newContent,
|
||||
Language = "fr",
|
||||
Tags = "tag2",
|
||||
IsPublished = false,
|
||||
LastUpdated = DateTime.UtcNow
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await service.SaveAndSyncContentItemAsync(dto, collectionName, false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(dto.Title, result.Title);
|
||||
Assert.Equal(dto.Description, result.Description);
|
||||
Assert.Equal(dto.Content, result.Content);
|
||||
Assert.Equal(dto.Language, result.Language);
|
||||
Assert.Equal(dto.Tags, result.Tags);
|
||||
Assert.Equal(dto.IsPublished, result.IsPublished);
|
||||
Assert.True(result.Version > 0);
|
||||
Assert.NotEmpty(dbContext.ContentChunks.Where(c => c.ContentItemId == contentItemId));
|
||||
qdrantServiceMock.Verify(x => x.DeletePointsAsync(It.IsAny<Guid[]>(), collectionName), Times.Once);
|
||||
qdrantServiceMock.Verify(x => x.QDrantInsertManyAsync(It.IsAny<List<WebPageContent>>(), collectionName), Times.Once);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BLAIzor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
namespace SeemGen.Tests;
|
||||
|
||||
public class UnitTest1
|
||||
{
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +82,10 @@ namespace BLAIzor.Services
|
|||
|
||||
using var responseStream = await response.Content.ReadAsStreamAsync();
|
||||
using var document = await JsonDocument.ParseAsync(responseStream);
|
||||
var inputTokens = document.RootElement.GetProperty("usage").GetProperty("prompt_tokens").GetInt32();
|
||||
var outputTokens = document.RootElement.GetProperty("usage").GetProperty("completion_tokens").GetInt32();
|
||||
var sum = inputTokens + outputTokens;
|
||||
Console.WriteLine($"USAGE STATS - Tokens: {inputTokens.ToString()} + {outputTokens.ToString()} = {sum.ToString()}");
|
||||
|
||||
return document.RootElement
|
||||
.GetProperty("choices")[0]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,215 @@
|
|||
using BLAIzor.Data;
|
||||
using BLAIzor.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Identity.Client;
|
||||
|
||||
namespace BLAIzor.Services
|
||||
{
|
||||
public class ContentEditorAIService
|
||||
{
|
||||
//private readonly AIService _aiService;
|
||||
private readonly OpenAIApiService _openAIApiService;
|
||||
//private readonly ApplicationDbContext _context;
|
||||
private readonly QDrantService _qDrantService;
|
||||
private readonly HtmlSnippetProcessor _htmlSnippetProcessor;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly ScopedContentService _scopedContentService;
|
||||
private readonly ContentEditorService _contentEditorService;
|
||||
|
||||
public static IConfiguration? _configuration;
|
||||
|
||||
public ContentEditorAIService(/*AIService aiService,*/ OpenAIApiService openAIApiService, /*ApplicationDbContext context,*/ QDrantService qDrantService, HtmlSnippetProcessor htmlSnippetProcessor, IServiceScopeFactory serviceScopeFactory, ScopedContentService scopedContentService, IConfiguration? configuration, ContentEditorService contentEditorService)
|
||||
{
|
||||
//_aiService = aiService;
|
||||
_openAIApiService = openAIApiService;
|
||||
//_context = context;
|
||||
_qDrantService = qDrantService;
|
||||
_htmlSnippetProcessor = htmlSnippetProcessor;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_scopedContentService = scopedContentService;
|
||||
_configuration = configuration;
|
||||
_contentEditorService = contentEditorService;
|
||||
}
|
||||
|
||||
private string GetAiEmbeddingSettings() =>
|
||||
_configuration?.GetSection("AiSettings")?.GetValue<string>("EmbeddingService") ?? string.Empty;
|
||||
|
||||
|
||||
// Existing methods
|
||||
public async Task<List<string>> GetMenuSuggestionsAsync(string sessionId, string prompt)
|
||||
{
|
||||
string systemMessage = "You are a helpful assistant that helps the user in creating a website.";
|
||||
var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
|
||||
|
||||
return result.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(line => line.Trim('-').Trim())
|
||||
.ToList() ?? new List<string>();
|
||||
}
|
||||
|
||||
public async Task<string> GetGeneratedContentAsync(string sessionId, string prompt)
|
||||
{
|
||||
string systemMessage = "You are a helpful assistant that helps the user in creating a website. Do not generate html, just plain text.";
|
||||
var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> ProcessMenuItems(int SiteId, bool hasCollection, List<MenuItemModel> ExtractedMenuItems, string subject, bool menuItemsSaved, bool updateVectorDatabase)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (!hasCollection)
|
||||
{
|
||||
//create colection
|
||||
await _qDrantService.CreateQdrantCollectionAsync("Site" + SiteId.ToString());
|
||||
hasCollection = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//check if Collection is really created already
|
||||
var collection = await _qDrantService.GetCollectionCount("Site" + SiteId.ToString());
|
||||
if (collection != null)
|
||||
{
|
||||
var siteInfo = await _contentEditorService.GetSiteInfoByIdAsync(SiteId);
|
||||
if (siteInfo.EmbeddingService != null)
|
||||
{
|
||||
var currentEmbeddingServiceName = GetAiEmbeddingSettings();
|
||||
if (siteInfo.EmbeddingService == currentEmbeddingServiceName)
|
||||
{
|
||||
//drop existing collection
|
||||
await _qDrantService.DeleteCollectionAsync("Site" + SiteId.ToString());
|
||||
//create will automatically handle the new vector size
|
||||
await _qDrantService.CreateQdrantCollectionAsync("Site" + SiteId.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var updateQdrantList = ExtractedMenuItems.OrderBy(mi => mi.MenuItem.ContentGroup.Items.FirstOrDefault().Id).ToList(); //FIXXXXXXX
|
||||
|
||||
for (int i = 0; i < updateQdrantList.Count(); i++)
|
||||
{
|
||||
MenuItem menuItem = new MenuItem();
|
||||
menuItem.Name = ExtractedMenuItems[i].MenuItem.Name;
|
||||
menuItem.SortOrder = i;
|
||||
menuItem.SiteInfoId = SiteId;
|
||||
menuItem.ShowInMainMenu = true;
|
||||
if (!string.IsNullOrEmpty(ExtractedMenuItems[i].Content)) //if we have content in the menuItem, let's update the vector DB
|
||||
{
|
||||
if (updateVectorDatabase)
|
||||
{
|
||||
Guid guid = Guid.NewGuid();
|
||||
WebPageContent content = new WebPageContent();
|
||||
content.Id = guid;
|
||||
content.UId = guid.ToString();
|
||||
content.Type = "page";
|
||||
content.SiteId = SiteId;
|
||||
content.Name = ExtractedMenuItems[i].MenuItem.Name;
|
||||
if (ExtractedMenuItems[i].ContentDescription == null)
|
||||
{
|
||||
content.Description = $"A section called {ExtractedMenuItems[i].MenuItem.Name} of a website of {subject}";
|
||||
}
|
||||
else
|
||||
{
|
||||
content.Description = ExtractedMenuItems[i].ContentDescription;
|
||||
}
|
||||
content.Content = ExtractedMenuItems[i].Content;
|
||||
content.LastUpdated = DateTime.UtcNow;
|
||||
//menuItem.QdrantPointId = guid; //FIXXXXXXX
|
||||
//menuItem.PointId = i; //FIXXXXXXX
|
||||
await _htmlSnippetProcessor.ProcessAndStoreWebContentAsync(guid, content, SiteId);
|
||||
}
|
||||
else
|
||||
{
|
||||
//menuItem.PointId = i; //FIXXXXXXX
|
||||
//menuItem.QdrantPointId = ExtractedMenuItems[i].MenuItem.QdrantPointId; //FIXXXXXXX
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!menuItemsSaved) //menuItems just extracted, no content yet because that can be generated only after menuItems have been saved first
|
||||
{
|
||||
//menuItem.Name = ExtractedMenuItems[i].MenuItem.Name;
|
||||
//menuItem.SortOrder = i;
|
||||
//menuItem.SiteInfoId = SiteId;
|
||||
//menuItem.ShowInMainMenu = true;
|
||||
await _contentEditorService.AddMenuItemAsync(menuItem);
|
||||
}
|
||||
else //menuItems available already
|
||||
{
|
||||
var menuItems = await _contentEditorService.GetMenuItemsBySiteIdAsync(SiteId);
|
||||
var thisItem = menuItems.FirstOrDefault(x => x.SiteInfoId == SiteId && x.Id == ExtractedMenuItems[i].MenuItem.Id);
|
||||
if (thisItem == null)
|
||||
{
|
||||
//new menu item?
|
||||
await _contentEditorService.AddMenuItemAsync(menuItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
//thisItem.PointId = menuItem.PointId; //FIXXXXXXX
|
||||
//thisItem.QdrantPointId = menuItem.QdrantPointId; //FIXXXXXXX
|
||||
thisItem.SortOrder = ExtractedMenuItems[i].MenuItem.SortOrder;
|
||||
thisItem.ShowInMainMenu = ExtractedMenuItems[i].MenuItem.ShowInMainMenu;
|
||||
await _contentEditorService.UpdateMenuItemAsync(thisItem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//await ContentEditorService.SaveMenuItemsAsync(menuItems);
|
||||
//menuItemsSaved = true;
|
||||
return "OK";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
//public async Task<string> AnalyzeFormFieldsFromText(string text)
|
||||
//{
|
||||
// string systemMessage = "You are a helpful assistant that extracts structured form field data from plain text.";
|
||||
|
||||
// string userMessage =
|
||||
// "This is the plain text extracted from a form document. " +
|
||||
// "Please extract all input fields a user is expected to fill in and return a JSON array of objects.\n\n" +
|
||||
// "Each object should have the following structure:\n" +
|
||||
// "- `label`: The label or prompt for the input\n" +
|
||||
// "- `type`: One of `text`, `number`, `date`, `checkbox`, `radio`, or `textarea`\n" +
|
||||
// "- `options`: If type is `radio` or `checkbox`, include the possible options as a list of strings; otherwise, return an empty list\n" +
|
||||
// "- `section`: The section title this field belongs to (if any)\n" +
|
||||
// "- `repeatable`: true if this field (or set of fields) is expected to be repeated for multiple entries (like multiple employers or qualifications), otherwise false\n\n" +
|
||||
// "Return only the JSON array, without any explanation or formatting like ```json.\n\n" +
|
||||
// "Here is the form text:\n\n" +
|
||||
// $"---\n{text}\n---";
|
||||
|
||||
// return await _openAIApiService.GetSimpleChatGPTResponseNoSession(systemMessage, userMessage).ConfigureAwait(false);
|
||||
//}
|
||||
|
||||
public async Task<string> AnalyzeGroupedFormFieldsFromText(string text)
|
||||
{
|
||||
string systemMessage = "You are a helpful assistant that analyzes plain form text and extracts structured, grouped form field definitions.";
|
||||
|
||||
string userMessage =
|
||||
"This is a plain text version of a printed form. Your task is to extract the **input fields** that a user must fill in. " +
|
||||
"Group related fields under sections where applicable, and return them in a JSON structure.\n\n" +
|
||||
"Each group should follow this structure:\n" +
|
||||
"- `groupName`: Name of the logical section or group (e.g. 'Work Experience')\n" +
|
||||
"- `repeatable`: true if this section can appear multiple times (e.g. multiple employers, qualifications)\n" +
|
||||
"- `fields`: an array of fields within the group\n\n" +
|
||||
"Each field must have:\n" +
|
||||
"- `label`: The field label/question\n" +
|
||||
"- `type`: one of `text`, `number`, `date`, `textarea`, `checkbox`, or `radio`\n" +
|
||||
"- `options`: list of string values if type is `radio` or `checkbox`, or an empty list otherwise\n" +
|
||||
"- `section`: optional section name (can match group name or be more specific)\n\n" +
|
||||
"Return only the JSON array. Do not include any explanation or formatting like ```json.\n\n" +
|
||||
$"Here is the form text:\n\n---\n{text}\n---";
|
||||
|
||||
return await _openAIApiService.GetSimpleChatGPTResponseNoSession(systemMessage, userMessage).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -212,7 +212,7 @@ namespace BLAIzor.Services
|
|||
// }
|
||||
//}
|
||||
|
||||
public async Task ProcessAndStoreWebContentAsync(int id, WebPageContent pageContent, int siteId)
|
||||
public async Task ProcessAndStoreWebContentAsync(PointId id, WebPageContent pageContent, int siteId)
|
||||
{
|
||||
_qdrantApiKey = GetApiKey();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
namespace BLAIzor.Services
|
||||
{
|
||||
public interface ISimpleLogger
|
||||
{
|
||||
Task InfoAsync(string message, string? details = null);
|
||||
Task WarnAsync(string message, string? details = null);
|
||||
Task ErrorAsync(string message, string? details = null);
|
||||
|
||||
void SetLevel(LogLevel level);
|
||||
void EnableConsole(bool enabled);
|
||||
}
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Info = 1,
|
||||
Warning = 2,
|
||||
Error = 3,
|
||||
None = 4 // disables all logs
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +85,10 @@ namespace BLAIzor.Services
|
|||
|
||||
using var responseStream = await response.Content.ReadAsStreamAsync();
|
||||
using var document = await JsonDocument.ParseAsync(responseStream);
|
||||
|
||||
var inputTokens = document.RootElement.GetProperty("usage").GetProperty("prompt_tokens").GetInt32();
|
||||
var outputTokens = document.RootElement.GetProperty("usage").GetProperty("completion_tokens").GetInt32();
|
||||
var sum = inputTokens + outputTokens;
|
||||
Console.WriteLine($"USAGE STATS - Tokens: {inputTokens.ToString()} + {outputTokens.ToString()} = {sum.ToString()}");
|
||||
return document.RootElement
|
||||
.GetProperty("choices")[0]
|
||||
.GetProperty("message")
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using BLAIzor.Models;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Collections;
|
||||
using Newtonsoft.Json;
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using static Qdrant.Client.Grpc.PointsUpdateOperation.Types;
|
||||
|
||||
namespace BLAIzor.Services
|
||||
{
|
||||
|
|
@ -41,7 +43,7 @@ namespace BLAIzor.Services
|
|||
|
||||
public async Task<int> GetCollectionCount(string collectionName)
|
||||
{
|
||||
|
||||
_apiKey = GetApiKey();
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
var result = await client.CountAsync(
|
||||
collectionName: collectionName,
|
||||
|
|
@ -51,52 +53,6 @@ namespace BLAIzor.Services
|
|||
return Convert.ToInt32(result);
|
||||
}
|
||||
|
||||
//public async Task CreateQdrantCollectionAsync()
|
||||
//{
|
||||
// _apiKey = GetApiKey();
|
||||
// var httpClient = new HttpClient();
|
||||
|
||||
// httpClient.DefaultRequestHeaders.Clear();
|
||||
// httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
|
||||
|
||||
// var collectionName = "web_content";
|
||||
|
||||
// var createCollectionPayload = new
|
||||
// {
|
||||
// vectors = new { size = 0, distance = "" } // Adjust size based on embedding model
|
||||
// };
|
||||
|
||||
// var embeddingServiceProvider = GetAiEmbeddingSettings();
|
||||
// if (embeddingServiceProvider == "local")
|
||||
// {
|
||||
// createCollectionPayload = new
|
||||
// {
|
||||
// vectors = new { size = 1024, distance = "Cosine" } // Adjust size based on embedding model
|
||||
// };
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// createCollectionPayload = new
|
||||
// {
|
||||
// vectors = new { size = 1536, distance = "Cosine" } // Adjust size based on embedding model
|
||||
// };
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// var content = new StringContent(JsonConvert.SerializeObject(createCollectionPayload), Encoding.UTF8, "application/json");
|
||||
// var response = await httpClient.PutAsync($"{qdrantUrl}/collections/{collectionName}", content);
|
||||
|
||||
// if (response.IsSuccessStatusCode)
|
||||
// {
|
||||
// Console.WriteLine("Collection created successfully!" + response.Content.ReadAsStringAsync());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Console.WriteLine($"Failed to create collection: {response.StatusCode}");
|
||||
// }
|
||||
//}
|
||||
|
||||
public async Task<string> GetCollectionBySiteIdAsync(int siteId)
|
||||
{
|
||||
return await GetCollectionByNameAsync("Site" + siteId);
|
||||
|
|
@ -109,12 +65,19 @@ namespace BLAIzor.Services
|
|||
|
||||
}
|
||||
|
||||
public async Task<bool> CollectionExistsAsync(string collectionName)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
return await client.CollectionExistsAsync(collectionName);
|
||||
}
|
||||
|
||||
public async Task<string> GetCollectionByNameAsync(string collectionName)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
bool doesExist = await client.CollectionExistsAsync(collectionName);
|
||||
bool doesExist = await CollectionExistsAsync(collectionName);
|
||||
if (doesExist)
|
||||
{
|
||||
var response = await client.GetCollectionInfoAsync(collectionName);
|
||||
|
|
@ -140,7 +103,7 @@ namespace BLAIzor.Services
|
|||
|
||||
}
|
||||
|
||||
public async Task CreateQdrantCollectionAsync(string collectionName)
|
||||
public async Task<bool> CreateQdrantCollectionAsync(string collectionName)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
var httpClient = new HttpClient();
|
||||
|
|
@ -175,35 +138,204 @@ namespace BLAIzor.Services
|
|||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
Console.WriteLine("Collection created successfully!" + response.Content.ReadAsStringAsync());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Failed to create collection: {response.StatusCode}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> GetContentAsync(int siteId, int contentId)
|
||||
public async Task<List<WebPageContent>> GetPointsFromQdrantAsyncByPointIds(SiteInfo site, PointId[] pointIds)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
var httpClient = new HttpClient();
|
||||
|
||||
httpClient.DefaultRequestHeaders.Clear();
|
||||
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
|
||||
List<WebPageContent> pageContent = new();
|
||||
|
||||
|
||||
var response = await httpClient.GetAsync($"{qdrantUrl}/collections/Site{siteId.ToString()}/points/{contentId.ToString()}");
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
if (string.IsNullOrEmpty(site.VectorCollectionName))
|
||||
{
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
//Console.WriteLine($"Query result: {result}");
|
||||
return result;
|
||||
pageContent.Add(new WebPageContent(Guid.Empty,
|
||||
Guid.Empty.ToString(),
|
||||
"404",
|
||||
site.Id,
|
||||
"ErrorPage",
|
||||
"A 404 error page for non existing content",
|
||||
"The page doesn't exist",
|
||||
null,
|
||||
DateTime.Now
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Failed to query snippet: {response.StatusCode}");
|
||||
return string.Empty;
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
bool doesExist = await client.CollectionExistsAsync(site.VectorCollectionName);
|
||||
if (!doesExist)
|
||||
{
|
||||
pageContent.Add(new WebPageContent(Guid.Empty,
|
||||
Guid.Empty.ToString(),
|
||||
"404",
|
||||
site.Id,
|
||||
"ErrorPage",
|
||||
"A 404 error page for non existing content",
|
||||
"The page doesn't exist",
|
||||
null,
|
||||
DateTime.Now
|
||||
));
|
||||
return pageContent;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await client.RetrieveAsync(
|
||||
collectionName: site.VectorCollectionName,
|
||||
ids: pointIds,
|
||||
withPayload: true,
|
||||
withVectors: true
|
||||
);
|
||||
if (result.Count == 0)
|
||||
{
|
||||
pageContent.Add(new WebPageContent(Guid.Empty,
|
||||
Guid.Empty.ToString(),
|
||||
"404",
|
||||
site.Id,
|
||||
"ErrorPage",
|
||||
"A 404 error page for non existing content",
|
||||
"The page doesn't exist",
|
||||
null,
|
||||
DateTime.Now
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var retrievedPoint in result)
|
||||
{
|
||||
float[] vectorArray = retrievedPoint.Vectors.Vector.Data.ToArray();
|
||||
string idValue;
|
||||
if (retrievedPoint.Id.HasNum)
|
||||
{
|
||||
idValue = retrievedPoint.Id.Num.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
idValue = retrievedPoint.Id.Uuid.ToString();
|
||||
}
|
||||
|
||||
Console.WriteLine($"POINTID: {idValue}, {retrievedPoint.Payload["name"]}");
|
||||
|
||||
|
||||
pageContent.Add(new WebPageContent(retrievedPoint.Id,
|
||||
retrievedPoint.Payload["uid"].StringValue,
|
||||
retrievedPoint.Payload["type"].StringValue,
|
||||
Convert.ToInt32(retrievedPoint.Payload["siteId"].IntegerValue),
|
||||
retrievedPoint.Payload["name"].StringValue,
|
||||
retrievedPoint.Payload["description"].StringValue,
|
||||
retrievedPoint.Payload["content"].StringValue,
|
||||
vectorArray,
|
||||
Convert.ToDateTime(retrievedPoint.Payload["lastUpdated"].StringValue)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return pageContent;
|
||||
}
|
||||
|
||||
public async Task<List<WebPageContent>> GetPointsFromQdrantAsyncByIntegerPointIds(string collectionName, PointId[] pointIds)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
|
||||
List<WebPageContent> pageContent = new();
|
||||
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
bool doesExist = await client.CollectionExistsAsync(collectionName);
|
||||
|
||||
var result = await client.RetrieveAsync(
|
||||
collectionName: collectionName,
|
||||
ids: pointIds,
|
||||
withPayload: true,
|
||||
withVectors: true
|
||||
);
|
||||
|
||||
foreach (var retrievedPoint in result)
|
||||
{
|
||||
float[] vectorArray = retrievedPoint.Vectors.Vector.Data.ToArray();
|
||||
string idValue;
|
||||
if (retrievedPoint.Id.HasNum)
|
||||
{
|
||||
idValue = retrievedPoint.Id.Num.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
idValue = retrievedPoint.Id.Uuid.ToString();
|
||||
}
|
||||
|
||||
Console.WriteLine($"POINTID: {idValue}, {retrievedPoint.Payload["name"]}");
|
||||
|
||||
|
||||
pageContent.Add(new WebPageContent(retrievedPoint.Id,
|
||||
retrievedPoint.Payload["uid"].StringValue,
|
||||
retrievedPoint.Payload["type"].StringValue,
|
||||
Convert.ToInt32(retrievedPoint.Payload["siteId"].IntegerValue),
|
||||
retrievedPoint.Payload["name"].StringValue,
|
||||
retrievedPoint.Payload["description"].StringValue,
|
||||
retrievedPoint.Payload["content"].StringValue,
|
||||
vectorArray,
|
||||
Convert.ToDateTime(retrievedPoint.Payload["lastUpdated"].StringValue)
|
||||
));
|
||||
}
|
||||
return pageContent;
|
||||
}
|
||||
|
||||
public async Task<List<WebPageContent>> GetPointFromQdrantAsyncByPointId(int siteId, int pointId)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
//var httpClient = new HttpClient();
|
||||
|
||||
//httpClient.DefaultRequestHeaders.Clear();
|
||||
//httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
|
||||
|
||||
List<WebPageContent> contentList = new();
|
||||
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
var result = await client.RetrieveAsync(
|
||||
collectionName: $"Site{siteId.ToString()}",
|
||||
id: Convert.ToUInt64(pointId),
|
||||
withPayload: true,
|
||||
withVectors: true
|
||||
);
|
||||
|
||||
foreach (var retrievedPoint in result)
|
||||
{
|
||||
float[] vectorArray = retrievedPoint.Vectors.Vector.Data.ToArray();
|
||||
string idValue;
|
||||
if (retrievedPoint.Id.HasNum)
|
||||
{
|
||||
idValue = retrievedPoint.Id.Num.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
idValue = retrievedPoint.Id.Uuid.ToString();
|
||||
}
|
||||
|
||||
Console.WriteLine($"POINTID: {idValue}, {retrievedPoint.Payload["name"]}");
|
||||
|
||||
contentList.Add(new WebPageContent(retrievedPoint.Id,
|
||||
retrievedPoint.Payload["uid"].StringValue,
|
||||
retrievedPoint.Payload["type"].StringValue,
|
||||
Convert.ToInt32(retrievedPoint.Payload["siteId"].IntegerValue),
|
||||
retrievedPoint.Payload["name"].StringValue,
|
||||
retrievedPoint.Payload["description"].StringValue,
|
||||
retrievedPoint.Payload["content"].StringValue,
|
||||
vectorArray,
|
||||
Convert.ToDateTime(retrievedPoint.Payload["lastUpdated"].StringValue)
|
||||
));
|
||||
}
|
||||
|
||||
return contentList;
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> GetSnippetAsync(int snippetId, string collectionName)
|
||||
|
|
@ -233,22 +365,9 @@ namespace BLAIzor.Services
|
|||
public async Task<int> QuerySnippetAsync(float[] queryVector, int limit = 1, string collectionName = "html_snippets")
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
//var httpClient = new HttpClient();
|
||||
|
||||
//httpClient.DefaultRequestHeaders.Clear();
|
||||
//httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
|
||||
|
||||
//var queryPayload = new
|
||||
//{
|
||||
// vector = queryVector,
|
||||
// limit = limit
|
||||
//};
|
||||
|
||||
var client = new QdrantClient(
|
||||
host: _qdrantHost,
|
||||
https: true,
|
||||
apiKey: _apiKey
|
||||
);
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
|
||||
var doesCollectionExist = await client.CollectionExistsAsync(collectionName);
|
||||
if (doesCollectionExist)
|
||||
|
|
@ -284,16 +403,15 @@ namespace BLAIzor.Services
|
|||
Console.WriteLine($"Failed to query snippet: no collection");
|
||||
return 0;
|
||||
}
|
||||
//var content = new StringContent(JsonConvert.SerializeObject(queryPayload), Encoding.UTF8, "application/json");
|
||||
//var response = await httpClient.PostAsync($"{qdrantUrl}/collections/{collectionName}/points/search", content);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task<int[]> QueryContentAsync(int siteId, float[] queryVector, int limit = 1)
|
||||
public async Task<List<WebPageContent>> QueryContentAsync(string collectionName, float[] queryVector, int limit = 1)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
var httpClient = new HttpClient();
|
||||
//var httpClient = new HttpClient();
|
||||
|
||||
List<WebPageContent> pageContent = new();
|
||||
|
||||
var client = new QdrantClient(
|
||||
host: _qdrantHost,
|
||||
|
|
@ -303,88 +421,58 @@ namespace BLAIzor.Services
|
|||
|
||||
|
||||
var response = await client.SearchAsync(
|
||||
collectionName: $"Site{siteId}",
|
||||
collectionName: collectionName,
|
||||
vector: queryVector,
|
||||
//filter: MatchKeyword("city", "London"),
|
||||
payloadSelector: true, //TODO comes with payload now, get the payload
|
||||
vectorsSelector: true, //filter: MatchKeyword("city", "London"),
|
||||
limit: 3
|
||||
);
|
||||
|
||||
//var content = new StringContent(JsonConvert.SerializeObject(queryPayload), Encoding.UTF8, "application/json");
|
||||
//var response = await httpClient.PostAsync($"{qdrantUrl}/collections/site{siteId}/points/search", content);
|
||||
|
||||
//if (response.IsSuccessStatusCode)
|
||||
//{
|
||||
// int[] sId = [];
|
||||
// var result = await response.Content.ReadAsStringAsync();
|
||||
// //Console.Write(result);
|
||||
// if (!string.IsNullOrEmpty(result))
|
||||
// {
|
||||
// QDrantQueryResult qdr = JsonConvert.DeserializeObject<QDrantQueryResult>(result)!;
|
||||
// int[] valami = new int[qdr.result.Count()];
|
||||
// for (int i = 0; i < qdr.result.Count(); i++)
|
||||
// {
|
||||
// sId[i] = qdr.result[i].id;
|
||||
// }
|
||||
// sId.AddRange(valami);
|
||||
// }
|
||||
// //var result = await response.Content.ReadFromJsonAsync<QDrantQueryResult>();
|
||||
// //Console.Write($"Query result: {sId}");
|
||||
// return sId;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// Console.WriteLine($"Failed to query snippet: {response.StatusCode}");
|
||||
// int[] nullResult = [];
|
||||
// return nullResult;
|
||||
//}
|
||||
if (response.Count() == 0)
|
||||
{
|
||||
int[] nullResult = [];
|
||||
Console.Write("None found");
|
||||
return nullResult;
|
||||
return pageContent;
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] intResult = new int[response.Count()];
|
||||
Console.Write("Found: " + response.FirstOrDefault()!.Id.Num);
|
||||
for (int i = 0; i < response.Count(); i++)
|
||||
|
||||
foreach (var retrievedPoint in response)
|
||||
{
|
||||
intResult[i] = Convert.ToInt32(response[i].Id.Num);
|
||||
float[] vectorArray = retrievedPoint.Vectors.Vector.Data.ToArray();
|
||||
string idValue;
|
||||
if (retrievedPoint.Id.HasNum)
|
||||
{
|
||||
idValue = retrievedPoint.Id.Num.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
idValue = retrievedPoint.Id.Uuid.ToString();
|
||||
}
|
||||
|
||||
Console.WriteLine($"POINTID: {idValue}, {retrievedPoint.Payload["name"]}");
|
||||
|
||||
|
||||
pageContent.Add(new WebPageContent(retrievedPoint.Id,
|
||||
retrievedPoint.Payload["uid"].StringValue,
|
||||
retrievedPoint.Payload["type"].StringValue,
|
||||
Convert.ToInt32(retrievedPoint.Payload["siteId"].IntegerValue),
|
||||
retrievedPoint.Payload["name"].StringValue,
|
||||
retrievedPoint.Payload["description"].StringValue,
|
||||
retrievedPoint.Payload["content"].StringValue,
|
||||
vectorArray,
|
||||
Convert.ToDateTime(retrievedPoint.Payload["lastUpdated"].StringValue)
|
||||
));
|
||||
}
|
||||
return intResult;
|
||||
|
||||
return pageContent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task QDrantInsertTest(List<int> ids, List<float[]> vectors, List<MapField<string, Value>> payloads)
|
||||
public async Task QDrantInsertPointAsync(PointId id, float[] vectors, MapField<string, Value> payload, string collectionName)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
var valamii = new List<PointStruct>();
|
||||
for (int i = 0; i < ids.Count; i++)
|
||||
{
|
||||
valamii.Add(new PointStruct
|
||||
{
|
||||
Id = (ulong)ids[i],
|
||||
Vectors = vectors[i],
|
||||
Payload = { payloads[i] }
|
||||
|
||||
});
|
||||
Console.WriteLine($"{valamii[i].Id} val bekerült {valamii[i].Payload["html"]}");
|
||||
}
|
||||
|
||||
Console.WriteLine(valamii.Count);
|
||||
|
||||
await client.UpsertAsync(
|
||||
collectionName: "html_snippets",
|
||||
points: valamii
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public async Task QDrantInsertPointAsync(int id, float[] vectors, MapField<string, Value> payload, string collectionName)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
|
|
@ -392,7 +480,7 @@ namespace BLAIzor.Services
|
|||
|
||||
pointStruct = new PointStruct
|
||||
{
|
||||
Id = (ulong)id,
|
||||
Id = id,
|
||||
Vectors = vectors,
|
||||
Payload = { payload }
|
||||
|
||||
|
|
@ -413,6 +501,8 @@ namespace BLAIzor.Services
|
|||
|
||||
public async Task QDrantInsertManyAsync(List<int> ids, List<float[]> vectors, List<MapField<string, Value>> payloads, string collectionName)
|
||||
{
|
||||
//FOR HTMLSNIPPETS, USES INT IDS FOR NOW
|
||||
|
||||
_apiKey = GetApiKey();
|
||||
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
|
|
@ -438,6 +528,77 @@ namespace BLAIzor.Services
|
|||
|
||||
}
|
||||
|
||||
public async Task QDrantInsertManyAsync(List<WebPageContent> chunks, string collectionName)
|
||||
{
|
||||
|
||||
//FOR WEBPAGECONTENT, USES POINTID IDS ALREADY
|
||||
|
||||
_apiKey = GetApiKey();
|
||||
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
var pointStructList = new List<PointStruct>();
|
||||
for (int i = 0; i < chunks.Count; i++)
|
||||
{
|
||||
//get payload from WebPageContent
|
||||
|
||||
//generate code to convert WebpageContent to MapField<string, Value>
|
||||
var payload = new MapField<string, Value>
|
||||
{
|
||||
{ "uid", new Value { StringValue = chunks[i].UId } }, // Correct usage of Value for string assignment
|
||||
{ "type", new Value { StringValue = chunks[i].Type } },
|
||||
{ "siteId", new Value { IntegerValue = chunks[i].SiteId } },
|
||||
{ "name", new Value { StringValue = chunks[i].Name } },
|
||||
{ "description", new Value { StringValue = chunks[i].Description } },
|
||||
{ "content", new Value { StringValue = chunks[i].Content } },
|
||||
{ "lastUpdated", new Value { StringValue = chunks[i].LastUpdated.ToString("o") } } // ISO 8601 format
|
||||
};
|
||||
|
||||
|
||||
pointStructList.Add(new PointStruct
|
||||
{
|
||||
Id = chunks[i].Id,
|
||||
Vectors = chunks[i].Vectors,
|
||||
Payload = { payload }
|
||||
|
||||
});
|
||||
Console.WriteLine($"{pointStructList[i].Id} val bekerül {pointStructList[i].Payload["name"]}");
|
||||
}
|
||||
|
||||
Console.WriteLine(pointStructList.Count);
|
||||
|
||||
await client.UpsertAsync(
|
||||
collectionName: collectionName,
|
||||
points: pointStructList
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public async Task DeletePointAsync(int pointId, string collectionName)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
|
||||
var result = await client.DeleteAsync(collectionName: "{collection_name}", ids: [(ulong)pointId]);
|
||||
Console.WriteLine(result.Status);
|
||||
}
|
||||
|
||||
public async Task DeletePointsAsync(ulong[] pointIds, string collectionName)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
|
||||
var result = await client.DeleteAsync(collectionName: collectionName, ids: pointIds);
|
||||
Console.WriteLine(result.Status);
|
||||
}
|
||||
public async Task DeletePointsAsync(Guid[] pointIds, string collectionName)
|
||||
{
|
||||
_apiKey = GetApiKey();
|
||||
var client = new QdrantClient(_qdrantHost, 6334, true, _apiKey);
|
||||
|
||||
var result = await client.DeleteAsync(collectionName: collectionName, ids: pointIds);
|
||||
Console.WriteLine(result.Status);
|
||||
}
|
||||
|
||||
public async Task DeleteCollectionAsync(string collectionName)
|
||||
{
|
||||
|
||||
|
|
@ -449,6 +610,7 @@ namespace BLAIzor.Services
|
|||
|
||||
}
|
||||
|
||||
|
||||
public class PointResult
|
||||
{
|
||||
public int id { get; set; }
|
||||
|
|
@ -463,15 +625,6 @@ namespace BLAIzor.Services
|
|||
public double time { get; set; }
|
||||
}
|
||||
|
||||
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
|
||||
//public class Payload
|
||||
//{
|
||||
// public string description { get; set; }
|
||||
// public string type { get; set; }
|
||||
// public string name { get; set; }
|
||||
// public string html { get; set; }
|
||||
//}
|
||||
|
||||
public class PointData
|
||||
{
|
||||
public int id { get; set; }
|
||||
|
|
|
|||
|
|
@ -32,10 +32,12 @@ namespace BLAIzor.Services
|
|||
}
|
||||
}
|
||||
|
||||
public List<HtmlSnippet> AvailableTemplateSnippets { get; set; }
|
||||
public List<WebPageContent> AvailableSiteContent { get; set; }
|
||||
public event Action OnBrandNameChanged;
|
||||
public int SelectedSiteId { get; set; } = 1;
|
||||
|
||||
public WebsiteContentModel WebsiteContentModel { get; set; }
|
||||
public List<HtmlSnippet> AvailableTemplateSnippets { get; set; }
|
||||
//public List<WebPageContent> AvailableSiteContent { get; set; }
|
||||
//public string SelectedDocument { get; set; } = "Poppixel.docx";
|
||||
//public string SelectedBrandName
|
||||
//{
|
||||
|
|
@ -47,155 +49,7 @@ namespace BLAIzor.Services
|
|||
|
||||
public string SessionId { get; set; }
|
||||
|
||||
public async Task<SiteInfo> GetSiteInfoByIdAsync(int SiteInfoId)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Now use dbContext safely without violating DI rules
|
||||
var result = await _context.SiteInfos.Where(x => x.Id == SiteInfoId).FirstOrDefaultAsync();
|
||||
if (result == null)
|
||||
{
|
||||
return await _context.SiteInfos.FirstOrDefaultAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<SiteInfo?> GetSiteInfoWithFormsByIdAsync(int siteId)
|
||||
{
|
||||
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Now use dbContext safely without violating DI rules
|
||||
var result = await _context.SiteInfos.Include(s => s.FormDefinitions).FirstOrDefaultAsync(s => s.Id == siteId);
|
||||
if (result == null)
|
||||
{
|
||||
return new SiteInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<SiteInfo> GetSiteInfoByNameAsync(string siteName)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Now use dbContext safely without violating DI rules
|
||||
var result = await _context.SiteInfos.Where(x => x.SiteName == siteName).FirstOrDefaultAsync();
|
||||
if (result == null)
|
||||
{
|
||||
return await _context.SiteInfos.FirstOrDefaultAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<SiteInfo> GetSiteInfoByUrlAsync(string url)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Now use dbContext safely without violating DI rules
|
||||
var result = await _context.SiteInfos.Where(x => x.DefaultUrl == url).FirstOrDefaultAsync();
|
||||
if (result == null)
|
||||
{
|
||||
return await _context.SiteInfos.FirstOrDefaultAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task UpdateSiteInfoAsync(SiteInfo siteInfo)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Now use dbContext safely without violating DI rules
|
||||
_context.SiteInfos.Update(siteInfo);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<List<SiteInfo>> GetUserSitesAsync(string userId)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Now use dbContext safely without violating DI rules
|
||||
return await _context.SiteInfos
|
||||
.Where(s => s.UserId == userId)
|
||||
.ToListAsync();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<SiteInfo>> GetSitesAsync()
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Now use dbContext safely without violating DI rules
|
||||
return await _context.SiteInfos.ToListAsync();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<SiteInfo> AddSiteInfoAsync(SiteInfo siteInfo)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var _context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Now use dbContext safely without violating DI rules
|
||||
if (siteInfo == null)
|
||||
throw new ArgumentNullException(nameof(siteInfo), "SiteInfo cannot be null.");
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SiteInfos.AddAsync(siteInfo);
|
||||
await _context.SaveChangesAsync();
|
||||
return siteInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the exception (using a logging framework like Serilog, NLog, etc.)
|
||||
throw new InvalidOperationException("An error occurred while adding the site info.", ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
using BLAIzor.Data;
|
||||
using BLAIzor.Models;
|
||||
using System;
|
||||
|
||||
namespace BLAIzor.Services
|
||||
{
|
||||
public class SimpleLogger : ISimpleLogger
|
||||
{
|
||||
private readonly ApplicationDbContext _dbContext;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private LogLevel _currentLevel = LogLevel.Info;
|
||||
private bool _consoleEnabled = true;
|
||||
|
||||
public SimpleLogger(ApplicationDbContext dbContext, IWebHostEnvironment env)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_env = env;
|
||||
}
|
||||
|
||||
public void SetLevel(LogLevel level) => _currentLevel = level;
|
||||
public void EnableConsole(bool enabled) => _consoleEnabled = enabled;
|
||||
|
||||
public Task InfoAsync(string message, string? details = null) =>
|
||||
LogAsync(LogLevel.Info, message, details);
|
||||
|
||||
public Task WarnAsync(string message, string? details = null) =>
|
||||
LogAsync(LogLevel.Warning, message, details);
|
||||
|
||||
public Task ErrorAsync(string message, string? details = null) =>
|
||||
LogAsync(LogLevel.Error, message, details);
|
||||
|
||||
private async Task LogAsync(LogLevel level, string message, string? details)
|
||||
{
|
||||
if (level < _currentLevel || _currentLevel == LogLevel.None)
|
||||
return;
|
||||
|
||||
var log = new AppLog
|
||||
{
|
||||
Severity = level.ToString(),
|
||||
Message = message,
|
||||
Details = details,
|
||||
Timestamp = DateTime.UtcNow
|
||||
};
|
||||
|
||||
if (_env.IsProduction())
|
||||
{
|
||||
_dbContext.Logs.Add(log);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (_consoleEnabled)
|
||||
{
|
||||
var color = Console.ForegroundColor;
|
||||
Console.ForegroundColor = level switch
|
||||
{
|
||||
LogLevel.Info => ConsoleColor.Gray,
|
||||
LogLevel.Warning => ConsoleColor.Yellow,
|
||||
LogLevel.Error => ConsoleColor.Red,
|
||||
_ => ConsoleColor.White
|
||||
};
|
||||
|
||||
Console.WriteLine($"[{log.Timestamp:HH:mm:ss}] [{log.Severity}] {log.Message}");
|
||||
if (!string.IsNullOrWhiteSpace(details))
|
||||
Console.WriteLine($" > {details}");
|
||||
|
||||
Console.ForegroundColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
using BLAIzor.Data;
|
||||
using BLAIzor.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Qdrant.Client.Grpc;
|
||||
using System.Linq;
|
||||
|
||||
namespace BLAIzor.Services
|
||||
{
|
||||
public class WebsiteContentLoaderService
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
|
||||
public WebsiteContentLoaderService(IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
_scopeFactory = scopeFactory;
|
||||
}
|
||||
|
||||
public async Task<ContentGroupModel?> LoadAsync(
|
||||
int siteInfoId,
|
||||
string contentGroupType,
|
||||
Func<int, PointId[], Task<List<WebPageContent>>> fetchVectorsFromQdrant)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
var contentGroup = await db.ContentGroups
|
||||
.Include(cg => cg.Items)
|
||||
.ThenInclude(ci => ci.Chunks)
|
||||
.FirstOrDefaultAsync(cg => cg.SiteInfoId == siteInfoId && cg.Type == contentGroupType);
|
||||
|
||||
if (contentGroup == null)
|
||||
return null;
|
||||
|
||||
// Extract PointId[] from all ContentChunks
|
||||
var allPointIds = contentGroup.Items
|
||||
.SelectMany(i => i.Chunks)
|
||||
.Where(c => c.ChunkIndex != null)
|
||||
.Select(c => new PointId(Convert.ToUInt64(c.ChunkIndex)))
|
||||
.ToArray();
|
||||
foreach (var item in contentGroup.Items)
|
||||
{
|
||||
foreach (var qdraintpoint in item.Chunks)
|
||||
{
|
||||
Console.WriteLine("ContentData: " + item.Title + "," + qdraintpoint.QdrantPointId);
|
||||
}
|
||||
|
||||
}
|
||||
// Fetch vector data from Qdrant
|
||||
var vectorData = await fetchVectorsFromQdrant(siteInfoId, allPointIds);
|
||||
|
||||
|
||||
// Map back to chunks and items
|
||||
var contentItems = contentGroup.Items.Select(ci =>
|
||||
{
|
||||
var chunkPoints = ci.Chunks
|
||||
.Select(chunk =>
|
||||
{
|
||||
var match = vectorData.FirstOrDefault(v => v.UId.Trim().ToLowerInvariant() == chunk.QdrantPointId.Trim().ToLowerInvariant());
|
||||
Console.WriteLine("COMPARE: " + chunk.QdrantPointId + ", " + vectorData.Any(v => v.UId == chunk.QdrantPointId));
|
||||
//Console.WriteLine("COMPARE: " + chunk.QdrantPointId + ", " + vectorData.FirstOrDefault().UId);
|
||||
return match;
|
||||
})
|
||||
.Where(p => p != null)
|
||||
.ToList();
|
||||
|
||||
return new ContentItemModel
|
||||
{
|
||||
ContentItem = ci,
|
||||
Chunks = ci.Chunks.ToList(),
|
||||
VectorPoints = chunkPoints!
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return new ContentGroupModel
|
||||
{
|
||||
SiteInfoId = siteInfoId,
|
||||
ContentGroup = contentGroup,
|
||||
ContentItems = contentItems
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<WebsiteContentModel?> LoadAllAsync(
|
||||
SiteInfo siteInfo,
|
||||
Func<SiteInfo, PointId[], Task<List<WebPageContent>>> fetchVectorsFromQdrant)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
var contentGroups = await db.ContentGroups
|
||||
.Include(cg => cg.Items)
|
||||
.ThenInclude(ci => ci.Chunks)
|
||||
.Where(cg => cg.SiteInfoId == siteInfo.Id)
|
||||
.ToListAsync();
|
||||
|
||||
if (!contentGroups.Any())
|
||||
return null;
|
||||
|
||||
// Get all chunk point IDs across all content groups
|
||||
var allPointIds = contentGroups
|
||||
.SelectMany(cg => cg.Items)
|
||||
.SelectMany(i => i.Chunks)
|
||||
.Where(c => c.QdrantPointId != null)
|
||||
//.Select(c => new PointId(Convert.ToUInt64(c.ChunkIndex)))
|
||||
.Select(c => new PointId(Guid.Parse(c.QdrantPointId)))
|
||||
.ToArray();
|
||||
//.Select(c => new PointId(Guid.Parse(c.QdrantPointId)))
|
||||
//.ToArray();
|
||||
|
||||
// Fetch vector data from Qdrant
|
||||
var vectorData = await fetchVectorsFromQdrant(siteInfo, allPointIds);
|
||||
|
||||
// Build full list of content items with resolved vectors
|
||||
var allContentItems = contentGroups
|
||||
.SelectMany(group => group.Items.Select(ci =>
|
||||
{
|
||||
var chunkPoints = ci.Chunks
|
||||
.Select(chunk =>
|
||||
vectorData.FirstOrDefault(v =>
|
||||
v.UId.Trim().ToLowerInvariant() == chunk.QdrantPointId.Trim().ToLowerInvariant()))
|
||||
.Where(p => p != null)
|
||||
.ToList();
|
||||
|
||||
return new ContentItemModel
|
||||
{
|
||||
ContentItem = ci,
|
||||
Chunks = ci.Chunks.ToList(),
|
||||
VectorPoints = chunkPoints
|
||||
};
|
||||
}))
|
||||
.ToList();
|
||||
|
||||
return new WebsiteContentModel
|
||||
{
|
||||
SiteInfoId = siteInfo.Id,
|
||||
ContentGroups = contentGroups, // <-- You may need to add this prop to WebsiteContentModel
|
||||
ContentItems = allContentItems
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -42,8 +42,8 @@
|
|||
//"CredentialsPath": "D:\\GOOGLECREDENTIALS\\client_secret_359861037120-m3mjvr3kg51i2c2qb38dav62uuqoqs5k.apps.googleusercontent.com.json"
|
||||
"ApiKey": "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",
|
||||
//"ApiKey": "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" //VG3Law
|
||||
"Model": "gpt-4.1-mini"
|
||||
//"Model": "gpt-4o-mini"
|
||||
//"Model": "gpt-4.1-mini"
|
||||
"Model": "gpt-4o-mini"
|
||||
//"Model": "gpt-4.1-nano"
|
||||
},
|
||||
"QDrant": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,251 @@
|
|||
/*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');*/
|
||||
|
||||
label {
|
||||
display: unset !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
* {
|
||||
scrollbar-width: auto;
|
||||
scrollbar-color: #87b1d6 #ffffff00;
|
||||
}
|
||||
|
||||
/* Chrome, Edge, and Safari */
|
||||
*::-webkit-scrollbar {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: #3e9fa3;
|
||||
border-radius: 10px;
|
||||
border: 3px solid #ffffff;
|
||||
}
|
||||
|
||||
|
||||
.reference-button {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
/*background: linear-gradient(to bottom, #533e7e, #3c3666);*/
|
||||
border-radius: 20px;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
color: #e0e0f0;
|
||||
/*box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4);*/
|
||||
/*margin-bottom: 15px;*/
|
||||
}
|
||||
|
||||
.text-content h4 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.text-content p {
|
||||
margin: 4px 0 0 0;
|
||||
font-size: 13px;
|
||||
color: #ccccdd;
|
||||
}
|
||||
|
||||
.icon-buttons {
|
||||
display: flex;
|
||||
gap: 8px; /* spacing between the icons */
|
||||
}
|
||||
|
||||
.icon-circle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: radial-gradient(circle at center, #6782c0, #3c4c8a);
|
||||
color: #ffffff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.bg-transparent {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.bg-panel {
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.bg-panel-gradient {
|
||||
background: linear-gradient(to bottom, #533e7e, #3c3666) !important;
|
||||
}
|
||||
|
||||
.bg-panel-gradient-highlight {
|
||||
background: linear-gradient(to bottom, #63358d, #7d3d7b) !important;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.rz-dialog-mask {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.rz-dialog-wrapper{
|
||||
z-index: 10008 !important;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.rz-dialog-confirm {
|
||||
z-index: 10010 !important;
|
||||
}
|
||||
|
||||
.rz-dialog-title {
|
||||
color: #b0daff !important;
|
||||
}
|
||||
|
||||
.rz-dialog {
|
||||
border-radius: 20px !important;
|
||||
background-color: #0c2533 !important;
|
||||
/*background: linear-gradient(153deg,rgba(12, 37, 51, 0.83) 0%, rgba(87, 188, 199, 0.81) 50%, rgba(237, 83, 196, 0.84) 100%);*/
|
||||
background: linear-gradient(307deg, rgba(12, 37, 51, 0.83) 7%, rgb(152 87 199 / 73%) 96%);
|
||||
box-shadow: 8px 6px 8px 2px rgba(0, 0, 0, 0.4);
|
||||
--rz-dialog-title-background-color: transparent;
|
||||
--rz-primary: #87b1d6;
|
||||
|
||||
}
|
||||
|
||||
.rz-dialog .rz-button {
|
||||
border-radius: 20px !important;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.rz-dialog h3 {
|
||||
color: #b0daff !important;
|
||||
}
|
||||
|
||||
.rz-dialog .text-muted {
|
||||
--bs-text-opacity: 1;
|
||||
color: #608AAD !important;
|
||||
}
|
||||
|
||||
.bg-panel .list-group {
|
||||
--bs-list-group-color: unset !important;
|
||||
--bs-list-group-bg: unset !important;
|
||||
}
|
||||
|
||||
.bg-panel .list-group-item {
|
||||
background-color: unset !important;
|
||||
border: unset !important;
|
||||
}
|
||||
|
||||
.bg-panel .list-group-item .btn {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.bg-panel .content-item-list {
|
||||
max-height: 300px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.rz-dialog-confirm-buttons .rz-base {
|
||||
background-color: #87B1D6 !important;
|
||||
}
|
||||
|
||||
.rz-dialog-confirm-buttons .rz-primary {
|
||||
background-color: red !important;
|
||||
}
|
||||
|
||||
.draggable-popup-dialog {
|
||||
z-index: 10008 !important;
|
||||
}
|
||||
|
||||
.draggable-popup-dialog .form-control {
|
||||
border-radius: 20px !important;
|
||||
background-color: #ffffff38 !important;
|
||||
/* background: linear-gradient(153deg, rgba(12, 37, 51, 0.83) 0%, rgba(87, 188, 199, 0.81) 50%, rgba(237, 83, 196, 0.84) 100%); */
|
||||
/* background
|
||||
Shorthand property for setting most background properties at the same place in the style sheet.
|
||||
Learn more
|
||||
Don't show
|
||||
: linear-gradient(307deg, rgba(12, 37, 51, 0.83) 7%, rgb(152 87 199 / 73%) 96%); */
|
||||
border-style: unset;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.draggable-popup-dialog-wrapper {
|
||||
/*background-color: rgba(0,0,0,0.3);*/
|
||||
/*backdrop-filter: blur(6px);*/
|
||||
z-index: 10008 !important;
|
||||
}
|
||||
|
||||
.editor-button {
|
||||
position: fixed;
|
||||
z-index: 10008 !important;
|
||||
top: 100px;
|
||||
left: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-right-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
}
|
||||
|
||||
.editor-window {
|
||||
position: fixed;
|
||||
background-color: #0c2533 !important;
|
||||
/*background: linear-gradient(153deg,rgba(12, 37, 51, 0.83) 0%, rgba(87, 188, 199, 0.81) 50%, rgba(237, 83, 196, 0.84) 100%);*/
|
||||
background: linear-gradient(307deg, rgba(12, 37, 51, 0.83) 7%, rgb(152 87 199 / 73%) 96%);
|
||||
backdrop-filter: blur(6px);
|
||||
color: #b0daff;
|
||||
z-index: 10008 !important;
|
||||
top: 150px;
|
||||
border-radius: 20px;
|
||||
min-width: 200px;
|
||||
max-width: 200px;
|
||||
height: 80vh;
|
||||
margin-left: 10px;
|
||||
box-shadow: 8px 6px 8px 2px rgba(0, 0, 0, 0.4);
|
||||
--rz-primary: #87b1d6;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.editor-window .btn {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.radzen-popup {
|
||||
display: none;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
height: 360px;
|
||||
width: 600px;
|
||||
border: var(--rz-panel-border);
|
||||
background-color: var(--rz-panel-background-color);
|
||||
box-shadow: var(--rz-panel-shadow);
|
||||
border-radius: var(--rz-border-radius);
|
||||
background-color: #0a4261;
|
||||
/*background-color: #396586;*/
|
||||
/*background-color: #608aad;*/
|
||||
color: #87b1d6;
|
||||
/*background-color: #b0daff;*/
|
||||
}
|
||||
|
||||
.radzen-popup p {
|
||||
color: #fff
|
||||
}
|
||||
|
||||
|
||||
.rz-panel {
|
||||
--rz-panel-padding: 0.6rem !important;
|
||||
}
|
||||
|
||||
.upload-image-container .img-fluid {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,29 @@
|
|||
/*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');*/
|
||||
|
||||
|
||||
.radzen-popup {
|
||||
display: none;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
height: 360px;
|
||||
width: 600px;
|
||||
border: var(--rz-panel-border);
|
||||
background-color: var(--rz-panel-background-color);
|
||||
box-shadow: var(--rz-panel-shadow);
|
||||
border-radius: var(--rz-border-radius);
|
||||
background-color: #0a4261;
|
||||
/*background-color: #396586;*/
|
||||
/*background-color: #608aad;*/
|
||||
color: #87b1d6;
|
||||
/*background-color: #b0daff;*/
|
||||
}
|
||||
|
||||
.radzen-popup p {
|
||||
color: #fff
|
||||
}
|
||||
|
||||
|
||||
.rz-panel {
|
||||
--rz-panel-padding: 0.6rem !important;
|
||||
}
|
||||
|
|
@ -32,6 +55,10 @@
|
|||
|
||||
}
|
||||
|
||||
.floating {
|
||||
animation: float 5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
box-shadow: 0 5px 15px 0px rgba(0,0,0,0.6);
|
||||
|
|
@ -66,7 +93,9 @@
|
|||
object-fit: cover; /* Ensures the image fills the container */
|
||||
}
|
||||
|
||||
|
||||
.footer {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
/*Search2*/
|
||||
.searchBox {
|
||||
|
|
@ -79,6 +108,9 @@
|
|||
margin: 0 auto;
|
||||
transition: 0.8s;
|
||||
overflow: hidden;
|
||||
animation: fadeInUp;
|
||||
animation-duration: 2s;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.searchInput:active > .searchBox{
|
||||
|
|
@ -147,6 +179,16 @@
|
|||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.show {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.navbar > .container-fluid {
|
||||
margin-bottom: 0px;
|
||||
padding-bottom: 0px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.navbar-toggler-icon {
|
||||
/*background-image: var(--bs-navbar-toggler-icon-bg);*/
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e;");
|
||||
|
|
@ -155,7 +197,8 @@
|
|||
|
||||
.navbar-brand {
|
||||
font-size: 1.7rem;
|
||||
color:aqua;
|
||||
color: aqua;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
|
|
@ -316,7 +359,7 @@ p {
|
|||
}
|
||||
|
||||
.navbar-collapse .nav-link {
|
||||
font-size: 1.1rem;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
@ -342,7 +385,7 @@ p {
|
|||
}
|
||||
|
||||
.navbar-collapse .nav-link {
|
||||
font-size: 1rem;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
|
|||
|
|
@ -1,471 +0,0 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
|
||||
|
||||
|
||||
/*search*/
|
||||
|
||||
|
||||
p {
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: 0;
|
||||
color: #000000;
|
||||
/* width: 98%; */
|
||||
font-weight: bold;
|
||||
border-radius: 10px;
|
||||
/*min-height: 50px;*/
|
||||
transition: all 0.2s ease;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.menubtn {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: 0;
|
||||
color: #000000;
|
||||
/* width: 98%; */
|
||||
font-weight: bold;
|
||||
border-radius: 10px;
|
||||
height: 40px;
|
||||
transition: all 0.2s ease;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 20px !important;
|
||||
}
|
||||
|
||||
|
||||
input.search_bar{
|
||||
border: none;
|
||||
outline: none;
|
||||
width: 75px;
|
||||
border-radius: 55px;
|
||||
margin: 0 auto;
|
||||
font-size: 1.3em;
|
||||
color: #0d2840;
|
||||
padding: 15px 30px 15px 45px;
|
||||
transition: all .3s cubic-bezier(0,0,.5,1.5);
|
||||
box-shadow: 0 3px 10px -2px rgba(0,0,0,.1);
|
||||
background: rgba(255, 255, 255, 0.3) url(https://i.imgur.com/seveWIw.png) no-repeat center center;
|
||||
}
|
||||
|
||||
input.search_bar:focus{
|
||||
width: 100%;
|
||||
background-position: calc(100% - 35px) center
|
||||
}
|
||||
|
||||
/*Removes default x in search fields (webkit only i guess)*/
|
||||
input[type=search]::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
/*Changes the color of the placeholder*/
|
||||
::-webkit-input-placeholder {
|
||||
color: #0d2840;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
:-moz-placeholder {
|
||||
color: #0d2840;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
color: #0d2840;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
color: #0d2840;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/*search*/
|
||||
|
||||
/*Search2*/
|
||||
.searchBox {
|
||||
width: 60px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
height: 60px;
|
||||
border-radius: 40px;
|
||||
padding: 10px;
|
||||
margin: 0 auto;
|
||||
transition: 0.8s;
|
||||
}
|
||||
|
||||
.searchInput:active > .searchBox{
|
||||
width:100%
|
||||
}
|
||||
.searchInput:focus > .searchBox {
|
||||
width: 100%
|
||||
}
|
||||
.searchInput::placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.searchBox:hover {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.searchBox:hover > .searchInput {
|
||||
width: calc(100% - 60px);
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.searchBox:hover > .searchButton {
|
||||
background: white;
|
||||
color: #2f3640;
|
||||
}
|
||||
|
||||
.searchButton {
|
||||
color: white;
|
||||
float: right;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #e9bb86;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
border: none;
|
||||
background: none;
|
||||
outline: none;
|
||||
font-size: 1.3em !important;
|
||||
color: #e9bb86 !important;
|
||||
float: left;
|
||||
padding: 0;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
transition: 0.4s;
|
||||
line-height: 40px;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
/*Search2*/
|
||||
|
||||
|
||||
.event {
|
||||
border-radius: 20px !important;
|
||||
background-color: rgba(255, 255, 255, 0.2) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
border: 0;
|
||||
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
/*card design*/
|
||||
.card {
|
||||
border-radius: 10px !important;
|
||||
overflow: hidden;
|
||||
background-color: rgba(255, 255, 255, 0.2) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
border: 0;
|
||||
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
|
||||
transition: all 0.15s ease;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.1), 0 10px 8px rgba(0, 0, 0, 0.015);
|
||||
}
|
||||
|
||||
.card-body .card-title {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.3px;
|
||||
font-size: 24px;
|
||||
color: #121212;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0.3px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.card .container {
|
||||
width: 88%;
|
||||
/*background: #F0EEF8;*/
|
||||
border-radius: 30px;
|
||||
/*height: 140px;*/
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container:hover > img {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.container img {
|
||||
/*padding: 75px;*/
|
||||
/*margin-top: -40px;
|
||||
margin-bottom: -40px;*/
|
||||
transition: 0.4s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #9b7d5a;
|
||||
}
|
||||
|
||||
.btn:focus {
|
||||
background: #9b7d5a;
|
||||
outline: 0;
|
||||
}
|
||||
/*card design*/
|
||||
|
||||
/*bg*/
|
||||
|
||||
|
||||
:root {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Quicksand', sans-serif;
|
||||
color: #f2d8bb;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: #000;
|
||||
/*background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));*/
|
||||
/*background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;*/
|
||||
/*background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;*/
|
||||
background-repeat: no-repeat;
|
||||
/*animation: 10s movement linear infinite;*/
|
||||
}
|
||||
|
||||
body::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.myspan {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 5rem;
|
||||
color: transparent;
|
||||
text-shadow: 0px 0px 1px rgba(255, 255, 255, .6), 0px 4px 4px rgba(0, 0, 0, .05);
|
||||
letter-spacing: .2rem;
|
||||
}
|
||||
|
||||
/*@keyframes movement {
|
||||
0%, 100% {
|
||||
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
|
||||
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
|
||||
}
|
||||
|
||||
25% {
|
||||
background-size: 100vmax 100vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 60vmax 60vmax;
|
||||
background-position: -60vmax -90vmax, 50vmax -40vmax, 0vmax -20vmax, -40vmax -20vmax, 40vmax 60vmax;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-size: 80vmax 80vmax, 110vmax 110vmax, 80vmax 80vmax, 60vmax 60vmax, 80vmax 80vmax;
|
||||
background-position: -50vmax -70vmax, 40vmax -30vmax, 10vmax 0vmax, 20vmax 10vmax, 30vmax 70vmax;
|
||||
}
|
||||
|
||||
75% {
|
||||
background-size: 90vmax 90vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 70vmax 70vmax;
|
||||
background-position: -50vmax -40vmax, 50vmax -30vmax, 20vmax 0vmax, -10vmax 10vmax, 40vmax 60vmax;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/*bg*/
|
||||
|
||||
.mytextarea {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
border-width: 0px;
|
||||
height: unset !important;
|
||||
}
|
||||
|
||||
.mytextarea:active {
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.mytextarea:focus-visible {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
border-width: 0px !important;
|
||||
outline: -webkit-focus-ring-color auto 0px;
|
||||
outline-color: transparent;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.7rem;
|
||||
color: #e9bb86;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 15px;
|
||||
display: unset !important;
|
||||
}
|
||||
|
||||
.form-select > option {
|
||||
background-color: rgba(255, 255, 255, 0.2)
|
||||
}
|
||||
|
||||
.contactform-overlay {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
padding: 100px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
/* padding-top: 10vh; */
|
||||
backdrop-filter: blur(20px);
|
||||
/* background-color: rgba(1, 1, 1, .4); */
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: rgba(255,255,255,0.4);
|
||||
border-radius: 15px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
form-control::placeholder{
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.contactform-close-overlay {
|
||||
position: relative;
|
||||
height: 10vh;
|
||||
}
|
||||
|
||||
.contactform-popup-content {
|
||||
height: 80vh;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.contactform-popup-close {
|
||||
position: relative;
|
||||
height: 10vh;
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
.calendly-overlay {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
top: 0px;
|
||||
/* padding-top: 10vh; */
|
||||
backdrop-filter: blur(20px);
|
||||
/* background-color: rgba(1, 1, 1, .4); */
|
||||
}
|
||||
|
||||
.calendly-close-overlay {
|
||||
position: relative;
|
||||
height: 10vh;
|
||||
}
|
||||
|
||||
.calendly-popup-content {
|
||||
height: 80vh;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.calendly-popup-close {
|
||||
position: relative;
|
||||
height: 10vh;
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
#myVideo {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: #f2d8bb !important;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
height: 100vh;
|
||||
/*display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;*/
|
||||
text-align: center !important;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-link {
|
||||
font-size: 1.4em;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-item:not(:last-child) {
|
||||
border-bottom: 0px solid white;
|
||||
padding: 0.2em 1em;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #1c120c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #f2d8bb !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.img-fluid {
|
||||
max-height: 50vh !important;
|
||||
width: auto;
|
||||
}
|
||||
|
|
@ -1,471 +0,0 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
|
||||
|
||||
|
||||
/*search*/
|
||||
|
||||
|
||||
p {
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: 0;
|
||||
color: #000000;
|
||||
/* width: 98%; */
|
||||
font-weight: bold;
|
||||
border-radius: 10px;
|
||||
/*min-height: 50px;*/
|
||||
transition: all 0.2s ease;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.menubtn {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: 0;
|
||||
color: #000000;
|
||||
/* width: 98%; */
|
||||
font-weight: bold;
|
||||
border-radius: 10px;
|
||||
height: 40px;
|
||||
transition: all 0.2s ease;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 20px !important;
|
||||
}
|
||||
|
||||
|
||||
input.search_bar{
|
||||
border: none;
|
||||
outline: none;
|
||||
width: 75px;
|
||||
border-radius: 55px;
|
||||
margin: 0 auto;
|
||||
font-size: 1.3em;
|
||||
color: #0d2840;
|
||||
padding: 15px 30px 15px 45px;
|
||||
transition: all .3s cubic-bezier(0,0,.5,1.5);
|
||||
box-shadow: 0 3px 10px -2px rgba(0,0,0,.1);
|
||||
background: rgba(255, 255, 255, 0.3) url(https://i.imgur.com/seveWIw.png) no-repeat center center;
|
||||
}
|
||||
|
||||
input.search_bar:focus{
|
||||
width: 100%;
|
||||
background-position: calc(100% - 35px) center
|
||||
}
|
||||
|
||||
/*Removes default x in search fields (webkit only i guess)*/
|
||||
input[type=search]::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
/*Changes the color of the placeholder*/
|
||||
::-webkit-input-placeholder {
|
||||
color: #0d2840;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
:-moz-placeholder {
|
||||
color: #0d2840;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
color: #0d2840;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
color: #0d2840;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/*search*/
|
||||
|
||||
/*Search2*/
|
||||
.searchBox {
|
||||
width: 60px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
height: 60px;
|
||||
border-radius: 40px;
|
||||
padding: 10px;
|
||||
margin: 0 auto;
|
||||
transition: 0.8s;
|
||||
}
|
||||
|
||||
.searchInput:active > .searchBox{
|
||||
width:100%
|
||||
}
|
||||
.searchInput:focus > .searchBox {
|
||||
width: 100%
|
||||
}
|
||||
.searchInput::placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.searchBox:hover {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.searchBox:hover > .searchInput {
|
||||
width: calc(100% - 60px);
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.searchBox:hover > .searchButton {
|
||||
background: white;
|
||||
color: #2f3640;
|
||||
}
|
||||
|
||||
.searchButton {
|
||||
color: white;
|
||||
float: right;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #e9bb86;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
border: none;
|
||||
background: none;
|
||||
outline: none;
|
||||
font-size: 1.3em !important;
|
||||
color: #e9bb86 !important;
|
||||
float: left;
|
||||
padding: 0;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
transition: 0.4s;
|
||||
line-height: 40px;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
/*Search2*/
|
||||
|
||||
|
||||
.event {
|
||||
border-radius: 20px !important;
|
||||
background-color: rgba(255, 255, 255, 0.2) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
border: 0;
|
||||
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
/*card design*/
|
||||
.card {
|
||||
border-radius: 10px !important;
|
||||
overflow: hidden;
|
||||
background-color: rgba(255, 255, 255, 0.2) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
border: 0;
|
||||
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
|
||||
transition: all 0.15s ease;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.1), 0 10px 8px rgba(0, 0, 0, 0.015);
|
||||
}
|
||||
|
||||
.card-body .card-title {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.3px;
|
||||
font-size: 24px;
|
||||
color: #121212;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0.3px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.card .container {
|
||||
width: 88%;
|
||||
/*background: #F0EEF8;*/
|
||||
border-radius: 30px;
|
||||
/*height: 140px;*/
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container:hover > img {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.container img {
|
||||
/*padding: 75px;*/
|
||||
/*margin-top: -40px;
|
||||
margin-bottom: -40px;*/
|
||||
transition: 0.4s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #9b7d5a;
|
||||
}
|
||||
|
||||
.btn:focus {
|
||||
background: #9b7d5a;
|
||||
outline: 0;
|
||||
}
|
||||
/*card design*/
|
||||
|
||||
/*bg*/
|
||||
|
||||
|
||||
:root {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Quicksand', sans-serif;
|
||||
color: #f2d8bb;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: #000;
|
||||
/*background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));*/
|
||||
/*background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;*/
|
||||
/*background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;*/
|
||||
background-repeat: no-repeat;
|
||||
/*animation: 10s movement linear infinite;*/
|
||||
}
|
||||
|
||||
body::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.myspan {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 5rem;
|
||||
color: transparent;
|
||||
text-shadow: 0px 0px 1px rgba(255, 255, 255, .6), 0px 4px 4px rgba(0, 0, 0, .05);
|
||||
letter-spacing: .2rem;
|
||||
}
|
||||
|
||||
/*@keyframes movement {
|
||||
0%, 100% {
|
||||
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
|
||||
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
|
||||
}
|
||||
|
||||
25% {
|
||||
background-size: 100vmax 100vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 60vmax 60vmax;
|
||||
background-position: -60vmax -90vmax, 50vmax -40vmax, 0vmax -20vmax, -40vmax -20vmax, 40vmax 60vmax;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-size: 80vmax 80vmax, 110vmax 110vmax, 80vmax 80vmax, 60vmax 60vmax, 80vmax 80vmax;
|
||||
background-position: -50vmax -70vmax, 40vmax -30vmax, 10vmax 0vmax, 20vmax 10vmax, 30vmax 70vmax;
|
||||
}
|
||||
|
||||
75% {
|
||||
background-size: 90vmax 90vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 70vmax 70vmax;
|
||||
background-position: -50vmax -40vmax, 50vmax -30vmax, 20vmax 0vmax, -10vmax 10vmax, 40vmax 60vmax;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/*bg*/
|
||||
|
||||
.mytextarea {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
border-width: 0px;
|
||||
height: unset !important;
|
||||
}
|
||||
|
||||
.mytextarea:active {
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.mytextarea:focus-visible {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
border-width: 0px !important;
|
||||
outline: -webkit-focus-ring-color auto 0px;
|
||||
outline-color: transparent;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.7rem;
|
||||
color: #e9bb86;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 15px;
|
||||
display: unset !important;
|
||||
}
|
||||
|
||||
.form-select > option {
|
||||
background-color: rgba(255, 255, 255, 0.2)
|
||||
}
|
||||
|
||||
.contactform-overlay {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
padding: 100px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
/* padding-top: 10vh; */
|
||||
backdrop-filter: blur(20px);
|
||||
/* background-color: rgba(1, 1, 1, .4); */
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: rgba(255,255,255,0.4);
|
||||
border-radius: 15px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
form-control::placeholder{
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.contactform-close-overlay {
|
||||
position: relative;
|
||||
height: 10vh;
|
||||
}
|
||||
|
||||
.contactform-popup-content {
|
||||
height: 80vh;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.contactform-popup-close {
|
||||
position: relative;
|
||||
height: 10vh;
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
.calendly-overlay {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
top: 0px;
|
||||
/* padding-top: 10vh; */
|
||||
backdrop-filter: blur(20px);
|
||||
/* background-color: rgba(1, 1, 1, .4); */
|
||||
}
|
||||
|
||||
.calendly-close-overlay {
|
||||
position: relative;
|
||||
height: 10vh;
|
||||
}
|
||||
|
||||
.calendly-popup-content {
|
||||
height: 80vh;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.calendly-popup-close {
|
||||
position: relative;
|
||||
height: 10vh;
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
#myVideo {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: #f2d8bb !important;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
height: 100vh;
|
||||
/*display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;*/
|
||||
text-align: center !important;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-link {
|
||||
font-size: 1.4em;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-item:not(:last-child) {
|
||||
border-bottom: 0px solid white;
|
||||
padding: 0.2em 1em;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #1c120c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #f2d8bb !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.img-fluid {
|
||||
max-height: 50vh !important;
|
||||
width: auto;
|
||||
}
|
||||
|
|
@ -1,52 +1,78 @@
|
|||
/*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');*/
|
||||
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');
|
||||
|
||||
.rz-html-editor-content {
|
||||
background-color: #00000033;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
/*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');*/
|
||||
|
||||
/*search*/
|
||||
|
||||
p {
|
||||
font-size: large;
|
||||
/*text-align: justify;*/
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
label {
|
||||
display: none;
|
||||
color: #000;
|
||||
/* display: none; */
|
||||
}
|
||||
|
||||
.img-fluid {
|
||||
max-height: 50vh;
|
||||
width: auto;
|
||||
|
||||
}
|
||||
|
||||
.pop-img {
|
||||
border-radius: 20px !important;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
#maincontrol {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
margin-bottom: 10px;
|
||||
z-index:10000;
|
||||
}
|
||||
|
||||
#currentContent {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.displaysearch {
|
||||
padding-left: 5vw;
|
||||
padding-right: 5vw;
|
||||
}
|
||||
|
||||
.card-img-top {
|
||||
max-height: 50vh;
|
||||
width: auto;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
background: #87b1d6;
|
||||
border: 0;
|
||||
color: #000000;
|
||||
/* width: 98%; */
|
||||
width: fit-content;
|
||||
font-weight: bold;
|
||||
border-radius: 20px;
|
||||
/*min-height: 50px;*/
|
||||
transition: all 0.2s ease;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
margin: 15px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.btn:hover{
|
||||
background: #e5b984;
|
||||
color:#000000;
|
||||
|
||||
.voicebutton {
|
||||
border-radius: 50% !important;
|
||||
padding: 10px !important;
|
||||
width: 40px;
|
||||
height:40px;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 20px !important;
|
||||
}
|
||||
|
||||
.menubtn {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: 0;
|
||||
color: #000000;
|
||||
width: fit-content;
|
||||
/* width: 98%; */
|
||||
font-weight: bold;
|
||||
border-radius: 20px;
|
||||
height: 40px;
|
||||
|
|
@ -59,14 +85,17 @@ label {
|
|||
background: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 20px !important;
|
||||
}
|
||||
|
||||
.bg-dark-secondary {
|
||||
background-color: #111422 !important;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #111422;
|
||||
}
|
||||
|
||||
input.search_bar{
|
||||
border: none;
|
||||
|
|
@ -132,6 +161,10 @@ input[type=search]::-webkit-search-cancel-button {
|
|||
width: 100%
|
||||
}
|
||||
|
||||
.searchInput::placeholder {
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.searchBox:hover {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -151,7 +184,7 @@ input[type=search]::-webkit-search-cancel-button {
|
|||
float: right;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
border-radius: 50px;
|
||||
background-color: #e493d0;
|
||||
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
|
||||
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
|
||||
|
|
@ -243,6 +276,23 @@ input[type=search]::-webkit-search-cancel-button {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #e493d0;
|
||||
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
|
||||
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
|
||||
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
|
||||
background-repeat: no-repeat;
|
||||
animation: 10s movement linear infinite;
|
||||
}
|
||||
|
||||
.btn:focus {
|
||||
background-color: #e493d0;
|
||||
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
|
||||
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
|
||||
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
|
||||
background-repeat: no-repeat;
|
||||
animation: 10s movement linear infinite;
|
||||
}
|
||||
/*card design*/
|
||||
|
||||
/*bg*/
|
||||
|
|
@ -253,16 +303,16 @@ input[type=search]::-webkit-search-cancel-button {
|
|||
}
|
||||
|
||||
body {
|
||||
font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;
|
||||
/*font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;*/
|
||||
/*font-family: 'Quicksand', sans-serif;*/
|
||||
color: #fff !important;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: #e493d0;
|
||||
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
|
||||
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
|
||||
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
|
||||
background-color: #060816;
|
||||
/*background: linear-gradient(295deg,#060816,#090f59,#440959,#000888);
|
||||
background-size: 240% 240%;
|
||||
animation: gradient-animation 24s ease infinite;*/
|
||||
background-repeat: no-repeat;
|
||||
/*animation: 10s movement linear infinite;*/
|
||||
}
|
||||
|
||||
body::after {
|
||||
|
|
@ -277,6 +327,18 @@ body {
|
|||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
@keyframes gradient-animation {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.myspan {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
|
@ -291,7 +353,7 @@ body {
|
|||
letter-spacing: .2rem;
|
||||
}
|
||||
|
||||
/*@keyframes movement {
|
||||
@keyframes movement {
|
||||
0%, 100% {
|
||||
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
|
||||
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
|
||||
|
|
@ -311,7 +373,7 @@ body {
|
|||
background-size: 90vmax 90vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 70vmax 70vmax;
|
||||
background-position: -50vmax -40vmax, 50vmax -30vmax, 20vmax 0vmax, -10vmax 10vmax, 40vmax 60vmax;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
/*bg*/
|
||||
|
|
@ -320,7 +382,7 @@ body {
|
|||
background-color: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 10px;
|
||||
border-radius: 25px !important;
|
||||
border-radius: 10px;
|
||||
border-width: 0px;
|
||||
height: unset !important;
|
||||
}
|
||||
|
|
@ -336,19 +398,20 @@ body {
|
|||
outline-color: transparent;
|
||||
}
|
||||
|
||||
.navbar-toggler-icon {
|
||||
/*background-image: var(--bs-navbar-toggler-icon-bg);*/
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e;");
|
||||
z-index: 10;
|
||||
.navbar-toggler {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.7rem;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 55px;
|
||||
border-radius: 5px;
|
||||
display: unset !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.form-select > option {
|
||||
|
|
@ -370,9 +433,12 @@ body {
|
|||
|
||||
.form-control {
|
||||
background-color: rgba(255,255,255,0.4);
|
||||
border-radius: 25px;
|
||||
border-radius: 5px;
|
||||
height: 50px;
|
||||
}
|
||||
.form-control::placeholder {
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.contactform-close-overlay {
|
||||
position: relative;
|
||||
|
|
@ -425,49 +491,86 @@ body {
|
|||
bottom: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
display: unset !important;
|
||||
.table {
|
||||
color: #fff !important;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.show {
|
||||
font-size: 1.6rem;
|
||||
letter-spacing: 2px;
|
||||
/*height: 100vh;*/
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
height: 100vh;
|
||||
/*height: 100vh;*/
|
||||
/*display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;*/
|
||||
text-align: center !important;
|
||||
align-content: center;
|
||||
overflow-y: scroll;
|
||||
/*overflow-y: scroll;*/
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-link {
|
||||
font-size: 1.5em;
|
||||
letter-spacing: 2px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-item:not(:last-child) {
|
||||
border-bottom: 1px solid gray;
|
||||
padding: 0.2em 4em;
|
||||
border-bottom: 0px solid white;
|
||||
/*padding: 0.2em 4em;*/
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #ffffff;
|
||||
color: #000;
|
||||
background-color: #111422;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.voicebutton {
|
||||
width: 42.5px;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
.img-fluid {
|
||||
max-height: 50vh;
|
||||
.row {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #EEEEEE;
|
||||
font-size: 1,1rem;
|
||||
}
|
||||
.container-fluid {
|
||||
margin-bottom:20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 🔽 Mobile Responsiveness */
|
||||
@media (max-width: 768px) {
|
||||
h1 { font-size: 2rem; }
|
||||
h2 { font-size: 1.6rem; }
|
||||
h3 { font-size: 1.4rem; }
|
||||
p, li { font-size: 1rem; }
|
||||
.navbar-collapse .nav-link { font-size: 1.1rem; }
|
||||
p {text-align: justify;}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
h1 { font-size: 1.6rem; }
|
||||
h2 { font-size: 1.3rem; }
|
||||
h3 { font-size: 1.1rem; }
|
||||
p, li { font-size: 0.95rem; }
|
||||
.navbar-collapse .nav-link { font-size: 1rem; }
|
||||
p {text-align: justify;}
|
||||
}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
|
||||
|
||||
.card {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.table {
|
||||
color: #f2d8bb !important;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
height: 100vh;
|
||||
text-align: center !important;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-link {
|
||||
font-size: 1.2rem;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-item:not(:last-child) {
|
||||
padding: 0.2em 1em;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #022c28;
|
||||
color: #d0eae9;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #d0eae9 !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #022c28;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
color: aqua;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-size: 1rem; /* Base size */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 700;
|
||||
font-size: 2.5rem;
|
||||
padding-top: 30px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 500;
|
||||
font-size: 2rem;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 400;
|
||||
font-size: 1.6rem;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #fff;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
li {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #d0eae9;
|
||||
background-color: #014d4e;
|
||||
border: 0px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
color: #fff;
|
||||
background-color: #086262;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding-bottom: 30px;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.searchInput::placeholder {
|
||||
color: #d0eae9;
|
||||
}
|
||||
|
||||
#myVideo {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: -100px;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
transform: translateX(calc((100% - 100vw) / 2));
|
||||
}
|
||||
|
||||
.img-fluid {
|
||||
max-height: 50vh;
|
||||
width: auto;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.sp-img {
|
||||
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
|
||||
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
|
||||
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
|
||||
}
|
||||
|
||||
.col {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 5px;
|
||||
display: unset !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.list-group-item,
|
||||
.bg-light {
|
||||
background-color: rgb(11 24 23 / 76%) !important;
|
||||
backdrop-filter: blur(8px) !important;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
--bs-text-opacity: 1;
|
||||
color: aqua;
|
||||
}
|
||||
|
||||
/* 🔽 Mobile Responsiveness */
|
||||
@media (max-width: 768px) {
|
||||
h1 { font-size: 2rem; }
|
||||
h2 { font-size: 1.6rem; }
|
||||
h3 { font-size: 1.4rem; }
|
||||
p, li { font-size: 1rem; }
|
||||
.navbar-collapse .nav-link { font-size: 1.1rem; }
|
||||
p {text-align: justify;}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
h1 { font-size: 1.6rem; }
|
||||
h2 { font-size: 1.3rem; }
|
||||
h3 { font-size: 1.1rem; }
|
||||
p, li { font-size: 0.95rem; }
|
||||
.navbar-collapse .nav-link { font-size: 1rem; }
|
||||
p {text-align: justify;}
|
||||
}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
|
||||
|
||||
.card {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.table {
|
||||
color: #f2d8bb !important;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
height: 100vh;
|
||||
text-align: center !important;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-link {
|
||||
font-size: 1.2rem;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.navbar-collapse .nav-item:not(:last-child) {
|
||||
padding: 0.2em 1em;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #022c28;
|
||||
color: #d0eae9;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #d0eae9 !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #022c28;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
color: aqua;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-size: 1rem; /* Base size */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 700;
|
||||
font-size: 2.5rem;
|
||||
padding-top: 30px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 500;
|
||||
font-size: 2rem;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 400;
|
||||
font-size: 1.6rem;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #fff;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
li {
|
||||
color: #fff;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #d0eae9;
|
||||
background-color: #014d4e;
|
||||
border: 0px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
color: #fff;
|
||||
background-color: #086262;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding-bottom: 30px;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.searchInput::placeholder {
|
||||
color: #d0eae9;
|
||||
}
|
||||
|
||||
#myVideo {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: -100px;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
transform: translateX(calc((100% - 100vw) / 2));
|
||||
}
|
||||
|
||||
.img-fluid {
|
||||
max-height: 50vh;
|
||||
width: auto;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.sp-img {
|
||||
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
|
||||
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
|
||||
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
|
||||
}
|
||||
|
||||
.col {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 5px;
|
||||
display: unset !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.list-group-item,
|
||||
.bg-light {
|
||||
background-color: rgb(11 24 23 / 76%) !important;
|
||||
backdrop-filter: blur(8px) !important;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
--bs-text-opacity: 1;
|
||||
color: aqua;
|
||||
}
|
||||
|
||||
/* 🔽 Mobile Responsiveness */
|
||||
@media (max-width: 768px) {
|
||||
h1 { font-size: 2rem; }
|
||||
h2 { font-size: 1.6rem; }
|
||||
h3 { font-size: 1.4rem; }
|
||||
p, li { font-size: 1rem; }
|
||||
.navbar-collapse .nav-link { font-size: 1.1rem; }
|
||||
p {text-align: justify;}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
h1 { font-size: 1.6rem; }
|
||||
h2 { font-size: 1.3rem; }
|
||||
h3 { font-size: 1.1rem; }
|
||||
p, li { font-size: 0.95rem; }
|
||||
.navbar-collapse .nav-link { font-size: 1rem; }
|
||||
p {text-align: justify;}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 28 KiB |