button clicks further development, admin area developments and fixes, snippet preview, create form

This commit is contained in:
Adam 2025-04-22 18:40:04 +02:00
parent 8bf5c1c787
commit fb6283628f
46 changed files with 4936 additions and 18256 deletions

View File

@ -49,7 +49,7 @@
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div> *@
<article class="content container-fluid px-4 text-center">
<article class="content container-fluid text-center">
@Body
</article>
</main>

View File

@ -0,0 +1,65 @@
@using BLAIzor.Services
@inherits LayoutComponentBase
@inject ScopedContentService _scopedContentService;
@inject NavigationManager _navigationManager;
<!-- The video -->
<div class="page" style="z-index: 1">
<main>
@* <div class="top-row px-4" style="z-index: 2">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div> *@
<article class="content container-fluid text-center">
@Body
</article>
</main>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script>
$(document).ready(function () {
var video = document.getElementById("myVideo");
video.oncanplaythrough = function () {
video.muted = true;
video.play();
};
});
</script>
<script>
function setHtmlEditorSourceMode() {
let retries = 10; // Max retries
let interval = setInterval(() => {
let sourceButton = document.querySelector('.rz-html-editor-button');
if (sourceButton) {
sourceButton.click();
clearInterval(interval); // Stop checking once clicked
}
if (--retries === 0) {
clearInterval(interval); // Stop trying after 10 attempts
}
}, 300); // Check every 300ms
}
</script>
@code{
public void HomeClick()
{
_navigationManager.Refresh(true);
}
protected override void OnInitialized()
{
}
}

View File

@ -0,0 +1,96 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@ -0,0 +1,91 @@
@page "/createform/{SiteId:int}"
@using System.IO
@using BLAIzor.Models
@using BLAIzor.Services
@using Microsoft.AspNetCore.Components.Forms
@using System.Text
@using UglyToad.PdfPig
@using BLAIzor.Components.Layout
@attribute [Authorize]
@layout AdminLayout
@inject ContentEditorService ContentEditorService
<h3>Create a new from from pdf or word</h3>
<InputFile OnChange="HandleFileSelected" />
@if (ExtractedFields.Any())
{
<h5>Extracted Fields:</h5>
<ul>
@foreach (var field in ExtractedFields)
{
<li><strong>@field.GroupName:</strong> @field.Fields.Count (@(field.Repeatable?"Repeatable":"Non repeateble"))</li>
}
</ul>
<div class="col-12">
<FormDescriptionEditor Groups="@ExtractedFields" TitleString="Form preview" isEditing=true></FormDescriptionEditor>
</div>
}
@code {
[Parameter]
public int SiteId { get; set; }
private string PdfText = string.Empty;
private List<FormFieldGroup> ExtractedFields = new();
private async Task HandleFileSelected(InputFileChangeEventArgs e)
{
var file = e.File;
if (file == null || !file.ContentType.Contains("pdf")) return;
var tempPath = Path.Combine(Path.GetTempPath(), file.Name);
await using (var stream = file.OpenReadStream())
await using (var fileStream = File.Create(tempPath))
{
await stream.CopyToAsync(fileStream);
}
PdfText = ExtractPdfText(tempPath);
// Call OpenAI to analyze and extract structured fields
ExtractedFields = await CallAiForFieldExtraction(PdfText);
}
private string ExtractPdfText(string filePath)
{
var sb = new StringBuilder();
using (var document = PdfDocument.Open(filePath))
{
foreach (var page in document.GetPages())
{
sb.AppendLine(page.Text);
}
}
return sb.ToString();
}
private async Task<List<FormFieldGroup>> CallAiForFieldExtraction(string plainText)
{
var jsonResult = await ContentEditorService.AnalyzeGroupedFormFieldsFromText(plainText);
Console.WriteLine($"📄 PDF form AI response: {jsonResult}");
try
{
var groups = System.Text.Json.JsonSerializer.Deserialize<List<FormFieldGroup>>(jsonResult, new System.Text.Json.JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return groups ?? new List<FormFieldGroup>();
}
catch (Exception ex)
{
Console.WriteLine($"❌ Error parsing AI JSON: {ex.Message}");
return new List<FormFieldGroup>();
}
}
}

View File

@ -15,13 +15,7 @@
<h3>Edit Design Template</h3>
<HeadContent>
@* <link rel="stylesheet" href="@(selectedBrandName+".css")" /> *@
@if (!string.IsNullOrEmpty(currentCssTemplate.CssContent))
{
<style>@currentCssTemplate.CssContent</style>
}
</HeadContent>
@if (isLoading)
{
@ -32,15 +26,21 @@ else
<EditForm Model="currentTemplate" OnValidSubmit="SaveTemplate">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="row">
<div class="form-group col-md-4">
<label>Template Name</label>
<InputText class="form-control" @bind-Value="currentTemplate.TemplateName" />
</div>
<div class="form-group">
<label>Template Name</label>
<InputText class="form-control" @bind-Value="currentTemplate.TemplateName" />
</div>
<div class="form-group col-md-4">
<label>Template photo url</label>
<InputText class="form-control" @bind-Value="currentTemplate.TemplatePhotoUrl" />
</div>
<div class="form-group">
<label>Template photo url</label>
<InputText class="form-control" @bind-Value="currentTemplate.TemplatePhotoUrl" />
<div class="form-group col-md-4">
<label>Tags (comma-separated)</label>
<InputText class="form-control" @bind-Value="currentTemplate.Tags" />
</div>
</div>
<div class="form-group">
@ -48,26 +48,20 @@ else
<InputTextArea class="form-control" @bind-Value="currentTemplate.Description" />
</div>
<div class="form-group">
<label>Tags (comma-separated)</label>
<InputText class="form-control" @bind-Value="currentTemplate.Tags" />
</div>
<div class="form-group">
<label>CSS Content</label>
<RadzenHtmlEditor
@bind-Value=@currentCssTemplate.CssContent
style="height: 450px; color:#000; background-color: rgba(255,255,255,0.4)"
Input=@OnInput
Change=@OnChange
Paste=@OnPaste
UploadComplete=@OnUploadComplete
Execute=@OnExecute
UploadUrl="upload/image" >
<RadzenHtmlEditor @bind-Value=@currentCssTemplate.CssContent
style="height: 450px; color:#000; background-color: rgba(255,255,255,0.4)"
Input=@OnInput
Change=@OnChange
Paste=@OnPaste
UploadComplete=@OnUploadComplete
Execute=@OnExecute
UploadUrl="upload/image">
<RadzenHtmlEditorUndo />
<RadzenHtmlEditorRedo />
<RadzenHtmlEditorSource />
</RadzenHtmlEditor>
<RadzenHtmlEditorSource />
</RadzenHtmlEditor>
@* <InputTextArea class="form-control" @bind-Value="currentCssTemplate.CssContent" /> *@
</div>
@ -78,18 +72,46 @@ else
<button class="btn btn-danger" @onclick="DeleteTemplate">Delete Template</button>
foreach(var snippet in SnippetList)
{
<TemplateSnippetEditor Snippet="snippet" OnContentUpdated="UpdateSnippet"></TemplateSnippetEditor>
}
<div class="rz-p-sm-12">
<RadzenAccordion>
<Items>
@{
foreach (var snippet in SnippetList)
{
<RadzenAccordionItem Text="@snippet.Name" Icon="account_balance_wallet" CollapseTitle="Collapse snippet."
ExpandTitle="Expand snippet." CollapseAriaLabel="Collapse the snippet details."
ExpandAriaLabel="Expand the snippet details.">
<TemplateSnippetEditor TemplateId="@TemplateId" Snippet="@snippet" OnContentUpdated="UpdateSnippet"></TemplateSnippetEditor>
</RadzenAccordionItem>
}
}
</Items>
</RadzenAccordion>
</div>
<button class="btn btn-primary mt-3" @onclick="AddSnippet">Add new html snippets</button>
<button class="btn btn-success mt-3" @onclick="SaveSnippets">Save html snippets</button>
}
<script>
var sessionId = null;
function setSessionId(id) {
sessionId = id;
console.log("Session ID set:", sessionId);
}
</script>
@code {
[Parameter]
public int TemplateId { get; set; }
@ -100,17 +122,27 @@ else
private bool hasCss = false;
private bool hasCollection = false;
private List<HtmlSnippet> SnippetList = [];
private string SessionId;
private static readonly Dictionary<string, EditTemplate> _instances = new();
protected override async Task OnInitializedAsync()
{
await LoadTemplate();
SessionId = Guid.NewGuid().ToString();
_instances[SessionId] = this;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("setSessionId", SessionId);
await JSRuntime.InvokeVoidAsync("setHtmlEditorSourceMode");
await LoadTemplate();
var cssPath = await CssTemplateService.SaveTempCssFileAsync(currentCssTemplate.CssContent, SessionId);
await JSRuntime.InvokeVoidAsync("seemgen.injectCssFile", cssPath);
}
}
@ -123,21 +155,21 @@ else
currentCssTemplate = await CssTemplateService.GetByDesignTemplateIdAsync(TemplateId);
var currentCollection = await QDrantService.GetCollectionByNameAsync(currentTemplate.QDrandCollectionName);
Console.Write(currentCollection);
if(currentCssTemplate == null)
if (currentCssTemplate == null)
{
hasCss = false;
currentCssTemplate = new();
currentCssTemplate.DesignTemplateId = TemplateId;
currentCssTemplate.DesignTemplateId = TemplateId;
}
else
{
hasCss = true;
}
if(!string.IsNullOrEmpty(currentCollection))
if (!string.IsNullOrEmpty(currentCollection))
{
hasCollection = true;
var collectionCount = await QDrantService.GetCollectionCount(currentTemplate.QDrandCollectionName);
for(int i=1; i <= collectionCount; i++)
for (int i = 1; i <= collectionCount; i++)
{
var snippet = await QDrantService.GetSnippetAsync(i, currentTemplate.QDrandCollectionName);
var selectedPoint = JsonConvert.DeserializeObject<QDrantGetPointResult>(snippet)!;
@ -149,11 +181,12 @@ else
}
else
{
//let's create a collection
//let's create a collection
var result = QDrantService.CreateQdrantCollectionAsync(currentTemplate.QDrandCollectionName);
}
isLoading = false;
await InvokeAsync(StateHasChanged);
}
private async Task SaveTemplate()
@ -161,7 +194,7 @@ else
isLoading = true;
currentCssTemplate.CssContent = currentCssTemplate.CssContent.Trim();
if(hasCss)
if (hasCss)
{
// Update the CSS template content
currentCssTemplate.LastUpdated = DateTime.UtcNow;
@ -214,11 +247,11 @@ else
private async Task SaveSnippets()
{
//I want to update all items without gaps in the ids always 1 to x
for (int i=1; i<=SnippetList.Count(); i++)
for (int i = 1; i <= SnippetList.Count(); i++)
{
SnippetList[i-1].Id = i;
SnippetList[i - 1].Id = i;
}
}
await HtmlSnippetProcessor.ProcessAndStoreTemplateSnippetAsync(currentTemplate.QDrandCollectionName, SnippetList);
}

View File

@ -44,7 +44,7 @@
<input @oninput="(e) => UserInput = e.Value.ToString()"
@onkeydown="@Enter" class="searchInput" type="text" name="" value="@UserInput" placeholder="Ask any question">
<button class="searchButton border-0" @onclick="ProcessWordFile" href="#">
<button class="searchButton border-0" @onclick="SendUserQuery" href="#">
<i class="fa-solid fa-magnifying-glass"></i>
</button>
</div>
@ -114,7 +114,7 @@
function callAI(inputString) {
console.log(sessionId);
DotNet.invokeMethodAsync('BLAIzor', 'CallCSharpMethod2', inputString, sessionId)
DotNet.invokeMethodAsync('BLAIzor', 'CallCSharpMethod2', inputString, sessionId, false)
.then(response => console.log(response))
.catch(error => console.error(error));
}
@ -288,7 +288,7 @@
public async void MenuClick(string menuName)
{
await CallCSharpMethod2(menuName, sessionId);
await CallCSharpMethod2(menuName, sessionId, true);
}
public void HomeClick()
@ -333,7 +333,7 @@
[JSInvokable("CallCSharpMethod2")]
public static async Task CallCSharpMethod2(string input, string sessionId)
public static async Task CallCSharpMethod2(string input, string sessionId, bool forceUnModified = false)
{
// if (myHome != null)
// {
@ -342,7 +342,7 @@
if (_instances.TryGetValue(sessionId, out var instance))
{
await instance.HandleJsCall("Please tell me more about: " + input, sessionId);
await instance.HandleJsCall(input, sessionId, forceUnModified);
}
Console.Write("Button clicked:" + input);
}
@ -485,21 +485,21 @@
UserInput = input;
await InvokeAsync(StateHasChanged);
await ProcessWordFile();
await SendUserQuery();
//UserInput = string.Empty;
}
public async Task HandleJsCall(string input, string sessionId)
public async Task HandleJsCall(string input, string sessionId, bool forceUnmodified)
{
HtmlContent.Clear();
await InvokeAsync(StateHasChanged);
UserInput = input;
await ProcessWordFile();
await DisplayMenuContent(input, forceUnmodified);
UserInput = string.Empty;
await InvokeAsync(StateHasChanged);
}
private async Task ProcessWordFile()
private async Task SendUserQuery()
{
if (!string.IsNullOrEmpty(UserInput))
{
@ -510,6 +510,17 @@
}
}
private async Task DisplayMenuContent(string input, bool forceUnmodified)
{
if (!string.IsNullOrEmpty(UserInput))
{
HtmlContent.Clear();
var menu = await GetMenuList();
await ChatGptService.ProcessContentRequest(sessionId, UserInput, SiteId, (int)SiteInfo.TemplateId!, collectionName, menu, forceUnmodified);
UserInput = string.Empty;
}
}
private async Task<string> GetMenuList()
{
List<MenuItem> menuItems = await _contentEditorService.GetMenuItemsBySiteIdAsync(SiteId);

View File

@ -39,6 +39,34 @@
<button class="btn btn-primary" type="submit">Save</button>
</EditForm>
<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; }
@ -55,7 +83,7 @@
userId = CustomAuthProvider.GetUserId();
userName = CustomAuthProvider.GetUserName();
}
siteInfo = await SiteInfoService.GetSiteInfoByIdAsync(SiteId) ?? new SiteInfo();
siteInfo = await SiteInfoService.GetSiteInfoWithFormsByIdAsync(SiteId) ?? new SiteInfo();
}
private async Task SaveSiteInfo()

View File

@ -47,7 +47,7 @@
<input @oninput="(e) => UserInput = e.Value.ToString()"
@onkeydown="@Enter" class="searchInput" type="text" name="" value="@UserInput" placeholder="Ask any question">
<button class="searchButton border-0" @onclick="ProcessWordFile" href="#">
<button class="searchButton border-0" @onclick="SendUserQuery" href="#">
<i class="fa-solid fa-magnifying-glass"></i>
</button>
</div>
@ -114,7 +114,7 @@
function callAI(inputString) {
console.log(sessionId);
DotNet.invokeMethodAsync('BLAIzor', 'CallCSharpMethod3', inputString, sessionId)
DotNet.invokeMethodAsync('BLAIzor', 'CallCSharpMethod3', inputString, sessionId, false)
.then(response => console.log(response))
.catch(error => console.error(error));
}
@ -220,7 +220,7 @@
public async void MenuClick(string menuName)
{
await CallCSharpMethod3(menuName, sessionId);
await CallCSharpMethod3(menuName, sessionId, true);
}
public void HomeClick()
@ -284,12 +284,12 @@
[JSInvokable("CallCSharpMethod3")]
public static async Task CallCSharpMethod3(string input, string sessionId)
public static async Task CallCSharpMethod3(string input, string sessionId, bool forceUnmodified)
{
if (_instances.TryGetValue(sessionId, out var instance))
{
await instance.HandleJsCall("Please search for me: " + input, sessionId);
await instance.HandleJsCall("Please search for me: " + input, sessionId, forceUnmodified);
}
Console.Write("Button clicked:" + input);
}
@ -471,22 +471,22 @@
UserInput = input;
await InvokeAsync(StateHasChanged);
await ProcessWordFile();
await SendUserQuery();
//UserInput = string.Empty;
}
public async Task HandleJsCall(string input, string sessionId)
public async Task HandleJsCall(string input, string sessionId, bool forceUnmodified)
{
HtmlContent.Clear();
await InvokeAsync(StateHasChanged);
UserInput = input;
Console.Write($"\n\n Input: {input} \n\n");
await ProcessWordFile();
await DisplayMenuContent(input, forceUnmodified);
UserInput = string.Empty;
await InvokeAsync(StateHasChanged);
}
private async Task ProcessWordFile()
private async Task SendUserQuery()
{
if (!string.IsNullOrEmpty(UserInput))
{
@ -498,6 +498,18 @@
}
}
private async Task DisplayMenuContent(string input, bool forceUnmodified)
{
Console.WriteLine($"DisplayMenuContent 1: {input}");
if (!string.IsNullOrEmpty(UserInput))
{
HtmlContent.Clear();
var menu = await GetMenuList();
await ChatGptService.ProcessContentRequest(sessionId, UserInput, siteid, (int)SiteInfo.TemplateId!, collectionName, menu, forceUnmodified);
UserInput = string.Empty;
}
}
private async Task<string> GetMenuList()
{
List<MenuItem> menuItems = await _contentEditorService.GetMenuItemsBySiteIdAsync(siteid);

View File

@ -0,0 +1,110 @@
@page "/preview-snippet/{TemplateId:int}/{Snippet}"
@using BLAIzor.Components.Layout
@using BLAIzor.Models
@using BLAIzor.Services
@using Newtonsoft.Json
@using BLAIzor.Components.Partials
@attribute [Authorize]
@layout PreviewLayout
@inject DesignTemplateService DesignTemplateService
@inject CssTemplateService CssTemplateService
@inject NavigationManager NavigationManager
@inject QDrantService QDrantService
@inject HtmlSnippetProcessor HtmlSnippetProcessor
@inject IJSRuntime JSRuntime
@((MarkupString)CurrentSnippet.SampleHtml)
<script>
var sessionId = null;
function setSessionId(id) {
sessionId = id;
console.log("Session ID set:", sessionId);
}
</script>
@code {
[Parameter]
public int TemplateId { get; set; }
[Parameter]
public string Snippet { get; set; } = "";
private bool isLoading = true;
private DesignTemplate currentTemplate = new();
private CssTemplate currentCssTemplate = new();
private bool hasCss = false;
private bool hasCollection = false;
private List<HtmlSnippet> SnippetList = [];
private HtmlSnippet CurrentSnippet = new();
private string SessionId = "";
private static readonly Dictionary<string, PreviewSnippet> _instances = new();
protected override async Task OnInitializedAsync()
{
SessionId = Guid.NewGuid().ToString();
_instances[SessionId] = this;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("setSessionId", SessionId);
await JSRuntime.InvokeVoidAsync("setHtmlEditorSourceMode");
await LoadTemplate();
var cssPath = await CssTemplateService.SaveTempCssFileAsync(currentCssTemplate.CssContent, SessionId);
await JSRuntime.InvokeVoidAsync("seemgen.injectCssFile", cssPath);
}
}
private async Task LoadTemplate()
{
isLoading = true;
// Load the design template and its associated CSS template from the database
currentTemplate = await DesignTemplateService.GetByIdAsync(TemplateId);
currentCssTemplate = await CssTemplateService.GetByDesignTemplateIdAsync(TemplateId);
var currentCollection = await QDrantService.GetCollectionByNameAsync(currentTemplate.QDrandCollectionName);
Console.Write(currentCollection);
if(currentCssTemplate == null)
{
hasCss = false;
currentCssTemplate = new();
currentCssTemplate.DesignTemplateId = TemplateId;
}
else
{
hasCss = true;
}
if(!string.IsNullOrEmpty(currentCollection))
{
hasCollection = true;
var collectionCount = await QDrantService.GetCollectionCount(currentTemplate.QDrandCollectionName);
for(int i=1; i <= collectionCount; i++)
{
var snippetItem = await QDrantService.GetSnippetAsync(i, currentTemplate.QDrandCollectionName);
var selectedPoint = JsonConvert.DeserializeObject<QDrantGetPointResult>(snippetItem)!;
Console.Write($"Id: {selectedPoint.result.id}, html: {selectedPoint.result.payload.Html}");
SnippetList.Add(selectedPoint.result.payload);
}
CurrentSnippet = SnippetList.Where(x => x.Name == Snippet).FirstOrDefault();
}
else
{
//let's create a collection
var result = QDrantService.CreateQdrantCollectionAsync(currentTemplate.QDrandCollectionName);
}
isLoading = false;
await InvokeAsync(StateHasChanged);
}
}

View File

@ -0,0 +1,230 @@
@using BLAIzor.Models
@using Radzen
@if (isEditing)
{
<EditForm EditContext="@editContext" OnValidSubmit="@HandleValidSubmit">
<div class="card p-4">
<h4 class="mb-3">@TitleString</h4>
@foreach (var group in Groups)
{
<RadzenCard class="rz-p-3">
<h5 class="mt-4"> GROUP: @group.GroupName</h5>
@for (int entryIndex = 0; entryIndex < group.EntryCount; entryIndex++)
{
var entryPrefix = $"{group.GroupName}_{entryIndex}";
<RadzenStack Orientation="Orientation.Vertical" Style="width: 100%" Gap="10px" Reverse="false" JustifyContent="@JustifyContent.Normal" AlignItems="@AlignItems.Start" Wrap="@FlexWrap.Wrap">
@foreach (var field in group.Fields)
{
var label = $"FIELD: {entryPrefix}_{field.Label}";
<RadzenStack Orientation="Orientation.Vertical" Style="width: 100%" Gap="5px" Reverse="false" JustifyContent="@JustifyContent.Normal" AlignItems="@AlignItems.Normal" Wrap="@FlexWrap.Wrap">
<RadzenStack Orientation="Orientation.Horizontal" Gap="10px" Reverse="false" JustifyContent="@JustifyContent.Center" AlignItems="@AlignItems.Center" Wrap="@FlexWrap.Wrap">
<p>
Field type: @field.Type
</p>
<p>Field label: @field.Label</p>
</RadzenStack>
<RadzenCard Variant="@Variant.Filled" class="rz-my-1 rz-mx-auto" Style="width: 100%">
<p>Preview:</p>
<RadzenStack Orientation="Orientation.Horizontal" Gap="10px" Reverse="false" JustifyContent="@JustifyContent.Normal" AlignItems="@AlignItems.Normal" Wrap="@FlexWrap.Wrap">
<label class="form-label">@field.Label</label>
@switch (field.Type?.ToLower())
{
case "textarea":
<RadzenTextArea Style="width:100%"
Disabled=true
Value="@GetValue(label)"
Change="@(args => SetValue(label, args))" />
break;
case "number":
<RadzenNumeric TValue="int"
Disabled=true
Value="@(Convert.ToInt32(GetValue(label)))"
Change="@(args => SetValue(label, args.ToString()))" />
break;
case "date":
<RadzenDatePicker TValue="DateTime?"
Disabled=true
Value="@GetDate(label)"
Change="@(args => SetDate(label, args))" />
break;
case "checkbox":
<RadzenCheckBox TValue="bool"
Disabled=true
Value="@GetCheckboxValue(label)"
Change="@(args => SetCheckboxValue(label, args))" />
break;
case "radio":
<RadzenRadioButtonList TValue="int"
Disabled=true
Value="@GetRadioValue(label)"
Change="@(args => SetRadioValue(label, args))">
<Items>
@foreach (var option in field.Options ?? new())
{
var split = option.Split(':');
var text = split[0];
var val = split.Length > 1 && int.TryParse(split[1], out var parsedVal) ? parsedVal : 0;
<RadzenRadioButtonListItem Text="@text" Value="@val" />
}
</Items>
</RadzenRadioButtonList>
break;
case "select":
<RadzenDropDown TValue="string"
Disabled=true
Data="@field.Options"
Value="@GetDropdownValue(label)"
Change="@(args => SetDropdownValue(label, args?.ToString()))" />
break;
default:
<RadzenTextBox Style="width:100%"
Disabled=true
Value="@GetValue(label)"
Change="@(args => SetValue(label, args?.ToString()))" />
break;
}
</RadzenStack>
</RadzenCard>
</RadzenStack>
}
</RadzenStack>
}
@if (group.Repeatable)
{
<RadzenButton Icon="add_circle"
Disabled=true
ButtonStyle="ButtonStyle.Light"
Text="Add Another"
Click="@(() => AddGroupEntry(group))" />
}
</RadzenCard>
<hr />
}
<RadzenButton ButtonStyle="ButtonStyle.Primary" Text="@ButtonTextString" Type="Submit" />
</div>
</EditForm>
}
else
{
<div class="card p-4">
<h4 class="mb-3">Details</h4>
@foreach (var kvp in formValues)
{
<p><strong>@kvp.Key:</strong> @kvp.Value</p>
}
</div>
}
<p class="mt-2">@FormSubmitResult</p>
@code {
[Parameter] public List<FormFieldGroup> Groups { get; set; } = new();
[Parameter] public List<string> IgnoreReflection { get; set; } = new();
[Parameter] public EventCallback<Dictionary<string, string>> OnSubmit { get; set; }
[Parameter] public bool isEditing { get; set; }
[Parameter] public string TitleString { get; set; } = "Edit your details";
[Parameter] public string ButtonTextString { get; set; } = "Submit";
private EditContext editContext;
private Dictionary<string, string> formValues = new();
private Dictionary<string, DateTime?> formDates = new();
private string FormSubmitResult = "";
protected override void OnParametersSet()
{
foreach (var group in Groups)
{
if (group.EntryCount == 0)
{
group.EntryCount = 1; // Always have at least one entry
}
for (int i = 0; i < group.EntryCount; i++)
{
foreach (var field in group.Fields)
{
var key = $"{group.GroupName}_{i}_{field.Label}";
if (!formValues.ContainsKey(key))
{
if (field.Type?.ToLower() == "radio" && field.Options?.Count > 0)
{
var defaultVal = field.Options.FirstOrDefault()?.Split(':').LastOrDefault() ?? "0";
formValues[key] = defaultVal;
}
else
{
formValues[key] = string.Empty;
}
}
if (field.Type?.ToLower() == "date" && !formDates.ContainsKey(key))
{
formDates[key] = DateTime.Today;
}
}
}
}
editContext = new EditContext(formValues);
}
private string GetValue(string label) => formValues.TryGetValue(label, out var value) ? value : "";
private void SetValue(string label, string? value) => formValues[label] = value ?? "";
private DateTime? GetDate(string label) => formDates.TryGetValue(label, out var date) ? date : null;
private void SetDate(string label, DateTime? value) => formDates[label] = value;
private string GetDropdownValue(string label) => formValues.TryGetValue(label, out var value) ? value : "";
private void SetDropdownValue(string label, string? value) => formValues[label] = value ?? "";
private bool GetCheckboxValue(string label) => formValues.TryGetValue(label, out var value) && bool.TryParse(value, out var result) && result;
private void SetCheckboxValue(string label, bool? value) => formValues[label] = (value ?? false).ToString();
private int GetRadioValue(string label) => formValues.TryGetValue(label, out var value) && int.TryParse(value, out var intValue) ? intValue : 0;
private void SetRadioValue(string label, int? value) => formValues[label] = value?.ToString() ?? "0";
private void AddGroupEntry(FormFieldGroup group)
{
group.EntryCount++;
StateHasChanged();
}
private async Task HandleValidSubmit()
{
foreach (var dateField in formDates)
{
formValues[dateField.Key] = dateField.Value?.ToString("yyyy-MM-dd") ?? "";
}
FormSubmitResult = "Success!";
await OnSubmit.InvokeAsync(formValues);
isEditing = false;
}
}

View File

@ -4,6 +4,7 @@
@inject NavigationManager _navigationManager;
@inject ContentEditorService _contentEditorService
@inject IHttpContextAccessor HttpContextAccessor
@inject IJSRuntime JS
@ -79,10 +80,19 @@
_navigationManager.Refresh(true);
}
protected override void OnInitialized()
protected override async Task OnInitializedAsync()
{
SelectedLanguage = "English";
_scopedContentService.SelectedLanguage = "English";
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()

View File

@ -4,29 +4,66 @@
@inject ContentEditorService ContentEditorService
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject CustomAuthenticationStateProvider CustomAuthProvider
@inject NavigationManager navigationManager
@inject IJSRuntime JSRuntime
<div class="menu-item-editor">
<div class="form-group">
<label>Template Name</label>
<InputText class="form-control" @bind-Value="Snippet.Name" />
<div class="row">
<h4>Snippet info</h4>
<p>The basic information about the code-block</p>
</div>
<div class="row">
<div class="form-group col-md-2">
<label>Snippet name</label>
<InputText class="form-control" @bind-Value="Snippet.Name" />
</div>
<div class="form-group col-md-2">
<label>Variant</label>
<InputText class="form-control" @bind-Value="Snippet.Variant" />
</div>
<div class="form-group col-md-4">
<label>Type (comma-separated)</label>
<InputText class="form-control" @bind-Value="Snippet.Type" />
</div>
<div class="form-group col-md-4">
<label>Tags (comma-separated)</label>
<InputText class="form-control" @bind-Value="Snippet.Tags" />
</div>
</div>
<div class="row">
<div class="form-group col-md-12">
<label>Description</label>
<InputTextArea class="form-control" @bind-Value="Snippet.Description" />
</div>
</div>
<div class="form-group">
<label>Description</label>
<InputTextArea class="form-control" @bind-Value="Snippet.Description" />
<div class="row">
<h5>Snippet parametrized code</h5>
<p>Your parametrized html template for a specific block, like article, card, hero or such</p>
<div class="form-group">
<label>Template</label>
<RadzenHtmlEditor @bind-Value=@Snippet.Html style="height: 450px; color:#000; background-color: rgba(255,255,255,0) !important" Input=@OnInput Change=@OnChange Paste=@OnPaste UploadComplete=@OnUploadComplete Execute=@OnExecute UploadUrl="upload/image">
<RadzenHtmlEditorUndo />
<RadzenHtmlEditorRedo />
<RadzenHtmlEditorSource />
</RadzenHtmlEditor>
</div>
</div>
<div class="form-group">
<label>Tags (comma-separated)</label>
<InputText class="form-control" @bind-Value="Snippet.Type" />
<div class="row">
<h5>Snippet sample code</h5>
<p>Your sample html template for a specific block, like article, card, hero or such. Here you can use sample data in the snippet, to see if your css is working well</p>
<div class="form-group">
<label>Sample</label><RadzenButton Click="@(() => ShowSnippetPreview(Snippet))">Preview snippet</RadzenButton>
<RadzenHtmlEditor @bind-Value=@Snippet.SampleHtml style="height: 450px; color:#000; background-color: rgba(255,255,255,0) !important" Input=@OnInput Change=@OnChange Paste=@OnPaste UploadComplete=@OnUploadComplete Execute=@OnExecute UploadUrl="upload/image">
<RadzenHtmlEditorUndo />
<RadzenHtmlEditorRedo />
<RadzenHtmlEditorSource />
</RadzenHtmlEditor>
</div>
</div>
<RadzenHtmlEditor @bind-Value=@Snippet.Html style="height: 450px; color:#000; background-color: rgba(255,255,255,0.4)" Input=@OnInput Change=@OnChange Paste=@OnPaste UploadComplete=@OnUploadComplete Execute=@OnExecute UploadUrl="upload/image">
<RadzenHtmlEditorUndo />
<RadzenHtmlEditorRedo />
<RadzenHtmlEditorSource />
</RadzenHtmlEditor>
<EventConsole @ref=@console />
@if (IsLoading)
{
@ -35,28 +72,49 @@
</div>
@code {
[Parameter] public HtmlSnippet Snippet { get; set; } = new HtmlSnippet();
[Parameter] public HtmlSnippet Snippet { get; set; } = new HtmlSnippet();
[Parameter] public EventCallback<HtmlSnippet> OnContentUpdated { get; set; }
[Parameter] public int TemplateId { get; set; }
private bool IsLoading = false;
private string? userId;
private string? userName;
private AuthenticationState? authState;
EventConsole console;
public async void ShowSnippetPreview(HtmlSnippet snippet)
{
//navigationManager.NavigateTo($"/preview-snippet/{TemplateId}/{snippet.Name}");
// await JSRuntime.InvokeAsync<object>("open", $"/preview-snippet/{TemplateId}/{snippet.Name}", "_blank");
await JSRuntime.InvokeVoidAsync("open", $"/preview-snippet/{TemplateId}/{snippet.Name}", "_blank");
}
protected override async Task OnParametersSetAsync()
{
{
authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState.User.Identity?.IsAuthenticated == true)
{
userId = CustomAuthProvider.GetUserId();
userName = CustomAuthProvider.GetUserName();
}
if(string.IsNullOrWhiteSpace(Snippet.Tags))
{
Snippet.Tags = Snippet.Name;
}
if (string.IsNullOrWhiteSpace(Snippet.SampleHtml))
{
Snippet.SampleHtml = Snippet.Html;
}
if (string.IsNullOrWhiteSpace(Snippet.Variant))
{
Snippet.Variant = "";
}
}
async Task OnPaste(HtmlEditorPasteEventArgs args)
{
console.Log($"Paste: {args.Html}");
@ -66,13 +124,13 @@
async Task OnChange(string html)
{
console.Log($"Change: {html}");
}
async Task OnInput(string html)
{
console.Log($"Input: {html}");
}
void OnExecute(HtmlEditorExecuteEventArgs args)

View File

@ -18,13 +18,15 @@ namespace BLAIzor.Data
public DbSet<CssTemplate> CssTemplates { get; set; }
public DbSet<FormDefinition> FormDefinitions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// SiteInfo configuration
modelBuilder.Entity<IdentityUser>().HasData(new IdentityUser
{
@ -54,7 +56,7 @@ namespace BLAIzor.Data
QDrandCollectionName = "html_snippets"
});
modelBuilder.Entity<SiteInfo>().HasData(new SiteInfo
{
@ -99,6 +101,16 @@ namespace BLAIzor.Data
.WithOne(dt => dt.CssTemplate) // Assuming DesignTemplate can have multiple CSS templates
.HasForeignKey<CssTemplate>(ct => ct.DesignTemplateId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<FormDefinition>()
.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 })
.IsUnique(); // Ensure each form slug is unique per site
}
}
}

View File

@ -0,0 +1,606 @@
// <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("20250413175505_AddFormDefinition")]
partial class AddFormDefinition
{
/// <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.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");
b.ToTable("FormDefinitions");
});
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<Guid?>("QdrantPointId")
.HasColumnType("uniqueidentifier");
b.Property<int>("SiteInfoId")
.HasColumnType("int");
b.Property<int>("SortOrder")
.HasColumnType("int");
b.HasKey("Id");
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>("BrandLogoUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultColor")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("DomainUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsPublished")
.HasColumnType("bit");
b.Property<string>("SiteName")
.HasColumnType("nvarchar(max)");
b.Property<int?>("TemplateId")
.HasColumnType("int");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
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,
SiteName = "Default Site",
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.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.SiteInfo", "SiteInfo")
.WithMany("MenuItems")
.HasForeignKey("SiteInfoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
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.DesignTemplate", b =>
{
b.Navigation("CssTemplate")
.IsRequired();
b.Navigation("Sites");
});
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.Navigation("FormDefinitions");
b.Navigation("MenuItems");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BLAIzor.Migrations
{
/// <inheritdoc />
public partial class AddFormDefinition : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "FormDefinitions",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
SiteInfoId = table.Column<int>(type: "int", nullable: false),
Title = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Slug = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
JsonDefinition = table.Column<string>(type: "nvarchar(max)", nullable: false),
Version = table.Column<int>(type: "int", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_FormDefinitions", x => x.Id);
table.ForeignKey(
name: "FK_FormDefinitions_SiteInfos_SiteInfoId",
column: x => x.SiteInfoId,
principalTable: "SiteInfos",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_FormDefinitions_SiteInfoId",
table: "FormDefinitions",
column: "SiteInfoId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "FormDefinitions");
}
}
}

View File

@ -0,0 +1,607 @@
// <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("20250413231424_AddFormDefinition2")]
partial class AddFormDefinition2
{
/// <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.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<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<Guid?>("QdrantPointId")
.HasColumnType("uniqueidentifier");
b.Property<int>("SiteInfoId")
.HasColumnType("int");
b.Property<int>("SortOrder")
.HasColumnType("int");
b.HasKey("Id");
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>("BrandLogoUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultColor")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("DomainUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsPublished")
.HasColumnType("bit");
b.Property<string>("SiteName")
.HasColumnType("nvarchar(max)");
b.Property<int?>("TemplateId")
.HasColumnType("int");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
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,
SiteName = "Default Site",
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.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.SiteInfo", "SiteInfo")
.WithMany("MenuItems")
.HasForeignKey("SiteInfoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
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.DesignTemplate", b =>
{
b.Navigation("CssTemplate")
.IsRequired();
b.Navigation("Sites");
});
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.Navigation("FormDefinitions");
b.Navigation("MenuItems");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BLAIzor.Migrations
{
/// <inheritdoc />
public partial class AddFormDefinition2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_FormDefinitions_SiteInfoId",
table: "FormDefinitions");
migrationBuilder.CreateIndex(
name: "IX_FormDefinitions_SiteInfoId_Slug",
table: "FormDefinitions",
columns: new[] { "SiteInfoId", "Slug" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_FormDefinitions_SiteInfoId_Slug",
table: "FormDefinitions");
migrationBuilder.CreateIndex(
name: "IX_FormDefinitions_SiteInfoId",
table: "FormDefinitions",
column: "SiteInfoId");
}
}
}

View File

@ -17,7 +17,7 @@ namespace BLAIzor.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("ProductVersion", "9.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@ -123,6 +123,48 @@ namespace BLAIzor.Migrations
});
});
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")
@ -451,6 +493,17 @@ namespace BLAIzor.Migrations
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.SiteInfo", "SiteInfo")
@ -541,6 +594,8 @@ namespace BLAIzor.Migrations
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.Navigation("FormDefinitions");
b.Navigation("MenuItems");
});
#pragma warning restore 612, 618

35
Models/FormDefinition.cs Normal file
View File

@ -0,0 +1,35 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BLAIzor.Models
{
public class FormDefinition
{
[Key]
public int Id { get; set; }
[Required]
public int SiteInfoId { get; set; }
[Required]
[MaxLength(100)]
public string Title { get; set; } = "";
[Required]
[MaxLength(100)]
public string Slug { get; set; } = ""; // URL-friendly ID like "contact-form"
public string? Description { get; set; }
[Required]
public string JsonDefinition { get; set; } = ""; // serialized List<FormFieldGroup>
public int Version { get; set; } = 1;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
[ForeignKey(nameof(SiteInfoId))]
public SiteInfo SiteInfo { get; set; } = default!;
}
}

View File

@ -1,12 +1,25 @@
namespace BLAIzor.Models
{
//public class HtmlSnippet
//{
// public int Id { get; set; }
// public string Type { get; set; }
// public string Name { get; set; }
// public string Description { get; set; }
// public string Html { get; set; }
//}
public class HtmlSnippet
{
public int Id { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Html { get; set; }
public string Tags { get; set; }
public string Type { get; set; } // e.g. "article", "form", "gallery"
public string? Variant { get; set; } // 👈 NEW: e.g. "image-left", "image-right", "no-image"
public string SampleHtml { get; set; }
}
}

View File

@ -23,13 +23,14 @@ namespace BLAIzor.Models
[Url(ErrorMessage = "The DefaultUrl field must be a valid URL.")]
public string DefaultUrl { get; set; } // For generated subdomains
public bool IsPublished { get; set; } // For generated subdomains
public int? TemplateId { get; set; }
// Navigation property for IdentityUser
public IdentityUser User { get; set; }
// Navigation property for Menu items
public ICollection<MenuItem> MenuItems { get; set; } = new List<MenuItem>();
public int? TemplateId { get; set; }
public DesignTemplate Template { get; set; }
public List<FormDefinition> FormDefinitions { get; set; } = new();
}
}

View File

@ -16,6 +16,7 @@ using NuGet.Packaging;
using Microsoft.AspNetCore.Mvc.Formatters;
using Google.Api;
using System.CodeDom;
using Microsoft.AspNetCore.Mvc;
namespace BLAIzor.Services
{
@ -61,7 +62,7 @@ namespace BLAIzor.Services
public static event Action<string>? OnContentReceiveFinished;
public static event Action<string, string>? OnContentReceivedError;
public static event Action<string, string>? OnStatusChangeReceived;
public string Mood = "and professional";
public string Mood = "cool, and professional";
private string _workingContent = null;
public bool UseWebsocket = false;
private string AiProvider = "";
@ -118,7 +119,7 @@ namespace BLAIzor.Services
}
string systemMessage = "You are a helpful " + Mood + " assistant that welcomes the user on a website of " + _scopedContentService.SelectedBrandName + " in " + _scopedContentService.SelectedLanguage + ". Use the following content: `" +
string systemMessage = "You are a helpful, " + Mood + " assistant that welcomes the user in the name of the brand or person described by the content, on a website of " + _scopedContentService.SelectedBrandName + " in " + _scopedContentService.SelectedLanguage + ". 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, aiming to explain clearly, what does the company/person offer, constructed as simple Bootstrap HTML codeblock with a <h1> tagged title and a paragraph." +
@ -211,6 +212,59 @@ namespace BLAIzor.Services
}
/// <summary>
/// No reasoning needed just content retrieved and displayed as html
/// </summary>
/// <param name="sessionId"></param>
/// <param name="userPrompt"></param>
/// <param name="siteId"></param>
/// <param name="templateId"></param>
/// <param name="collectionName"></param>
/// <param name="menuList"></param>
/// <returns></returns>
public async Task ProcessContentRequest(string sessionId, string requestedMenuName, int siteId, int templateId, string collectionName, string menuList = "", bool forceUnmodified = false)
{
Console.Write($"\n\n SessionId: {sessionId}\n\n");
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
float[] vector = [];
OnStatusChangeReceived?.Invoke(sessionId, "Determining search vectors");
vector = await _openAIEmbeddingService.GenerateEmbeddingAsync(requestedMenuName);
OnStatusChangeReceived?.Invoke(sessionId, "Looking up content in the knowledge database");
var pointId = await _qDrantService.QueryContentAsync(siteId, vector, 1);
string extractedText = "";
if (pointId.Length > 0)
{
foreach (var item in pointId)
{
string qDrantData = await _qDrantService.GetContentAsync(siteId, item);
QDrantGetContentPointResult _selectedPoint = new QDrantGetContentPointResult();
if (qDrantData != null)
{
_selectedPoint = JsonConvert.DeserializeObject<QDrantGetContentPointResult>(qDrantData)!;
}
extractedText += _selectedPoint.result.payload.name + ": " + _selectedPoint.result.payload.content + ", ";
}
}
else
{
extractedText = "VECTOR ERROR: ZERO INFORMATION FOUND";
}
string contentJson = await GetContentFromQuery(sessionId,
"Enhance this text if needed, making its style and grammar suitable to be displayed as the content of a webpage",
extractedText,
forceUnmodified);
await ProcessContent(sessionId, contentJson, templateId, collectionName);
}
// Refactored helper methods
private async Task ProcessMethodResult(string sessionId, string resultJson)
@ -231,7 +285,7 @@ namespace BLAIzor.Services
if (fixedResult != null)
{
string contentJson = await GetContentFromQuery(sessionId, fixedResult.Text, _workingContent);
//validate and fix json object?
Console.Write("\r \n ProcessTextResult: Content: " + contentJson + "\r \n");
await ProcessContent(sessionId, contentJson, templateId, collectionName);
}
}
@ -360,7 +414,7 @@ namespace BLAIzor.Services
/// </summary>
/// <param name="userPrompt"></param>
/// <returns></returns>
public async Task<string> GetContentFromQuery(string sessionId, string userPrompt, string content = null)
public async Task<string> GetContentFromQuery(string sessionId, string userPrompt, string content = null, bool forceUnmodified = false)
{
string extractedText;
if (content == null)
@ -373,31 +427,62 @@ namespace BLAIzor.Services
_apiKey = GetApiKey();
//Console.Write("GetJSONResult called: " +extractedText);
var systemMessage = "You are a helpful assistant forming JSON objects on request. Respond strictly in " + _scopedContentService.SelectedLanguage + " with a plain JSON object in the following format:\r\n\r\n1. " +
//"**chatGPTContentResult**:\r\n " +
"- `type`: A string with value `contentresult`.\r\n " +
"- `text`: A string with the actual response.\r\n " +
"- `topics`: A list of sections of the initial document." +
"- `photos`: A dictionary of string key and string values, where the keys are the name of the subject that the photo is related to (like a person's name, or a section)," +
" and the value is the actual, unmodified photo url.\r\n" +
"**Document-Specific Instructions**:\r\n- Base responses solely on the initial document: {" + extractedText + "}.\r\n" +
"- Start with defining above mentioned key topics of the initial document, and making the list of them. " +
"- After that add the above mentioned relevant image urls list." +
"- For missing information: Inform the user and provide a clarification. " +
//"- Do not generate lengthy answers." +
"- If the user prompt is clear and they ask specific, well defined question, do not add other infromation or welcome message." +
"- If the user prompt is unclear, or makes no sense, ask for clarification." +
"You can decorate your clarification" +
"request with this image URL: `https://www.reactiongifs.com/r/martin.gif` added to the photo dictionary.\r\n\r\n" +
"- For compliments from the user: Express our happiness about it " +
"and apply this image URL: `https://www.reactiongifs.com/r/review.gif` in the photo dictionary.\r\n\r\n" +
"**Style and Image Handling**:\r\n" +
"- Make sure the json is valid json." +
"- Do NOT include extraneous text outside the JSON structure.\r\n\r\n" +
"When you understand the input, follow these rules strictly. Otherwise, seek clarification.\r\n" +
"Do not include linebreaks or any formatting, just the plain json string. Make sure it is valid json, and every objects is closed properly" +
"Do NOT mark your answer with anything like `````json, and do not add any explanation.";
string systemMessage = "";
if(forceUnmodified)
{
systemMessage = "You are a helpful assistant of a website. Respond in the name of the brand or person in the content, strictly in " + _scopedContentService.SelectedLanguage + " with a plain JSON object in the following format:\r\n\r\n1. " +
//"**chatGPTContentResult**:\r\n " +
"- `type`: A string with value `contentresult`.\r\n " +
"- `text`: A string with the actual response.\r\n " +
"- `topics`: A list of sections of the initial document." +
"- `photos`: A dictionary of string key and string values, where the keys are the name of the subject that the photo is related to (like a person's name, or a section)," +
" and the value is the actual, unmodified photo url.\r\n" +
"**Document-Specific Instructions**:\r\n" +
"Step 1: Start with defining above mentioned key topics of the initial document, and making the list of them. " +
"Step 2: After that add the above mentioned relevant image urls list." +
"Step 3: " +
"- Turn the following content into a nice informative webpage content. " +
"- Structure it nicely without leaving out any information. " +
"*** CONTENT START *** {" + extractedText + "} *** CONTENT END ***.\r\n" +
"**Style and Image Handling**:\r\n" +
"- Make sure the json is valid json." +
"- Do NOT include extraneous text outside the JSON structure.\r\n\r\n" +
"When you understand the input, follow these rules strictly. Otherwise, seek clarification.\r\n" +
"Do not include linebreaks or any formatting, just the plain json string. Make sure it is valid json, and every objects is closed properly" +
"Do NOT mark your answer with anything like `````json, and do not add any explanation.";
}
else
{
systemMessage = "You are a helpful assistant of a website. Respond in the name of the brand or person in the content, strictly in " + _scopedContentService.SelectedLanguage + " with a plain JSON object in the following format:\r\n\r\n1. " +
//"**chatGPTContentResult**:\r\n " +
"- `type`: A string with value `contentresult`.\r\n " +
"- `text`: A string with the actual response.\r\n " +
"- `topics`: A list of sections of the initial document." +
"- `photos`: A dictionary of string key and string values, where the keys are the name of the subject that the photo is related to (like a person's name, or a section)," +
" and the value is the actual, unmodified photo url.\r\n" +
"**Document-Specific Instructions**:\r\n" +
"Step 1: Start with defining above mentioned key topics of the initial document, and making the list of them. " +
"Step 2: After that add the above mentioned relevant image urls list." +
"Step 3: " +
"- Base a detailed, but not lengthy response solely on the initial document provided below. " +
"- In your response, summarize all relevant information in the document, that is connected to the question." +
"*** CONTENT START *** {" + extractedText + "} *** CONTENT END ***.\r\n" +
"- For missing information: Inform the user and provide a clarification. " +
//"- Do not generate lengthy answers." +
"- If the user prompt is clear and they ask specific, well defined question, do not add other infromation or welcome message." +
"- If the user prompt is unclear, or makes no sense, ask for clarification." +
//"You can decorate your clarification" +
//"request with this image URL: `https://www.reactiongifs.com/r/martin.gif` added to the photo dictionary.\r\n\r\n" +
//"- For compliments from the user: Express our happiness about it " +
//"and apply this image URL: `https://www.reactiongifs.com/r/review.gif` in the photo dictionary.\r\n\r\n" +
"**Style and Image Handling**:\r\n" +
"- Make sure the json is valid json." +
"- Do NOT include extraneous text outside the JSON structure.\r\n\r\n" +
"When you understand the input, follow these rules strictly. Otherwise, seek clarification.\r\n" +
"Do not include linebreaks or any formatting, just the plain json string. Make sure it is valid json, and every objects is closed properly" +
"Do NOT mark your answer with anything like `````json, and do not add any explanation.";
}
OnStatusChangeReceived?.Invoke(sessionId, "Constructing the answer");
string interMediateResult = string.Empty;
if (!UseWebsocket)
@ -425,7 +510,7 @@ namespace BLAIzor.Services
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
}
OnStatusChangeReceived?.Invoke(sessionId, "Mkay, I know now");
Console.Write("Result decision: " + interMediateResult);
Console.Write("GetContentFromQuery: Result decision: " + interMediateResult);
return interMediateResult;
}
@ -492,7 +577,7 @@ namespace BLAIzor.Services
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
}
OnStatusChangeReceived?.Invoke(sessionId, "Mkay, I know now");
Console.Write("Result decision: " + interMediateResult);
Console.Write("GetExaminationResult: Result decision: " + interMediateResult);
return interMediateResult;
}
@ -576,7 +661,7 @@ namespace BLAIzor.Services
OnStatusChangeReceived?.Invoke(sessionId, "Looking up content in the knowledge database");
var pointId = await _qDrantService.QueryContentAsync(siteId, vector, 3);
string extractedText = "";
string extractedText = "Sections: ";
if (pointId.Length > 0)
{
foreach (var item in pointId)
@ -598,7 +683,7 @@ namespace BLAIzor.Services
}
_workingContent = extractedText;
Console.Write("\r \n GetJsonResultFromQuery: Working content: " + _workingContent + "\r \n");
//string extractedText = WordFileReader.ExtractText(rootpath);
Console.Write("GetJSONResult called!");
@ -677,7 +762,7 @@ namespace BLAIzor.Services
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
}
Console.Write("\r \n Result decision: " + interMediateResult + "\r \n");
Console.Write("\r \n GetJsonResultFromQuery: Result decision: " + interMediateResult + "\r \n");
return interMediateResult;
}
@ -873,10 +958,7 @@ namespace BLAIzor.Services
Console.WriteLine($"DISPLAYHTML Photos: {photos} \n\n");
Console.WriteLine($"DISPLAYHTML Topics: {topics} \n\n");
_apiKey = GetApiKey();
StringBuilder lst = new StringBuilder("You are a helpful assistant generating HTML responses in " + _scopedContentService.SelectedLanguage + " using Bootstrap. \n\n" +
StringBuilder lst = new StringBuilder("You are a helpful assistant generating HTML content in " + _scopedContentService.SelectedLanguage + " using Bootstrap. \n\n" +
"### Rules to Follow: \n" +
"- Please generate clean and structured HTML that fits inside a Bootstrap container.\n" +
"- DO NOT include `<head>`, `<body>`, or `<script>` tags—only content inside the Bootstrap container.\n" +
@ -910,7 +992,7 @@ namespace BLAIzor.Services
" </div>\n" +
" ```\n\n" +
"- Use Bootstrap classes to ensure proper spacing and alignment.\n" +
" - Use 'row justify-content-center' for layout, 'col-xs-12 col-sm-6 col-md-3 text-center' for product cards, and 'img-fluid' for images. \r\n" +
" - Use 'row justify-content-center' for layout, 'col-xx-x' classes for columns (IF multiple columns are needed), and always use 'img-fluid' class for images. \r\n" +
"- If a snippet contains a button, ensure it is placed inside a `div.text-center` for proper alignment.\n");
}
@ -933,15 +1015,16 @@ namespace BLAIzor.Services
if (topics != null && topics.Any())
{
lst.AppendLine(
"### Generating Topic Buttons:\n" +
"### Generating Topic Buttons:\n" +
"Start this section with a title `Related` " +
"- Create a **separate button** for each topic.\n" +
$"- Make sure the topics are in {_scopedContentService.SelectedLanguage}, if not, translate them." +
"- Each button should use `btn btn-primary` and call `callAI('{topicName}')` on click.\n" +
"- List of topics:\n" +
" " + string.Join(", ", topics) + "\n\n" +
"- Example:\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"
"Put this section always as last, on the bottom of the page.\n"
);
}
@ -954,12 +1037,13 @@ namespace BLAIzor.Services
lst.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** generate or assume image URLs.\n" +
"- DO **NOT** modify the photo urls in any way." +
"- Do **NOT** generate or assume new image URLs.\n" +
"- If the snippet contains an image, but there is no photo url available, skip the image element." +
"- **Never** add explanations or start with ```html syntax markers.\n");
string systemMessage = lst.ToString();
string userMessage = "Make a nice Bootstrap 5 page content from the provided text, please.";
string userMessage = "Create a perfect, production ready, structured Bootstrap 5 page content from the provided text, please.";
string assistantMessage = "`Provided text to display as HTML`: `" + interMediateResult + "`";
//int mode = -1;

View File

@ -6,6 +6,7 @@ using BLAIzor.Models;
using DocumentFormat.OpenXml.Office2010.Excel;
using Google.Protobuf.Collections;
using Google.Type;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.Extensions.Configuration;
using Qdrant.Client.Grpc;
@ -93,8 +94,13 @@ namespace BLAIzor.Services
{
try
{
// Combine details to generate the embedding
var combinedText = $"{snippet.Name}: {snippet.Description} - {snippet.Html}";
var combinedText = $"{snippet.Name}: {snippet.Description}. " +
$"Type: {snippet.Type}. " +
$"Variant: {snippet.Variant ?? "default"}. " +
$"Tags: {snippet.Tags}. " +
$"HTML: {snippet.Html}";
var embedding = await _embeddingService.GenerateEmbeddingAsync(combinedText);
// Add data for batch insertion
@ -104,8 +110,11 @@ namespace BLAIzor.Services
{
["type"] = snippet.Type,
["name"] = snippet.Name,
["variant"] = string.IsNullOrWhiteSpace(snippet.Variant)? "" : snippet.Variant,
["tags"] = snippet.Tags,
["description"] = snippet.Description,
["html"] = snippet.Html
["html"] = snippet.Html,
["sampleHtml"] = snippet.SampleHtml
});
}
catch (Exception ex)

View File

@ -396,7 +396,7 @@ namespace BLAIzor.Services
Payload = { payloads[i] }
});
Console.WriteLine($"{pointStructList[i].Id} val bekerült {pointStructList[i].Payload["html"]}");
Console.WriteLine($"{pointStructList[i].Id} val bekerül {pointStructList[i].Payload["name"]}");
}
Console.WriteLine(pointStructList.Count);

View File

@ -66,6 +66,27 @@ namespace BLAIzor.Services
}
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)

154
wwwroot/htmlpage.html Normal file
View File

@ -0,0 +1,154 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<!-- Hero Block with Image -->
<section class="hero-section py-5 bg-light">
<div class="container">
<div class="row align-items-center">
<div class="col-md-6">
<h1>{{title}}</h1>
<p class="lead">{{subtitle}}</p>
<a href="{{cta_link}}" class="btn btn-primary">{{cta_text}}</a>
</div>
<div class="col-md-6 text-center">
<img src="{{image_url}}" class="img-fluid" alt="Hero Image" />
</div>
</div>
</div>
</section>
<!-- Feature 3-Column Block -->
<section class="features-section py-5">
<div class="container">
<div class="row text-center">
{{#each features}}
<div class="col-md-4 mb-4">
<i class="bi {{icon}} display-4 text-primary"></i>
<h5 class="mt-3">{{title}}</h5>
<p>{{description}}</p>
</div>
{{/each}}
</div>
</div>
</section>
<!-- Testimonial Block -->
<section class="testimonials py-5 bg-white border-top">
<div class="container">
<h2 class="text-center mb-4">What people are saying</h2>
<div class="row">
{{#each testimonials}}
<div class="col-md-4">
<blockquote class="blockquote">
<p class="mb-0">"{{quote}}"</p>
<footer class="blockquote-footer mt-2">{{author}}</footer>
</blockquote>
</div>
{{/each}}
</div>
</div>
</section>
<!-- Call to Action Block -->
<section class="cta-section bg-primary text-white py-5">
<div class="container text-center">
<h2>{{cta_heading}}</h2>
<p class="mb-4">{{cta_description}}</p>
<a href="{{cta_button_link}}" class="btn btn-light">{{cta_button_text}}</a>
</div>
</section>
<!-- Footer Block -->
<footer class="footer py-4 bg-dark text-white">
<div class="container text-center">
<p class="mb-1">&copy; {{year}} {{brand_name}}. All rights reserved.</p>
<small>{{footer_links}}</small>
</div>
</footer>
<section class="py-5 bg-light">
<div class="container">
<h2 class="mb-4">{{title}}</h2>
<form>
{{#each fields}}
<div class="mb-3">
<label class="form-label">{{label}}</label>
<input type="{{type}}" class="form-control" placeholder="{{placeholder}}">
</div>
{{/each}}
<button type="submit" class="btn btn-primary">{{buttonText}}</button>
</form>
</div>
</section>
<section class="py-5 text-center">
<div class="container">
<h2 class="mb-5">{{title}}</h2>
<div class="row g-4">
{{#each images}}
<div class="col-6 col-md-4 col-lg-3">
<img src="{{url}}" alt="{{alt}}" class="img-fluid rounded shadow-sm" />
</div>
{{/each}}
</div>
</div>
</section>
<section class="py-5 bg-light">
<div class="container text-center">
<h2 class="mb-5">{{title}}</h2>
<div class="row">
{{#each plans}}
<div class="col-md-4 mb-4">
<div class="card h-100 border-0 shadow">
<div class="card-body">
<h5 class="card-title">{{name}}</h5>
<h6 class="card-price display-5 fw-bold">{{price}}</h6>
<ul class="list-unstyled my-4">
{{#each features}}
<li>✔️ {{.}}</li>
{{/each}}
</ul>
<a href="{{buttonLink}}" class="btn btn-outline-primary">{{buttonText}}</a>
</div>
</div>
</div>
{{/each}}
</div>
</div>
</section>
<article class="py-5">
<div class="container">
<h1 class="mb-3">{{title}}</h1>
<p class="text-muted">{{date}}</p>
{{#each paragraphs}}
<p>{{.}}</p>
{{/each}}
</div>
</article>
<article class="py-5">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-6 mb-4 mb-lg-0">
<img src="{{imageUrl}}" alt="{{imageAlt}}" class="img-fluid rounded shadow-sm" />
</div>
<div class="col-lg-6">
<h1 class="mb-3">{{title}}</h1>
<p class="text-muted">{{date}}</p>
{{#each paragraphs}}
<p>{{.}}</p>
{{/each}}
</div>
</div>
</div>
</article>
</body>
</html>

View File

@ -114,6 +114,10 @@
background-color: rgba(255, 255, 255, 0.2)
}
.form-group {
margin-bottom: 5px;
}
.contactform-overlay {
position: fixed;
z-index: 100;
@ -208,3 +212,7 @@
ul {
list-style-type: none;
}
.rz-html-editor {
background-color: transparent !important;
}

View File

@ -9,4 +9,8 @@
link.id = "seemgen-css";
document.head.appendChild(link);
}
};
window.getUserLanguage = () => {
return navigator.language || navigator.userLanguage || "en";
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.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: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
/*background-image: url('/uploads/0988758e-e16c-4c2c-8c1e-efa3ac5f0274/images/ribi1_brighter.png') !important;*/
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;
}
h1 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: bold;
padding-top: 30px;
padding-bottom: 10px;
font-size: 2.986rem;
}
h2 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
padding-top: 15px;
padding-bottom: 10px;
font-size: 2.488rem;
}
h3 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
padding-top: 10px;
padding-bottom: 10px;
font-size: 2.074rem;
}
p {
color: #fff;
font-size: x-large;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
}

View File

@ -0,0 +1,100 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.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: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
/*background-image: url('/uploads/0988758e-e16c-4c2c-8c1e-efa3ac5f0274/images/ribi1_brighter.png') !important;*/
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;
}
h1 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: bold;
padding-top: 30px;
padding-bottom: 10px;
font-size: 2.986rem;
}
h2 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
padding-top: 15px;
padding-bottom: 10px;
font-size: 2.488rem;
}
h3 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
padding-top: 10px;
padding-bottom: 10px;
font-size: 2.074rem;
}
p {
color: #fff;
font-size: x-large;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
}

View File

@ -0,0 +1,100 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.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: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
/*background-image: url('/uploads/0988758e-e16c-4c2c-8c1e-efa3ac5f0274/images/ribi1_brighter.png') !important;*/
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;
}
h1 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: bold;
padding-top: 30px;
padding-bottom: 10px;
font-size: 2.986rem;
}
h2 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
padding-top: 15px;
padding-bottom: 10px;
font-size: 2.488rem;
}
h3 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
padding-top: 10px;
padding-bottom: 10px;
font-size: 2.074rem;
}
p {
color: #fff;
font-size: x-large;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
}

View File

@ -13,7 +13,8 @@ li {
}
label {
display: none;
color: #000;
/* display: none; */
}
.btn {
@ -447,6 +448,9 @@ body {
.table {
color: #fff !important;
padding-top: 10px;
padding-bottom: 10px;
background-color: transparent;
}
.navbar-collapse {
@ -474,7 +478,7 @@ body {
}
.nav-link {
color: #fff;
color: #fff !important;
}
.content {

View File

@ -0,0 +1,486 @@
/*@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: x-large;
}
li {
list-style: none;
}
label {
color: #000;
/* display: none; */
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
width: fit-content;
font-weight: bold;
transition: all 0.2s ease;
margin: 15px;
}
.voicebutton {
border-radius: 50% !important;
padding: 10px !important;
width: 40px;
height:40px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 20px;
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: 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;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #0d2840 !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: 20px !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;
}
.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: #4E4E4E;
}
.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-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*/
:root {
font-size: 15px;
}
body {
/*font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;*/
/*font-family: 'Quicksand', sans-serif;*/
color: #fff !important;
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:#fff;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.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: 5px;
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%;
opacity: 0.2;
}
.table {
color: #fff !important;
padding-top: 10px;
padding-bottom: 10px;
background-color: transparent;
}
.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.5em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 4em;
}
.navbar {
background-color: #040206;
color: #fff;
}
.nav-link {
color: #fff !important;
}
.content {
top: 60px;
}

View File

@ -0,0 +1,486 @@
/*@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: x-large;
}
li {
list-style: none;
}
label {
color: #000;
/* display: none; */
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
width: fit-content;
font-weight: bold;
transition: all 0.2s ease;
margin: 15px;
}
.voicebutton {
border-radius: 50% !important;
padding: 10px !important;
width: 40px;
height:40px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 20px;
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: 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;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #0d2840 !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: 20px !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;
}
.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: #4E4E4E;
}
.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-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*/
:root {
font-size: 15px;
}
body {
/*font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;*/
/*font-family: 'Quicksand', sans-serif;*/
color: #fff !important;
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:#fff;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.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: 5px;
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%;
opacity: 0.2;
}
.table {
color: #fff !important;
padding-top: 10px;
padding-bottom: 10px;
background-color: transparent;
}
.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.5em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 4em;
}
.navbar {
background-color: #040206;
color: #fff;
}
.nav-link {
color: #fff !important;
}
.content {
top: 60px;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,486 @@
/*@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: x-large;
}
li {
list-style: none;
}
label {
color: #000;
/* display: none; */
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
width: fit-content;
font-weight: bold;
transition: all 0.2s ease;
margin: 15px;
}
.voicebutton {
border-radius: 50% !important;
padding: 10px !important;
width: 40px;
height:40px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 20px;
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: 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;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #0d2840 !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: 20px !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;
}
.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: #4E4E4E;
}
.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-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*/
:root {
font-size: 15px;
}
body {
/*font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;*/
/*font-family: 'Quicksand', sans-serif;*/
color: #fff !important;
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:#fff;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.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: 5px;
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%;
opacity: 0.2;
}
.table {
color: #fff !important;
padding-top: 10px;
padding-bottom: 10px;
background-color: transparent;
}
.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.5em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 4em;
}
.navbar {
background-color: #040206;
color: #fff;
}
.nav-link {
color: #fff !important;
}
.content {
top: 60px;
}

View File

@ -0,0 +1,100 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.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: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
/*background-image: url('/uploads/0988758e-e16c-4c2c-8c1e-efa3ac5f0274/images/ribi1_brighter.png') !important;*/
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;
}
h1 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: bold;
padding-top: 30px;
padding-bottom: 10px;
font-size: 2.986rem;
}
h2 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
padding-top: 15px;
padding-bottom: 10px;
font-size: 2.488rem;
}
h3 {
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
padding-top: 10px;
padding-bottom: 10px;
font-size: 2.074rem;
}
p {
color: #fff;
font-size: x-large;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
}

View File

@ -0,0 +1,486 @@
/*@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: x-large;
}
li {
list-style: none;
}
label {
color: #000;
/* display: none; */
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
width: fit-content;
font-weight: bold;
transition: all 0.2s ease;
margin: 15px;
}
.voicebutton {
border-radius: 50% !important;
padding: 10px !important;
width: 40px;
height:40px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 20px;
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: 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;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #0d2840 !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: 20px !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;
}
.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: #4E4E4E;
}
.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-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*/
:root {
font-size: 15px;
}
body {
/*font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;*/
/*font-family: 'Quicksand', sans-serif;*/
color: #fff !important;
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:#fff;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.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: 5px;
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%;
opacity: 0.2;
}
.table {
color: #fff !important;
padding-top: 10px;
padding-bottom: 10px;
background-color: transparent;
}
.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.5em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 4em;
}
.navbar {
background-color: #040206;
color: #fff;
}
.nav-link {
color: #fff !important;
}
.content {
top: 60px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.