Alert banner

This commit is contained in:
Henrik Larsson 2023-08-15 13:32:36 +02:00
parent ffdc12fcca
commit 4245628da1
9 changed files with 138 additions and 48 deletions

View 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>
);
}

View File

@ -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} />

View File

@ -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}

View File

@ -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
View 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 };

View File

@ -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

View File

@ -33,6 +33,8 @@ export const modules = `
variant,
headingLevel,
text,
color,
overlay,
link {
title,
reference->{title, slug, "locale": language}

View File

@ -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 : ''}`
};
}
},
},
})
}
});

View File

@ -1,5 +1,5 @@
import {defineField} from 'sanity'
import {StarIcon} from '@sanity/icons'
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,
},
},