Merge branch 'development' of gitlab.com:mbugroup/lti-web-client into feat/FE/US-334/expedition-hpp-report

This commit is contained in:
rstubryan
2025-12-23 10:43:56 +07:00
53 changed files with 6330 additions and 689 deletions
+45 -83
View File
@@ -3,15 +3,20 @@ import axios from 'axios';
import { BaseApiService } from '@/services/api/base';
import {
Closing,
ClosingFinance,
ClosingGeneralInformation,
ClosingIncomingSapronak,
ClosingOutgoingSapronak,
ClosingOverhead,
ClosingSapronakCalculation,
ClosingProductionData,
ClosingHppExpedition,
} from '@/types/api/closing';
import { httpClient, httpClientFetcher } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
import { httpClient, httpClientFetcher } from '@/services/http/client';
import { ClosingSales } from '@/types/api/closing';
// TODO: delete these dummy data later
import {
dummyGetAllFetcher,
dummyGetSingle,
@@ -20,47 +25,15 @@ import {
dummyGetGeneralInfo,
dummyGetPerhitunganSapronak,
dummyGetOverhead,
dummyClosingProductionData,
} from '@/dummy/closing.dummy';
import { ClosingSales } from '@/types/api/closing';
import { sleep } from '@/lib/helper';
export class ClosingApiService extends BaseApiService<Closing, null, null> {
constructor(basePath: string) {
super(basePath);
}
async getAllFetcher(endpoint: string): Promise<BaseApiResponse<Closing[]>> {
// TODO: Remove this block when backend is ready
// return await dummyGetAllFetcher();
// Uncomment this when backend is ready
return await httpClientFetcher<BaseApiResponse<Closing[]>>(endpoint);
}
async getSingle(id: number): Promise<BaseApiResponse<Closing> | undefined> {
// TODO: Remove this block when backend is ready
// try {
// return await dummyGetSingle(id);
// } catch (error) {
// if (axios.isAxiosError<BaseApiResponse<Closing>>(error)) {
// return error.response?.data;
// }
// return undefined;
// }
// Uncomment this when backend is ready
try {
const getSinglePath = `${this.basePath}/${id}`;
const getSingleRes =
await httpClient<BaseApiResponse<Closing>>(getSinglePath);
return getSingleRes;
} catch (error) {
if (axios.isAxiosError<BaseApiResponse<Closing>>(error)) {
return error.response?.data;
}
return undefined;
}
}
async getPenjualan(
id: number
): Promise<BaseApiResponse<ClosingSales> | undefined> {
@@ -81,10 +54,6 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
async getAllIncomingSapronakFetcher(
endpoint: string
): Promise<BaseApiResponse<ClosingIncomingSapronak[]>> {
// TODO: Remove this block when backend is ready
// return await dummyGetAllIncomingSapronakFetcher();
// Uncomment this when backend is ready
return await httpClientFetcher<BaseApiResponse<ClosingIncomingSapronak[]>>(
endpoint
);
@@ -93,31 +62,14 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
async getAllOutgoingSapronakFetcher(
endpoint: string
): Promise<BaseApiResponse<ClosingOutgoingSapronak[]>> {
// TODO: Remove this block when backend is ready
return await dummyGetAllOutgoingSapronakFetcher();
// Uncomment this when backend is ready
// return await httpClientFetcher<BaseApiResponse<ClosingOutgoingSapronak[]>>(
// endpoint
// );
return await httpClientFetcher<BaseApiResponse<ClosingOutgoingSapronak[]>>(
endpoint
);
}
async getGeneralInfo(
id: number
): Promise<BaseApiResponse<ClosingGeneralInformation> | undefined> {
// TODO: Remove this block when backend is ready
// try {
// return await dummyGetGeneralInfo(id);
// } catch (error) {
// if (
// axios.isAxiosError<BaseApiResponse<ClosingGeneralInformation>>(error)
// ) {
// return error.response?.data;
// }
// return undefined;
// }
// Uncomment this when backend is ready
try {
const getGeneralInfoPath = `${this.basePath}/${id}`;
const getGeneralInfoRes =
@@ -135,22 +87,27 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
}
}
async getProductionData(
id: number
): Promise<BaseApiResponse<ClosingProductionData> | undefined> {
try {
const getProductionDataPath = `${this.basePath}/${id}/production-data`;
const getProductionDataRes = await httpClient<
BaseApiResponse<ClosingProductionData>
>(getProductionDataPath);
return getProductionDataRes;
} catch (error) {
if (axios.isAxiosError<BaseApiResponse<ClosingProductionData>>(error)) {
return error.response?.data;
}
return undefined;
}
}
async getPerhitunganSapronak(
id: number
): Promise<BaseApiResponse<ClosingSapronakCalculation> | undefined> {
// TODO: Remove this block when backend is ready
// try {
// return await dummyGetPerhitunganSapronak(id);
// } catch (error) {
// if (
// axios.isAxiosError<BaseApiResponse<ClosingSapronakCalculation>>(error)
// ) {
// return error.response?.data;
// }
// return undefined;
// }
// Uncomment this when backend is ready
try {
const path = `${this.basePath}/${id}/perhitungan_sapronak`;
return await httpClient<BaseApiResponse<ClosingSapronakCalculation>>(
@@ -172,17 +129,6 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
async getOverhead(
id: number
): Promise<BaseApiResponse<ClosingOverhead> | undefined> {
// TODO: Remove this block when backend is ready
// try {
// return await dummyGetOverhead(id);
// } catch (error) {
// if (axios.isAxiosError<BaseApiResponse<ClosingOverhead>>(error)) {
// return error.response?.data;
// }
// return undefined;
// }
// Uncomment this when backend is ready
try {
const path = `${this.basePath}/${id}/overhead`;
return await httpClient<BaseApiResponse<ClosingOverhead>>(path, {
@@ -196,6 +142,22 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
}
}
async getFinance(
id: number
): Promise<BaseApiResponse<ClosingFinance> | undefined> {
try {
const path = `${this.basePath}/${id}/keuangan`;
return await httpClient<BaseApiResponse<ClosingFinance>>(path, {
method: 'GET',
});
} catch (error) {
if (axios.isAxiosError<BaseApiResponse<ClosingFinance>>(error)) {
return error.response?.data;
}
return undefined;
}
}
async getHppEkspedisi(
id: number
): Promise<BaseApiResponse<ClosingHppExpedition> | undefined> {
-37
View File
@@ -1,5 +1,3 @@
import { dummyMarketings } from '@/dummy/marketing.dummy';
import { sleep } from '@/lib/helper';
import { BaseApiService } from '@/services/api/base';
import { httpClient } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
@@ -31,41 +29,6 @@ export class SalesOrderService extends BaseApiService<
super(basePath);
}
// /**
// * Override: Mengambil semua data Marketing dari dummyMarketings
// */
// async getAllFetcher(endpoint: string): Promise<BaseApiResponse<Marketing[]>> {
// // Simulasi delay jaringan
// await sleep(500);
// // Filter data marketing yang valid (jika menggunakan BaseMarketing[])
// const data = dummyMarketings as Marketing[];
// return createDummyResponse<Marketing[]>(data);
// }
// /**
// * Override: Mengambil satu data Marketing berdasarkan ID dari dummyMarketings
// */
// async getSingle(id: number): Promise<BaseApiResponse<Marketing> | undefined> {
// // Simulasi delay jaringan
// await sleep(300);
// const foundData = dummyMarketings.find((m) => m.id == id);
// if (foundData) {
// // Data ditemukan, kembalikan respons sukses
// return createDummyResponse<Marketing>(foundData as Marketing);
// } else {
// // Data tidak ditemukan, simulasi respons error
// return {
// code: 404,
// status: 'error',
// message: 'Marketing data not found (MOCK)',
// };
// }
// }
/**
* Approve single marketing data
*/
+23
View File
@@ -0,0 +1,23 @@
import { BaseApiService } from '@/services/api/base';
import { httpClient, httpClientFetcher } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
import { ReportExpense } from '@/types/api/report/report-expense';
import axios from 'axios';
export class ReportExpenseApiService extends BaseApiService<
ReportExpense,
unknown,
unknown
> {
constructor(basePath: string) {
super(basePath);
}
async getAllFetcher(
endpoint: string
): Promise<BaseApiResponse<ReportExpense[]>> {
return await httpClientFetcher<BaseApiResponse<ReportExpense[]>>(endpoint);
}
}
export const ReportExpenseApi = new ReportExpenseApiService('/reports/expense');
@@ -0,0 +1,75 @@
import * as XLSX from 'xlsx';
import toast from 'react-hot-toast';
import { BaseApiService } from '@/services/api/base';
import { httpClient, httpClientFetcher } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
import { DailyMarketingReport } from '@/types/api/report/marketing';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { formatDate, sleep } from '@/lib/helper';
export class MarketingReportApiService extends BaseApiService<
DailyMarketingReport,
unknown,
unknown
> {
constructor(basePath: string = '/reports/marketings/daily-marketing') {
super(basePath);
}
async getAllDailyMarketingFetcher(
endpoint: string
): Promise<BaseApiResponse<DailyMarketingReport>> {
return await httpClientFetcher<BaseApiResponse<DailyMarketingReport>>(
endpoint
);
}
async exportDailyMarketingToExcel(initialQueryString: string) {
const params = new URLSearchParams(initialQueryString);
params.set('limit', '9999999');
const queryString = `?${params.toString()}`;
try {
const dailyMarketingsReport = await httpClientFetcher<
BaseApiResponse<DailyMarketingReport>
>(`${this.basePath}${queryString}`);
if (isResponseError(dailyMarketingsReport)) {
toast.error('Gagal melakukan export penjualan harian! Coba lagi.');
return;
}
const rows = dailyMarketingsReport.data.rows;
const formattedRows = [];
for (let i = 0; i < rows.length; i++) {
formattedRows.push({
...rows[i],
created_user: rows[i].created_user.name,
created_at: formatDate(rows[i].created_at, 'YYYY-MM-DD'),
updated_at: formatDate(rows[i].updated_at, 'YYYY-MM-DD'),
warehouse: rows[i].warehouse.name,
customer: rows[i].customer.name,
product: rows[i].product.name,
});
}
const ws = XLSX.utils.json_to_sheet(formattedRows);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'laporan-penjualan-harian');
// triggers download in browser
XLSX.writeFile(wb, 'laporan-penjualan-harian.xlsx');
} catch (error) {
toast.error('Gagal melakukan export penjualan harian! Coba lagi.');
}
}
}
export const MarketingReportApi = new MarketingReportApiService(
'/reports/marketings/daily-marketing'
);
+53
View File
@@ -0,0 +1,53 @@
import { BaseApiService } from '@/services/api/base';
import { BaseApiResponse } from '@/types/api/api-general';
import { HppPerKandangReport } from '@/types/api/report/hpp-per-kandang';
export class MarketingSaleReportService extends BaseApiService<
HppPerKandangReport,
unknown,
unknown
> {
constructor(basePath: string) {
super(basePath);
}
async getHppPerKandangReport(
area_id?: string,
location_id?: string,
kandang_id?: string,
weight_min?: string,
weight_max?: string,
period?: string,
sort_by?: string,
show_unrecorded?: boolean,
page?: number,
limit?: number
): Promise<BaseApiResponse<HppPerKandangReport> | undefined> {
return await this.customRequest<BaseApiResponse<HppPerKandangReport>>(
`hpp-per-kandang`,
{
method: 'GET',
params: {
area_id: area_id,
location_id: location_id,
kandang_id: kandang_id,
weight_min: weight_min,
weight_max: weight_max,
period: period,
sort_by: sort_by,
show_unrecorded: show_unrecorded,
page: page,
limit: limit,
},
}
);
}
}
export const SaleReportApi = new MarketingSaleReportService(
'reports/marketings'
);
// export const SaleReportApi = new MarketingSaleReportService(
// 'http://localhost:4010/api/reports/marketings'
// );