mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 192a2be8b9 | |||
| 11353809f0 | |||
| 6463b7a572 | |||
| 7a5ee2aca1 | |||
| 5e907d7e53 | |||
| 71edc9c68a | |||
| 2a33fdbbbe | |||
| 178c659b58 | |||
| c1d6436583 | |||
| 8dc62453bd | |||
| 244d800874 |
@@ -173,8 +173,8 @@ const DashboardProduction = () => {
|
|||||||
loadMore: loadMoreKandang,
|
loadMore: loadMoreKandang,
|
||||||
} = useSelect<ProjectFlockKandang>(
|
} = useSelect<ProjectFlockKandang>(
|
||||||
ProjectFlockKandangApi.basePath,
|
ProjectFlockKandangApi.basePath,
|
||||||
'kandang_id',
|
'id',
|
||||||
'kandang.name',
|
'name_with_period',
|
||||||
'search',
|
'search',
|
||||||
{
|
{
|
||||||
location_id:
|
location_id:
|
||||||
@@ -362,7 +362,7 @@ const DashboardProduction = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Hidden container for all charts (used for PDF export in OVERVIEW mode) */}
|
{/* Hidden container for all charts (used for PDF export in OVERVIEW mode) */}
|
||||||
{dashboardProductionData && (
|
{exporting && dashboardProductionData && (
|
||||||
<>
|
<>
|
||||||
{/* Export Stats Charts */}
|
{/* Export Stats Charts */}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
@@ -9,6 +10,7 @@ import ExpenseRequestContent from '@/components/pages/expense/ExpenseRequestCont
|
|||||||
import ExpenseRealizationContent from '@/components/pages/expense/ExpenseRealizationContent';
|
import ExpenseRealizationContent from '@/components/pages/expense/ExpenseRealizationContent';
|
||||||
|
|
||||||
import { Expense } from '@/types/api/expense';
|
import { Expense } from '@/types/api/expense';
|
||||||
|
import { getExpenseListReturnTo } from '@/lib/expense-list-navigation';
|
||||||
|
|
||||||
interface ExpenseDetailProps {
|
interface ExpenseDetailProps {
|
||||||
initialValues?: Expense;
|
initialValues?: Expense;
|
||||||
@@ -16,6 +18,8 @@ interface ExpenseDetailProps {
|
|||||||
|
|
||||||
const ExpenseDetail: React.FC<ExpenseDetailProps> = ({ initialValues }) => {
|
const ExpenseDetail: React.FC<ExpenseDetailProps> = ({ initialValues }) => {
|
||||||
const [activeTab, setActiveTab] = useState<string>('request');
|
const [activeTab, setActiveTab] = useState<string>('request');
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const returnTo = getExpenseListReturnTo(searchParams);
|
||||||
|
|
||||||
const expenseDetailTabs = useMemo(() => {
|
const expenseDetailTabs = useMemo(() => {
|
||||||
const validTabs = [
|
const validTabs = [
|
||||||
@@ -46,7 +50,7 @@ const ExpenseDetail: React.FC<ExpenseDetailProps> = ({ initialValues }) => {
|
|||||||
<section className='w-full max-w-full pb-16'>
|
<section className='w-full max-w-full pb-16'>
|
||||||
<header className='flex flex-col gap-4'>
|
<header className='flex flex-col gap-4'>
|
||||||
<Button
|
<Button
|
||||||
href='/expense'
|
href={returnTo}
|
||||||
variant='link'
|
variant='link'
|
||||||
className='w-fit p-0 text-primary'
|
className='w-fit p-0 text-primary'
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
@@ -16,6 +19,7 @@ import {
|
|||||||
} from '@/components/pages/expense/form/ExpenseRequestForm.schema';
|
} from '@/components/pages/expense/form/ExpenseRequestForm.schema';
|
||||||
import { ExpenseApi } from '@/services/api/expense';
|
import { ExpenseApi } from '@/services/api/expense';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
|
import { buildExpenseActionHref } from '@/lib/expense-list-navigation';
|
||||||
import { ACCEPTED_FILE_TYPE, S3_PUBLIC_BASE_URL } from '@/config/constant';
|
import { ACCEPTED_FILE_TYPE, S3_PUBLIC_BASE_URL } from '@/config/constant';
|
||||||
|
|
||||||
interface ExpenseRealizationContentProps {
|
interface ExpenseRealizationContentProps {
|
||||||
@@ -25,6 +29,8 @@ interface ExpenseRealizationContentProps {
|
|||||||
const ExpenseRealizationContent = ({
|
const ExpenseRealizationContent = ({
|
||||||
initialValues,
|
initialValues,
|
||||||
}: ExpenseRealizationContentProps) => {
|
}: ExpenseRealizationContentProps) => {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const formik = useFormik<UploadRequestDocumentsFormValues>({
|
const formik = useFormik<UploadRequestDocumentsFormValues>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
documents: [],
|
documents: [],
|
||||||
@@ -74,7 +80,11 @@ const ExpenseRealizationContent = ({
|
|||||||
<Button
|
<Button
|
||||||
type='button'
|
type='button'
|
||||||
color='warning'
|
color='warning'
|
||||||
href={`/expense/realization/edit/?expenseId=${initialValues?.id}`}
|
href={buildExpenseActionHref(
|
||||||
|
'/expense/realization/edit/',
|
||||||
|
initialValues?.id as number,
|
||||||
|
searchParams
|
||||||
|
)}
|
||||||
className='px-4 grow sm:grow-0'
|
className='px-4 grow sm:grow-0'
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:pencil-outline' width={24} height={24} />
|
<Icon icon='mdi:pencil-outline' width={24} height={24} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
@@ -31,6 +31,10 @@ import { ExpenseApi } from '@/services/api/expense';
|
|||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { EXPENSE_REQUEST_APPROVAL_LINE } from '@/config/approval-line';
|
import { EXPENSE_REQUEST_APPROVAL_LINE } from '@/config/approval-line';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
|
import {
|
||||||
|
buildExpenseActionHref,
|
||||||
|
getExpenseListReturnTo,
|
||||||
|
} from '@/lib/expense-list-navigation';
|
||||||
|
|
||||||
interface ExpenseRequestContentProps {
|
interface ExpenseRequestContentProps {
|
||||||
initialValues?: Expense;
|
initialValues?: Expense;
|
||||||
@@ -40,6 +44,8 @@ const ExpenseRequestContent = ({
|
|||||||
initialValues,
|
initialValues,
|
||||||
}: ExpenseRequestContentProps) => {
|
}: ExpenseRequestContentProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const returnTo = getExpenseListReturnTo(searchParams);
|
||||||
|
|
||||||
const { approvals: approvalHistory, isLoading: isLoadingApprovalHistory } =
|
const { approvals: approvalHistory, isLoading: isLoadingApprovalHistory } =
|
||||||
useApprovalSteps({
|
useApprovalSteps({
|
||||||
@@ -148,7 +154,7 @@ const ExpenseRequestContent = ({
|
|||||||
|
|
||||||
if (isResponseSuccess(deleteResponse)) {
|
if (isResponseSuccess(deleteResponse)) {
|
||||||
toast.success('Berhasil menghapus data biaya operasional!');
|
toast.success('Berhasil menghapus data biaya operasional!');
|
||||||
router.push('/expense');
|
router.push(returnTo);
|
||||||
} else {
|
} else {
|
||||||
toast.error('Gagal menghapus data biaya operasional!');
|
toast.error('Gagal menghapus data biaya operasional!');
|
||||||
}
|
}
|
||||||
@@ -164,7 +170,7 @@ const ExpenseRequestContent = ({
|
|||||||
|
|
||||||
if (isResponseSuccess(completeRes)) {
|
if (isResponseSuccess(completeRes)) {
|
||||||
toast.success(completeRes.message);
|
toast.success(completeRes.message);
|
||||||
router.push('/expense');
|
router.push(returnTo);
|
||||||
} else {
|
} else {
|
||||||
toast.error(completeRes?.message as string);
|
toast.error(completeRes?.message as string);
|
||||||
}
|
}
|
||||||
@@ -204,7 +210,7 @@ const ExpenseRequestContent = ({
|
|||||||
|
|
||||||
toast.success(approveResponse?.message);
|
toast.success(approveResponse?.message);
|
||||||
setApprovalNotes('');
|
setApprovalNotes('');
|
||||||
router.push('/expense');
|
router.push(returnTo);
|
||||||
} else {
|
} else {
|
||||||
approveModal.closeModal();
|
approveModal.closeModal();
|
||||||
|
|
||||||
@@ -239,7 +245,7 @@ const ExpenseRequestContent = ({
|
|||||||
|
|
||||||
toast.success(rejectResponse.message);
|
toast.success(rejectResponse.message);
|
||||||
setApprovalNotes('');
|
setApprovalNotes('');
|
||||||
router.push('/expense');
|
router.push(returnTo);
|
||||||
} else {
|
} else {
|
||||||
rejectModal.closeModal();
|
rejectModal.closeModal();
|
||||||
|
|
||||||
@@ -365,7 +371,11 @@ const ExpenseRequestContent = ({
|
|||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
color='info'
|
color='info'
|
||||||
href={`/expense/realization/?expenseId=${initialValues?.id}`}
|
href={buildExpenseActionHref(
|
||||||
|
'/expense/realization/',
|
||||||
|
initialValues?.id as number,
|
||||||
|
searchParams
|
||||||
|
)}
|
||||||
className='w-full sm:w-fit'
|
className='w-full sm:w-fit'
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
@@ -384,7 +394,11 @@ const ExpenseRequestContent = ({
|
|||||||
<Button
|
<Button
|
||||||
type='button'
|
type='button'
|
||||||
color='warning'
|
color='warning'
|
||||||
href={`/expense/detail/edit/?expenseId=${initialValues?.id}`}
|
href={buildExpenseActionHref(
|
||||||
|
'/expense/detail/edit/',
|
||||||
|
initialValues?.id as number,
|
||||||
|
searchParams
|
||||||
|
)}
|
||||||
className='px-4 grow sm:grow-0'
|
className='px-4 grow sm:grow-0'
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:pencil-outline' width={24} height={24} />
|
<Icon icon='mdi:pencil-outline' width={24} height={24} />
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
import {
|
||||||
import { usePathname } from 'next/navigation';
|
ChangeEventHandler,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
import { useUiStore } from '@/stores/ui/ui.store';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import {
|
import {
|
||||||
@@ -31,19 +37,32 @@ import ExpenseTableSkeleton from '@/components/pages/expense/skeleton/ExpenseTab
|
|||||||
|
|
||||||
import { Expense } from '@/types/api/expense';
|
import { Expense } from '@/types/api/expense';
|
||||||
import { ExpenseApi } from '@/services/api/expense';
|
import { ExpenseApi } from '@/services/api/expense';
|
||||||
|
import { buildExpenseActionHref } from '@/lib/expense-list-navigation';
|
||||||
import { cn, formatCurrency, formatDate } from '@/lib/helper';
|
import { cn, formatCurrency, formatDate } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
|
|
||||||
|
type ExpenseTableFilters = {
|
||||||
|
search: string;
|
||||||
|
nameSort: string;
|
||||||
|
transactionDate: string;
|
||||||
|
realizationDate: string;
|
||||||
|
locationId: string;
|
||||||
|
vendorId: string;
|
||||||
|
userId: string;
|
||||||
|
};
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
popoverPosition = 'bottom',
|
popoverPosition = 'bottom',
|
||||||
props,
|
props,
|
||||||
deleteClickHandler,
|
deleteClickHandler,
|
||||||
|
returnToSearchParams,
|
||||||
}: {
|
}: {
|
||||||
popoverPosition: 'bottom' | 'top';
|
popoverPosition: 'bottom' | 'top';
|
||||||
props: CellContext<Expense, unknown>;
|
props: CellContext<Expense, unknown>;
|
||||||
deleteClickHandler: () => void;
|
deleteClickHandler: () => void;
|
||||||
|
returnToSearchParams: URLSearchParams;
|
||||||
}) => {
|
}) => {
|
||||||
const popoverId = `expense#${props.row.original.id}`;
|
const popoverId = `expense#${props.row.original.id}`;
|
||||||
const popoverAnchorName = `--anchor-expense#${props.row.original.id}`;
|
const popoverAnchorName = `--anchor-expense#${props.row.original.id}`;
|
||||||
@@ -86,7 +105,11 @@ const RowOptionsMenu = ({
|
|||||||
<div className='flex flex-col bg-base-100 rounded-xl'>
|
<div className='flex flex-col bg-base-100 rounded-xl'>
|
||||||
<RequirePermission permissions='lti.expense.detail'>
|
<RequirePermission permissions='lti.expense.detail'>
|
||||||
<Button
|
<Button
|
||||||
href={`/expense/detail/?expenseId=${props.row.original.id}`}
|
href={buildExpenseActionHref(
|
||||||
|
'/expense/detail/',
|
||||||
|
props.row.original.id,
|
||||||
|
returnToSearchParams
|
||||||
|
)}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='none'
|
color='none'
|
||||||
className='p-3 justify-start text-sm font-semibold w-full'
|
className='p-3 justify-start text-sm font-semibold w-full'
|
||||||
@@ -100,7 +123,11 @@ const RowOptionsMenu = ({
|
|||||||
{showEditButton && (
|
{showEditButton && (
|
||||||
<RequirePermission permissions='lti.expense.update'>
|
<RequirePermission permissions='lti.expense.update'>
|
||||||
<Button
|
<Button
|
||||||
href={`/expense/detail/edit/?expenseId=${props.row.original.id}`}
|
href={buildExpenseActionHref(
|
||||||
|
'/expense/detail/edit/',
|
||||||
|
props.row.original.id,
|
||||||
|
returnToSearchParams
|
||||||
|
)}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='none'
|
color='none'
|
||||||
className='p-3 justify-start text-sm font-semibold w-full'
|
className='p-3 justify-start text-sm font-semibold w-full'
|
||||||
@@ -115,7 +142,11 @@ const RowOptionsMenu = ({
|
|||||||
{showRealizationButton && (
|
{showRealizationButton && (
|
||||||
<RequirePermission permissions='lti.expense.create.realization'>
|
<RequirePermission permissions='lti.expense.create.realization'>
|
||||||
<Button
|
<Button
|
||||||
href={`/expense/realization/?expenseId=${props.row.original.id}`}
|
href={buildExpenseActionHref(
|
||||||
|
'/expense/realization/',
|
||||||
|
props.row.original.id,
|
||||||
|
returnToSearchParams
|
||||||
|
)}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='none'
|
color='none'
|
||||||
className='p-3 justify-start text-sm font-semibold w-full'
|
className='p-3 justify-start text-sm font-semibold w-full'
|
||||||
@@ -155,6 +186,8 @@ const RowOptionsMenu = ({
|
|||||||
const ExpensesTable = () => {
|
const ExpensesTable = () => {
|
||||||
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
const { searchValue, setSearchValue, setTableState } = useUiStore();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
@@ -162,9 +195,11 @@ const ExpensesTable = () => {
|
|||||||
setPage,
|
setPage,
|
||||||
setPageSize,
|
setPageSize,
|
||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter<ExpenseTableFilters>({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
page: Number(searchParams.get('page')) || 1,
|
||||||
|
pageSize: Number(searchParams.get('limit')) || 10,
|
||||||
|
search: searchValue,
|
||||||
nameSort: '',
|
nameSort: '',
|
||||||
transactionDate: '',
|
transactionDate: '',
|
||||||
realizationDate: '',
|
realizationDate: '',
|
||||||
@@ -193,6 +228,54 @@ const ExpensesTable = () => {
|
|||||||
ExpenseApi.getAllFetcher
|
ExpenseApi.getAllFetcher
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const syncPaginationToUrl = useCallback(
|
||||||
|
(page: number, pageSize: number) => {
|
||||||
|
const nextQueryString = new URLSearchParams({
|
||||||
|
page: String(page),
|
||||||
|
limit: String(pageSize),
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
router.replace(
|
||||||
|
nextQueryString ? `${pathname}?${nextQueryString}` : pathname,
|
||||||
|
{
|
||||||
|
scroll: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[pathname, router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageChangeHandler = useCallback(
|
||||||
|
(page: number) => {
|
||||||
|
setPage(page);
|
||||||
|
syncPaginationToUrl(page, tableFilterState.pageSize);
|
||||||
|
},
|
||||||
|
[setPage, syncPaginationToUrl, tableFilterState.pageSize]
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageSizeChangeHandler = useCallback(
|
||||||
|
(pageSize: number) => {
|
||||||
|
setPageSize(pageSize);
|
||||||
|
syncPaginationToUrl(1, pageSize);
|
||||||
|
},
|
||||||
|
[setPageSize, syncPaginationToUrl]
|
||||||
|
);
|
||||||
|
|
||||||
|
const returnToSearchParams = useMemo(() => {
|
||||||
|
const returnToParams = new URLSearchParams();
|
||||||
|
const queryString = new URLSearchParams({
|
||||||
|
page: String(tableFilterState.page),
|
||||||
|
limit: String(tableFilterState.pageSize),
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
returnToParams.set(
|
||||||
|
'returnTo',
|
||||||
|
queryString ? `${pathname}?${queryString}` : pathname
|
||||||
|
);
|
||||||
|
|
||||||
|
return returnToParams;
|
||||||
|
}, [pathname, tableFilterState.page, tableFilterState.pageSize]);
|
||||||
|
|
||||||
const deleteModal = useModal();
|
const deleteModal = useModal();
|
||||||
const approveModal = useModal();
|
const approveModal = useModal();
|
||||||
const rejectModal = useModal();
|
const rejectModal = useModal();
|
||||||
@@ -373,6 +456,7 @@ const ExpensesTable = () => {
|
|||||||
popoverPosition={isLast2Rows ? 'top' : 'bottom'}
|
popoverPosition={isLast2Rows ? 'top' : 'bottom'}
|
||||||
props={props}
|
props={props}
|
||||||
deleteClickHandler={deleteClickHandler}
|
deleteClickHandler={deleteClickHandler}
|
||||||
|
returnToSearchParams={returnToSearchParams}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -512,10 +596,6 @@ const ExpensesTable = () => {
|
|||||||
setIsRejectLoading(false);
|
setIsRejectLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateFilter('search', searchValue);
|
|
||||||
}, [searchValue, updateFilter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTableState('expense-table', pathname);
|
setTableState('expense-table', pathname);
|
||||||
}, [pathname, setTableState]);
|
}, [pathname, setTableState]);
|
||||||
@@ -554,7 +634,7 @@ const ExpensesTable = () => {
|
|||||||
const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name');
|
const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name');
|
||||||
|
|
||||||
if (!isNameSorted) {
|
if (!isNameSorted) {
|
||||||
updateFilter('nameSort', '');
|
updateFilter('nameSort', '', false);
|
||||||
} else {
|
} else {
|
||||||
updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc');
|
updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc');
|
||||||
}
|
}
|
||||||
@@ -734,8 +814,8 @@ const ExpensesTable = () => {
|
|||||||
totalItems={
|
totalItems={
|
||||||
isResponseSuccess(expenses) ? expenses?.meta?.total_results : 0
|
isResponseSuccess(expenses) ? expenses?.meta?.total_results : 0
|
||||||
}
|
}
|
||||||
onPageChange={setPage}
|
onPageChange={pageChangeHandler}
|
||||||
onPageSizeChange={setPageSize}
|
onPageSizeChange={pageSizeChangeHandler}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
sorting={sorting}
|
sorting={sorting}
|
||||||
setSorting={setSorting}
|
setSorting={setSorting}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ import { isResponseError } from '@/lib/api-helper';
|
|||||||
import { LocationApi, SupplierApi } from '@/services/api/master-data';
|
import { LocationApi, SupplierApi } from '@/services/api/master-data';
|
||||||
import { Supplier } from '@/types/api/master-data/supplier';
|
import { Supplier } from '@/types/api/master-data/supplier';
|
||||||
import { ACCEPTED_FILE_TYPE } from '@/config/constant';
|
import { ACCEPTED_FILE_TYPE } from '@/config/constant';
|
||||||
|
import { getExpenseListReturnTo } from '@/lib/expense-list-navigation';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
|
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
|
||||||
|
|
||||||
@@ -48,6 +49,8 @@ const ExpenseRealizationForm = ({
|
|||||||
initialValues,
|
initialValues,
|
||||||
}: ExpenseRealizationFormProps) => {
|
}: ExpenseRealizationFormProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const returnTo = getExpenseListReturnTo(searchParams);
|
||||||
|
|
||||||
const [expenseFormErrorMessage, setExpenseFormErrorMessage] = useState('');
|
const [expenseFormErrorMessage, setExpenseFormErrorMessage] = useState('');
|
||||||
|
|
||||||
@@ -64,9 +67,9 @@ const ExpenseRealizationForm = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
toast.success(createExpenseRes?.message as string);
|
toast.success(createExpenseRes?.message as string);
|
||||||
router.push('/expense');
|
router.push(returnTo);
|
||||||
},
|
},
|
||||||
[router]
|
[initialValues?.id, returnTo, router]
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateExpenseHandler = useCallback(
|
const updateExpenseHandler = useCallback(
|
||||||
@@ -83,9 +86,9 @@ const ExpenseRealizationForm = ({
|
|||||||
|
|
||||||
toast.success(updateExpenseRes?.message as string);
|
toast.success(updateExpenseRes?.message as string);
|
||||||
router.refresh();
|
router.refresh();
|
||||||
router.push('/expense');
|
router.push(returnTo);
|
||||||
},
|
},
|
||||||
[router]
|
[returnTo, router]
|
||||||
);
|
);
|
||||||
|
|
||||||
const formik = useFormik<ExpenseRealizationFormValues>({
|
const formik = useFormik<ExpenseRealizationFormValues>({
|
||||||
@@ -258,7 +261,7 @@ const ExpenseRealizationForm = ({
|
|||||||
<section className='w-full'>
|
<section className='w-full'>
|
||||||
<header className='flex flex-col gap-4'>
|
<header className='flex flex-col gap-4'>
|
||||||
<Button
|
<Button
|
||||||
href='/expense'
|
href={returnTo}
|
||||||
variant='link'
|
variant='link'
|
||||||
className='w-fit p-0 text-primary'
|
className='w-fit p-0 text-primary'
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -611,7 +611,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
} = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', {
|
} = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', {
|
||||||
flags: 'PAKAN,OVK',
|
flags: 'PAKAN,OVK',
|
||||||
limit: '100',
|
limit: '100',
|
||||||
available_only: 'true',
|
available_only: 'false',
|
||||||
location_id: stockProductsLocationId,
|
location_id: stockProductsLocationId,
|
||||||
...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}),
|
...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Uniformity } from '@/types/api/production/uniformity';
|
|||||||
|
|
||||||
type UniformityFormSchemaType = {
|
type UniformityFormSchemaType = {
|
||||||
date: string;
|
date: string;
|
||||||
week: number;
|
|
||||||
location?: {
|
location?: {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -45,10 +44,6 @@ const FileSchema = Yup.mixed<File>()
|
|||||||
export const UniformityFormSchema: Yup.ObjectSchema<UniformityFormSchemaType> =
|
export const UniformityFormSchema: Yup.ObjectSchema<UniformityFormSchemaType> =
|
||||||
Yup.object({
|
Yup.object({
|
||||||
date: Yup.string().required('Tanggal wajib diisi!'),
|
date: Yup.string().required('Tanggal wajib diisi!'),
|
||||||
week: Yup.number()
|
|
||||||
.min(1, 'Minggu ke wajib diisi!')
|
|
||||||
.required('Minggu ke wajib diisi!')
|
|
||||||
.typeError('Minggu ke wajib diisi!'),
|
|
||||||
location: Yup.object({
|
location: Yup.object({
|
||||||
value: Yup.number().min(1).required(),
|
value: Yup.number().min(1).required(),
|
||||||
label: Yup.string().required(),
|
label: Yup.string().required(),
|
||||||
@@ -81,7 +76,6 @@ export type UniformityFormValues = Yup.InferType<typeof UniformityFormSchema>;
|
|||||||
|
|
||||||
export type UniformityFormData = {
|
export type UniformityFormData = {
|
||||||
date: string;
|
date: string;
|
||||||
week: number;
|
|
||||||
project_flock_kandang_id: number;
|
project_flock_kandang_id: number;
|
||||||
document: File | null;
|
document: File | null;
|
||||||
document_name: string;
|
document_name: string;
|
||||||
@@ -91,8 +85,7 @@ export const getUniformityFormInitialValues = (
|
|||||||
initialValues?: Partial<Uniformity>
|
initialValues?: Partial<Uniformity>
|
||||||
): UniformityFormValues => {
|
): UniformityFormValues => {
|
||||||
return {
|
return {
|
||||||
date: initialValues?.week ? '' : '',
|
date: '',
|
||||||
week: initialValues?.week ?? 0,
|
|
||||||
location: null,
|
location: null,
|
||||||
location_id: 0,
|
location_id: 0,
|
||||||
project_flock: null,
|
project_flock: null,
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import { LocationApi } from '@/services/api/master-data';
|
|||||||
import {
|
import {
|
||||||
ProjectFlockApi,
|
ProjectFlockApi,
|
||||||
ProjectFlockKandangApi,
|
ProjectFlockKandangApi,
|
||||||
RecordingApi,
|
|
||||||
} from '@/services/api/production';
|
} from '@/services/api/production';
|
||||||
import { UniformityApi } from '@/services/api/uniformity';
|
import { UniformityApi } from '@/services/api/uniformity';
|
||||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
@@ -40,7 +39,6 @@ import {
|
|||||||
ProjectFlockKandangLookup,
|
ProjectFlockKandangLookup,
|
||||||
ProjectFlock,
|
ProjectFlock,
|
||||||
} from '@/types/api/production/project-flock';
|
} from '@/types/api/production/project-flock';
|
||||||
import { Recording } from '@/types/api/production/recording';
|
|
||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
import { Kandang } from '@/types/api/master-data/kandang';
|
||||||
import UniformityPreviewForm from '@/components/pages/production/uniformity/form/UniformityPreviewForm';
|
import UniformityPreviewForm from '@/components/pages/production/uniformity/form/UniformityPreviewForm';
|
||||||
import UniformityResultForm from '@/components/pages/production/uniformity/form/UniformityResultForm';
|
import UniformityResultForm from '@/components/pages/production/uniformity/form/UniformityResultForm';
|
||||||
@@ -204,23 +202,6 @@ const UniformityForm = ({
|
|||||||
? projectFlockKandangLookupData.data
|
? projectFlockKandangLookupData.data
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
// ===== RECORDINGS DATA (FOR WEEK CALCULATION) =====
|
|
||||||
const recordingsUrl = useMemo(() => {
|
|
||||||
if (!projectFlockKandangLookup?.project_flock_kandang_id) return null;
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
page: '1',
|
|
||||||
limit: '100',
|
|
||||||
project_flock_kandang_id:
|
|
||||||
projectFlockKandangLookup.project_flock_kandang_id.toString(),
|
|
||||||
});
|
|
||||||
return `${RecordingApi.basePath}?${params.toString()}`;
|
|
||||||
}, [projectFlockKandangLookup?.project_flock_kandang_id]);
|
|
||||||
|
|
||||||
const { data: recordingsData } = useSWR(
|
|
||||||
recordingsUrl,
|
|
||||||
recordingsUrl ? RecordingApi.getAllFetcher : null
|
|
||||||
);
|
|
||||||
|
|
||||||
// ===== FORM CONFIGURATION =====
|
// ===== FORM CONFIGURATION =====
|
||||||
const formikInitialValues = useMemo<UniformityFormValues>(
|
const formikInitialValues = useMemo<UniformityFormValues>(
|
||||||
() => getUniformityFormInitialValues(initialValues),
|
() => getUniformityFormInitialValues(initialValues),
|
||||||
@@ -246,7 +227,6 @@ const UniformityForm = ({
|
|||||||
|
|
||||||
setUniformityFormData({
|
setUniformityFormData({
|
||||||
date: values.date,
|
date: values.date,
|
||||||
week: values.week,
|
|
||||||
project_flock_kandang_id: projectFlockKandangId,
|
project_flock_kandang_id: projectFlockKandangId,
|
||||||
document: values.document as File,
|
document: values.document as File,
|
||||||
document_name: (values.document as File).name,
|
document_name: (values.document as File).name,
|
||||||
@@ -475,59 +455,6 @@ const UniformityForm = ({
|
|||||||
generateUniformityTemplate(population, projectFlockKandangLookup);
|
generateUniformityTemplate(population, projectFlockKandangLookup);
|
||||||
}, [projectFlockKandangLookup]);
|
}, [projectFlockKandangLookup]);
|
||||||
|
|
||||||
// ===== SIDE EFFECTS =====
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
projectFlockKandangLookup?.chick_in_date &&
|
|
||||||
projectFlockKandangLookup?.project_flock_kandang_id
|
|
||||||
) {
|
|
||||||
const chickInDate = new Date(projectFlockKandangLookup.chick_in_date);
|
|
||||||
chickInDate.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
let initialWeek = 18;
|
|
||||||
|
|
||||||
if (
|
|
||||||
isResponseSuccess(recordingsData) &&
|
|
||||||
recordingsData.data &&
|
|
||||||
recordingsData.data.length > 0
|
|
||||||
) {
|
|
||||||
const sortedRecordings = [...recordingsData.data].sort(
|
|
||||||
(a: Recording, b: Recording) =>
|
|
||||||
new Date(a.record_datetime).getTime() -
|
|
||||||
new Date(b.record_datetime).getTime()
|
|
||||||
);
|
|
||||||
|
|
||||||
const earliestRecording = sortedRecordings[0];
|
|
||||||
if (earliestRecording?.project_flock?.production_standart?.week) {
|
|
||||||
initialWeek =
|
|
||||||
earliestRecording.project_flock.production_standart.week;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formik.values.date) {
|
|
||||||
const selectedDate = new Date(formik.values.date);
|
|
||||||
selectedDate.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
const daysDiff = Math.floor(
|
|
||||||
(selectedDate.getTime() - chickInDate.getTime()) /
|
|
||||||
(1000 * 60 * 60 * 24)
|
|
||||||
);
|
|
||||||
|
|
||||||
const weeksDiff = Math.floor(daysDiff / 7);
|
|
||||||
|
|
||||||
setFieldValue('week', initialWeek + weeksDiff);
|
|
||||||
} else {
|
|
||||||
setFieldValue('week', initialWeek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
projectFlockKandangLookup?.chick_in_date,
|
|
||||||
projectFlockKandangLookup?.project_flock_kandang_id,
|
|
||||||
recordingsData,
|
|
||||||
formik.values.date,
|
|
||||||
setFieldValue,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsub = subscribeValidate(() => {
|
const unsub = subscribeValidate(() => {
|
||||||
setIsValid(true);
|
setIsValid(true);
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ const UniformityResultForm = () => {
|
|||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
date: uniformityFormData.date,
|
date: uniformityFormData.date,
|
||||||
week: uniformityFormData.week,
|
|
||||||
project_flock_kandang_id: uniformityFormData.project_flock_kandang_id,
|
project_flock_kandang_id: uniformityFormData.project_flock_kandang_id,
|
||||||
document: uniformityFormData.document,
|
document: uniformityFormData.document,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -294,7 +294,6 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
item.expedition_vendor_id || item.expedition_vendor?.id || null;
|
item.expedition_vendor_id || item.expedition_vendor?.id || null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
purchase_item: null,
|
|
||||||
purchase_item_id: item.id,
|
purchase_item_id: item.id,
|
||||||
received_date: item.received_date
|
received_date: item.received_date
|
||||||
? new Date(item.received_date).toISOString().split('T')[0]
|
? new Date(item.received_date).toISOString().split('T')[0]
|
||||||
@@ -573,7 +572,7 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
<td>
|
<td>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
isClearable={true}
|
isClearable={true}
|
||||||
value={formItem?.expedition_vendor}
|
value={formItem?.expedition_vendor ?? null}
|
||||||
key={`expedition-vendor-${idx}`}
|
key={`expedition-vendor-${idx}`}
|
||||||
onChange={(val) =>
|
onChange={(val) =>
|
||||||
expeditionVendorChangeHandler(idx, val)
|
expeditionVendorChangeHandler(idx, val)
|
||||||
|
|||||||
@@ -31,10 +31,6 @@ type PurchaseRequestAcceptApprovalFormSchemaType = {
|
|||||||
action: 'APPROVED' | 'REJECTED';
|
action: 'APPROVED' | 'REJECTED';
|
||||||
notes: string | null;
|
notes: string | null;
|
||||||
items: {
|
items: {
|
||||||
purchase_item?: {
|
|
||||||
value: number;
|
|
||||||
label: string;
|
|
||||||
} | null;
|
|
||||||
purchase_item_id: number;
|
purchase_item_id: number;
|
||||||
received_date: string;
|
received_date: string;
|
||||||
travel_number: string;
|
travel_number: string;
|
||||||
@@ -68,10 +64,6 @@ export type PurchaseStaffApprovalItemSchema = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type PurchaseAcceptApprovalItemSchema = {
|
export type PurchaseAcceptApprovalItemSchema = {
|
||||||
purchase_item?: {
|
|
||||||
value: number;
|
|
||||||
label: string;
|
|
||||||
} | null;
|
|
||||||
purchase_item_id: number;
|
purchase_item_id: number;
|
||||||
received_date: string;
|
received_date: string;
|
||||||
travel_number: string;
|
travel_number: string;
|
||||||
@@ -160,12 +152,6 @@ const PurchaseManagerApprovalObjectSchema: Yup.ObjectSchema<PurchaseRequestManag
|
|||||||
|
|
||||||
const PurchaseAcceptApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseAcceptApprovalItemSchema> =
|
const PurchaseAcceptApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseAcceptApprovalItemSchema> =
|
||||||
Yup.object({
|
Yup.object({
|
||||||
purchase_item: Yup.object({
|
|
||||||
value: Yup.number().min(1).required(),
|
|
||||||
label: Yup.string().required(),
|
|
||||||
})
|
|
||||||
.nullable()
|
|
||||||
.optional(),
|
|
||||||
purchase_item_id: Yup.number()
|
purchase_item_id: Yup.number()
|
||||||
.min(1, 'Purchase item is required!')
|
.min(1, 'Purchase item is required!')
|
||||||
.required('Purchase item is required!')
|
.required('Purchase item is required!')
|
||||||
@@ -185,9 +171,8 @@ const PurchaseAcceptApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseAcceptApp
|
|||||||
.typeError('No. Surat jalan wajib diisi!'),
|
.typeError('No. Surat jalan wajib diisi!'),
|
||||||
vehicle_number: Yup.string()
|
vehicle_number: Yup.string()
|
||||||
.nullable()
|
.nullable()
|
||||||
.when('expedition_vendor', {
|
.when('expedition_vendor_id', {
|
||||||
is: (expeditionVendor?: { value?: number; label?: string } | null) =>
|
is: (expeditionVendorId?: number | null) => Boolean(expeditionVendorId),
|
||||||
Boolean(expeditionVendor?.value),
|
|
||||||
then: (schema) => schema.required('Nomor kendaraan wajib diisi!'),
|
then: (schema) => schema.required('Nomor kendaraan wajib diisi!'),
|
||||||
otherwise: (schema) => schema.optional(),
|
otherwise: (schema) => schema.optional(),
|
||||||
})
|
})
|
||||||
@@ -196,6 +181,7 @@ const PurchaseAcceptApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseAcceptApp
|
|||||||
value: Yup.number().min(1).required(),
|
value: Yup.number().min(1).required(),
|
||||||
label: Yup.string().required(),
|
label: Yup.string().required(),
|
||||||
})
|
})
|
||||||
|
.default(undefined)
|
||||||
.nullable()
|
.nullable()
|
||||||
.optional(),
|
.optional(),
|
||||||
expedition_vendor_id: Yup.number()
|
expedition_vendor_id: Yup.number()
|
||||||
@@ -218,9 +204,8 @@ const PurchaseAcceptApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseAcceptApp
|
|||||||
.typeError('Jumlah diterima harus berupa angka!'),
|
.typeError('Jumlah diterima harus berupa angka!'),
|
||||||
transport_per_item: Yup.mixed<string | number>()
|
transport_per_item: Yup.mixed<string | number>()
|
||||||
.nullable()
|
.nullable()
|
||||||
.when('expedition_vendor', {
|
.when('expedition_vendor_id', {
|
||||||
is: (expeditionVendor?: { value?: number; label?: string } | null) =>
|
is: (expeditionVendorId?: number | null) => Boolean(expeditionVendorId),
|
||||||
Boolean(expeditionVendor?.value),
|
|
||||||
then: (schema) =>
|
then: (schema) =>
|
||||||
schema.required('Biaya transport per item wajib diisi!'),
|
schema.required('Biaya transport per item wajib diisi!'),
|
||||||
otherwise: (schema) => schema.optional(),
|
otherwise: (schema) => schema.optional(),
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
type SearchParamsLike = {
|
||||||
|
get: (name: string) => string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EXPENSE_LIST_PATH = '/expense';
|
||||||
|
|
||||||
|
export const getExpenseListReturnTo = (searchParams: SearchParamsLike) => {
|
||||||
|
const existingReturnTo = searchParams.get('returnTo');
|
||||||
|
|
||||||
|
if (existingReturnTo?.startsWith(EXPENSE_LIST_PATH)) {
|
||||||
|
return existingReturnTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
const page = searchParams.get('page');
|
||||||
|
const limit = searchParams.get('limit');
|
||||||
|
|
||||||
|
if (page) params.set('page', page);
|
||||||
|
if (limit) params.set('limit', limit);
|
||||||
|
|
||||||
|
const queryString = params.toString();
|
||||||
|
|
||||||
|
return queryString
|
||||||
|
? `${EXPENSE_LIST_PATH}?${queryString}`
|
||||||
|
: EXPENSE_LIST_PATH;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildExpenseActionHref = (
|
||||||
|
path: string,
|
||||||
|
expenseId: number | string,
|
||||||
|
searchParams: SearchParamsLike
|
||||||
|
) => {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
expenseId: String(expenseId),
|
||||||
|
returnTo: getExpenseListReturnTo(searchParams),
|
||||||
|
});
|
||||||
|
|
||||||
|
return `${path}?${params.toString()}`;
|
||||||
|
};
|
||||||
@@ -56,7 +56,6 @@ export class UniformityApiService extends BaseApiService<
|
|||||||
): Promise<BaseApiResponse<UniformityDetail> | undefined> {
|
): Promise<BaseApiResponse<UniformityDetail> | undefined> {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('date', payload.date);
|
formData.append('date', payload.date);
|
||||||
formData.append('week', payload.week.toString());
|
|
||||||
formData.append(
|
formData.append(
|
||||||
'project_flock_kandang_id',
|
'project_flock_kandang_id',
|
||||||
payload.project_flock_kandang_id.toString()
|
payload.project_flock_kandang_id.toString()
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export function useTableFilter<TExtra extends Record<string, unknown>>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const updateFilter = useCallback(
|
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({ type: 'UPDATE_FILTER', key, value, resetPage });
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
|
|||||||
-1
@@ -146,7 +146,6 @@ export type CreateUniformityPayload = {
|
|||||||
date: string;
|
date: string;
|
||||||
project_flock_kandang_id: number;
|
project_flock_kandang_id: number;
|
||||||
document: File;
|
document: File;
|
||||||
week: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VerifyUniformityPayload = {
|
export type VerifyUniformityPayload = {
|
||||||
|
|||||||
Reference in New Issue
Block a user