mirror of
https://github.com/vercel/commerce.git
synced 2025-05-19 16:07:01 +00:00
Alert banner
This commit is contained in:
parent
ffdc12fcca
commit
4245628da1
13
components/layout/header/alert-banner/alert-banner.tsx
Normal file
13
components/layout/header/alert-banner/alert-banner.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
|
||||
export function AlertBanner() {
|
||||
return (
|
||||
<Alert variant="destructive">
|
||||
<ExclamationTriangleIcon className="h-4 w-4" />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>Your session has expired. Please log in again.</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
}
|
@ -26,7 +26,7 @@ export default async function Header({ locale }: HeaderProps) {
|
||||
return (
|
||||
<HeaderRoot>
|
||||
<div className="relative flex flex-col border-b border-ui-border bg-app">
|
||||
<div className="relative flex h-14 w-full items-center justify-between px-4 py-2 lg:h-16 lg:px-8 lg:py-3 2xl:px-16">
|
||||
<div className="relative flex w-full items-center justify-between px-4 py-2 lg:px-8 2xl:px-16">
|
||||
<div className="-translate-x-2 transform md:hidden">
|
||||
<Suspense fallback={<OpenMobileMenu />}>
|
||||
<MobileMenuModal items={mainMenu} />
|
||||
|
@ -8,6 +8,8 @@ interface HeroProps {
|
||||
label?: string;
|
||||
title: string;
|
||||
image: object | any;
|
||||
color?: string;
|
||||
overlay?: boolean;
|
||||
link: {
|
||||
title: string;
|
||||
reference: {
|
||||
@ -26,7 +28,7 @@ const heroSize = {
|
||||
halfScreen: 'aspect-square max-h-[50vh] lg:aspect-auto lg:min-h-[50vh]'
|
||||
};
|
||||
|
||||
const Hero = ({ variant, title, text, label, image, link }: HeroProps) => {
|
||||
const Hero = ({ variant, title, text, label, image, link, color, overlay }: HeroProps) => {
|
||||
const heroClass = heroSize[variant as HeroSize] || heroSize.fullScreen;
|
||||
|
||||
return (
|
||||
@ -43,7 +45,12 @@ const Hero = ({ variant, title, text, label, image, link }: HeroProps) => {
|
||||
fill
|
||||
/>
|
||||
)}
|
||||
<div className="absolute bottom-5 left-4 z-50 flex max-w-md flex-col items-start text-high-contrast lg:bottom-8 lg:left-8 lg:max-w-2xl 2xl:bottom-16 2xl:left-16">
|
||||
{overlay && <div className="absolute inset-0 z-10 h-full w-full bg-black/70" />}
|
||||
<div
|
||||
className={`${
|
||||
color === 'dark' ? 'text-high-contrast' : 'text-white'
|
||||
} items-star absolute bottom-5 left-4 z-50 flex max-w-md flex-col lg:bottom-8 lg:left-8 lg:max-w-2xl 2xl:bottom-16 2xl:left-16`}
|
||||
>
|
||||
{label && (
|
||||
<Text className="mb-1 lg:mb-2" variant="label">
|
||||
{label}
|
||||
|
@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
'use client';
|
||||
|
||||
import SanityImage from '../../ui/sanity-image';
|
||||
|
||||
interface USPSectionProps {
|
||||
usps: [] | any
|
||||
usps: [] | any;
|
||||
}
|
||||
|
||||
const USPSection = ({ usps }: USPSectionProps) => {
|
||||
@ -12,14 +12,12 @@ const USPSection = ({ usps }: USPSectionProps) => {
|
||||
return (
|
||||
<div className="px-4 lg:px-8 2xl:px-16">
|
||||
<div
|
||||
className={`w-full grid grid-cols-2 gap-x-4 gap-y-6 lg:gap-8 2xl:gap-x-16 ${desktopGridLayout}`}
|
||||
className={`grid w-full grid-cols-2 gap-x-4 gap-y-6 lg:gap-8 2xl:gap-x-16 ${desktopGridLayout}`}
|
||||
>
|
||||
{usps.map((usp: any, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`w-full flex flex-col items-center text-center`}
|
||||
>
|
||||
<div className="w-20 h-20 lg:w-24 lg:h-24">
|
||||
<div key={index} className={`flex w-full flex-col items-center text-center`}>
|
||||
{usp?.image && (
|
||||
<div className="h-20 w-20 lg:h-24 lg:w-24">
|
||||
<SanityImage
|
||||
className="object-cover"
|
||||
image={usp?.image}
|
||||
@ -29,18 +27,18 @@ const USPSection = ({ usps }: USPSectionProps) => {
|
||||
sizes="96px"
|
||||
/>
|
||||
</div>
|
||||
<h2 className="text-xl mt-4 lg:mt-8 lg:text-2xl">{usp.title}</h2>
|
||||
)}
|
||||
<h2 className="mt-4 text-xl lg:mt-8 lg:text-2xl">{usp.title}</h2>
|
||||
{usp.text && (
|
||||
<p className="text-sm mt-2 text-low-contrast max-w-xs lg:text-base lg:mt-4">
|
||||
<p className="mt-2 max-w-xs text-sm text-low-contrast lg:mt-4 lg:text-base">
|
||||
{usp.text}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default USPSection
|
||||
export default USPSection;
|
||||
|
49
components/ui/alert.tsx
Normal file
49
components/ui/alert.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const alertVariants = cva(
|
||||
'relative w-full border px-4 py-2 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-background text-foreground',
|
||||
destructive:
|
||||
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
|
||||
));
|
||||
Alert.displayName = 'Alert';
|
||||
|
||||
const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn('mb-1 font-medium leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
AlertTitle.displayName = 'AlertTitle';
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />
|
||||
));
|
||||
AlertDescription.displayName = 'AlertDescription';
|
||||
|
||||
export { Alert, AlertDescription, AlertTitle };
|
@ -70,7 +70,7 @@ const Text: FunctionComponent<TextProps> = ({
|
||||
['max-w-prose font-display text-2xl font-extrabold leading-none md:text-3xl md:leading-none lg:text-4xl lg:leading-none']:
|
||||
variant === 'sectionHeading',
|
||||
['text-sm leading-tight lg:text-base']: variant === 'listChildHeading',
|
||||
['max-w-prose text-lg text-high-contrast lg:text-xl']: variant === 'label',
|
||||
['max-w-prose text-lg lg:text-xl']: variant === 'label',
|
||||
['max-w-prose']: variant === 'paragraph'
|
||||
},
|
||||
className
|
||||
|
@ -33,6 +33,8 @@ export const modules = `
|
||||
variant,
|
||||
headingLevel,
|
||||
text,
|
||||
color,
|
||||
overlay,
|
||||
link {
|
||||
title,
|
||||
reference->{title, slug, "locale": language}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {StarIcon} from '@sanity/icons'
|
||||
import {defineField} from 'sanity'
|
||||
import {languages} from '../../languages'
|
||||
import {validateImage} from '../../utils/validation'
|
||||
import { StarIcon } from '@sanity/icons';
|
||||
import { defineField } from 'sanity';
|
||||
import { languages } from '../../languages';
|
||||
|
||||
export default defineField({
|
||||
name: 'usp',
|
||||
@ -13,7 +12,7 @@ export default defineField({
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
readOnly: true,
|
||||
description: 'Language of this document.',
|
||||
description: 'Language of this document.'
|
||||
// hidden: true,
|
||||
}),
|
||||
// Title
|
||||
@ -22,15 +21,14 @@ export default defineField({
|
||||
title: 'Title',
|
||||
type: 'string',
|
||||
description: 'USP title',
|
||||
validation: (Rule) => Rule.required(),
|
||||
validation: (Rule) => Rule.required()
|
||||
}),
|
||||
// Image
|
||||
defineField({
|
||||
name: 'image',
|
||||
title: 'Image',
|
||||
type: 'mainImage',
|
||||
description: 'USP icon',
|
||||
validation: (Rule) => validateImage(Rule, true),
|
||||
description: 'USP icon'
|
||||
}),
|
||||
// Text
|
||||
defineField({
|
||||
@ -38,25 +36,25 @@ export default defineField({
|
||||
title: 'Text',
|
||||
type: 'text',
|
||||
description: 'Small text displayed below title.',
|
||||
rows: 5,
|
||||
}),
|
||||
rows: 5
|
||||
})
|
||||
],
|
||||
preview: {
|
||||
select: {
|
||||
title: 'title',
|
||||
image: 'image',
|
||||
language: 'language',
|
||||
language: 'language'
|
||||
},
|
||||
prepare(selection) {
|
||||
const {image, title, language} = selection
|
||||
const { image, title, language } = selection;
|
||||
|
||||
const currentLang = languages.find((lang) => lang.id === language)
|
||||
const currentLang = languages.find((lang) => lang.id === language);
|
||||
|
||||
return {
|
||||
media: image,
|
||||
title,
|
||||
subtitle: `${currentLang ? currentLang.title : ''}`,
|
||||
subtitle: `${currentLang ? currentLang.title : ''}`
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {defineField} from 'sanity'
|
||||
import { StarIcon } from '@sanity/icons'
|
||||
import { defineField } from 'sanity'
|
||||
import { validateImage } from '../../utils/validation'
|
||||
|
||||
export default defineField({
|
||||
@ -58,6 +58,30 @@ export default defineField({
|
||||
layout: 'radio',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
type: 'string',
|
||||
title: 'Color',
|
||||
initialValue: 'dark',
|
||||
fieldset: 'settings',
|
||||
description: 'Set appropriate color depending on image characteristics.',
|
||||
options: {
|
||||
list: [
|
||||
{title: 'Dark', value: 'dark'},
|
||||
{title: 'Light', value: 'light'},
|
||||
],
|
||||
layout: 'radio',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'overlay',
|
||||
type: 'boolean',
|
||||
title: 'Overlay?',
|
||||
fieldset: 'settings',
|
||||
description: 'Adds a dark overlay to the image.',
|
||||
initialValue: false,
|
||||
validation: (Rule) => Rule.required(),
|
||||
},
|
||||
{
|
||||
name: 'label',
|
||||
type: 'string',
|
||||
@ -101,7 +125,6 @@ export default defineField({
|
||||
title: 'Link',
|
||||
description: 'Link to internal page.',
|
||||
options: {
|
||||
collapsed: true,
|
||||
collapsible: true,
|
||||
},
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user