mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
Merge branch 'fix/marketing-report' into 'development'
[FIX/FE] Daily Marketing Report See merge request mbugroup/lti-web-client!162
This commit is contained in:
@@ -31,7 +31,10 @@ import { MarketingReportApi } from '@/services/api/report/marketing-report';
|
|||||||
import { MARKETING_TYPE_OPTIONS } from '@/config/constant';
|
import { MARKETING_TYPE_OPTIONS } from '@/config/constant';
|
||||||
import { httpClient } from '@/services/http/client';
|
import { httpClient } from '@/services/http/client';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
import { DailyMarketingReport } from '@/types/api/report/marketing';
|
import {
|
||||||
|
DailyMarketingReport,
|
||||||
|
DailyMarketingReportResponse,
|
||||||
|
} from '@/types/api/report/marketing';
|
||||||
import { isResponseError } from '@/lib/api-helper';
|
import { isResponseError } from '@/lib/api-helper';
|
||||||
|
|
||||||
const DailyMarketingReportContent = () => {
|
const DailyMarketingReportContent = () => {
|
||||||
@@ -191,9 +194,10 @@ const DailyMarketingReportContent = () => {
|
|||||||
const queryString = `?${params.toString()}`;
|
const queryString = `?${params.toString()}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dailyMarketingsReport = await httpClient<
|
const dailyMarketingsReport =
|
||||||
BaseApiResponse<DailyMarketingReport>
|
await httpClient<DailyMarketingReportResponse>(
|
||||||
>(`${MarketingReportApi.basePath}${queryString}`);
|
`${MarketingReportApi.basePath}${queryString}`
|
||||||
|
);
|
||||||
|
|
||||||
if (isResponseError(dailyMarketingsReport)) {
|
if (isResponseError(dailyMarketingsReport)) {
|
||||||
toast.error('Gagal melakukan export penjualan harian! Coba lagi.');
|
toast.error('Gagal melakukan export penjualan harian! Coba lagi.');
|
||||||
@@ -202,7 +206,10 @@ const DailyMarketingReportContent = () => {
|
|||||||
|
|
||||||
const openPdf = async () => {
|
const openPdf = async () => {
|
||||||
const dailyMarketingReportPdfBlob = await pdf(
|
const dailyMarketingReportPdfBlob = await pdf(
|
||||||
<DailyMarketingReportPDF data={dailyMarketingsReport.data} />
|
<DailyMarketingReportPDF
|
||||||
|
data={dailyMarketingsReport.data}
|
||||||
|
total={dailyMarketingsReport.total}
|
||||||
|
/>
|
||||||
).toBlob();
|
).toBlob();
|
||||||
|
|
||||||
const dailyMarketingReportPdfUrl = URL.createObjectURL(
|
const dailyMarketingReportPdfUrl = URL.createObjectURL(
|
||||||
@@ -213,7 +220,10 @@ const DailyMarketingReportContent = () => {
|
|||||||
|
|
||||||
const downloadPdf = async () => {
|
const downloadPdf = async () => {
|
||||||
const blob = await pdf(
|
const blob = await pdf(
|
||||||
<DailyMarketingReportPDF data={dailyMarketingsReport.data} />
|
<DailyMarketingReportPDF
|
||||||
|
data={dailyMarketingsReport.data}
|
||||||
|
total={dailyMarketingsReport.total}
|
||||||
|
/>
|
||||||
).toBlob();
|
).toBlob();
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ import {
|
|||||||
View,
|
View,
|
||||||
} from '@react-pdf/renderer';
|
} from '@react-pdf/renderer';
|
||||||
|
|
||||||
import { DailyMarketingReport } from '@/types/api/report/marketing';
|
import {
|
||||||
|
DailyMarketingReport,
|
||||||
|
SalesSummary,
|
||||||
|
} from '@/types/api/report/marketing';
|
||||||
import { formatCurrency, formatDate, formatNumber } from '@/lib/helper';
|
import { formatCurrency, formatDate, formatNumber } from '@/lib/helper';
|
||||||
|
|
||||||
interface DailyMarketingReportPDFProps {
|
interface DailyMarketingReportPDFProps {
|
||||||
data?: DailyMarketingReport;
|
data?: DailyMarketingReport;
|
||||||
|
total?: SalesSummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DailyMarketingReportPDFStyle = StyleSheet.create({
|
const DailyMarketingReportPDFStyle = StyleSheet.create({
|
||||||
@@ -267,9 +271,12 @@ const DailyMarketingReportPDFStyle = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const DailyMarketingReportPDF = ({ data }: DailyMarketingReportPDFProps) => {
|
const DailyMarketingReportPDF = ({
|
||||||
const rows = data?.rows || [];
|
data,
|
||||||
const summary = data?.summary;
|
total,
|
||||||
|
}: DailyMarketingReportPDFProps) => {
|
||||||
|
const rows = data || [];
|
||||||
|
const summary = total;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Document>
|
<Document>
|
||||||
@@ -409,7 +416,7 @@ const DailyMarketingReportPDF = ({ data }: DailyMarketingReportPDFProps) => {
|
|||||||
</View>
|
</View>
|
||||||
<View style={DailyMarketingReportPDFStyle.colDoDate}>
|
<View style={DailyMarketingReportPDFStyle.colDoDate}>
|
||||||
<Text style={DailyMarketingReportPDFStyle.cellText}>
|
<Text style={DailyMarketingReportPDFStyle.cellText}>
|
||||||
{formatDate(row.do_date, 'DD/MM/YYYY')}
|
{formatDate(row.realization_date, 'DD/MM/YYYY')}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={DailyMarketingReportPDFStyle.colAging}>
|
<View style={DailyMarketingReportPDFStyle.colAging}>
|
||||||
@@ -429,7 +436,7 @@ const DailyMarketingReportPDF = ({ data }: DailyMarketingReportPDFProps) => {
|
|||||||
</View>
|
</View>
|
||||||
<View style={DailyMarketingReportPDFStyle.colSales}>
|
<View style={DailyMarketingReportPDFStyle.colSales}>
|
||||||
<Text style={DailyMarketingReportPDFStyle.cellText}>
|
<Text style={DailyMarketingReportPDFStyle.cellText}>
|
||||||
{row.sales}
|
{row.sales.name}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={DailyMarketingReportPDFStyle.colProduct}>
|
<View style={DailyMarketingReportPDFStyle.colProduct}>
|
||||||
@@ -518,6 +525,19 @@ const DailyMarketingReportPDF = ({ data }: DailyMarketingReportPDFProps) => {
|
|||||||
{formatCurrency(summary?.total_sales_amount ?? 0)}
|
{formatCurrency(summary?.total_sales_amount ?? 0)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
DailyMarketingReportPDFStyle.summaryRow,
|
||||||
|
{ borderBottomWidth: 0 },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text style={DailyMarketingReportPDFStyle.summaryLabel}>
|
||||||
|
Total HPP Per KG:
|
||||||
|
</Text>
|
||||||
|
<Text style={DailyMarketingReportPDFStyle.summaryValue}>
|
||||||
|
{formatCurrency(summary?.total_hpp_price_per_kg ?? 0)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
DailyMarketingReportPDFStyle.summaryRow,
|
DailyMarketingReportPDFStyle.summaryRow,
|
||||||
|
|||||||
@@ -60,9 +60,10 @@ const DailyMarketingsTable = ({
|
|||||||
footer: 'Total',
|
footer: 'Total',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'do_date',
|
accessorKey: 'realization_date',
|
||||||
header: 'Tanggal DO',
|
header: 'Tanggal Realisasi',
|
||||||
cell: (props) => formatDate(props.row.original.do_date, 'DD-MMM-YYYY'),
|
cell: (props) =>
|
||||||
|
formatDate(props.row.original.realization_date, 'DD-MMM-YYYY'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'aging_days',
|
accessorKey: 'aging_days',
|
||||||
@@ -106,10 +107,10 @@ const DailyMarketingsTable = ({
|
|||||||
cell: (props) => formatNumber(props.row.original.qty),
|
cell: (props) => formatNumber(props.row.original.qty),
|
||||||
footer: () => {
|
footer: () => {
|
||||||
const totalQty = isResponseSuccess(dailyMarketings)
|
const totalQty = isResponseSuccess(dailyMarketings)
|
||||||
? dailyMarketings.data.summary.total_qty
|
? dailyMarketings?.total?.total_qty
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
return formatNumber(totalQty);
|
return totalQty ? formatNumber(totalQty) : '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -123,10 +124,10 @@ const DailyMarketingsTable = ({
|
|||||||
cell: (props) => formatNumber(props.row.original.total_weight_kg),
|
cell: (props) => formatNumber(props.row.original.total_weight_kg),
|
||||||
footer: () => {
|
footer: () => {
|
||||||
const totalWeightKg = isResponseSuccess(dailyMarketings)
|
const totalWeightKg = isResponseSuccess(dailyMarketings)
|
||||||
? dailyMarketings.data.summary.total_weight_kg
|
? dailyMarketings?.total?.total_weight_kg
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
return formatNumber(totalWeightKg);
|
return totalWeightKg ? formatNumber(totalWeightKg) : '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -138,6 +139,13 @@ const DailyMarketingsTable = ({
|
|||||||
accessorKey: 'hpp_price_per_kg',
|
accessorKey: 'hpp_price_per_kg',
|
||||||
header: 'HPP (Rp)',
|
header: 'HPP (Rp)',
|
||||||
cell: (props) => formatCurrency(props.row.original.hpp_price_per_kg),
|
cell: (props) => formatCurrency(props.row.original.hpp_price_per_kg),
|
||||||
|
footer: () => {
|
||||||
|
const totalHppPricePerKg = isResponseSuccess(dailyMarketings)
|
||||||
|
? dailyMarketings?.total?.total_hpp_price_per_kg
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return totalHppPricePerKg ? formatCurrency(totalHppPricePerKg) : '-';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'sales_amount',
|
accessorKey: 'sales_amount',
|
||||||
@@ -145,10 +153,10 @@ const DailyMarketingsTable = ({
|
|||||||
cell: (props) => formatCurrency(props.row.original.sales_amount),
|
cell: (props) => formatCurrency(props.row.original.sales_amount),
|
||||||
footer: () => {
|
footer: () => {
|
||||||
const totalSalesAmount = isResponseSuccess(dailyMarketings)
|
const totalSalesAmount = isResponseSuccess(dailyMarketings)
|
||||||
? dailyMarketings.data.summary.total_sales_amount
|
? dailyMarketings?.total?.total_sales_amount
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
return formatCurrency(totalSalesAmount);
|
return totalSalesAmount ? formatCurrency(totalSalesAmount) : '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -167,7 +175,7 @@ const DailyMarketingsTable = ({
|
|||||||
if (!open) {
|
if (!open) {
|
||||||
setOpen(
|
setOpen(
|
||||||
isResponseSuccess(dailyMarketings)
|
isResponseSuccess(dailyMarketings)
|
||||||
? dailyMarketings.data.rows.length > 0
|
? dailyMarketings.data.length > 0
|
||||||
: false
|
: false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -215,9 +223,7 @@ const DailyMarketingsTable = ({
|
|||||||
|
|
||||||
<Table<DailyMarketingRow>
|
<Table<DailyMarketingRow>
|
||||||
data={
|
data={
|
||||||
isResponseSuccess(dailyMarketings)
|
isResponseSuccess(dailyMarketings) ? dailyMarketings?.data : []
|
||||||
? dailyMarketings?.data.rows
|
|
||||||
: []
|
|
||||||
}
|
}
|
||||||
columns={dailyMarketingColumns}
|
columns={dailyMarketingColumns}
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
@@ -242,7 +248,7 @@ const DailyMarketingsTable = ({
|
|||||||
containerClassName: cn({
|
containerClassName: cn({
|
||||||
'w-full mb-20':
|
'w-full mb-20':
|
||||||
isResponseSuccess(dailyMarketings) &&
|
isResponseSuccess(dailyMarketings) &&
|
||||||
dailyMarketings?.data?.rows.length === 0,
|
dailyMarketings?.data?.length === 0,
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import * as XLSX from 'xlsx';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
import { BaseApiService } from '@/services/api/base';
|
import { BaseApiService } from '@/services/api/base';
|
||||||
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
import { httpClientFetcher } from '@/services/http/client';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
import { DailyMarketingReport } from '@/types/api/report/marketing';
|
import {
|
||||||
|
DailyMarketingReport,
|
||||||
|
DailyMarketingReportResponse,
|
||||||
|
} from '@/types/api/report/marketing';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { formatDate, sleep } from '@/lib/helper';
|
import { formatDate } from '@/lib/helper';
|
||||||
|
|
||||||
export class MarketingReportApiService extends BaseApiService<
|
export class MarketingReportApiService extends BaseApiService<
|
||||||
DailyMarketingReport,
|
DailyMarketingReport,
|
||||||
@@ -19,10 +22,8 @@ export class MarketingReportApiService extends BaseApiService<
|
|||||||
|
|
||||||
async getAllDailyMarketingFetcher(
|
async getAllDailyMarketingFetcher(
|
||||||
endpoint: string
|
endpoint: string
|
||||||
): Promise<BaseApiResponse<DailyMarketingReport>> {
|
): Promise<DailyMarketingReportResponse> {
|
||||||
return await httpClientFetcher<BaseApiResponse<DailyMarketingReport>>(
|
return await httpClientFetcher<DailyMarketingReportResponse>(endpoint);
|
||||||
endpoint
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async exportDailyMarketingToExcel(initialQueryString: string) {
|
async exportDailyMarketingToExcel(initialQueryString: string) {
|
||||||
@@ -42,16 +43,19 @@ export class MarketingReportApiService extends BaseApiService<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rows = dailyMarketingsReport.data.rows;
|
const rows = dailyMarketingsReport.data;
|
||||||
|
|
||||||
const formattedRows = [];
|
const formattedRows = [];
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
formattedRows.push({
|
formattedRows.push({
|
||||||
...rows[i],
|
...rows[i],
|
||||||
created_user: rows[i].created_user.name,
|
// created_user: rows[i].created_user.name,
|
||||||
created_at: formatDate(rows[i].created_at, 'YYYY-MM-DD'),
|
// created_at: formatDate(rows[i].created_at, 'YYYY-MM-DD'),
|
||||||
updated_at: formatDate(rows[i].updated_at, 'YYYY-MM-DD'),
|
// updated_at: formatDate(rows[i].updated_at, 'YYYY-MM-DD'),
|
||||||
|
so_date: formatDate(rows[i].so_date, 'YYYY-MM-DD'),
|
||||||
|
realization_date: formatDate(rows[i].realization_date, 'YYYY-MM-DD'),
|
||||||
|
sales: rows[i].sales.name,
|
||||||
warehouse: rows[i].warehouse.name,
|
warehouse: rows[i].warehouse.name,
|
||||||
customer: rows[i].customer.name,
|
customer: rows[i].customer.name,
|
||||||
product: rows[i].product.name,
|
product: rows[i].product.name,
|
||||||
|
|||||||
Vendored
+11
-9
@@ -1,4 +1,4 @@
|
|||||||
import { BaseMetadata } from '@/types/api/api-general';
|
import { BaseApiResponse, BaseMetadata } from '@/types/api/api-general';
|
||||||
import { BaseCustomer, Customer } from '@/types/api/master-data/customer';
|
import { BaseCustomer, Customer } from '@/types/api/master-data/customer';
|
||||||
import {
|
import {
|
||||||
BaseWarehouseArea,
|
BaseWarehouseArea,
|
||||||
@@ -9,16 +9,17 @@ import {
|
|||||||
import { Location } from '@/types/api/master-data/location';
|
import { Location } from '@/types/api/master-data/location';
|
||||||
import { Area } from '@/types/api/master-data/area';
|
import { Area } from '@/types/api/master-data/area';
|
||||||
import { BaseProduct } from '@/types/api/master-data/product';
|
import { BaseProduct } from '@/types/api/master-data/product';
|
||||||
|
import { BaseUser } from '@/types/api/user';
|
||||||
|
|
||||||
export type BaseDailyMarketingRow = {
|
export type BaseDailyMarketingRow = {
|
||||||
no: number;
|
id: number;
|
||||||
so_date: string; // e.g. "01-Dec-2025"
|
so_date: string;
|
||||||
do_date: string; // e.g. "08-Dec-2025"
|
realization_date: string;
|
||||||
aging_days: number;
|
aging_days: number;
|
||||||
|
|
||||||
warehouse: BaseWarehouseArea | BaseWarehouseLocation | BaseWarehouseKandang;
|
warehouse: BaseWarehouseArea | BaseWarehouseLocation | BaseWarehouseKandang;
|
||||||
customer: BaseCustomer;
|
customer: BaseCustomer;
|
||||||
sales: string;
|
sales: BaseUser;
|
||||||
product: BaseProduct;
|
product: BaseProduct;
|
||||||
|
|
||||||
do_number: string;
|
do_number: string;
|
||||||
@@ -43,12 +44,13 @@ export interface SalesSummary {
|
|||||||
total_weight_kg: number;
|
total_weight_kg: number;
|
||||||
total_sales_amount: number;
|
total_sales_amount: number;
|
||||||
total_hpp_amount: number;
|
total_hpp_amount: number;
|
||||||
|
total_hpp_price_per_kg: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DailyMarketingReport = {
|
export type DailyMarketingReport = DailyMarketingRow[];
|
||||||
rows: DailyMarketingRow[];
|
|
||||||
summary: SalesSummary;
|
export type DailyMarketingReportResponse =
|
||||||
};
|
BaseApiResponse<DailyMarketingReport> & { total: SalesSummary };
|
||||||
|
|
||||||
export type MarketingReportFilters = {
|
export type MarketingReportFilters = {
|
||||||
area_id?: number;
|
area_id?: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user