Add contact feature
This commit is contained in:
@@ -11,4 +11,8 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.2" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BeauFindlay.Shared\BeauFindlay.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<div class="p-4">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
@if (Type == AlertType.Success)
|
||||
{
|
||||
<svg class="h-5 w-5 text-green-500" v iewBox="0 0 20 20"
|
||||
fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||
clip-rule="evenodd"/>
|
||||
</svg>
|
||||
}
|
||||
else
|
||||
{
|
||||
<svg class="h-5 w-5 text-red-500" v iewBox="0 0 20 20"
|
||||
fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z"
|
||||
clip-rule="evenodd"/>
|
||||
</svg>
|
||||
}
|
||||
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="font-medium">
|
||||
@Title
|
||||
</h3>
|
||||
<div class="mt-2">
|
||||
<p>@ChildContent</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public AlertType Type { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace BeauFindlay.Client.Components.Alert;
|
||||
|
||||
public enum AlertType
|
||||
{
|
||||
Success,
|
||||
Error
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<button type="@Type" disabled="@IsLoading"
|
||||
class="border-0 ring-1 ring-inset ring-gray-300 bg-black px-3.5 py-2.5 text-sm font-semibold text-white shadow hover:bg-gray-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600 disabled:bg-gray-800">
|
||||
@if (IsLoading)
|
||||
{
|
||||
<LoadingSpinner Size="LoadingSpinnerSize.Small"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</button>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Type { get; set; } = "button";
|
||||
|
||||
[Parameter]
|
||||
public bool IsLoading { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<div role="status">
|
||||
<svg aria-hidden="true"
|
||||
class="@sizeCss inline text-gray-200 animate-spin dark:text-gray-600 fill-gray-600 dark:fill-gray-300"
|
||||
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor"/>
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill"/>
|
||||
</svg>
|
||||
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public LoadingSpinnerSize Size { get; set; }
|
||||
|
||||
private string sizeCss = "w-8 h-8";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
SetSpinnerSize();
|
||||
}
|
||||
|
||||
private void SetSpinnerSize()
|
||||
{
|
||||
sizeCss = Size switch
|
||||
{
|
||||
LoadingSpinnerSize.Small => "w-6 h-6",
|
||||
LoadingSpinnerSize.Medium => "w-8 h-8",
|
||||
LoadingSpinnerSize.Large => "w-12 h-12",
|
||||
_ => sizeCss
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BeauFindlay.Client.Components.LoadingSpinner;
|
||||
|
||||
public enum LoadingSpinnerSize
|
||||
{
|
||||
Small,
|
||||
Medium,
|
||||
Large
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<footer class="mt-auto fade-in">
|
||||
<div class="mx-auto p-8 lg:p-8">
|
||||
<div class="mx-auto py-8 px-4 md:px-12">
|
||||
<div class="md:flex md:items-center md:justify-between">
|
||||
<div class="flex space-x-6 md:order-2">
|
||||
<a href="https://github.com/bdfin" class="text-slate-200 hover:text-slate-500">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="flex flex-col min-h-screen">
|
||||
<NavBar/>
|
||||
|
||||
<div class="px-4 md:px-8">
|
||||
<div class="px-4 md:px-12 py-4">
|
||||
@Body
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,23 +1,122 @@
|
||||
@page "/contact"
|
||||
@using BeauFindlay.Shared.Contracts
|
||||
|
||||
@inject HttpClient HttpClient
|
||||
@inject IJSRuntime JsRuntime
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(apiResponse))
|
||||
<div class="text-center">
|
||||
<h1 class="text-4xl">Contact</h1>
|
||||
|
||||
<p class="text-lg mt-8">
|
||||
If you think I can help with your project or you'd just like to talk tech, send me a message!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-xl pt-10 pb-4">
|
||||
<EditForm Model="contactInput" OnValidSubmit="HandleValidSubmit">
|
||||
<DataAnnotationsValidator/>
|
||||
|
||||
<div class="grid grid-cols-1 gap-x-8 gap-y-6 sm:grid-cols-2">
|
||||
<div class="sm:col-span-2">
|
||||
<label for="name" class="block font-semibold leading-6">Name</label>
|
||||
<div class="mt-2.5">
|
||||
<InputText id="name" @bind-Value="contactInput.Name" class="block w-full text-lg border-0 px-3.5 py-2 bg-black shadow ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600"/>
|
||||
<ValidationMessage For="() => contactInput.Name" class="text-red-600"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label for="email" class="block font-semibold leading-6">Email</label>
|
||||
<div class="mt-2.5">
|
||||
<InputText id="email" @bind-Value="contactInput.Email" class="block w-full text-lg border-0 px-3.5 py-2 bg-black shadow ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600"/>
|
||||
<ValidationMessage For="() => contactInput.Email" class="text-red-600"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label for="message" class="block font-semibold leading-6">Message</label>
|
||||
<div class="mt-2.5">
|
||||
<InputTextArea id="message" @bind-Value="contactInput.Message" rows="4" class="block w-full text-lg border-0 px-3.5 py-2 bg-black shadow ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600"/>
|
||||
<ValidationMessage For="() => contactInput.Message" class="text-red-600"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-8 flex items-center justify-center">
|
||||
<Button Type="submit" IsLoading="@isSubmitting">
|
||||
<i class="fa-solid fa-paper-plane"></i> Send
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</EditForm>
|
||||
</div>
|
||||
|
||||
@if (isSubmitted)
|
||||
{
|
||||
<p class="text-xl">Response from Azure Function API: @apiResponse</p>
|
||||
<div class="m-auto max-w-xl">
|
||||
@if (sendEmailSuccess)
|
||||
{
|
||||
<Alert Type="AlertType.Success" Title="Email sent successfully">
|
||||
Thanks for getting in touch! I'll get back to you as soon as I can.
|
||||
</Alert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Alert Type="AlertType.Error" Title="Email sending failed">
|
||||
Looks like something went wrong trying to send that email. Please try again.
|
||||
</Alert>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string apiResponse = "";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var response = await HttpClient.GetStringAsync("/api/Function1");
|
||||
<script src="https://www.google.com/recaptcha/api.js?render=6LcvxZIpAAAAAOIP5L6kGngwDZRpwkTdMezPn06x" async
|
||||
defer></script>
|
||||
<script src="js/recaptcha.js"></script>
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(response))
|
||||
@code {
|
||||
private readonly ContactInputModel contactInput = new();
|
||||
|
||||
private bool isSubmitting;
|
||||
private bool isSubmitted;
|
||||
private bool sendEmailSuccess;
|
||||
|
||||
private class ContactInputModel
|
||||
{
|
||||
[Required(ErrorMessage = "Please enter your name.")]
|
||||
[MaxLength(50, ErrorMessage = "Please use a shorter name. 50 characters max.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "Please enter your email.")]
|
||||
[EmailAddress(ErrorMessage = "Please enter a valid email address.")]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "Please include a message.")]
|
||||
[MaxLength(500, ErrorMessage = "Please enter a shorter message. 500 characters max.")]
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
private async Task HandleValidSubmit()
|
||||
{
|
||||
isSubmitting = true;
|
||||
|
||||
var recaptchaResponse = await JsRuntime.InvokeAsync<string>("executeRecaptcha");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(recaptchaResponse))
|
||||
{
|
||||
apiResponse = response;
|
||||
sendEmailSuccess = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var sendEmailRequest = new SendContactEmailRequest(
|
||||
contactInput.Name,
|
||||
contactInput.Email,
|
||||
contactInput.Message,
|
||||
recaptchaResponse);
|
||||
|
||||
var response = await HttpClient.PostAsJsonAsync("/api/send-contact-email", sendEmailRequest);
|
||||
|
||||
sendEmailSuccess = response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
isSubmitting = false;
|
||||
isSubmitted = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@@ -8,4 +9,7 @@
|
||||
@using Microsoft.JSInterop
|
||||
@using BeauFindlay.Client
|
||||
@using BeauFindlay.Client.Layout
|
||||
@using BeauFindlay.Client.Components.Typewriter
|
||||
@using BeauFindlay.Client.Components.Alert
|
||||
@using BeauFindlay.Client.Components.Typewriter
|
||||
@using BeauFindlay.Client.Components.Button
|
||||
@using BeauFindlay.Client.Components.LoadingSpinner
|
||||
@@ -560,15 +560,31 @@ video {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.m-auto {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.ml-3 {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-2\.5 {
|
||||
margin-top: 0.625rem;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
@@ -581,16 +597,36 @@ video {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 2.5rem;
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.h-20 {
|
||||
height: 5rem;
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.h-12 {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.h-5 {
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.h-6 {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.h-8 {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
@@ -601,6 +637,48 @@ video {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.w-12 {
|
||||
width: 3rem;
|
||||
}
|
||||
|
||||
.w-5 {
|
||||
width: 1.25rem;
|
||||
}
|
||||
|
||||
.w-6 {
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.w-8 {
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.max-w-xl {
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.grid-cols-1 {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -613,6 +691,15 @@ video {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gap-x-8 {
|
||||
-moz-column-gap: 2rem;
|
||||
column-gap: 2rem;
|
||||
}
|
||||
|
||||
.gap-y-6 {
|
||||
row-gap: 1.5rem;
|
||||
}
|
||||
|
||||
.space-x-6 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(1.5rem * var(--tw-space-x-reverse));
|
||||
@@ -629,6 +716,18 @@ video {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.rounded-md {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.border-0 {
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.border {
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.border-l-2 {
|
||||
border-left-width: 2px;
|
||||
}
|
||||
@@ -642,8 +741,27 @@ video {
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.p-8 {
|
||||
padding: 2rem;
|
||||
.bg-green-50 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(240 253 244 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-red-50 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254 242 242 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.fill-gray-600 {
|
||||
fill: #4b5563;
|
||||
}
|
||||
|
||||
.p-4 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.px-10 {
|
||||
padding-left: 2.5rem;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.px-2 {
|
||||
@@ -651,6 +769,16 @@ video {
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.px-3 {
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
.px-3\.5 {
|
||||
padding-left: 0.875rem;
|
||||
padding-right: 0.875rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
@@ -661,21 +789,43 @@ video {
|
||||
padding-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.py-28 {
|
||||
padding-top: 7rem;
|
||||
padding-bottom: 7rem;
|
||||
}
|
||||
|
||||
.py-20 {
|
||||
padding-top: 5rem;
|
||||
padding-bottom: 5rem;
|
||||
}
|
||||
|
||||
.py-12 {
|
||||
padding-top: 3rem;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
.py-2 {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.py-2\.5 {
|
||||
padding-top: 0.625rem;
|
||||
padding-bottom: 0.625rem;
|
||||
}
|
||||
|
||||
.py-4 {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.py-8 {
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.pt-10 {
|
||||
padding-top: 2.5rem;
|
||||
}
|
||||
|
||||
.pb-4 {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.font-mono {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
@@ -685,11 +835,26 @@ video {
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem;
|
||||
}
|
||||
|
||||
.text-4xl {
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
@@ -700,9 +865,8 @@ video {
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem;
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
@@ -713,6 +877,50 @@ video {
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.leading-6 {
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.text-gray-200 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(229 231 235 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-green-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(74 222 128 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-green-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(21 128 61 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-green-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(22 101 52 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(248 113 113 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(220 38 38 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(185 28 28 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(153 27 27 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-slate-100 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(241 245 249 / var(--tw-text-opacity));
|
||||
@@ -728,12 +936,28 @@ video {
|
||||
color: rgb(248 250 252 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
.text-white {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.underline-offset-4 {
|
||||
text-underline-offset: 4px;
|
||||
.text-green-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(22 163 74 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-green-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(34 197 94 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(239 68 68 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.antialiased {
|
||||
@@ -741,6 +965,27 @@ video {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.ring-1 {
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
}
|
||||
|
||||
.ring-inset {
|
||||
--tw-ring-inset: inset;
|
||||
}
|
||||
|
||||
.ring-gray-300 {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
from, to {
|
||||
opacity: 1
|
||||
@@ -771,11 +1016,72 @@ video {
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder\:text-gray-400::-moz-placeholder {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.placeholder\:text-gray-400::placeholder {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gray-800:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(31 41 55 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-slate-500:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(100 116 139 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.focus\:ring-2:focus {
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
}
|
||||
|
||||
.focus\:ring-inset:focus {
|
||||
--tw-ring-inset: inset;
|
||||
}
|
||||
|
||||
.focus\:ring-gray-600:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(75 85 99 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
.focus-visible\:outline:focus-visible {
|
||||
outline-style: solid;
|
||||
}
|
||||
|
||||
.focus-visible\:outline-2:focus-visible {
|
||||
outline-width: 2px;
|
||||
}
|
||||
|
||||
.focus-visible\:outline-offset-2:focus-visible {
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.focus-visible\:outline-gray-600:focus-visible {
|
||||
outline-color: #4b5563;
|
||||
}
|
||||
|
||||
.disabled\:bg-gray-800:disabled {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(31 41 55 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.sm\:col-span-2 {
|
||||
grid-column: span 2 / span 2;
|
||||
}
|
||||
|
||||
.sm\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\:order-1 {
|
||||
order: 1;
|
||||
@@ -801,14 +1107,19 @@ video {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.md\:px-8 {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
.md\:px-12 {
|
||||
padding-left: 3rem;
|
||||
padding-right: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:p-8 {
|
||||
padding: 2rem;
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dark\:fill-gray-300 {
|
||||
fill: #d1d5db;
|
||||
}
|
||||
|
||||
.dark\:text-gray-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(75 85 99 / var(--tw-text-opacity));
|
||||
}
|
||||
}
|
||||
18
BeauFindlay/src/BeauFindlay.Client/wwwroot/js/recaptcha.js
Normal file
18
BeauFindlay/src/BeauFindlay.Client/wwwroot/js/recaptcha.js
Normal file
@@ -0,0 +1,18 @@
|
||||
window.executeRecaptcha = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
grecaptcha.ready(function () {
|
||||
grecaptcha
|
||||
.execute("6LcvxZIpAAAAAOIP5L6kGngwDZRpwkTdMezPn06x", {
|
||||
action: "submit",
|
||||
})
|
||||
.then(
|
||||
function (token) {
|
||||
resolve(token);
|
||||
},
|
||||
function (error) {
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user