feat: enhance product and raw material models with additional stock formatting attributes and update image preview components to include descriptions
This commit is contained in:
parent
1b48f5c24f
commit
fbdad6923c
@ -17,7 +17,7 @@
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
#[Appends(['formatted_name', 'formatted_stock'])]
|
||||
#[Appends(['formatted_name', 'formatted_stock', 'formatted_reject_stock', 'formatted_retail_stock'])]
|
||||
#[Guarded(['id'])]
|
||||
#[ScopedBy([ProductVariantScope::class])]
|
||||
class ProductVariant extends Model implements HasMedia
|
||||
@ -47,6 +47,20 @@ protected function formattedStock(): Attribute
|
||||
);
|
||||
}
|
||||
|
||||
protected function formattedRejectStock(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => number_format($this->reject_stock, 0, ',', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function formattedRetailStock(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => number_format($this->retail_stock, 0, ',', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
public function orderItems(): HasMany
|
||||
{
|
||||
return $this->hasMany(OrderItem::class);
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
#[Appends(['formatted_price'])]
|
||||
#[Appends(['formatted_price', 'formatted_stock'])]
|
||||
#[Guarded(['id'])]
|
||||
#[ScopedBy([RawMaterialPriceScope::class])]
|
||||
class RawMaterialPrice extends Model implements HasMedia
|
||||
@ -40,6 +40,13 @@ protected function formattedPrice(): Attribute
|
||||
);
|
||||
}
|
||||
|
||||
protected function formattedStock(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => number_format($this->stock, 0, ',', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
public function cuttingMaterials(): HasMany
|
||||
{
|
||||
return $this->hasMany(CuttingMaterial::class);
|
||||
|
||||
@ -6,6 +6,7 @@ type ImagePreviewButtonProps = {
|
||||
modalSrc?: string;
|
||||
modalSrcs?: string[];
|
||||
title?: string;
|
||||
description?: string;
|
||||
alt?: string;
|
||||
className?: string;
|
||||
};
|
||||
@ -15,6 +16,7 @@ export function ImagePreviewButton({
|
||||
modalSrc,
|
||||
modalSrcs,
|
||||
title,
|
||||
description,
|
||||
alt,
|
||||
className = 'h-10 w-10',
|
||||
}: ImagePreviewButtonProps) {
|
||||
@ -46,6 +48,7 @@ export function ImagePreviewButton({
|
||||
src={modalSources?.[0] ?? srcs[0]}
|
||||
sources={modalSources ?? srcs}
|
||||
title={title}
|
||||
description={description}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -4,6 +4,7 @@ import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
@ -13,6 +14,7 @@ type ImagePreviewModalProps = {
|
||||
onOpenChange: (open: boolean) => void;
|
||||
src: string | null;
|
||||
title?: string;
|
||||
description?: string;
|
||||
alt?: string;
|
||||
sources?: string[];
|
||||
};
|
||||
@ -22,6 +24,7 @@ export function ImagePreviewModal({
|
||||
onOpenChange,
|
||||
src,
|
||||
title,
|
||||
description,
|
||||
alt = 'Preview',
|
||||
sources,
|
||||
}: ImagePreviewModalProps) {
|
||||
@ -55,9 +58,10 @@ setCurrentIndex(0);
|
||||
}}
|
||||
>
|
||||
<DialogContent showCloseButton>
|
||||
{title && (
|
||||
{(title || description) && (
|
||||
<DialogHeader>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
{title && <DialogTitle>{title}</DialogTitle>}
|
||||
{description && <DialogDescription>{description}</DialogDescription>}
|
||||
</DialogHeader>
|
||||
)}
|
||||
{currentSrc && (
|
||||
|
||||
@ -133,7 +133,8 @@ export function CuttingCardRow({
|
||||
<ImagePreviewButton
|
||||
srcs={[cutting.photo_conversion_url ?? cutting.photo_url]}
|
||||
modalSrc={cutting.photo_url}
|
||||
title="Foto Cutting"
|
||||
title={productName}
|
||||
description={cutting.description ?? formatDateTime(cutting.created_at)}
|
||||
className="h-16 w-16"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -120,7 +120,8 @@ export function PurchaseCardRow({
|
||||
<ImagePreviewButton
|
||||
srcs={[purchase.photo_conversion_url ?? purchase.photo_url]}
|
||||
modalSrc={purchase.photo_url}
|
||||
title="Foto Belanja"
|
||||
title={purchase.supplier.name}
|
||||
description={formatDateTime(purchase.created_at)}
|
||||
className="h-16 w-16"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -203,7 +203,8 @@ export function TransactionCardRow({
|
||||
<ImagePreviewButton
|
||||
srcs={[transaction.photo_conversion_url ?? transaction.photo_url]}
|
||||
modalSrc={transaction.photo_url}
|
||||
title="Foto Pesanan"
|
||||
title={transaction.customer?.name ?? transaction.order_number}
|
||||
description={transaction.notes ?? formatDateTime(transaction.created_at)}
|
||||
className="h-16 w-16"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -11,9 +11,11 @@ export type ProductVariant = {
|
||||
id: number;
|
||||
name: string;
|
||||
stock: number;
|
||||
formatted_stock: string
|
||||
formatted_stock: string;
|
||||
reject_stock: number;
|
||||
formatted_reject_stock: string;
|
||||
retail_stock: number;
|
||||
formatted_retail_stock: string;
|
||||
photo_urls: string[];
|
||||
photo_conversion_urls: string[];
|
||||
product_prices: {
|
||||
|
||||
@ -93,6 +93,7 @@ export function VariantSubRow({
|
||||
srcs={variant.photo_conversion_urls?.length > 0 ? variant.photo_conversion_urls : variant.photo_urls}
|
||||
modalSrcs={variant.photo_urls}
|
||||
title={variant.name}
|
||||
description={`Stok Bagus: ${variant.formatted_stock} | Stok Reject: ${variant.formatted_reject_stock} | Stok Ecer: ${variant.formatted_retail_stock}`}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-muted text-xs text-muted-foreground">
|
||||
|
||||
@ -3,6 +3,7 @@ export type RawMaterialVariant = {
|
||||
variant: string;
|
||||
price: number;
|
||||
stock: number;
|
||||
formatted_stock: string;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
};
|
||||
|
||||
@ -90,6 +90,7 @@ export function RawMaterialVariantSubRow({
|
||||
srcs={[variant.photo_conversion_url ?? variant.photo_url]}
|
||||
modalSrc={variant.photo_url}
|
||||
title={variant.variant}
|
||||
description={`Stok: ${variant.formatted_stock}`}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-muted text-xs text-muted-foreground">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user