feat: add receipt conversion URLs for media handling across various services and components
This commit is contained in:
parent
ef987360f4
commit
5335e5ad58
@ -193,6 +193,7 @@ private function formatTransaction(CashTransaction $transaction): array
|
||||
return $transaction->toArray() + [
|
||||
'receipt_key' => null,
|
||||
'receipt_url' => null,
|
||||
'receipt_conversion_url' => null,
|
||||
];
|
||||
}
|
||||
|
||||
@ -201,6 +202,7 @@ private function formatTransaction(CashTransaction $transaction): array
|
||||
return $transaction->toArray() + [
|
||||
'receipt_key' => $s3Key,
|
||||
'receipt_url' => $this->s3Service->getTemporaryUrl($s3Key),
|
||||
'receipt_conversion_url' => $this->s3Service->getTemporaryUrl($media->getPath('thumb')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,6 +155,7 @@ private function formatExpense(Expense $expense): array
|
||||
return $expense->toArray() + [
|
||||
'receipt_key' => null,
|
||||
'receipt_url' => null,
|
||||
'receipt_conversion_url' => null,
|
||||
];
|
||||
}
|
||||
|
||||
@ -165,9 +166,15 @@ private function formatExpense(Expense $expense): array
|
||||
return $this->s3Service->getTemporaryUrl($s3Key);
|
||||
});
|
||||
|
||||
$conversionCacheKey = "expense_receipt_conversion_{$media->id}";
|
||||
$receiptConversionUrl = Cache::remember($conversionCacheKey, now()->addMinutes(55), function () use ($media) {
|
||||
return $this->s3Service->getTemporaryUrl($media->getPath('thumb'));
|
||||
});
|
||||
|
||||
return $expense->toArray() + [
|
||||
'receipt_key' => $s3Key,
|
||||
'receipt_url' => $receiptUrl,
|
||||
'receipt_conversion_url' => $receiptConversionUrl,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +46,9 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
$cutting->photo_url = $cuttingMedia
|
||||
? $this->s3Service->getTemporaryUrl($cuttingMedia->getPath())
|
||||
: null;
|
||||
$cutting->photo_conversion_url = $cuttingMedia
|
||||
? $this->s3Service->getTemporaryUrl($cuttingMedia->getPath('thumb'))
|
||||
: null;
|
||||
|
||||
$cutting->cuttingMaterials->each(function (CuttingMaterial $material) {
|
||||
$media = $material->rawMaterialPrice?->getFirstMedia('images');
|
||||
@ -53,6 +56,9 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
$material->rawMaterialPrice->photo_url = $media
|
||||
? $this->s3Service->getTemporaryUrl($media->getPath())
|
||||
: null;
|
||||
$material->rawMaterialPrice->photo_conversion_url = $media
|
||||
? $this->s3Service->getTemporaryUrl($media->getPath('thumb'))
|
||||
: null;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -49,15 +49,21 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
$purchase->photo_url = $purchaseMedia
|
||||
? $this->s3Service->getTemporaryUrl($purchaseMedia->getPath())
|
||||
: null;
|
||||
$purchase->photo_conversion_url = $purchaseMedia
|
||||
? $this->s3Service->getTemporaryUrl($purchaseMedia->getPath('thumb'))
|
||||
: null;
|
||||
|
||||
$purchase->purchaseItems->each(function (PurchaseItem $item) {
|
||||
if (! $item->rawMaterialPrice) {
|
||||
return;
|
||||
}
|
||||
|
||||
$media = $item->rawMaterialPrice->getMedia('images');
|
||||
$item->rawMaterialPrice->photo_url = $media->first()
|
||||
? $this->s3Service->getTemporaryUrl($media->first()->getPath())
|
||||
$media = $item->rawMaterialPrice->getFirstMedia('images');
|
||||
$item->rawMaterialPrice->photo_url = $media
|
||||
? $this->s3Service->getTemporaryUrl($media->getPath())
|
||||
: null;
|
||||
$item->rawMaterialPrice->photo_conversion_url = $media
|
||||
? $this->s3Service->getTemporaryUrl($media->getPath('thumb'))
|
||||
: null;
|
||||
});
|
||||
});
|
||||
|
||||
@ -53,6 +53,9 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
$item->productVariant->photo_url = $media
|
||||
? $this->s3Service->getTemporaryUrl($media->getPath())
|
||||
: null;
|
||||
$item->productVariant->photo_conversion_url = $media
|
||||
? $this->s3Service->getTemporaryUrl($media->getPath('thumb'))
|
||||
: null;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -79,6 +79,9 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
$order->photo_url = $orderMedia
|
||||
? $this->s3Service->getTemporaryUrl($orderMedia->getPath())
|
||||
: null;
|
||||
$order->photo_conversion_url = $orderMedia
|
||||
? $this->s3Service->getTemporaryUrl($orderMedia->getPath('thumb'))
|
||||
: null;
|
||||
|
||||
$order->orderItems->each(function (OrderItem $item) {
|
||||
if (! $item->productVariant) {
|
||||
@ -89,6 +92,9 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
$item->productVariant->photo_url = $media
|
||||
? $this->s3Service->getTemporaryUrl($media->getPath())
|
||||
: null;
|
||||
$item->productVariant->photo_conversion_url = $media
|
||||
? $this->s3Service->getTemporaryUrl($media->getPath('thumb'))
|
||||
: null;
|
||||
});
|
||||
|
||||
$order->profit = $order->total_amount - $order->cogs;
|
||||
|
||||
@ -60,6 +60,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
$product->productVariants->each(function ($variant) {
|
||||
$media = $variant->getMedia('images');
|
||||
$variant->photo_urls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray();
|
||||
$variant->photo_conversion_urls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath('thumb')))->toArray();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -48,10 +48,14 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
|
||||
$paginator->getCollection()->each(function ($rawMaterial) {
|
||||
$rawMaterial->rawMaterialPrices->each(function ($price) {
|
||||
$media = $price->getMedia('images');
|
||||
$price->photo_url = $media->first()
|
||||
? $this->s3Service->getTemporaryUrl($media->first()->getPath())
|
||||
: null;
|
||||
$media = $price->getFirstMedia('images');
|
||||
if ($media) {
|
||||
$price->photo_url = $this->s3Service->getTemporaryUrl($media->getPath());
|
||||
$price->photo_conversion_url = $this->s3Service->getTemporaryUrl($media->getPath('thumb'));
|
||||
} else {
|
||||
$price->photo_url = null;
|
||||
$price->photo_conversion_url = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@ import { ImagePreviewModal } from '@/components/dialogs';
|
||||
|
||||
type ImagePreviewButtonProps = {
|
||||
srcs: string[];
|
||||
modalSrc?: string;
|
||||
modalSrcs?: string[];
|
||||
title?: string;
|
||||
alt?: string;
|
||||
className?: string;
|
||||
@ -10,12 +12,16 @@ type ImagePreviewButtonProps = {
|
||||
|
||||
export function ImagePreviewButton({
|
||||
srcs,
|
||||
modalSrc,
|
||||
modalSrcs,
|
||||
title,
|
||||
alt,
|
||||
className = 'h-10 w-10',
|
||||
}: ImagePreviewButtonProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const modalSources = modalSrcs ?? (modalSrc ? [modalSrc] : null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
@ -37,8 +43,8 @@ export function ImagePreviewButton({
|
||||
<ImagePreviewModal
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
src={srcs[0]}
|
||||
sources={srcs}
|
||||
src={modalSources?.[0] ?? srcs[0]}
|
||||
sources={modalSources ?? srcs}
|
||||
title={title}
|
||||
/>
|
||||
</>
|
||||
|
||||
@ -13,6 +13,7 @@ export type CashTransaction = {
|
||||
description: string;
|
||||
receipt_key: string | null;
|
||||
receipt_url: string | null;
|
||||
receipt_conversion_url: string | null;
|
||||
created_at: string;
|
||||
formatted_created_at: string;
|
||||
created_by: {
|
||||
@ -113,15 +114,17 @@ export function createTransactionColumns(
|
||||
id: 'receipt',
|
||||
header: () => <span>Bukti</span>,
|
||||
cell: ({ row }) => {
|
||||
const receiptConversionUrl = row.original.receipt_conversion_url;
|
||||
const receiptUrl = row.original.receipt_url;
|
||||
|
||||
if (!receiptUrl) {
|
||||
if (!receiptConversionUrl) {
|
||||
return <span className="text-muted-foreground">-</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ImagePreviewButton
|
||||
srcs={[receiptUrl]}
|
||||
srcs={[receiptConversionUrl]}
|
||||
modalSrc={receiptUrl ?? undefined}
|
||||
title={row.original.description}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -10,6 +10,7 @@ export type Expense = {
|
||||
description: string;
|
||||
receipt_key: string | null;
|
||||
receipt_url: string | null;
|
||||
receipt_conversion_url: string | null;
|
||||
created_at: string;
|
||||
formatted_created_at: string;
|
||||
created_by: {
|
||||
@ -51,15 +52,17 @@ export function createExpenseColumns(
|
||||
id: 'receipt',
|
||||
header: () => <span>Bukti</span>,
|
||||
cell: ({ row }) => {
|
||||
const receiptConversionUrl = row.original.receipt_conversion_url;
|
||||
const receiptUrl = row.original.receipt_url;
|
||||
|
||||
if (!receiptUrl) {
|
||||
if (!receiptConversionUrl) {
|
||||
return <span className="text-muted-foreground">-</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ImagePreviewButton
|
||||
srcs={[receiptUrl]}
|
||||
srcs={[receiptConversionUrl]}
|
||||
modalSrc={receiptUrl ?? undefined}
|
||||
title={row.original.description}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -28,6 +28,7 @@ export type Cutting = {
|
||||
total_material_cost: number | null;
|
||||
cost_per_unit: number | null;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
created_at: string;
|
||||
created_by: {
|
||||
id: number;
|
||||
@ -48,6 +49,7 @@ export type Cutting = {
|
||||
price: number;
|
||||
stock: number;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
raw_material: {
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
@ -131,7 +131,8 @@ export function CuttingCardRow({
|
||||
{cutting.photo_url && (
|
||||
<div className="mt-2">
|
||||
<ImagePreviewButton
|
||||
srcs={[cutting.photo_url]}
|
||||
srcs={[cutting.photo_conversion_url ?? cutting.photo_url]}
|
||||
modalSrc={cutting.photo_url}
|
||||
title="Foto Cutting"
|
||||
className="h-16 w-16"
|
||||
/>
|
||||
|
||||
@ -127,9 +127,17 @@ acc[name] = [];
|
||||
<ImagePreviewButton
|
||||
srcs={[
|
||||
item
|
||||
.raw_material_price
|
||||
.photo_conversion_url ??
|
||||
item
|
||||
.raw_material_price
|
||||
.photo_url,
|
||||
]}
|
||||
modalSrc={
|
||||
item
|
||||
.raw_material_price
|
||||
.photo_url
|
||||
}
|
||||
title={
|
||||
item
|
||||
.raw_material_price
|
||||
@ -222,9 +230,17 @@ acc[name] = [];
|
||||
<ImagePreviewButton
|
||||
srcs={[
|
||||
item
|
||||
.raw_material_price
|
||||
.photo_conversion_url ??
|
||||
item
|
||||
.raw_material_price
|
||||
.photo_url,
|
||||
]}
|
||||
modalSrc={
|
||||
item
|
||||
.raw_material_price
|
||||
.photo_url
|
||||
}
|
||||
title={
|
||||
item
|
||||
.raw_material_price
|
||||
|
||||
@ -17,6 +17,7 @@ export type Purchase = {
|
||||
total: number;
|
||||
notes: string | null;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
created_at: string;
|
||||
supplier: {
|
||||
id: number;
|
||||
@ -40,6 +41,7 @@ export type Purchase = {
|
||||
price: number;
|
||||
stock: number;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
raw_material: {
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
@ -118,7 +118,8 @@ export function PurchaseCardRow({
|
||||
{purchase.photo_url && (
|
||||
<div className="mt-2">
|
||||
<ImagePreviewButton
|
||||
srcs={[purchase.photo_url]}
|
||||
srcs={[purchase.photo_conversion_url ?? purchase.photo_url]}
|
||||
modalSrc={purchase.photo_url}
|
||||
title="Foto Belanja"
|
||||
className="h-16 w-16"
|
||||
/>
|
||||
|
||||
@ -50,9 +50,9 @@ export function PurchaseItemSubRow({ purchase }: { purchase: Purchase }) {
|
||||
{item.raw_material_price?.photo_url ? (
|
||||
<ImagePreviewButton
|
||||
srcs={[
|
||||
item.raw_material_price
|
||||
.photo_url,
|
||||
item.raw_material_price.photo_conversion_url ?? item.raw_material_price.photo_url,
|
||||
]}
|
||||
modalSrc={item.raw_material_price.photo_url}
|
||||
title={
|
||||
item.raw_material_price.variant
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ export type RestockItem = {
|
||||
id: number;
|
||||
name: string;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
product: {
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
@ -101,9 +101,17 @@ acc[name] = [];
|
||||
<ImagePreviewButton
|
||||
srcs={[
|
||||
item
|
||||
.product_variant
|
||||
.photo_conversion_url ??
|
||||
item
|
||||
.product_variant
|
||||
.photo_url,
|
||||
]}
|
||||
modalSrc={
|
||||
item
|
||||
.product_variant
|
||||
.photo_url
|
||||
}
|
||||
title={
|
||||
item
|
||||
.product_variant
|
||||
|
||||
@ -19,6 +19,7 @@ export type TransactionItem = {
|
||||
id: number;
|
||||
name: string;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
product: {
|
||||
id: number;
|
||||
name: string;
|
||||
@ -50,6 +51,7 @@ export type Transaction = {
|
||||
total_amount: number;
|
||||
notes: string | null;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
created_at: string;
|
||||
created_by: {
|
||||
id: number;
|
||||
|
||||
@ -201,7 +201,8 @@ export function TransactionCardRow({
|
||||
{transaction.photo_url && (
|
||||
<div className="mt-2">
|
||||
<ImagePreviewButton
|
||||
srcs={[transaction.photo_url]}
|
||||
srcs={[transaction.photo_conversion_url ?? transaction.photo_url]}
|
||||
modalSrc={transaction.photo_url}
|
||||
title="Foto Pesanan"
|
||||
className="h-16 w-16"
|
||||
/>
|
||||
|
||||
@ -52,8 +52,9 @@ export function TransactionItemSubRow({ transaction }: { transaction: Transactio
|
||||
{item.product_variant?.photo_url ? (
|
||||
<ImagePreviewButton
|
||||
srcs={[
|
||||
item.product_variant.photo_url,
|
||||
item.product_variant.photo_conversion_url ?? item.product_variant.photo_url,
|
||||
]}
|
||||
modalSrc={item.product_variant.photo_url}
|
||||
title={item.product_variant.name}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -15,6 +15,7 @@ export type ProductVariant = {
|
||||
reject_stock: number;
|
||||
retail_stock: number;
|
||||
photo_urls: string[];
|
||||
photo_conversion_urls: string[];
|
||||
product_prices: {
|
||||
id: number;
|
||||
type: string;
|
||||
|
||||
@ -90,7 +90,8 @@ export function VariantSubRow({
|
||||
<TableCell>
|
||||
{variant.photo_urls?.length > 0 ? (
|
||||
<ImagePreviewButton
|
||||
srcs={variant.photo_urls}
|
||||
srcs={variant.photo_conversion_urls?.length > 0 ? variant.photo_conversion_urls : variant.photo_urls}
|
||||
modalSrcs={variant.photo_urls}
|
||||
title={variant.name}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -4,6 +4,7 @@ export type RawMaterialVariant = {
|
||||
price: number;
|
||||
stock: number;
|
||||
photo_url: string | null;
|
||||
photo_conversion_url: string | null;
|
||||
};
|
||||
|
||||
export type RawMaterial = {
|
||||
|
||||
@ -87,7 +87,8 @@ export function RawMaterialVariantSubRow({
|
||||
<TableCell>
|
||||
{variant.photo_url ? (
|
||||
<ImagePreviewButton
|
||||
srcs={[variant.photo_url]}
|
||||
srcs={[variant.photo_conversion_url ?? variant.photo_url]}
|
||||
modalSrc={variant.photo_url}
|
||||
title={variant.variant}
|
||||
/>
|
||||
) : (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user