Merge branch 'development' into 'schema/bulk-approve-marketings-expenses'

# Conflicts:
#   src/services/api/expense.ts
This commit is contained in:
Adnan Zahir
2026-04-22 10:14:17 +07:00
38 changed files with 1805 additions and 280 deletions
@@ -192,6 +192,29 @@ export class DailyChecklistApiService extends BaseApiService<
}
}
async bulkApprove(ids: string[]) {
try {
const formData = new FormData();
formData.append('ids', ids.join(','));
formData.append('status', 'APPROVED');
formData.append('reject_reason', '');
const approvePath = `${this.basePath}/bulk-update`;
const approveRes = await httpClient<BaseApiResponse>(approvePath, {
method: 'PATCH',
body: formData,
});
return approveRes;
} catch (error) {
if (axios.isAxiosError<BaseApiResponse>(error)) {
return error.response?.data;
}
return undefined;
}
}
async reject(id: string, rejectReason: string) {
try {
const formData = new FormData();
@@ -215,6 +238,29 @@ export class DailyChecklistApiService extends BaseApiService<
}
}
async bulkReject(ids: string[], rejectReason: string) {
try {
const formData = new FormData();
formData.append('ids', ids.join(','));
formData.append('status', 'REJECTED');
formData.append('reject_reason', rejectReason);
const rejectPath = `${this.basePath}/bulk-update`;
const rejectRes = await httpClient<BaseApiResponse>(rejectPath, {
method: 'PATCH',
body: formData,
});
return rejectRes;
} catch (error) {
if (axios.isAxiosError<BaseApiResponse>(error)) {
return error.response?.data;
}
return undefined;
}
}
async uploadImage(
id: number,
status: string,
+23
View File
@@ -126,6 +126,29 @@ class MarketingExportService extends BaseApiService<
super(basePath);
}
async bulkApprovals(
ids: number[],
status: 'SALES_ORDER' | 'DELIVERY_ORDER',
date: string, // YYYY-MM-DD
notes: string
): Promise<BaseApiResponse<Marketing[] | Marketing> | undefined> {
try {
const path = `${this.basePath}/approvals/bulk`;
return await httpClient<BaseApiResponse<Marketing[] | Marketing>>(path, {
method: 'POST',
body: {
approvable_ids: ids,
status: status,
date: date,
notes: notes,
},
});
} catch (error) {
throw error;
}
}
/**
* Export to Excel
*/
+2
View File
@@ -95,6 +95,8 @@ export class RecordingService extends BaseApiService<
const params = new URLSearchParams(initialQueryString);
params.set('export', 'excel');
params.set('page', '1');
params.set('limit', '99999999999');
const queryString = `?${params.toString()}`;
@@ -1,7 +1,10 @@
import { BaseApiService } from '@/services/api/base';
import { httpClientFetcher } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
import { ReportExpense } from '@/types/api/report/report-expense';
import {
ReportDepreciation,
ReportExpense,
} from '@/types/api/report/report-expense';
export class ReportExpenseApiService extends BaseApiService<
ReportExpense,
@@ -20,3 +23,9 @@ export class ReportExpenseApiService extends BaseApiService<
}
export const ReportExpenseApi = new ReportExpenseApiService('/reports/expense');
export const DepreciationReportApi = new BaseApiService<
ReportDepreciation,
unknown,
unknown
>('/reports/expense/depreciation');
-1
View File
@@ -56,7 +56,6 @@ export class UniformityApiService extends BaseApiService<
): Promise<BaseApiResponse<UniformityDetail> | undefined> {
const formData = new FormData();
formData.append('date', payload.date);
formData.append('week', payload.week.toString());
formData.append(
'project_flock_kandang_id',
payload.project_flock_kandang_id.toString()
+55 -9
View File
@@ -1,4 +1,5 @@
import { useCallback, useMemo, useReducer } from 'react';
import { useCallback, useEffect, useMemo, useReducer } from 'react';
import { useTableFilterStore } from '@/stores/table/table-filter.store';
/** Core filter shape (page + pageSize) extended by your custom fields */
export type TableFilterState<TExtra extends Record<string, unknown>> = {
@@ -30,6 +31,9 @@ export type UseTableFilterOptions<TExtra extends Record<string, unknown>> = {
paramMap?: Partial<Record<keyof TableFilterState<TExtra>, string>>;
/** If true, `toSearchParams`/`toQueryString` will omit values equal to defaults */
omitDefaultsInUrl?: boolean;
persist?: boolean;
storeName?: string;
};
function clampToInt(n: number, min = 1) {
@@ -90,9 +94,37 @@ function shallowEqual<T extends Record<string, unknown>>(
export function useTableFilter<TExtra extends Record<string, unknown>>(
options?: UseTableFilterOptions<TExtra>
) {
const defaults = useMemo(
() => createInitialState<TExtra>(options),
[options]
if (options?.persist && !options?.storeName) {
throw new Error(
'storeName is required if persist is true in useTableFilter!'
);
}
const storeName = options?.storeName ?? '';
const persistedState = useTableFilterStore(
useCallback(
(storeState) =>
storeName
? (storeState.data[storeName] as Partial<TableFilterState<TExtra>>)
: undefined,
[storeName]
)
);
const setTableData = useTableFilterStore(
(storeState) => storeState.setTableData
);
const defaults = useMemo(() => {
return createInitialState<TExtra>(options);
}, [options]);
const initialState = useMemo(
() =>
({
...defaults,
...(persistedState as object),
}) as TableFilterState<TExtra>,
[defaults, persistedState]
);
const [state, dispatch] = useReducer(
@@ -106,15 +138,22 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
case 'SET_PAGE_SIZE': {
const pageSize = clampToInt(a.pageSize);
const page = a.resetPage ? 1 : s.page;
return { ...s, pageSize, page };
}
case 'SET_FILTERS': {
const page = a.resetPage ? 1 : s.page;
return { ...s, ...a.filters, page };
}
case 'UPDATE_FILTER': {
const page = a.resetPage ? 1 : s.page;
return { ...s, [a.key]: a.value, page } as TableFilterState<TExtra>;
return {
...s,
[a.key]: a.value,
page,
} as TableFilterState<TExtra>;
}
case 'REPLACE_ALL':
return {
@@ -128,12 +167,19 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
return s;
}
},
defaults
initialState
);
// Notify consumer on change (stable ref)
useEffect(() => {
if (!options?.persist || !storeName) {
return;
}
setTableData(storeName, state);
}, [options?.persist, setTableData, state, storeName]);
const onChange = options?.onChange;
useMemo(() => {
useEffect(() => {
if (onChange) onChange(state);
}, [state, onChange]);
@@ -154,7 +200,7 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
);
const updateFilter = useCallback(
<K extends keyof TExtra>(key: K, value: TExtra[K], resetPage = true) => {
<K extends keyof TExtra>(key: K, value: TExtra[K], resetPage = false) => {
dispatch({ type: 'UPDATE_FILTER', key, value, resetPage });
},
[dispatch]