Add new UI components and secure headers configuration

Introduced new UI components including testimonial section, avatar, and image elements. Configured secure headers in vercel.json file to enhance security. These changes improve both the aesthetic appeal and security of the website.
This commit is contained in:
Emil Gulamov 2024-02-14 22:57:02 +04:00
parent a9b4e8c0dc
commit 6025dc07db
10 changed files with 287 additions and 0 deletions

View file

@ -0,0 +1,56 @@
---
import { Image } from "astro:assets";
import product5 from "../images/features-image.avif";
const title: string = "Why Choose ScrewFast?";
const subTitle: string =
"Transform your ideas into tangible results with ScrewFast tools. Whether you're starting with a sketch on a napkin or diving into a comprehensive construction project, our tools are engineered to help you build with confidence.";
const benefits: string[] = [
"Robust and reliable tools for long-lasting performance.",
"Innovative solutions tailored to modern construction needs.",
"Customer support dedicated to your project's success.",
];
const ListItemMarker:string = `<svg fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mt-0.5 h-6 w-6 text-[#fa5a15] dark:text-[#fb713b]"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg>`;
---
<div
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
>
<!-- Grid -->
<div class="lg:grid lg:grid-cols-12 lg:items-center lg:gap-16">
<div class="lg:col-span-7">
<Image class="rounded-xl" src={product5} alt="Mockup of floating boxes" />
</div>
<div class="mt-5 sm:mt-10 lg:col-span-5 lg:mt-0">
<div class="space-y-6 sm:space-y-8">
<div class="space-y-2 md:space-y-4">
<h2
class="text-balance text-3xl font-bold text-neutral-800 dark:text-neutral-200 lg:text-4xl"
>
{title}
</h2>
<p class="text-pretty text-neutral-600 dark:text-neutral-400">
{subTitle}
</p>
</div>
<ul role="list" class="space-y-2 sm:space-y-4">
{
benefits.map((item) => (
<li class="flex space-x-3">
<Fragment set:html={ListItemMarker}/>
<span class="text-pretty text-sm font-medium text-neutral-600 dark:text-neutral-400 sm:text-base">
{item}
</span>
</li>
))
}
</ul>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,94 @@
---
// Import the necessary dependencies from individual component files
import AvatarTestimonialSection2 from "./ui/avatars/AvatarTestimonialSection2.astro";
// Variables for customization of the LoginModal Component
// Main heading
const title: string = "What Our Customers Say";
/* TypeScript type for product. */
type Testimonial = {
content: string;
author: string;
role: string;
avatarSrc: string;
avatarAlt: string;
};
const testimonials: Testimonial[] = [
{
content:
" \"Since switching to ScrewFast's hardware tools, the efficiency on our construction sites has skyrocketed. The durability of the hex bolts and precision of the machine screws are simply unmatched. It's refreshing to work with a company that truly understands the daily demands of the industry.\" ",
author: "Jason Clark",
role: "Site Foreman | TopBuild",
avatarSrc:
"https://images.unsplash.com/photo-1500648767791-00dcc994a43e?q=80&w=1374&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=facearea&facepad=2&w=320&h=320&q=80",
avatarAlt: "Image Description",
},
{
content:
" \"As an interior designer, I'm always looking for high-quality materials and tools that help bring my visions to life. ScrewFast's mixed screws assortment has been a game-changer for my projects, providing the perfect blend of quality and variety. The outstanding customer support was just the cherry on top!\" ",
author: "Maria Gonzalez",
role: "Interior Designer | Creative Spaces",
avatarSrc:
"https://images.unsplash.com/photo-1544005313-94ddf0286df2?q=80&w=1376&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D8&auto=format&fit=facearea&facepad=2&w=320&h=320&q=80",
avatarAlt: "Image Description",
},
{
content:
" \"Ive been a professional carpenter for over 15 years, and I can sincerely say that ScrewFasts tap bolts and nuts are some of the best I've used. They grip like no other, and I have full confidence in every joint and fixture. Plus, the service is impeccable they truly care about my project's success.\" ",
author: "Richard Kim",
role: "Master Carpenter | WoodWright",
avatarSrc:
"https://images.unsplash.com/photo-1474176857210-7287d38d27c6?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D8&auto=format&fit=facearea&facepad=2&w=320&h=320&q=80",
avatarAlt: "Image Description",
},
];
---
<div
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
id="testimonials"
>
<div class="mb-6 w-3/4 max-w-2xl sm:mb-10 md:mb-16 lg:w-1/2">
<h2
class="text-balance text-2xl font-bold text-neutral-800 dark:text-neutral-200 sm:text-3xl lg:text-4xl"
>
{title}
</h2>
</div>
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{
testimonials.map((testimonial) => (
<div class="flex h-auto">
<div class="flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-700">
<div class="flex-auto p-4 md:p-6">
<p class="text-pretty text-base italic text-neutral-600 dark:text-neutral-300 md:text-lg">
{testimonial.content}
</p>
</div>
<div class="rounded-b-xl bg-neutral-300/30 p-4 dark:bg-neutral-900/30 md:px-7">
<div class="flex items-center">
<AvatarTestimonialSection2
src={testimonial.avatarSrc}
alt={testimonial.avatarAlt}
/>
<div class="ms-3 grow">
<p class="text-sm font-bold text-neutral-800 dark:text-neutral-200 sm:text-base">
{testimonial.author}
</p>
<p class="text-xs text-neutral-600 dark:text-neutral-400">
{testimonial.role}
</p>
</div>
</div>
</div>
</div>
</div>
))
}
</div>
</div>

View file

@ -0,0 +1,16 @@
---
const { src, alt } = Astro.props;
interface Props {
src: string;
alt: string;
}
---
<div class="flex-shrink-0">
<img
class="size-8 rounded-full sm:h-[2.875rem] sm:w-[2.875rem]"
src={src}
alt={alt}
/>
</div>

View file

@ -0,0 +1,41 @@
---
import { Image } from "astro:assets";
const { title, src, alt, url } = Astro.props;
interface Props {
title?: string;
src: any;
alt: string;
url: string;
}
const imageClass = "absolute inset-0 h-full w-full object-cover object-center transition duration-[600ms] ease-[cubic-bezier(0.45,0,0.55,1)] group-hover:scale-110";
const AnchorSVG = `
<svg fill="none" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor" class="ml-0.5 w-3 h-3 md:w-4 md:h-4 inline pb-0.5">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
</svg>`;
---
<a
href={url}
class="group relative flex h-48 items-end overflow-hidden rounded-xl shadow-lg md:h-80"
>
<Image
src={src}
alt={alt}
class={imageClass}
draggable={"false"}
format={"avif"}
/>
<div
class="pointer-events-none absolute inset-0 bg-gradient-to-t from-neutral-800 via-transparent to-transparent opacity-50 dark:from-neutral-700"
>
</div>
<span
class="relative mb-3 ml-4 inline-block text-sm font-bold text-neutral-50 transition duration-[600ms] ease-[cubic-bezier(0.45,0,0.55,1)] group-hover:scale-110 md:ml-5 md:text-lg"
>{title} <Fragment set:html={AnchorSVG} />
</span
>
</a>

View file

@ -0,0 +1,39 @@
---
import { Image } from "astro:assets";
const { title, src, alt, url } = Astro.props;
interface Props {
title?: string;
src: any;
alt: string;
url: string;
}
const imageClass = "absolute inset-0 h-full w-full object-cover object-center transition duration-[600ms] ease-[cubic-bezier(0.45,0,0.55,1)] group-hover:scale-110";
const AnchorSVG = `
<svg fill="none" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor" class="ml-0.5 w-3 h-3 md:w-4 md:h-4 inline pb-0.5">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
</svg>`;
---
<a
href={url}
class="group relative flex h-48 items-end overflow-hidden rounded-lg shadow-xl md:col-span-2 md:h-80"
>
<Image
src={src}
alt={alt}
draggable={"false"}
class={imageClass}
/>
<div
class="pointer-events-none absolute inset-0 bg-gradient-to-t from-neutral-800 via-transparent to-transparent opacity-50 dark:from-neutral-700"
>
</div>
<span
class="relative mb-3 ml-4 inline-block text-sm font-bold text-neutral-50 transition duration-[600ms] ease-[cubic-bezier(0.45,0,0.55,1)] group-hover:scale-110 md:ml-5 md:text-lg"
>{title} <Fragment set:html={AnchorSVG} /></span
>
</a>

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

41
vercel.json Normal file
View file

@ -0,0 +1,41 @@
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; base-uri 'self'; form-action 'self'; frame-src 'self'; frame-ancestors 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; object-src 'none'; upgrade-insecure-requests; block-all-mixed-content"
},
{
"key": "Permissions-Policy",
"value": "interest-cohort=()"
},
{
"key": "Referrer-Policy",
"value": "no-referrer-when-downgrade"
},
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "SAMEORIGIN"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
},
{
"key": "Cache-Control",
"value": "public, max-age=0, must-revalidate"
},
{
"key": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains; preload"
}
]
}
]
}