add order status alert
This commit is contained in:
parent
f6154d5fc2
commit
24f71a9b66
@ -1,7 +1,13 @@
|
||||
import { getOrder } from '@/actions/api/orders';
|
||||
import { Container } from '@/components/layout';
|
||||
import { PageHeader } from '@/components/navigation';
|
||||
import { OrderButtons, OrderContacts, OrderDateTime, OrderServices } from '@/components/orders';
|
||||
import {
|
||||
OrderButtons,
|
||||
OrderContacts,
|
||||
OrderDateTime,
|
||||
OrderServices,
|
||||
OrderStatus,
|
||||
} from '@/components/orders';
|
||||
import { type OrderPageParameters } from '@/components/orders/types';
|
||||
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
|
||||
|
||||
@ -23,6 +29,7 @@ export default async function ProfilePage(props: Readonly<Props>) {
|
||||
<PageHeader title="Запись" />
|
||||
<Container>
|
||||
<OrderDateTime {...parameters} />
|
||||
<OrderStatus {...parameters} />
|
||||
<OrderContacts {...parameters} />
|
||||
<OrderServices {...parameters} />
|
||||
<div className="pb-24" />
|
||||
|
||||
@ -4,4 +4,5 @@ export * from './order-contacts';
|
||||
export * from './order-datetime';
|
||||
export * from './order-form';
|
||||
export * from './order-services';
|
||||
export * from './order-status';
|
||||
export * from './orders-list';
|
||||
|
||||
25
apps/web/components/orders/order-status.tsx
Normal file
25
apps/web/components/orders/order-status.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
'use client';
|
||||
|
||||
import { type OrderComponentProps } from './types';
|
||||
import { useOrderQuery } from '@/hooks/api/orders';
|
||||
import { getAlertStyles, getBadgeText, getStatusIcon } from '@/utils/components/order';
|
||||
import { Alert, AlertTitle } from '@repo/ui/components/ui/alert';
|
||||
|
||||
export function OrderStatus({ documentId }: Readonly<OrderComponentProps>) {
|
||||
const { data: { order } = {} } = useOrderQuery({ documentId });
|
||||
|
||||
const state = order?.state;
|
||||
|
||||
if (!state) return null;
|
||||
|
||||
const title = getBadgeText(state);
|
||||
const styles = getAlertStyles(state);
|
||||
const { icon: StatusIcon, iconStyles } = getStatusIcon(state);
|
||||
|
||||
return (
|
||||
<Alert className={styles}>
|
||||
<StatusIcon className={iconStyles} />
|
||||
<AlertTitle>{title}</AlertTitle>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { ReadonlyTimeRange } from './time-range/readonly';
|
||||
import { type Enum_Order_State } from '@repo/graphql/types';
|
||||
import { getBadgeStyles, getBadgeText } from '@/utils/components/order';
|
||||
import type * as GQL from '@repo/graphql/types';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@repo/ui/components/ui/avatar';
|
||||
import { Badge } from '@repo/ui/components/ui/badge';
|
||||
@ -44,31 +44,3 @@ function ClientAvatar({ client }: { readonly client: OrderClient }) {
|
||||
</Avatar>
|
||||
);
|
||||
}
|
||||
|
||||
const MAP_BADGE_TEXT: Record<Enum_Order_State, string> = {
|
||||
approved: 'Подтверждено',
|
||||
cancelled: 'Отменено',
|
||||
cancelling: 'Отменяется',
|
||||
completed: 'Завершено',
|
||||
created: 'Создано',
|
||||
scheduled: 'Запланировано',
|
||||
};
|
||||
|
||||
const MAP_BADGE_STYLES: Record<Enum_Order_State, string> = {
|
||||
approved: 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-100',
|
||||
cancelled: 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-100',
|
||||
cancelling: 'bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-100',
|
||||
completed: 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-100',
|
||||
created: '',
|
||||
scheduled: 'bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-100',
|
||||
};
|
||||
|
||||
function getBadgeStyles(state: Enum_Order_State) {
|
||||
if (!state) return '';
|
||||
return MAP_BADGE_STYLES[state];
|
||||
}
|
||||
|
||||
function getBadgeText(state: Enum_Order_State) {
|
||||
if (!state) return '';
|
||||
return MAP_BADGE_TEXT[state];
|
||||
}
|
||||
|
||||
1
apps/web/utils/components/order/index.ts
Normal file
1
apps/web/utils/components/order/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './status';
|
||||
92
apps/web/utils/components/order/status.ts
Normal file
92
apps/web/utils/components/order/status.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { type Enum_Order_State } from '@repo/graphql/types';
|
||||
import { AlertCircle, Calendar, CheckCircle, FileText, XCircle } from 'lucide-react';
|
||||
|
||||
const BADGE_TEXT: Record<Enum_Order_State, string> = {
|
||||
approved: 'Подтверждено',
|
||||
cancelled: 'Отменено',
|
||||
cancelling: 'Отменяется',
|
||||
completed: 'Завершено',
|
||||
created: 'Создано',
|
||||
scheduled: 'Запланировано',
|
||||
};
|
||||
|
||||
const BADGE_STYLES: Record<Enum_Order_State, string> = {
|
||||
approved: 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-100',
|
||||
cancelled: 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-100',
|
||||
cancelling: 'bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-100',
|
||||
completed: 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-100',
|
||||
created: '',
|
||||
scheduled: 'bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-100',
|
||||
};
|
||||
|
||||
const ALERT_STYLES: Record<Enum_Order_State, string> = {
|
||||
approved:
|
||||
'border-blue-200 bg-blue-50 text-blue-800 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-200',
|
||||
cancelled:
|
||||
'border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950 dark:text-red-200',
|
||||
cancelling:
|
||||
'border-orange-200 bg-orange-50 text-orange-800 dark:border-orange-800 dark:bg-orange-950 dark:text-orange-200',
|
||||
completed:
|
||||
'border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950 dark:text-green-200',
|
||||
created:
|
||||
'border-gray-200 bg-gray-50 text-gray-800 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200',
|
||||
scheduled:
|
||||
'border-purple-200 bg-purple-50 text-purple-800 dark:border-purple-800 dark:bg-purple-950 dark:text-purple-200',
|
||||
};
|
||||
|
||||
const STATUS_ICONS: Record<
|
||||
Enum_Order_State,
|
||||
{
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
iconStyles: string;
|
||||
}
|
||||
> = {
|
||||
approved: {
|
||||
icon: CheckCircle,
|
||||
iconStyles: 'text-blue-600 dark:text-blue-400',
|
||||
},
|
||||
cancelled: {
|
||||
icon: XCircle,
|
||||
iconStyles: 'text-red-600 dark:text-red-400',
|
||||
},
|
||||
cancelling: {
|
||||
icon: AlertCircle,
|
||||
iconStyles: 'text-orange-600 dark:text-orange-400',
|
||||
},
|
||||
completed: {
|
||||
icon: CheckCircle,
|
||||
iconStyles: 'text-green-600 dark:text-green-400',
|
||||
},
|
||||
created: {
|
||||
icon: FileText,
|
||||
iconStyles: 'text-gray-600 dark:text-gray-400',
|
||||
},
|
||||
scheduled: {
|
||||
icon: Calendar,
|
||||
iconStyles: 'text-purple-600 dark:text-purple-400',
|
||||
},
|
||||
};
|
||||
|
||||
export function getAlertStyles(state: Enum_Order_State) {
|
||||
if (!state) return '';
|
||||
|
||||
return ALERT_STYLES[state];
|
||||
}
|
||||
|
||||
export function getBadgeStyles(state: Enum_Order_State) {
|
||||
if (!state) return '';
|
||||
|
||||
return BADGE_STYLES[state];
|
||||
}
|
||||
|
||||
export function getBadgeText(state: Enum_Order_State) {
|
||||
if (!state) return '';
|
||||
|
||||
return BADGE_TEXT[state];
|
||||
}
|
||||
|
||||
export function getStatusIcon(state: Enum_Order_State) {
|
||||
if (!state) return { icon: () => null, iconStyles: '' };
|
||||
|
||||
return STATUS_ICONS[state];
|
||||
}
|
||||
59
packages/ui/src/components/ui/alert.tsx
Normal file
59
packages/ui/src/components/ui/alert.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { cn } from '@repo/ui/lib/utils';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import { type ComponentProps } from 'react';
|
||||
|
||||
const alertVariants = cva(
|
||||
'relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
|
||||
{
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-card text-card-foreground',
|
||||
destructive:
|
||||
'bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
function Alert({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}: ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
data-slot="alert"
|
||||
role="alert"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDescription({ className, ...props }: ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed',
|
||||
className,
|
||||
)}
|
||||
data-slot="alert-description"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertTitle({ className, ...props }: ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
className={cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', className)}
|
||||
data-slot="alert-title"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Alert, AlertDescription, AlertTitle };
|
||||
Loading…
x
Reference in New Issue
Block a user