mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 15:25:46 +00:00
Merge branch 'dev/hotfix/restu' into 'development'
[FIX/FE] Add Tooltip, Support Comma on Recording Egg Values (Total Berat) and Add Global State Search for Table See merge request mbugroup/lti-web-client!234
This commit is contained in:
@@ -1,6 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useCallback, useState, useMemo, useEffect } from 'react';
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useState,
|
||||||
|
useMemo,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
@@ -28,6 +34,7 @@ import { useTableFilter } from '@/services/hooks/useTableFilter';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import Badge from '@/components/Badge';
|
import Badge from '@/components/Badge';
|
||||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||||
|
import { useUiStore } from '@/stores/ui/ui.store';
|
||||||
import { BaseApproval, BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApproval, BaseApiResponse } from '@/types/api/api-general';
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
@@ -344,6 +351,9 @@ const ApprovalHistoryModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const RecordingTable = () => {
|
const RecordingTable = () => {
|
||||||
|
const { searchValue, setSearchValue, resetSearchValue } = useUiStore();
|
||||||
|
const previousPathRef = useRef<string | null>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: tableFilterState,
|
state: tableFilterState,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
@@ -352,7 +362,7 @@ const RecordingTable = () => {
|
|||||||
toQueryString: getTableFilterQueryString,
|
toQueryString: getTableFilterQueryString,
|
||||||
} = useTableFilter({
|
} = useTableFilter({
|
||||||
initial: {
|
initial: {
|
||||||
search: '',
|
search: searchValue,
|
||||||
areaFilter: '',
|
areaFilter: '',
|
||||||
locationFilter: '',
|
locationFilter: '',
|
||||||
kandangFilter: '',
|
kandangFilter: '',
|
||||||
@@ -403,12 +413,35 @@ const RecordingTable = () => {
|
|||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Store current path on mount
|
||||||
|
previousPathRef.current = window.location.pathname;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const currentPath = window.location.pathname;
|
||||||
|
|
||||||
|
// if both paths are within /production/recording module
|
||||||
|
const isCurrentPathRecording = currentPath.includes(
|
||||||
|
'/production/recording'
|
||||||
|
);
|
||||||
|
const isPreviousPathRecording = previousPathRef.current?.includes(
|
||||||
|
'/production/recording'
|
||||||
|
);
|
||||||
|
|
||||||
|
// reset if we outside recording module entirely
|
||||||
|
if (isPreviousPathRecording && !isCurrentPathRecording) {
|
||||||
|
resetSearchValue();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [resetSearchValue]);
|
||||||
|
|
||||||
const searchChangeHandler = useCallback(
|
const searchChangeHandler = useCallback(
|
||||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
updateFilter('search', e.target.value);
|
updateFilter('search', e.target.value);
|
||||||
|
setSearchValue(e.target.value);
|
||||||
setPage(1);
|
setPage(1);
|
||||||
},
|
},
|
||||||
[updateFilter, setPage]
|
[updateFilter, setSearchValue, setPage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageSizeChangeHandler = useCallback(
|
const pageSizeChangeHandler = useCallback(
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWith
|
|||||||
import Modal, { useModal } from '@/components/Modal';
|
import Modal, { useModal } from '@/components/Modal';
|
||||||
import AlertErrorList from '@/components/helper/form/FormErrors';
|
import AlertErrorList from '@/components/helper/form/FormErrors';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
|
import Tooltip from '@/components/Tooltip';
|
||||||
import { type ColumnDef } from '@tanstack/react-table';
|
import { type ColumnDef } from '@tanstack/react-table';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -197,6 +198,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// ===== STATE MANAGEMENT =====
|
// ===== STATE MANAGEMENT =====
|
||||||
|
const [selectedRecordDate, setSelectedRecordDate] = useState<string>(
|
||||||
|
initialValues?.record_datetime
|
||||||
|
? new Date(initialValues.record_datetime).toISOString().split('T')[0]
|
||||||
|
: new Date().toISOString().split('T')[0]
|
||||||
|
);
|
||||||
const [selectedStocks, setSelectedStocks] = useState<number[]>([]);
|
const [selectedStocks, setSelectedStocks] = useState<number[]>([]);
|
||||||
const [selectedDepletions, setSelectedDepletions] = useState<number[]>([]);
|
const [selectedDepletions, setSelectedDepletions] = useState<number[]>([]);
|
||||||
const [selectedEggs, setSelectedEggs] = useState<number[]>([]);
|
const [selectedEggs, setSelectedEggs] = useState<number[]>([]);
|
||||||
@@ -911,6 +917,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
baseValues = getRecordingGrowingFormInitialValues(initialValues);
|
baseValues = getRecordingGrowingFormInitialValues(initialValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
baseValues.record_date = selectedRecordDate;
|
||||||
|
|
||||||
if (type === 'add') {
|
if (type === 'add') {
|
||||||
baseValues.location = selectedLocation
|
baseValues.location = selectedLocation
|
||||||
? {
|
? {
|
||||||
@@ -967,13 +975,22 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return baseValues;
|
return baseValues;
|
||||||
}, [initialValues, isLayingCategory, projectFlockKandangDetail, type]);
|
}, [
|
||||||
|
initialValues,
|
||||||
|
isLayingCategory,
|
||||||
|
projectFlockKandangDetail,
|
||||||
|
type,
|
||||||
|
selectedRecordDate,
|
||||||
|
selectedLocation,
|
||||||
|
selectedProjectFlock,
|
||||||
|
selectedKandang,
|
||||||
|
]);
|
||||||
|
|
||||||
const formik = useFormik<
|
const formik = useFormik<
|
||||||
RecordingGrowingFormValues | RecordingLayingFormValues
|
RecordingGrowingFormValues | RecordingLayingFormValues
|
||||||
>({
|
>({
|
||||||
initialValues: formikInitialValues,
|
initialValues: formikInitialValues,
|
||||||
enableReinitialize: true,
|
enableReinitialize: false,
|
||||||
validationSchema: (() => {
|
validationSchema: (() => {
|
||||||
let schema;
|
let schema;
|
||||||
if (isLayingCategory) {
|
if (isLayingCategory) {
|
||||||
@@ -1333,6 +1350,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const newDate = e.target.value;
|
const newDate = e.target.value;
|
||||||
formik.setFieldValue('record_date', newDate);
|
formik.setFieldValue('record_date', newDate);
|
||||||
|
setSelectedRecordDate(newDate);
|
||||||
setCurrentRecordDate(newDate);
|
setCurrentRecordDate(newDate);
|
||||||
if (duplicateErrorShown) {
|
if (duplicateErrorShown) {
|
||||||
toast.dismiss();
|
toast.dismiss();
|
||||||
@@ -2799,7 +2817,23 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
)}
|
)}
|
||||||
<th>Kondisi Telur</th>
|
<th>Kondisi Telur</th>
|
||||||
<th>Jumlah</th>
|
<th>Jumlah</th>
|
||||||
<th>Total Berat (Kilogram)</th>
|
<th className='flex items-center gap-1'>
|
||||||
|
Total Berat (Kilogram)
|
||||||
|
<Tooltip
|
||||||
|
className={{
|
||||||
|
wrapper: 'cursor-pointer',
|
||||||
|
}}
|
||||||
|
position='bottom'
|
||||||
|
content='Untuk menggunakan koma bisa menekan tombol titik (.) pada keyboard, Misal 0.123'
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon='heroicons:information-circle'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className='text-gray-400 hover:text-gray-600 shrink-0'
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</th>
|
||||||
{(type as 'add' | 'edit' | 'detail') !== 'detail' && (
|
{(type as 'add' | 'edit' | 'detail') !== 'detail' && (
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
)}
|
)}
|
||||||
@@ -2905,7 +2939,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
value={egg.weight ?? ''}
|
value={egg.weight ?? ''}
|
||||||
onChange={handleEggWeightChangeWrapper(idx)}
|
onChange={handleEggWeightChangeWrapper(idx)}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
decimalScale={0}
|
decimalScale={3}
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
thousandSeparator=','
|
thousandSeparator=','
|
||||||
decimalSeparator='.'
|
decimalSeparator='.'
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { StateCreator } from 'zustand';
|
||||||
|
|
||||||
|
export interface TableState {
|
||||||
|
searchValue: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableUISlice {
|
||||||
|
searchValue: string;
|
||||||
|
setSearchValue: (value: string) => void;
|
||||||
|
resetSearchValue: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createTableUISlice: StateCreator<
|
||||||
|
TableUISlice,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
TableUISlice
|
||||||
|
> = (set) => ({
|
||||||
|
// Initial state
|
||||||
|
searchValue: '',
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
setSearchValue: (value) => set({ searchValue: value }),
|
||||||
|
|
||||||
|
resetSearchValue: () => {
|
||||||
|
return set({ searchValue: '' });
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,18 +1,28 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { devtools } from 'zustand/middleware';
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
|
|
||||||
import { UIStore } from '@/types/stores';
|
import { UIStore } from '@/types/stores';
|
||||||
import { createMainUiSlice } from '@/stores/ui/slices/main.slice';
|
import { createMainUiSlice } from '@/stores/ui/slices/main.slice';
|
||||||
import { createDrawerUISlice } from '@/stores/ui/slices/drawer.slice';
|
import { createDrawerUISlice } from '@/stores/ui/slices/drawer.slice';
|
||||||
|
import { createTableUISlice } from '@/stores/ui/slices/table.slice';
|
||||||
|
|
||||||
export const useUiStore = create<UIStore>()(
|
export const useUiStore = create<UIStore>()(
|
||||||
devtools(
|
devtools(
|
||||||
(...args) => ({
|
persist(
|
||||||
...createMainUiSlice(...args),
|
(...args) => ({
|
||||||
...createDrawerUISlice(...args),
|
...createMainUiSlice(...args),
|
||||||
}),
|
...createDrawerUISlice(...args),
|
||||||
|
...createTableUISlice(...args),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: 'ui-cache',
|
||||||
|
partialize: (state) => ({
|
||||||
|
searchValue: state.searchValue,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
),
|
||||||
{
|
{
|
||||||
name: 'UIStore',
|
name: 'UIStore',
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+7
-1
@@ -26,7 +26,13 @@ type DrawerUISlice = {
|
|||||||
setIsNextStep: (v: boolean) => void;
|
setIsNextStep: (v: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UIStore = MainUiSlice & DrawerUISlice;
|
type TableUISlice = {
|
||||||
|
searchValue: string;
|
||||||
|
setSearchValue: (value: string) => void;
|
||||||
|
resetSearchValue: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UIStore = MainUiSlice & DrawerUISlice & TableUISlice;
|
||||||
|
|
||||||
type ProductionStandardFormSlice = {
|
type ProductionStandardFormSlice = {
|
||||||
formData: {
|
formData: {
|
||||||
|
|||||||
Reference in New Issue
Block a user