Privacy page restyling (#22)

This commit is contained in:
Riccardo Senica
2024-11-23 13:01:48 +01:00
committed by GitHub
parent c300b2501d
commit d8170747c7
12 changed files with 275 additions and 183 deletions

View File

@@ -7,10 +7,14 @@ export type ButtonProps = {
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ asChild = false, ...props }, ref) => {
({ asChild = false, className, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp className={cn('btn-grad', 'btn-grad-hover')} ref={ref} {...props} />
<Comp
className={className ?? cn('btn-grad', 'btn-grad-hover')}
ref={ref}
{...props}
/>
);
}
);

View File

@@ -7,7 +7,10 @@ const Card = React.forwardRef<
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('rounded-lg bg-card text-card-foreground', className)}
className={cn(
'rounded-lg bg-card text-card-foreground transition-all duration-200',
className
)}
{...props}
/>
));

View File

@@ -21,7 +21,7 @@ export const CustomCard = ({
title,
description,
content,
className,
className = '',
footer = true
}: CardProps) => {
const [isLoaded, setIsLoaded] = useState(false);
@@ -35,22 +35,26 @@ export const CustomCard = ({
}
return (
<div className='gradient-border shadow-2xl shadow-black'>
<Card className={`z-10 max-w-[90vw] p-8 ${className}`}>
<CardHeader>
<p className='text-xs uppercase text-gray-500'>
Hackernews + newsletter
</p>
<CardTitle>{title}</CardTitle>
<CardDescription>{description}</CardDescription>
</CardHeader>
<CardContent>{content}</CardContent>
{footer && (
<CardFooter>
<Footer />
</CardFooter>
)}
</Card>
<div className='mx-auto w-full max-w-screen-lg px-4 sm:px-6 lg:px-8'>
<div className='gradient-border shadow-2xl shadow-black'>
<Card
className={`z-10 w-full transform p-8 transition-all duration-300 ${className}`}
>
<CardHeader>
<p className='text-xs uppercase text-gray-500'>
Hackernews + newsletter
</p>
<CardTitle>{title}</CardTitle>
{description && <CardDescription>{description}</CardDescription>}
</CardHeader>
<CardContent>{content}</CardContent>
{footer && (
<CardFooter>
<Footer />
</CardFooter>
)}
</Card>
</div>
</div>
);
};

View File

@@ -5,11 +5,12 @@ import { Button } from './Button';
interface LinkProps {
path: string;
text: string;
className?: string;
}
export default function CustomLink({ path, text }: LinkProps) {
export default function CustomLink({ path, text, className }: LinkProps) {
return (
<Button asChild>
<Button asChild className={className}>
<Link href={path}>{text}</Link>
</Button>
);

View File

@@ -3,38 +3,50 @@ import Link from 'next/link';
import { usePathname } from 'next/navigation';
import CustomLink from './CustomLink';
const links = [{ name: 'Subscribe', path: '/' }];
export const Footer = () => {
const pathname = usePathname();
return (
<div>
{pathname === '/' ? (
<p className='text-center text-xs text-gray-600'>
By subscribing, you agree to our{' '}
<Link
className='font-medium text-indigo-600 hover:text-indigo-500'
href='/privacy'
>
Privacy Policy
</Link>
.
</p>
) : (
<div className='flex justify-center space-x-4'>
{links.map(
link =>
pathname !== link.path &&
!(pathname === '/confirmation' && link.path === '/') && (
<CustomLink key={link.path} path={link.path} text={link.name} />
)
)}
{pathname === '/privacy' && (
<CustomLink path='/unsubscribe' text='Unsubscribe' />
)}
if (pathname === '/confirmation') {
return;
}
if (pathname === '/unsubscribe') {
return (
<div className='flex justify-center space-x-4'>
<CustomLink path='/' text='Subscribe' />
</div>
);
}
if (pathname === '/privacy') {
return (
<div className='relative flex w-full items-center'>
<div className='flex w-full justify-center'>
<div className='inline-flex'>
<CustomLink path='/' text='Subscribe' />
</div>
</div>
)}
</div>
<div className='absolute right-0'>
<CustomLink
path='/unsubscribe'
text='Unsubscribe'
className='rounded bg-gray-50 px-3 py-1.5 text-sm text-gray-500 transition-colors duration-200 hover:bg-gray-100'
/>
</div>
</div>
);
}
return (
<p className='text-center text-xs text-gray-600'>
By subscribing, you agree to our{' '}
<Link
className='font-medium text-indigo-600 hover:text-indigo-500'
href='/privacy'
>
Privacy Policy
</Link>
.
</p>
);
};

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { Button, ButtonProps } from './Button';
import { Loader2 } from 'lucide-react';
import { cn } from '@utils/cn';
interface LoadingButtonProps extends ButtonProps {
loading?: boolean;
}
const LoadingButton = React.forwardRef<HTMLButtonElement, LoadingButtonProps>(
({ loading, children, disabled, ...props }, ref) => {
return (
<Button {...props} disabled={disabled || loading} ref={ref}>
<div className='relative'>
<span
className={cn(
'flex items-center justify-center',
loading ? 'invisible' : 'visible'
)}
>
{children}
</span>
{loading && (
<span className='absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center gap-2'>
<Loader2 className='h-4 w-4 animate-spin' />
Loading
</span>
)}
</div>
</Button>
);
}
);
LoadingButton.displayName = 'LoadingButton';
export { LoadingButton };

View File

@@ -0,0 +1,25 @@
import { useFormField } from '@hooks/useFormField';
import { XCircle } from 'lucide-react';
import * as React from 'react';
export const FormErrorMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(() => {
const { error, formMessageId } = useFormField();
const body = error?.message;
return (
body && (
<div
id={formMessageId}
className='flex items-center justify-center gap-2 text-sm text-red-500 duration-200 animate-in fade-in slide-in-from-top-1'
>
<XCircle className='h-4 w-4' />
<span>{body}</span>
</div>
)
);
});
FormErrorMessage.displayName = 'FormErrorMessage';

View File

@@ -1,28 +0,0 @@
import { useFormField } from '@hooks/useFormField';
import { cn } from '@utils/cn';
import * as React from 'react';
export const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;
return (
<>
{!body ? null : (
<p
ref={ref}
id={formMessageId}
className={cn('text-sm font-medium text-destructive', className)}
{...props}
>
{body}
</p>
)}
</>
);
});
FormMessage.displayName = 'FormMessage';