Merge branch 'fix/ISSUE-236/table-dropdown-issue' into 'development'

Fix: LTI Issue #236

See merge request mbugroup/lti-web-client!41
This commit is contained in:
Adnan Zahir
2025-11-03 09:08:21 +07:00
90 changed files with 1613 additions and 1509 deletions
+9 -9
View File
@@ -1,6 +1,6 @@
import { dirname } from "path"; import { dirname } from 'path';
import { fileURLToPath } from "url"; import { fileURLToPath } from 'url';
import { FlatCompat } from "@eslint/eslintrc"; import { FlatCompat } from '@eslint/eslintrc';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
@@ -10,14 +10,14 @@ const compat = new FlatCompat({
}); });
const eslintConfig = [ const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"), ...compat.extends('next/core-web-vitals', 'next/typescript'),
{ {
ignores: [ ignores: [
"node_modules/**", 'node_modules/**',
".next/**", '.next/**',
"out/**", 'out/**',
"build/**", 'build/**',
"next-env.d.ts", 'next-env.d.ts',
], ],
}, },
]; ];
+17
View File
@@ -39,6 +39,7 @@
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.5.3", "eslint-config-next": "15.5.3",
"husky": "^9.1.7", "husky": "^9.1.7",
"prettier": "^3.6.2",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5" "typescript": "^5"
} }
@@ -5669,6 +5670,22 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prettier": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prop-types": { "node_modules/prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+3 -1
View File
@@ -7,7 +7,8 @@
"build": "next build --turbopack", "build": "next build --turbopack",
"start": "next start", "start": "next start",
"lint": "eslint", "lint": "eslint",
"prepare": "husky" "prepare": "husky",
"format": "prettier --write ."
}, },
"dependencies": { "dependencies": {
"@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/match-sorter-utils": "^8.19.4",
@@ -41,6 +42,7 @@
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.5.3", "eslint-config-next": "15.5.3",
"husky": "^9.1.7", "husky": "^9.1.7",
"prettier": "^3.6.2",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5" "typescript": "^5"
} }
+1 -1
View File
@@ -1,5 +1,5 @@
const config = { const config = {
plugins: ["@tailwindcss/postcss"], plugins: ['@tailwindcss/postcss'],
}; };
export default config; export default config;
+2 -4
View File
@@ -3,10 +3,10 @@
@import '../styles/daisyui.css'; @import '../styles/daisyui.css';
@plugin "daisyui/theme" { @plugin "daisyui/theme" {
name: "lti"; name: 'lti';
default: false; default: false;
prefersdark: false; prefersdark: false;
color-scheme: "light"; color-scheme: 'light';
--color-base-100: oklch(98% 0.001 106.423); --color-base-100: oklch(98% 0.001 106.423);
--color-base-200: oklch(97% 0.001 106.424); --color-base-200: oklch(97% 0.001 106.424);
--color-base-300: oklch(92% 0.003 48.717); --color-base-300: oklch(92% 0.003 48.717);
@@ -37,8 +37,6 @@
--noise: 0; --noise: 0;
} }
:root { :root {
--color-primary: #1f74bf; --color-primary: #1f74bf;
} }
+5 -5
View File
@@ -1,11 +1,11 @@
import InventoryAdjustmentForm from "@/components/pages/inventory/adjustment/form/InventoryAdjustmentForm"; import InventoryAdjustmentForm from '@/components/pages/inventory/adjustment/form/InventoryAdjustmentForm';
const CreateInventoryAdjustment = () => { const CreateInventoryAdjustment = () => {
return ( return (
<section className="w-full p-4 flex flex-row justify-center"> <section className='w-full p-4 flex flex-row justify-center'>
<InventoryAdjustmentForm/> <InventoryAdjustmentForm />
</section> </section>
); );
} };
export default CreateInventoryAdjustment; export default CreateInventoryAdjustment;
@@ -1,11 +1,11 @@
import SuspenseHelper from "@/components/helper/SuspenseHelper" import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({ const Layout = ({
children children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode children: React.ReactNode;
}>) => { }>) => {
return <SuspenseHelper>{children}</SuspenseHelper> return <SuspenseHelper>{children}</SuspenseHelper>;
} };
export default Layout; export default Layout;
+8 -7
View File
@@ -7,11 +7,12 @@ import type { InventoryAdjustment } from '@/types/api/inventory/adjustment';
const DetailInventoryAdjustment = () => { const DetailInventoryAdjustment = () => {
const router = useRouter(); const router = useRouter();
const [inventoryAdjustment, setInventoryAdjustment] = useState<InventoryAdjustment | null>(null); const [inventoryAdjustment, setInventoryAdjustment] =
useState<InventoryAdjustment | null>(null);
// Ambil data dari router state // Ambil data dari router state
useEffect(() => { useEffect(() => {
console.log("Router State"); console.log('Router State');
console.log(window.history.state); console.log(window.history.state);
const state = window.history.state?.usr as const state = window.history.state?.usr as
| { inventoryAdjustment?: InventoryAdjustment } | { inventoryAdjustment?: InventoryAdjustment }
@@ -24,20 +25,20 @@ const DetailInventoryAdjustment = () => {
}, [router]); }, [router]);
const finalData = inventoryAdjustment; const finalData = inventoryAdjustment;
console.log("Final Data"); console.log('Final Data');
console.log(finalData); console.log(finalData);
if (!finalData) { if (!finalData) {
return ( return (
<div className="w-full flex flex-row justify-center items-center p-4"> <div className='w-full flex flex-row justify-center items-center p-4'>
<span className="loading loading-spinner loading-xl" /> <span className='loading loading-spinner loading-xl' />
</div> </div>
); );
} }
return ( return (
<section className="w-full p-4 flex flex-row justify-center"> <section className='w-full p-4 flex flex-row justify-center'>
<InventoryAdjustmentForm initialValues={finalData} /> <InventoryAdjustmentForm initialValues={finalData} />
</section> </section>
); );
+5 -5
View File
@@ -1,11 +1,11 @@
import CustomerForm from "@/components/pages/master-data/customer/form/CustomerForm"; import CustomerForm from '@/components/pages/master-data/customer/form/CustomerForm';
const AddCustomer = () => { const AddCustomer = () => {
return ( return (
<section className="w-full p-4 flex flex-row justify-center"> <section className='w-full p-4 flex flex-row justify-center'>
<CustomerForm/> <CustomerForm />
</section> </section>
); );
} };
export default AddCustomer; export default AddCustomer;
+17 -15
View File
@@ -1,45 +1,47 @@
'use client' 'use client';
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from "swr"; import useSWR from 'swr';
import { CustomerApi } from '@/services/api/master-data'; import { CustomerApi } from '@/services/api/master-data';
import { isResponseError, isResponseSuccess } from "@/lib/api-helper"; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import CustomerForm from "@/components/pages/master-data/customer/form/CustomerForm"; import CustomerForm from '@/components/pages/master-data/customer/form/CustomerForm';
const CustomerDetail = () => { const CustomerDetail = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const costumerId = searchParams.get("customerId"); const costumerId = searchParams.get('customerId');
const { data: costumer, isLoading: isLoadingCostumer } = useSWR( const { data: costumer, isLoading: isLoadingCostumer } = useSWR(
costumerId, costumerId,
(id: number) => CustomerApi.getSingle(id) (id: number) => CustomerApi.getSingle(id)
); );
if(!costumerId){ if (!costumerId) {
router.back(); router.back();
return ( return (
<div className="w-full flex flex-row justify-center items-center p-4"> <div className='w-full flex flex-row justify-center items-center p-4'>
<span className="loading loading-spinner loading-xl" /> <span className='loading loading-spinner loading-xl' />
</div> </div>
); );
} }
if(!isLoadingCostumer && (!costumer || isResponseError(costumer))){ if (!isLoadingCostumer && (!costumer || isResponseError(costumer))) {
router.replace("/404"); router.replace('/404');
return; return;
} }
return ( return (
<div className="w-full p-4 flex flex-row justify-center"> <div className='w-full p-4 flex flex-row justify-center'>
{isLoadingCostumer && <span className="loading loading-spinner loading-xl" />} {isLoadingCostumer && (
<span className='loading loading-spinner loading-xl' />
)}
{!isLoadingCostumer && isResponseSuccess(costumer) && ( {!isLoadingCostumer && isResponseSuccess(costumer) && (
<CustomerForm formType="detail" initialValues={costumer.data} /> <CustomerForm formType='detail' initialValues={costumer.data} />
)} )}
</div> </div>
) );
}; };
export default CustomerDetail; export default CustomerDetail;
+4 -4
View File
@@ -1,11 +1,11 @@
import CustomersTable from "@/components/pages/master-data/customer/CustomersTable"; import CustomersTable from '@/components/pages/master-data/customer/CustomersTable';
const Customer = () => { const Customer = () => {
return ( return (
<section className="w-full p-4"> <section className='w-full p-4'>
<CustomersTable /> <CustomersTable />
</section> </section>
) );
}; };
export default Customer; export default Customer;
+3 -3
View File
@@ -1,11 +1,11 @@
import FlockForm from "@/components/pages/master-data/flock/form/FlockForm"; import FlockForm from '@/components/pages/master-data/flock/form/FlockForm';
const AddFlock = () => { const AddFlock = () => {
return ( return (
<section className="w-full p-4 flex flex-row justify-center"> <section className='w-full p-4 flex flex-row justify-center'>
<FlockForm /> <FlockForm />
</section> </section>
); );
} };
export default AddFlock; export default AddFlock;
@@ -1,10 +1,10 @@
'use client' 'use client';
import FlockForm from "@/components/pages/master-data/flock/form/FlockForm"; import FlockForm from '@/components/pages/master-data/flock/form/FlockForm';
import { isResponseError, isResponseSuccess } from "@/lib/api-helper"; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { FlockApi } from "@/services/api/master-data"; import { FlockApi } from '@/services/api/master-data';
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from "swr"; import useSWR from 'swr';
const FlockEdit = () => { const FlockEdit = () => {
const router = useRouter(); const router = useRouter();
@@ -44,6 +44,6 @@ const FlockEdit = () => {
)} )}
</div> </div>
); );
} };
export default FlockEdit; export default FlockEdit;
+6 -6
View File
@@ -1,11 +1,11 @@
import SuspenseHelper from "@/components/helper/SuspenseHelper" import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({ const Layout = ({
children children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode children: React.ReactNode;
}>) => { }>) => {
return <SuspenseHelper>{children}</SuspenseHelper> return <SuspenseHelper>{children}</SuspenseHelper>;
} };
export default Layout; export default Layout;
+19 -16
View File
@@ -1,10 +1,10 @@
'use client' 'use client';
import FlockForm from "@/components/pages/master-data/flock/form/FlockForm"; import FlockForm from '@/components/pages/master-data/flock/form/FlockForm';
import { isResponseError, isResponseSuccess } from "@/lib/api-helper"; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { FlockApi } from "@/services/api/master-data"; import { FlockApi } from '@/services/api/master-data';
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from "swr"; import useSWR from 'swr';
const FlockDetail = () => { const FlockDetail = () => {
const router = useRouter(); const router = useRouter();
@@ -14,33 +14,36 @@ const FlockDetail = () => {
const flockId = searchParams.get('flockId'); const flockId = searchParams.get('flockId');
// Fetch Data // Fetch Data
const { data: flock, isLoading: isLoadingFlock } = useSWR(flockId, (id: number) => FlockApi.getSingle(id)); const { data: flock, isLoading: isLoadingFlock } = useSWR(
flockId,
(id: number) => FlockApi.getSingle(id)
);
if(!flockId){ if (!flockId) {
router.back(); router.back();
return ( return (
<div className="w-full flex flex-row justify-center items-center p-4"> <div className='w-full flex flex-row justify-center items-center p-4'>
<span className="loading loading-spinner loading-xl" /> <span className='loading loading-spinner loading-xl' />
</div> </div>
); );
} }
if(!isLoadingFlock && (!flock || isResponseError(flock))){ if (!isLoadingFlock && (!flock || isResponseError(flock))) {
router.replace('/404'); router.replace('/404');
return; return;
} }
return ( return (
<div className="w-full p-4 flex flex-row justify-center"> <div className='w-full p-4 flex flex-row justify-center'>
{isLoadingFlock && ( {isLoadingFlock && (
<span className="loading loading-spinner loading-xl" /> <span className='loading loading-spinner loading-xl' />
)} )}
{!isLoadingFlock && isResponseSuccess(flock) && ( {!isLoadingFlock && isResponseSuccess(flock) && (
<FlockForm formType="detail" initialValues={flock.data} /> <FlockForm formType='detail' initialValues={flock.data} />
)} )}
</div> </div>
); );
} };
export default FlockDetail; export default FlockDetail;
+5 -5
View File
@@ -1,11 +1,11 @@
import FlockTable from "@/components/pages/master-data/flock/FlocksTable"; import FlockTable from '@/components/pages/master-data/flock/FlocksTable';
const Flock = () => { const Flock = () => {
return ( return (
<section className="w-full p-4"> <section className='w-full p-4'>
<FlockTable/> <FlockTable />
</section> </section>
); );
} };
export default Flock; export default Flock;
@@ -1,11 +1,11 @@
import ProductCategoryForm from "@/components/pages/master-data/product-category/form/ProductCategoryForm"; import ProductCategoryForm from '@/components/pages/master-data/product-category/form/ProductCategoryForm';
const AddProductCategory = () => { const AddProductCategory = () => {
return ( return (
<div className="w-full p-4 flex flex-row justify-center"> <div className='w-full p-4 flex flex-row justify-center'>
<ProductCategoryForm /> <ProductCategoryForm />
</div> </div>
); );
}; };
export default AddProductCategory; export default AddProductCategory;
@@ -9,39 +9,44 @@ import { ProductCategoryApi } from '@/services/api/master-data';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
const ProductCategoryEdit = () => { const ProductCategoryEdit = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const productCategoryId = searchParams.get('productCategoryId'); const productCategoryId = searchParams.get('productCategoryId');
const { data: productCategory, isLoading: isLoadingProductCategory } = useSWR( const { data: productCategory, isLoading: isLoadingProductCategory } = useSWR(
productCategoryId, productCategoryId,
(id: number) => ProductCategoryApi.getSingle(id) (id: number) => ProductCategoryApi.getSingle(id)
); );
if (!productCategoryId) { if (!productCategoryId) {
router.back(); router.back();
return (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
);
}
if (!isLoadingProductCategory && (!productCategory || isResponseError(productCategory))) {
router.replace('/404');
return;
}
return ( return (
<div className='w-full p-4 flex flex-row justify-center'> <div className='w-full flex flex-row justify-center items-center p-4'>
{isLoadingProductCategory && <span className='loading loading-spinner loading-xl' />} <span className='loading loading-spinner loading-xl' />
{!isLoadingProductCategory && isResponseSuccess(productCategory) && ( </div>
<ProductCategoryForm type='edit' initialValues={productCategory.data} />
)}
</div>
); );
} }
export default ProductCategoryEdit; if (
!isLoadingProductCategory &&
(!productCategory || isResponseError(productCategory))
) {
router.replace('/404');
return;
}
return (
<div className='w-full p-4 flex flex-row justify-center'>
{isLoadingProductCategory && (
<span className='loading loading-spinner loading-xl' />
)}
{!isLoadingProductCategory && isResponseSuccess(productCategory) && (
<ProductCategoryForm type='edit' initialValues={productCategory.data} />
)}
</div>
);
};
export default ProductCategoryEdit;
@@ -29,16 +29,24 @@ const ProductCategoryDetail = () => {
); );
} }
if (!isLoadingProductCategory && (!productCategory || isResponseError(productCategory))) { if (
!isLoadingProductCategory &&
(!productCategory || isResponseError(productCategory))
) {
router.replace('/404'); router.replace('/404');
return; return;
} }
return ( return (
<div className='w-full p-4 flex flex-row justify-center'> <div className='w-full p-4 flex flex-row justify-center'>
{isLoadingProductCategory && <span className='loading loading-spinner loading-xl' />} {isLoadingProductCategory && (
<span className='loading loading-spinner loading-xl' />
)}
{!isLoadingProductCategory && isResponseSuccess(productCategory) && ( {!isLoadingProductCategory && isResponseSuccess(productCategory) && (
<ProductCategoryForm type='detail' initialValues={productCategory.data} /> <ProductCategoryForm
type='detail'
initialValues={productCategory.data}
/>
)} )}
</div> </div>
); );
@@ -1,11 +1,11 @@
import ProductCategoryTable from "@/components/pages/master-data/product-category/ProductCategoryTable"; import ProductCategoryTable from '@/components/pages/master-data/product-category/ProductCategoryTable';
const ProductCategory = () => { const ProductCategory = () => {
return ( return (
<section className="w-full p-4"> <section className='w-full p-4'>
<ProductCategoryTable /> <ProductCategoryTable />
</section> </section>
); );
}; };
export default ProductCategory; export default ProductCategory;
+2 -2
View File
@@ -2,10 +2,10 @@ import ProductForm from '@/components/pages/master-data/product/form/ProductForm
const AddProduct = () => { const AddProduct = () => {
return ( return (
<div className="w-full p-4 flex flex-row justify-center"> <div className='w-full p-4 flex flex-row justify-center'>
<ProductForm /> <ProductForm />
</div> </div>
); );
}; };
export default AddProduct; export default AddProduct;
@@ -13,9 +13,8 @@ const ProductEdit = () => {
const productId = searchParams.get('productId'); const productId = searchParams.get('productId');
const { data: product, isLoading } = useSWR( const { data: product, isLoading } = useSWR(productId, (id: number) =>
productId, ProductApi.getSingle(id)
(id: number) => ProductApi.getSingle(id)
); );
if (!productId) { if (!productId) {
@@ -42,4 +41,4 @@ const ProductEdit = () => {
); );
}; };
export default ProductEdit; export default ProductEdit;
+3 -4
View File
@@ -13,9 +13,8 @@ const ProductDetail = () => {
const productId = searchParams.get('productId'); const productId = searchParams.get('productId');
const { data: product, isLoading } = useSWR( const { data: product, isLoading } = useSWR(productId, (id: number) =>
productId, ProductApi.getSingle(id)
(id: number) => ProductApi.getSingle(id)
); );
if (!productId) { if (!productId) {
@@ -42,4 +41,4 @@ const ProductDetail = () => {
); );
}; };
export default ProductDetail; export default ProductDetail;
+4 -4
View File
@@ -1,11 +1,11 @@
import ProductsTable from "@/components/pages/master-data/product/ProductTable"; import ProductsTable from '@/components/pages/master-data/product/ProductTable';
const Product = () => { const Product = () => {
return ( return (
<section className="w-full p-4"> <section className='w-full p-4'>
<ProductsTable /> <ProductsTable />
</section> </section>
); );
}; };
export default Product; export default Product;
+1 -1
View File
@@ -8,4 +8,4 @@ const AddSupplier = () => {
); );
}; };
export default AddSupplier; export default AddSupplier;
+1 -1
View File
@@ -46,4 +46,4 @@ const SupplierDetail = () => {
); );
}; };
export default SupplierDetail; export default SupplierDetail;
+1 -1
View File
@@ -1,4 +1,4 @@
import SuppliersTable from "@/components/pages/master-data/supplier/SupplierTable"; import SuppliersTable from '@/components/pages/master-data/supplier/SupplierTable';
const Supplier = () => { const Supplier = () => {
return ( return (
+6 -6
View File
@@ -1,11 +1,11 @@
import SuspenseHelper from "@/components/helper/SuspenseHelper" import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({ const Layout = ({
children children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode children: React.ReactNode;
}>) => { }>) => {
return <SuspenseHelper>{children}</SuspenseHelper> return <SuspenseHelper>{children}</SuspenseHelper>;
} };
export default Layout; export default Layout;
+6 -6
View File
@@ -1,11 +1,11 @@
import SuspenseHelper from "@/components/helper/SuspenseHelper" import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({ const Layout = ({
children children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode children: React.ReactNode;
}>) => { }>) => {
return <SuspenseHelper>{children}</SuspenseHelper> return <SuspenseHelper>{children}</SuspenseHelper>;
} };
export default Layout; export default Layout;
+5 -5
View File
@@ -20,7 +20,7 @@ import useSWR from 'swr';
/** /**
* TODO: Refactor code - pindahin detail ke reuseable component * TODO: Refactor code - pindahin detail ke reuseable component
* setelah implement approval and reject * setelah implement approval and reject
*/ */
const DetailChickin = () => { const DetailChickin = () => {
@@ -43,9 +43,8 @@ const DetailChickin = () => {
// chickin.data?.approval.step_number == 1 ? false : true // chickin.data?.approval.step_number == 1 ? false : true
true true
); );
const [isRejectedDisabled, setIsRejectedDisabled] = useState( const [isRejectedDisabled, setIsRejectedDisabled] =
!isApprovedDisabled useState(!isApprovedDisabled);
);
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>( const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
!isApprovedDisabled ? 'APPROVED' : 'REJECTED' !isApprovedDisabled ? 'APPROVED' : 'REJECTED'
); );
@@ -264,7 +263,8 @@ const DetailChickin = () => {
<Icon icon='mdi:times' width={24} height={24} /> <Icon icon='mdi:times' width={24} height={24} />
Delete Delete
</Button> </Button>
<Button color='warning' <Button
color='warning'
onClick={() => { onClick={() => {
chickinModal.openModal(); chickinModal.openModal();
}} }}
+5 -5
View File
@@ -1,10 +1,10 @@
import ChickinTable from "@/components/pages/production/chickin/ChickinTable"; import ChickinTable from '@/components/pages/production/chickin/ChickinTable';
const Chickin = () => { const Chickin = () => {
return ( return (
<section className="w-full p-4"> <section className='w-full p-4'>
<ChickinTable/> <ChickinTable />
</section> </section>
); );
} };
export default Chickin; export default Chickin;
@@ -1,13 +1,13 @@
'use client' 'use client';
import ProjectFlockForm from "@/components/pages/production/project-flock/form/ProjectFlockForm"; import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
const AddProjectFlock = () => { const AddProjectFlock = () => {
return ( return (
<section className="w-full p-4 flex flex-row justify-center"> <section className='w-full p-4 flex flex-row justify-center'>
<ProjectFlockForm formType="add"/> <ProjectFlockForm formType='add' />
</section> </section>
); );
} };
export default AddProjectFlock; export default AddProjectFlock;
@@ -1,46 +1,47 @@
'use client' 'use client';
import ProjectFlockForm from '@/components/pages/production/project-flock/form/ProjectFlockForm';
import ProjectFlockForm from "@/components/pages/production/project-flock/form/ProjectFlockForm"; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { isResponseError, isResponseSuccess } from "@/lib/api-helper"; import { ProjectFlockApi } from '@/services/api/production';
import { ProjectFlockApi } from "@/services/api/production"; import { useRouter, useSearchParams } from 'next/navigation';
import { useRouter, useSearchParams } from "next/navigation"; import useSWR from 'swr';
import useSWR from "swr";
const ProjectFlockEdit = () => { const ProjectFlockEdit = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const projectFlockId = searchParams.get("projectFlockId"); const projectFlockId = searchParams.get('projectFlockId');
const { data: projectFlock, isLoading: isLoadingCostumer } = useSWR( const { data: projectFlock, isLoading: isLoadingCostumer } = useSWR(
projectFlockId, projectFlockId,
(id: number) => ProjectFlockApi.getSingle(id) (id: number) => ProjectFlockApi.getSingle(id)
); );
if(!projectFlockId){ if (!projectFlockId) {
router.back(); router.back();
return ( return (
<div className="w-full flex flex-row justify-center items-center p-4"> <div className='w-full flex flex-row justify-center items-center p-4'>
<span className="loading loading-spinner loading-xl" /> <span className='loading loading-spinner loading-xl' />
</div> </div>
); );
} }
if(!isLoadingCostumer && (!projectFlock || isResponseError(projectFlock))){ if (!isLoadingCostumer && (!projectFlock || isResponseError(projectFlock))) {
router.replace("/404"); router.replace('/404');
return; return;
} }
return ( return (
<div className="w-full p-4 flex flex-row justify-center"> <div className='w-full p-4 flex flex-row justify-center'>
{isLoadingCostumer && <span className="loading loading-spinner loading-xl" />} {isLoadingCostumer && (
<span className='loading loading-spinner loading-xl' />
)}
{!isLoadingCostumer && isResponseSuccess(projectFlock) && ( {!isLoadingCostumer && isResponseSuccess(projectFlock) && (
<ProjectFlockForm formType="edit" initialValues={projectFlock.data} /> <ProjectFlockForm formType='edit' initialValues={projectFlock.data} />
)} )}
</div> </div>
) );
} };
export default ProjectFlockEdit; export default ProjectFlockEdit;
@@ -1,11 +1,11 @@
import SuspenseHelper from "@/components/helper/SuspenseHelper" import SuspenseHelper from '@/components/helper/SuspenseHelper';
const Layout = ({ const Layout = ({
children children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode children: React.ReactNode;
}>) => { }>) => {
return <SuspenseHelper>{children}</SuspenseHelper> return <SuspenseHelper>{children}</SuspenseHelper>;
} };
export default Layout; export default Layout;
@@ -42,7 +42,11 @@ const ProjectFlockDetail = () => {
<span className='loading loading-spinner loading-xl' /> <span className='loading loading-spinner loading-xl' />
)} )}
{!isLoadingProjectFlock && isResponseSuccess(projectFlock) && ( {!isLoadingProjectFlock && isResponseSuccess(projectFlock) && (
<ProjectFlockForm formType='detail' initialValues={projectFlock.data} refreshProjectFlocks={refreshProjectFlock} /> <ProjectFlockForm
formType='detail'
initialValues={projectFlock.data}
refreshProjectFlocks={refreshProjectFlock}
/>
)} )}
</div> </div>
); );
+4 -4
View File
@@ -1,11 +1,11 @@
import ProjectFlockTable from "@/components/pages/production/project-flock/ProjectFlockTable"; import ProjectFlockTable from '@/components/pages/production/project-flock/ProjectFlockTable';
const ProjectFlock = () => { const ProjectFlock = () => {
return ( return (
<section className="w-full p-4"> <section className='w-full p-4'>
<ProjectFlockTable/> <ProjectFlockTable />
</section> </section>
); );
} };
export default ProjectFlock; export default ProjectFlock;
+13 -15
View File
@@ -1,13 +1,11 @@
'use client'; 'use client';
import { import { HTMLAttributes, ReactNode } from 'react';
HTMLAttributes,
ReactNode,
} from 'react';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
export interface CardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> { export interface CardProps
extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
title?: string; title?: string;
subtitle?: string; subtitle?: string;
image?: string; image?: string;
@@ -44,17 +42,17 @@ const Card = ({
const baseClasses = 'card bg-base-100'; const baseClasses = 'card bg-base-100';
const variantClasses = { const variantClasses = {
'default': '', default: '',
'compact': 'card-compact', compact: 'card-compact',
'bordered': 'border border-base-300', bordered: 'border border-base-300',
'shadow': 'shadow-xl', shadow: 'shadow-xl',
'image-full': 'card-side card-compact shadow-xl', 'image-full': 'card-side card-compact shadow-xl',
}; };
const sizeClasses = { const sizeClasses = {
'sm': 'w-64', sm: 'w-64',
'md': 'w-96', md: 'w-96',
'lg': 'w-[28rem]', lg: 'w-[28rem]',
}; };
return cn( return cn(
@@ -84,9 +82,9 @@ const Card = ({
const getTitleClasses = () => { const getTitleClasses = () => {
const sizeClasses = { const sizeClasses = {
'sm': 'text-lg', sm: 'text-lg',
'md': 'text-xl', md: 'text-xl',
'lg': 'text-2xl', lg: 'text-2xl',
}; };
return cn('card-title font-bold', sizeClasses[size], className?.title); return cn('card-title font-bold', sizeClasses[size], className?.title);
+10 -10
View File
@@ -185,17 +185,17 @@ const Pagination = ({
currentPage <= 2 currentPage <= 2
? currentPage + 2 ? currentPage + 2
: currentPage === totalPages - 2 : currentPage === totalPages - 2
? 3 ? 3
: currentPage >= totalPages - 1 : currentPage >= totalPages - 1
? 4 ? 4
: 1 : 1
} }
endPage={ endPage={
currentPage <= 2 || currentPage >= totalPages - 1 currentPage <= 2 || currentPage >= totalPages - 1
? totalPages - 3 ? totalPages - 3
: currentPage === totalPages - 2 : currentPage === totalPages - 2
? totalPages - 4 ? totalPages - 4
: 2 : 2
} }
onPageItemClick={pageChangeHandler} onPageItemClick={pageChangeHandler}
/> />
@@ -242,15 +242,15 @@ const Pagination = ({
currentPage <= 3 currentPage <= 3
? currentPage + 2 ? currentPage + 2
: currentPage >= 4 : currentPage >= 4
? currentPage + 2 ? currentPage + 2
: 1 : 1
} }
endPage={ endPage={
currentPage <= 3 currentPage <= 3
? totalPages - 2 ? totalPages - 2
: currentPage >= 4 : currentPage >= 4
? totalPages - 1 ? totalPages - 1
: 0 : 0
} }
onPageItemClick={pageChangeHandler} onPageItemClick={pageChangeHandler}
/> />
+2 -9
View File
@@ -1,10 +1,6 @@
'use client'; 'use client';
import { import { ChangeEventHandler, FocusEventHandler, ReactNode } from 'react';
ChangeEventHandler,
FocusEventHandler,
ReactNode,
} from 'react';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
@@ -109,10 +105,7 @@ const DateInput = ({
min={min} min={min}
max={max} max={max}
disabled={disabled} disabled={disabled}
className={cn( className={cn('grow bg-transparent cursor-pointer', className?.input)}
'grow bg-transparent cursor-pointer',
className?.input
)}
readOnly={readOnly} readOnly={readOnly}
/> />
+1 -4
View File
@@ -69,10 +69,7 @@ const FileInput = ({
onChange={onChange} onChange={onChange}
onBlur={onBlur} onBlur={onBlur}
disabled={disabled} disabled={disabled}
className={cn( className={cn('grow file-input w-full h-12 rounded', className?.input)}
'grow file-input w-full h-12 rounded',
className?.input
)}
readOnly={readOnly} readOnly={readOnly}
/> />
+8 -8
View File
@@ -1,10 +1,10 @@
"use client"; 'use client';
import { ChangeEvent, ReactNode } from "react"; import { ChangeEvent, ReactNode } from 'react';
import { NumericFormat, OnValueChange } from "react-number-format"; import { NumericFormat, OnValueChange } from 'react-number-format';
import TextInput, { TextInputProps } from "@/components/input/TextInput"; import TextInput, { TextInputProps } from '@/components/input/TextInput';
interface NumberInputProps extends Omit<TextInputProps, "type"> { interface NumberInputProps extends Omit<TextInputProps, 'type'> {
thousandSeparator?: string; thousandSeparator?: string;
decimalSeparator?: string; decimalSeparator?: string;
decimalScale?: number; decimalScale?: number;
@@ -17,8 +17,8 @@ interface NumberInputProps extends Omit<TextInputProps, "type"> {
} }
const NumberInput = ({ const NumberInput = ({
thousandSeparator = ",", thousandSeparator = ',',
decimalSeparator = ".", decimalSeparator = '.',
decimalScale = 5, decimalScale = 5,
allowNegative = true, allowNegative = true,
onChange, onChange,
@@ -28,7 +28,7 @@ const NumberInput = ({
}: NumberInputProps) => { }: NumberInputProps) => {
const valueChangeHandler: OnValueChange = ( const valueChangeHandler: OnValueChange = (
numberFormatValues, numberFormatValues,
sourceInfo, sourceInfo
) => { ) => {
const newChangeEvent = sourceInfo.event as const newChangeEvent = sourceInfo.event as
| ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLInputElement>
+6 -2
View File
@@ -49,14 +49,18 @@ const MenuItem = ({
); );
return ( return (
<li onClick={onClick}> <li>
{href && ( {href && (
<Link href={href} className={menuItemBaseClassName}> <Link href={href} className={menuItemBaseClassName}>
{menuItemContent} {menuItemContent}
</Link> </Link>
)} )}
{!href && <a className={menuItemBaseClassName}>{menuItemContent}</a>} {!href && (
<button className={menuItemBaseClassName} onClick={onClick}>
{menuItemContent}
</button>
)}
</li> </li>
); );
}; };
+10 -10
View File
@@ -31,21 +31,21 @@ const ApprovalSteps = ({ approvals }: ApprovalStepsProps) => {
approval.status === 'APPROVED' approval.status === 'APPROVED'
? 'success' ? 'success'
: approval.status === 'REJECTED' : approval.status === 'REJECTED'
? 'error' ? 'error'
: approval.status === 'WAITING' : approval.status === 'WAITING'
? 'warning' ? 'warning'
: undefined; : undefined;
const stepItemIcon = const stepItemIcon =
approval.status === 'APPROVED' approval.status === 'APPROVED'
? 'material-symbols:check-rounded' ? 'material-symbols:check-rounded'
: approval.status === 'REJECTED' : approval.status === 'REJECTED'
? 'material-symbols:close-rounded' ? 'material-symbols:close-rounded'
: approval.status === 'WAITING' : approval.status === 'WAITING'
? 'pajamas:dash-circle' ? 'pajamas:dash-circle'
: approval.logs && approval.logs.length > 0 : approval.logs && approval.logs.length > 0
? 'material-symbols:info-outline-rounded' ? 'material-symbols:info-outline-rounded'
: 'bxs:hourglass'; : 'bxs:hourglass';
return ( return (
<StepItem <StepItem
@@ -10,11 +10,7 @@ import { inventoryAdjustmentApi } from '@/services/api/inventory';
import { useTableFilter } from '@/services/hooks/useTableFilter'; import { useTableFilter } from '@/services/hooks/useTableFilter';
import { InventoryAdjustment } from '@/types/api/inventory/adjustment'; import { InventoryAdjustment } from '@/types/api/inventory/adjustment';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { import { ColumnDef, ColumnSort, SortingState } from '@tanstack/react-table';
ColumnDef,
ColumnSort,
SortingState,
} from '@tanstack/react-table';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
@@ -44,10 +40,7 @@ const InventoryAdjustmentTable = () => {
}); });
// Fetch Data // Fetch Data
const { const { data: inventoryAdjustments, isLoading } = useSWR(
data: inventoryAdjustments,
isLoading,
} = useSWR(
`${inventoryAdjustmentApi.basePath}${getTableFilterQueryString()}`, `${inventoryAdjustmentApi.basePath}${getTableFilterQueryString()}`,
inventoryAdjustmentApi.getAllFetcher inventoryAdjustmentApi.getAllFetcher
); );
@@ -113,8 +106,8 @@ const InventoryAdjustmentTable = () => {
type === 'INCREASE' type === 'INCREASE'
? 'Peningkatan' ? 'Peningkatan'
: type === 'DECREASE' : type === 'DECREASE'
? 'Penurunan' ? 'Penurunan'
: '-'; : '-';
return ( return (
<div <div
@@ -187,8 +180,13 @@ const InventoryAdjustmentTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/inventory/adjustment/add' color='primary'> <Button
href='/inventory/adjustment/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Tambah
</Button> </Button>
@@ -211,7 +209,7 @@ const InventoryAdjustmentTable = () => {
value: tableFilterState.pageSize, value: tableFilterState.pageSize,
}} }}
onChange={pageSizeChangeHandler} onChange={pageSizeChangeHandler}
className={{ wrapper: 'max-w-28' }} className={{ wrapper: 'min-w-28' }}
/> />
</div> </div>
</div> </div>
@@ -4,24 +4,21 @@ export const InventoryAdjustmentFormSchema = Yup.object({
product_category: Yup.object({ product_category: Yup.object({
value: Yup.number().required('ID Kategori Produk wajib diisi!'), value: Yup.number().required('ID Kategori Produk wajib diisi!'),
label: Yup.string().required('Nama Kategori Produk wajib diisi!'), label: Yup.string().required('Nama Kategori Produk wajib diisi!'),
}) }).nullable(),
.nullable(),
product_category_id: Yup.number().nullable(), product_category_id: Yup.number().nullable(),
product: Yup.object({ product: Yup.object({
value: Yup.number().required('ID Produk wajib diisi!'), value: Yup.number().required('ID Produk wajib diisi!'),
label: Yup.string().required('Nama Produk wajib diisi!'), label: Yup.string().required('Nama Produk wajib diisi!'),
}) }).nullable(),
.nullable(),
product_id: Yup.number().nullable(), product_id: Yup.number().nullable(),
warehouse: Yup.object({ warehouse: Yup.object({
value: Yup.number().required('ID Gudang wajib diisi!'), value: Yup.number().required('ID Gudang wajib diisi!'),
label: Yup.string().required('Nama Gudang wajib diisi!'), label: Yup.string().required('Nama Gudang wajib diisi!'),
}) }).nullable(),
.nullable(),
warehouse_id: Yup.number().nullable(), warehouse_id: Yup.number().nullable(),
@@ -51,9 +51,8 @@ const InventoryAdjustmentForm = ({
// Submit Handler // Submit Handler
const createInventoryAdjustmentHandler = useCallback( const createInventoryAdjustmentHandler = useCallback(
async (payload: CreateInventoryAdjustmentPayload) => { async (payload: CreateInventoryAdjustmentPayload) => {
const createInventoryAdjustmentRes = await inventoryAdjustmentApi.create( const createInventoryAdjustmentRes =
payload await inventoryAdjustmentApi.create(payload);
);
if (isResponseError(createInventoryAdjustmentRes)) { if (isResponseError(createInventoryAdjustmentRes)) {
setInventoryAdjustmentFormErrorMessage( setInventoryAdjustmentFormErrorMessage(
@@ -68,7 +67,9 @@ const InventoryAdjustmentForm = ({
[router] [router]
); );
const formikInitialValues = useMemo<Partial<InventoryAdjustmentFormValues>>(() => { const formikInitialValues = useMemo<
Partial<InventoryAdjustmentFormValues>
>(() => {
return { return {
product_category_id: initialValues?.product_category?.id ?? 0, product_category_id: initialValues?.product_category?.id ?? 0,
product_id: initialValues?.product?.id ?? 0, product_id: initialValues?.product?.id ?? 0,
@@ -185,7 +186,6 @@ const InventoryAdjustmentForm = ({
warehouseChangeHandler(null); warehouseChangeHandler(null);
}; };
const { setValues: formikSetValues } = formik; const { setValues: formikSetValues } = formik;
// Effect // Effect
@@ -225,7 +225,13 @@ const InventoryAdjustmentForm = ({
const type = initialValues.transaction_type.toLowerCase(); const type = initialValues.transaction_type.toLowerCase();
setQuantityLabel(type === 'increase' ? 'Tambah Stok' : 'Kurangi Stok'); setQuantityLabel(type === 'increase' ? 'Tambah Stok' : 'Kurangi Stok');
} }
}, [formik, initialValues, setQuantityLabel, setDisabledProduct, setSelectedProductCategories]); }, [
formik,
initialValues,
setQuantityLabel,
setDisabledProduct,
setSelectedProductCategories,
]);
useEffect(() => { useEffect(() => {
formikSetValues(formikInitialValues as InventoryAdjustmentFormValues); formikSetValues(formikInitialValues as InventoryAdjustmentFormValues);
}, [formikSetValues, formikInitialValues]); }, [formikSetValues, formikInitialValues]);
@@ -364,15 +370,19 @@ const InventoryAdjustmentForm = ({
errorMessage={formik.errors.transaction_type as string} errorMessage={formik.errors.transaction_type as string}
variant='radio-primary' variant='radio-primary'
required required
bottomLabel={formik.values.transaction_type == undefined ? 'Pilih salah satu tipe transaksi' : undefined} bottomLabel={
formik.values.transaction_type == undefined
? 'Pilih salah satu tipe transaksi'
: undefined
}
disabled={type === 'detail'} disabled={type === 'detail'}
/> />
{/* Number Input Stock */} {/* Number Input Stock */}
<TextInput <TextInput
className={{ className={{
wrapper: `${formik.values.transaction_type != undefined ? '' : 'hidden'}`, wrapper: `${formik.values.transaction_type != undefined ? '' : 'hidden'}`,
}} }}
required required
label={quantityLabel} label={quantityLabel}
name='quantity' name='quantity'
@@ -395,8 +405,6 @@ const InventoryAdjustmentForm = ({
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
{/* Text Area Input Reason */} {/* Text Area Input Reason */}
<TextArea <TextArea
required required
@@ -413,14 +421,23 @@ const InventoryAdjustmentForm = ({
<div className='flex flex-row justify-between gap-2 flex-wrap'> <div className='flex flex-row justify-between gap-2 flex-wrap'>
{type !== 'detail' && ( {type !== 'detail' && (
<div className='flex flex-row justify-end gap-2'> <div className='flex flex-row justify-end gap-2'>
<Button type='button' color='warning' className='px-4' onClick={resetHandler}> <Button
type='button'
color='warning'
className='px-4'
onClick={resetHandler}
>
Reset Reset
</Button> </Button>
<Button <Button
type='submit' type='submit'
color='primary' color='primary'
isLoading={formik.isSubmitting} isLoading={formik.isSubmitting}
disabled={!formik.isValid || formik.isSubmitting || formik.values.product == undefined} disabled={
!formik.isValid ||
formik.isSubmitting ||
formik.values.product == undefined
}
className='px-4' className='px-4'
> >
Submit Submit
@@ -77,7 +77,7 @@ const MovementTable = () => {
<TableToolbar <TableToolbar
addButton={{ addButton={{
href: '/inventory/movement/add', href: '/inventory/movement/add',
label: 'Tambah Movement', label: 'Tambah',
}} }}
search={{ search={{
value: tableFilterState.search, value: tableFilterState.search,
@@ -14,6 +14,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Area } from '@/types/api/master-data/area'; import { Area } from '@/types/api/master-data/area';
import { AreaApi } from '@/services/api/master-data'; import { AreaApi } from '@/services/api/master-data';
@@ -32,16 +33,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/area/detail/?areaId=${props.row.original.id}`} href={`/master-data/area/detail/?areaId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -76,7 +68,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -150,7 +142,7 @@ const AreasTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -199,10 +191,15 @@ const AreasTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/area/add' color='primary'> <Button
href='/master-data/area/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Area Tambah
</Button> </Button>
</div> </div>
@@ -14,6 +14,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Bank } from '@/types/api/master-data/bank'; import { Bank } from '@/types/api/master-data/bank';
import { BankApi } from '@/services/api/master-data'; import { BankApi } from '@/services/api/master-data';
@@ -32,16 +33,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/bank/detail/?bankId=${props.row.original.id}`} href={`/master-data/bank/detail/?bankId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -66,7 +58,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -76,7 +68,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -163,7 +155,7 @@ const BanksTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -212,10 +204,15 @@ const BanksTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/bank/add' color='primary'> <Button
href='/master-data/bank/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Bank Tambah
</Button> </Button>
</div> </div>
@@ -8,6 +8,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import Table from '@/components/Table'; import Table from '@/components/Table';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { ROWS_OPTIONS } from '@/config/constant'; import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseSuccess } from '@/lib/api-helper'; import { isResponseSuccess } from '@/lib/api-helper';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
@@ -15,10 +16,7 @@ import { CustomerApi } from '@/services/api/master-data';
import { useTableFilter } from '@/services/hooks/useTableFilter'; import { useTableFilter } from '@/services/hooks/useTableFilter';
import { Customer } from '@/types/api/master-data/customer'; import { Customer } from '@/types/api/master-data/customer';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { import { CellContext, ColumnDef } from '@tanstack/react-table';
CellContext,
ColumnDef,
} from '@tanstack/react-table';
import { useState } from 'react'; import { useState } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import useSWR from 'swr'; import useSWR from 'swr';
@@ -33,16 +31,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type == 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/customer/detail/?customerId=${props.row.original.id}`} href={`/master-data/customer/detail/?customerId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -53,10 +42,10 @@ const RowOptionsMenu = ({
Detail Detail
</Button> </Button>
<Button <Button
className='justify-start text-sm'
href={`/master-data/customer/detail/edit/?customerId=${props.row.original.id}`} href={`/master-data/customer/detail/edit/?customerId=${props.row.original.id}`}
variant='ghost' variant='ghost'
color='warning' color='warning'
className='justify-start text-sm'
> >
<Icon icon='material-symbols:edit-outline' width={16} height={16} /> <Icon icon='material-symbols:edit-outline' width={16} height={16} />
Edit Edit
@@ -65,7 +54,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -75,7 +64,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -174,7 +163,7 @@ const CustomersTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -210,10 +199,15 @@ const CustomersTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/customer/add' color='primary'> <Button
href='/master-data/customer/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Customer Tambah
</Button> </Button>
</div> </div>
@@ -285,4 +279,4 @@ const CustomersTable = () => {
); );
}; };
export default CustomersTable; export default CustomersTable;
@@ -11,7 +11,11 @@ import {
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { CustomerFormSchema, CustomerFormValues, UpdateCustomerFormSchema } from '@/components/pages/master-data/customer/form/CustomerForm.schema'; import {
CustomerFormSchema,
CustomerFormValues,
UpdateCustomerFormSchema,
} from '@/components/pages/master-data/customer/form/CustomerForm.schema';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import Button from '@/components/Button'; import Button from '@/components/Button';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
@@ -150,7 +154,8 @@ const CustomerForm = ({
const formik = useFormik<CustomerFormValues>({ const formik = useFormik<CustomerFormValues>({
initialValues: formikInitialValues, initialValues: formikInitialValues,
enableReinitialize: true, enableReinitialize: true,
validationSchema: formType === 'edit' ? UpdateCustomerFormSchema : CustomerFormSchema, validationSchema:
formType === 'edit' ? UpdateCustomerFormSchema : CustomerFormSchema,
onSubmit: async (values) => { onSubmit: async (values) => {
// reset error message // reset error message
setCustomerFormErrorMessage(''); setCustomerFormErrorMessage('');
@@ -14,6 +14,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Fcr } from '@/types/api/master-data/fcr'; import { Fcr } from '@/types/api/master-data/fcr';
import { FcrApi } from '@/services/api/master-data'; import { FcrApi } from '@/services/api/master-data';
@@ -32,16 +33,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/fcr/detail/?fcrId=${props.row.original.id}`} href={`/master-data/fcr/detail/?fcrId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -66,7 +58,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -76,7 +68,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -150,7 +142,7 @@ const FcrsTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -199,10 +191,15 @@ const FcrsTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/fcr/add' color='primary'> <Button
href='/master-data/fcr/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah FCR Tambah
</Button> </Button>
</div> </div>
@@ -12,6 +12,7 @@ import { FlockApi } from '@/services/api/master-data';
import { useModal } from '@/components/Modal'; import { useModal } from '@/components/Modal';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import DebouncedTextInput from '@/components/input/DebouncedTextInput';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
@@ -30,16 +31,7 @@ const RowsOptions = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type == 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/flock/detail/edit/?flockId=${props.row.original.id}`} href={`/master-data/flock/detail/edit/?flockId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -54,7 +46,7 @@ const RowsOptions = ({
/> />
Edit Edit
</Button> </Button>
<Button <Button
href={`/master-data/flock/detail/?flockId=${props.row.original.id}`} href={`/master-data/flock/detail/?flockId=${props.row.original.id}`}
variant='ghost' variant='ghost'
color='primary' color='primary'
@@ -72,7 +64,7 @@ const RowsOptions = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -82,7 +74,7 @@ const RowsOptions = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -203,9 +195,15 @@ const FlockTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/flock/add' color='primary'> <Button
Tambah Flock href='/master-data/flock/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} />
Tambah
</Button> </Button>
</div> </div>
@@ -275,4 +273,4 @@ const FlockTable = () => {
); );
}; };
export default FlockTable; export default FlockTable;
@@ -3,10 +3,7 @@ import * as Yup from 'yup';
export const FlockFormSchema = Yup.object({ export const FlockFormSchema = Yup.object({
name: Yup.string() name: Yup.string()
.required('Nama wajib diisi!') .required('Nama wajib diisi!')
.matches( .matches(/^[\p{L}\p{N}\s]+$/u, 'Nama tidak boleh mengandung simbol'),
/^[\p{L}\p{N}\s]+$/u,
'Nama tidak boleh mengandung simbol'
),
}); });
export const UpdateFlockFormSchema = FlockFormSchema; export const UpdateFlockFormSchema = FlockFormSchema;
@@ -1,11 +1,15 @@
'use client' 'use client';
import { useModal } from '@/components/Modal'; import { useModal } from '@/components/Modal';
import { FlockApi } from '@/services/api/master-data'; import { FlockApi } from '@/services/api/master-data';
import { Flock } from '@/types/api/master-data/flock'; import { Flock } from '@/types/api/master-data/flock';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { FlockFormSchema, FlockFormValues, UpdateFlockFormSchema } from '@/components/pages/master-data/flock/form/FlockForm.schema'; import {
FlockFormSchema,
FlockFormValues,
UpdateFlockFormSchema,
} from '@/components/pages/master-data/flock/form/FlockForm.schema';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import Button from '@/components/Button'; import Button from '@/components/Button';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
@@ -48,7 +52,8 @@ const FlockForm = ({ formType = 'add', initialValues }: FlockCustomProps) => {
const formik = useFormik<FlockFormValues>({ const formik = useFormik<FlockFormValues>({
initialValues: formikInitialValue, initialValues: formikInitialValue,
enableReinitialize: true, enableReinitialize: true,
validationSchema: formType === 'edit' ? UpdateFlockFormSchema : FlockFormSchema, validationSchema:
formType === 'edit' ? UpdateFlockFormSchema : FlockFormSchema,
onSubmit: async (values) => { onSubmit: async (values) => {
// reset error message // reset error message
setFlockFormErrorMessage(''); setFlockFormErrorMessage('');
@@ -19,6 +19,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Kandang } from '@/types/api/master-data/kandang'; import { Kandang } from '@/types/api/master-data/kandang';
import { KandangApi } from '@/services/api/master-data'; import { KandangApi } from '@/services/api/master-data';
@@ -37,16 +38,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/kandang/detail/?kandangId=${props.row.original.id}`} href={`/master-data/kandang/detail/?kandangId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -71,7 +63,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -81,7 +73,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -173,7 +165,7 @@ const KandangsTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -238,10 +230,15 @@ const KandangsTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/kandang/add' color='primary'> <Button
href='/master-data/kandang/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Kandang Tambah
</Button> </Button>
</div> </div>
@@ -19,6 +19,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Location } from '@/types/api/master-data/location'; import { Location } from '@/types/api/master-data/location';
import { LocationApi } from '@/services/api/master-data'; import { LocationApi } from '@/services/api/master-data';
@@ -37,16 +38,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/location/detail/?locationId=${props.row.original.id}`} href={`/master-data/location/detail/?locationId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -71,7 +63,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -81,7 +73,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -172,7 +164,7 @@ const LocationsTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -237,10 +229,15 @@ const LocationsTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/location/add' color='primary'> <Button
href='/master-data/location/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Location Tambah
</Button> </Button>
</div> </div>
@@ -19,6 +19,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Nonstock } from '@/types/api/master-data/nonstock'; import { Nonstock } from '@/types/api/master-data/nonstock';
import { NonstockApi } from '@/services/api/master-data'; import { NonstockApi } from '@/services/api/master-data';
@@ -37,16 +38,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/nonstock/detail/?nonstockId=${props.row.original.id}`} href={`/master-data/nonstock/detail/?nonstockId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -71,7 +63,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -81,7 +73,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -184,7 +176,7 @@ const NonstocksTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -249,10 +241,15 @@ const NonstocksTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/nonstock/add' color='primary'> <Button
href='/master-data/nonstock/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Nonstock Tambah
</Button> </Button>
</div> </div>
@@ -14,6 +14,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { ProductCategory } from '@/types/api/master-data/product-category'; import { ProductCategory } from '@/types/api/master-data/product-category';
import { ProductCategoryApi } from '@/services/api/master-data'; import { ProductCategoryApi } from '@/services/api/master-data';
@@ -32,16 +33,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/product-category/detail/?productCategoryId=${props.row.original.id}`} href={`/master-data/product-category/detail/?productCategoryId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -64,7 +56,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='mdi:delete-outline' icon='mdi:delete-outline'
@@ -74,7 +66,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -154,7 +146,7 @@ const ProductCategoryTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -200,10 +192,15 @@ const ProductCategoryTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/product-category/add' color='primary'> <Button
href='/master-data/product-category/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Product Category Tambah
</Button> </Button>
</div> </div>
<DebouncedTextInput <DebouncedTextInput
@@ -1,10 +1,14 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
export const ProductCategoryFormSchema = Yup.object({ export const ProductCategoryFormSchema = Yup.object({
code: Yup.string().required('Kode wajib diisi!').max(3, 'Kode kategori produk melebihi 3 karakter!'), code: Yup.string()
.required('Kode wajib diisi!')
.max(3, 'Kode kategori produk melebihi 3 karakter!'),
name: Yup.string().required('Nama wajib diisi!'), name: Yup.string().required('Nama wajib diisi!'),
}); });
export const UpdateProductCategoryFormSchema = ProductCategoryFormSchema; export const UpdateProductCategoryFormSchema = ProductCategoryFormSchema;
export type ProductCategoryFormValues = Yup.InferType<typeof ProductCategoryFormSchema>; export type ProductCategoryFormValues = Yup.InferType<
typeof ProductCategoryFormSchema
>;
@@ -30,7 +30,10 @@ interface ProductCategoryFormProps {
initialValues?: ProductCategory; initialValues?: ProductCategory;
} }
const ProductCategoryForm = ({ type = 'add', initialValues }: ProductCategoryFormProps) => { const ProductCategoryForm = ({
type = 'add',
initialValues,
}: ProductCategoryFormProps) => {
const router = useRouter(); const router = useRouter();
const deleteModal = useModal(); const deleteModal = useModal();
@@ -77,7 +80,10 @@ const ProductCategoryForm = ({ type = 'add', initialValues }: ProductCategoryFor
const formik = useFormik<ProductCategoryFormValues>({ const formik = useFormik<ProductCategoryFormValues>({
initialValues: formikInitialValues, initialValues: formikInitialValues,
validationSchema: type === 'edit' ? UpdateProductCategoryFormSchema : ProductCategoryFormSchema, validationSchema:
type === 'edit'
? UpdateProductCategoryFormSchema
: ProductCategoryFormSchema,
onSubmit: async (values) => { onSubmit: async (values) => {
setFormErrorMessage(''); setFormErrorMessage('');
@@ -91,7 +97,10 @@ const ProductCategoryForm = ({ type = 'add', initialValues }: ProductCategoryFor
await createProductCategoryHandler(payload); await createProductCategoryHandler(payload);
break; break;
case 'edit': case 'edit':
await updateProductCategoryHandler(initialValues?.id as number, payload); await updateProductCategoryHandler(
initialValues?.id as number,
payload
);
break; break;
} }
}, },
@@ -263,4 +272,4 @@ const ProductCategoryForm = ({ type = 'add', initialValues }: ProductCategoryFor
); );
}; };
export default ProductCategoryForm; export default ProductCategoryForm;
@@ -19,6 +19,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Product } from '@/types/api/master-data/product'; import { Product } from '@/types/api/master-data/product';
import { ProductApi } from '@/services/api/master-data'; import { ProductApi } from '@/services/api/master-data';
@@ -36,16 +37,7 @@ const RowOptionsMenu = ({
props: CellContext<Product, unknown>; props: CellContext<Product, unknown>;
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => ( }) => (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/product/detail/?productId=${props.row.original.id}`} href={`/master-data/product/detail/?productId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -68,7 +60,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -78,7 +70,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
const ProductsTable = () => { const ProductsTable = () => {
@@ -217,7 +209,7 @@ const ProductsTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -280,10 +272,15 @@ const ProductsTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/product/add' color='primary'> <Button
href='/master-data/product/add'
variant='outline'
className='w-full sm:w-fit'
color='primary'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Produk Tambah
</Button> </Button>
</div> </div>
<DebouncedTextInput <DebouncedTextInput
@@ -5,49 +5,50 @@ export const ProductFormSchema = Yup.object({
brand: Yup.string().required('Merek wajib diisi!'), brand: Yup.string().required('Merek wajib diisi!'),
sku: Yup.string().required('SKU wajib diisi!'), sku: Yup.string().required('SKU wajib diisi!'),
uom: Yup.object({ uom: Yup.object({
value: Yup.number().min(1).required(), value: Yup.number().min(1).required(),
label: Yup.string().required(), label: Yup.string().required(),
}).nullable(), }).nullable(),
uom_id: Yup.number().required('Satuan wajib diisi!').typeError('Satuan wajib diisi!'), uom_id: Yup.number()
.required('Satuan wajib diisi!')
.typeError('Satuan wajib diisi!'),
product_category: Yup.object({ product_category: Yup.object({
value: Yup.number().min(1).required(), value: Yup.number().min(1).required(),
label: Yup.string().required(), label: Yup.string().required(),
}).nullable(), }).nullable(),
product_category_id: Yup.number() product_category_id: Yup.number()
.required('Kategori produk wajib diisi!') .required('Kategori produk wajib diisi!')
.typeError('Kategori produk wajib diisi!'), .typeError('Kategori produk wajib diisi!'),
product_price: Yup.number() product_price: Yup.number()
.required('Harga produk wajib diisi!') .required('Harga produk wajib diisi!')
.typeError('Harga produk wajib diisi!') .typeError('Harga produk wajib diisi!')
.min(0, 'Harga produk tidak boleh kurang dari 0!'), .min(0, 'Harga produk tidak boleh kurang dari 0!'),
selling_price: Yup.number() selling_price: Yup.number()
.required('Harga jual wajib diisi!') .required('Harga jual wajib diisi!')
.typeError('Harga jual wajib diisi!') .typeError('Harga jual wajib diisi!')
.min(0, 'Harga jual tidak boleh kurang dari 0!'), .min(0, 'Harga jual tidak boleh kurang dari 0!'),
tax: Yup.number() tax: Yup.number()
.required('Pajak wajib diisi!') .required('Pajak wajib diisi!')
.typeError('Pajak wajib diisi!') .typeError('Pajak wajib diisi!')
.min(0, 'Pajak tidak boleh kurang dari 0!') .min(0, 'Pajak tidak boleh kurang dari 0!')
.max(100, 'Pajak tidak boleh lebih dari 100%!'), .max(100, 'Pajak tidak boleh lebih dari 100%!'),
expiry_period: Yup.number() expiry_period: Yup.number()
.required('Periode kadaluarsa wajib diisi!') .required('Periode kadaluarsa wajib diisi!')
.typeError('Periode kadaluarsa wajib diisi!') .typeError('Periode kadaluarsa wajib diisi!')
.min(0, 'Periode kadaluarsa tidak boleh kurang dari 0!'), .min(0, 'Periode kadaluarsa tidak boleh kurang dari 0!'),
supplier: Yup.object({ supplier: Yup.object({
value: Yup.number().min(1).required(), value: Yup.number().min(1).required(),
label: Yup.string().required(), label: Yup.string().required(),
}).nullable(), }).nullable(),
supplier_ids: Yup.array() supplier_ids: Yup.array()
.of(Yup.number().typeError('Supplier tidak valid!')) .of(Yup.number().typeError('Supplier tidak valid!'))
.min(1, 'Minimal harus ada 1 supplier!') .min(1, 'Minimal harus ada 1 supplier!')
.required('Supplier wajib diisi!'), .required('Supplier wajib diisi!'),
flags: Yup.array() flags: Yup.array()
.of(Yup.string()) .of(Yup.string())
.min(1, 'Minimal harus ada 1 flag!') .min(1, 'Minimal harus ada 1 flag!')
.required('Flag wajib diisi!'), .required('Flag wajib diisi!'),
}); });
export const UpdateProductFormSchema = ProductFormSchema; export const UpdateProductFormSchema = ProductFormSchema;
export type ProductFormValues = Yup.InferType<typeof ProductFormSchema>; export type ProductFormValues = Yup.InferType<typeof ProductFormSchema>;
@@ -24,7 +24,12 @@ import {
CreateProductPayload, CreateProductPayload,
UpdateProductPayload, UpdateProductPayload,
} from '@/types/api/master-data/product'; } from '@/types/api/master-data/product';
import { UomApi, ProductCategoryApi, SupplierApi, ProductApi } from '@/services/api/master-data'; import {
UomApi,
ProductCategoryApi,
SupplierApi,
ProductApi,
} from '@/services/api/master-data';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
import { PRODUCT_FLAG_OPTIONS } from '@/config/constant'; import { PRODUCT_FLAG_OPTIONS } from '@/config/constant';
@@ -67,30 +72,37 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
[router] [router]
); );
const formikInitialValues = useMemo<ProductFormValues>(() => ({ const formikInitialValues = useMemo<ProductFormValues>(
name: initialValues?.name ?? '', () => ({
brand: initialValues?.brand ?? '', name: initialValues?.name ?? '',
sku: initialValues?.sku ?? '', brand: initialValues?.brand ?? '',
uom: initialValues?.uom sku: initialValues?.sku ?? '',
? { value: initialValues.uom.id, label: initialValues.uom.name } uom: initialValues?.uom
: null, ? { value: initialValues.uom.id, label: initialValues.uom.name }
uom_id: initialValues?.uom?.id ?? 0, : null,
product_category: initialValues?.product_category uom_id: initialValues?.uom?.id ?? 0,
? { value: initialValues.product_category.id, label: initialValues.product_category.name } product_category: initialValues?.product_category
: null, ? {
product_category_id: initialValues?.product_category?.id ?? 0, value: initialValues.product_category.id,
product_price: initialValues?.product_price ?? 0, label: initialValues.product_category.name,
selling_price: initialValues?.selling_price ?? 0, }
tax: initialValues?.tax ?? 0, : null,
expiry_period: initialValues?.expiry_period ?? 0, product_category_id: initialValues?.product_category?.id ?? 0,
supplier: null, // not used for payload, just for UI product_price: initialValues?.product_price ?? 0,
supplier_ids: initialValues?.suppliers?.map(s => s.id) ?? [], selling_price: initialValues?.selling_price ?? 0,
flags: initialValues?.flags ?? [], tax: initialValues?.tax ?? 0,
}), [initialValues]); expiry_period: initialValues?.expiry_period ?? 0,
supplier: null, // not used for payload, just for UI
supplier_ids: initialValues?.suppliers?.map((s) => s.id) ?? [],
flags: initialValues?.flags ?? [],
}),
[initialValues]
);
const formik = useFormik<ProductFormValues>({ const formik = useFormik<ProductFormValues>({
initialValues: formikInitialValues, initialValues: formikInitialValues,
validationSchema: type === 'edit' ? UpdateProductFormSchema : ProductFormSchema, validationSchema:
type === 'edit' ? UpdateProductFormSchema : ProductFormSchema,
onSubmit: async (values) => { onSubmit: async (values) => {
setProductFormErrorMessage(''); setProductFormErrorMessage('');
const payload: CreateProductPayload = { const payload: CreateProductPayload = {
@@ -103,8 +115,12 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
selling_price: values.selling_price, selling_price: values.selling_price,
tax: values.tax, tax: values.tax,
expiry_period: values.expiry_period, expiry_period: values.expiry_period,
supplier_ids: (values.supplier_ids ?? []).filter((id): id is number => typeof id === 'number'), supplier_ids: (values.supplier_ids ?? []).filter(
flags: (values.flags ?? []).filter((f): f is string => typeof f === 'string'), (id): id is number => typeof id === 'number'
),
flags: (values.flags ?? []).filter(
(f): f is string => typeof f === 'string'
),
}; };
switch (type) { switch (type) {
case 'add': case 'add':
@@ -122,7 +138,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
// UOM // UOM
const [uomSelectInputValue, setUomSelectInputValue] = useState(''); const [uomSelectInputValue, setUomSelectInputValue] = useState('');
const uomsUrl = `${UomApi.basePath}?${new URLSearchParams({ search: uomSelectInputValue ?? '' }).toString()}`; const uomsUrl = `${UomApi.basePath}?${new URLSearchParams({ search: uomSelectInputValue ?? '' }).toString()}`;
const { data: uoms, isLoading: isLoadingUoms } = useSWR(uomsUrl, UomApi.getAllFetcher); const { data: uoms, isLoading: isLoadingUoms } = useSWR(
uomsUrl,
UomApi.getAllFetcher
);
const uomOptions = isResponseSuccess(uoms) const uomOptions = isResponseSuccess(uoms)
? uoms?.data.map((uom) => ({ value: uom.id, label: uom.name })) ? uoms?.data.map((uom) => ({ value: uom.id, label: uom.name }))
: []; : [];
@@ -136,7 +155,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
// Product Category // Product Category
const [categorySelectInputValue, setCategorySelectInputValue] = useState(''); const [categorySelectInputValue, setCategorySelectInputValue] = useState('');
const categoriesUrl = `${ProductCategoryApi.basePath}?${new URLSearchParams({ search: categorySelectInputValue ?? '' }).toString()}`; const categoriesUrl = `${ProductCategoryApi.basePath}?${new URLSearchParams({ search: categorySelectInputValue ?? '' }).toString()}`;
const { data: categories, isLoading: isLoadingCategories } = useSWR(categoriesUrl, ProductCategoryApi.getAllFetcher); const { data: categories, isLoading: isLoadingCategories } = useSWR(
categoriesUrl,
ProductCategoryApi.getAllFetcher
);
const categoryOptions = isResponseSuccess(categories) const categoryOptions = isResponseSuccess(categories)
? categories?.data.map((cat) => ({ value: cat.id, label: cat.name })) ? categories?.data.map((cat) => ({ value: cat.id, label: cat.name }))
: []; : [];
@@ -150,16 +172,22 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
// Supplier (multi select) // Supplier (multi select)
const [supplierSelectInputValue, setSupplierSelectInputValue] = useState(''); const [supplierSelectInputValue, setSupplierSelectInputValue] = useState('');
const suppliersUrl = `${SupplierApi.basePath}?${new URLSearchParams({ search: supplierSelectInputValue ?? '' }).toString()}`; const suppliersUrl = `${SupplierApi.basePath}?${new URLSearchParams({ search: supplierSelectInputValue ?? '' }).toString()}`;
const { data: suppliers, isLoading: isLoadingSuppliers } = useSWR(suppliersUrl, SupplierApi.getAllFetcher); const { data: suppliers, isLoading: isLoadingSuppliers } = useSWR(
suppliersUrl,
SupplierApi.getAllFetcher
);
const supplierOptions = isResponseSuccess(suppliers) const supplierOptions = isResponseSuccess(suppliers)
? suppliers?.data ? suppliers?.data
.filter((sup) => sup.category === 'SAPRONAK') .filter((sup) => sup.category === 'SAPRONAK')
.map((sup) => ({ value: sup.id, label: sup.name })) .map((sup) => ({ value: sup.id, label: sup.name }))
: []; : [];
const supplierChangeHandler = (val: OptionType | OptionType[] | null) => { const supplierChangeHandler = (val: OptionType | OptionType[] | null) => {
const arr = Array.isArray(val) ? val : val ? [val] : []; const arr = Array.isArray(val) ? val : val ? [val] : [];
formik.setFieldTouched('supplier_ids', true); formik.setFieldTouched('supplier_ids', true);
formik.setFieldValue('supplier_ids', arr.map((v) => (v as OptionType).value)); formik.setFieldValue(
'supplier_ids',
arr.map((v) => (v as OptionType).value)
);
}; };
const deleteProductClickHandler = () => { const deleteProductClickHandler = () => {
@@ -260,7 +288,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
options={categoryOptions} options={categoryOptions}
onInputChange={setCategorySelectInputValue} onInputChange={setCategorySelectInputValue}
isLoading={isLoadingCategories} isLoading={isLoadingCategories}
isError={formik.touched.product_category_id && Boolean(formik.errors.product_category_id)} isError={
formik.touched.product_category_id &&
Boolean(formik.errors.product_category_id)
}
errorMessage={formik.errors.product_category_id as string} errorMessage={formik.errors.product_category_id as string}
isDisabled={type === 'detail'} isDisabled={type === 'detail'}
isClearable isClearable
@@ -274,7 +305,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
value={formik.values.product_price} value={formik.values.product_price}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
isError={formik.touched.product_price && Boolean(formik.errors.product_price)} isError={
formik.touched.product_price &&
Boolean(formik.errors.product_price)
}
errorMessage={formik.errors.product_price as string} errorMessage={formik.errors.product_price as string}
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
@@ -287,7 +321,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
value={formik.values.selling_price} value={formik.values.selling_price}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
isError={formik.touched.selling_price && Boolean(formik.errors.selling_price)} isError={
formik.touched.selling_price &&
Boolean(formik.errors.selling_price)
}
errorMessage={formik.errors.selling_price as string} errorMessage={formik.errors.selling_price as string}
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
@@ -313,7 +350,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
value={formik.values.expiry_period} value={formik.values.expiry_period}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
isError={formik.touched.expiry_period && Boolean(formik.errors.expiry_period)} isError={
formik.touched.expiry_period &&
Boolean(formik.errors.expiry_period)
}
errorMessage={formik.errors.expiry_period as string} errorMessage={formik.errors.expiry_period as string}
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
@@ -321,12 +361,17 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='Supplier' label='Supplier'
isMulti isMulti
value={supplierOptions.filter(opt => formik.values.supplier_ids.includes(opt.value))} value={supplierOptions.filter((opt) =>
formik.values.supplier_ids.includes(opt.value)
)}
onChange={supplierChangeHandler} onChange={supplierChangeHandler}
options={supplierOptions} options={supplierOptions}
onInputChange={setSupplierSelectInputValue} onInputChange={setSupplierSelectInputValue}
isLoading={isLoadingSuppliers} isLoading={isLoadingSuppliers}
isError={formik.touched.supplier_ids && Boolean(formik.errors.supplier_ids)} isError={
formik.touched.supplier_ids &&
Boolean(formik.errors.supplier_ids)
}
errorMessage={formik.errors.supplier_ids as string} errorMessage={formik.errors.supplier_ids as string}
isDisabled={type === 'detail'} isDisabled={type === 'detail'}
isClearable isClearable
@@ -335,10 +380,15 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='Flags' label='Flags'
isMulti isMulti
value={PRODUCT_FLAG_OPTIONS.filter(opt => formik.values.flags.includes(opt.value))} value={PRODUCT_FLAG_OPTIONS.filter((opt) =>
onChange={val => { formik.values.flags.includes(opt.value)
)}
onChange={(val) => {
const arr = Array.isArray(val) ? val : val ? [val] : []; const arr = Array.isArray(val) ? val : val ? [val] : [];
formik.setFieldValue('flags', arr.map((v) => (v as OptionType).value)); formik.setFieldValue(
'flags',
arr.map((v) => (v as OptionType).value)
);
}} }}
options={PRODUCT_FLAG_OPTIONS} options={PRODUCT_FLAG_OPTIONS}
isError={formik.touched.flags && Boolean(formik.errors.flags)} isError={formik.touched.flags && Boolean(formik.errors.flags)}
@@ -435,4 +485,4 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
); );
}; };
export default ProductForm; export default ProductForm;
@@ -8,6 +8,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import Table from '@/components/Table'; import Table from '@/components/Table';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { ROWS_OPTIONS } from '@/config/constant'; import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseSuccess } from '@/lib/api-helper'; import { isResponseSuccess } from '@/lib/api-helper';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
@@ -30,16 +31,7 @@ const RowOptions = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type == 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/supplier/detail/?supplierId=${props.row.original.id}`} href={`/master-data/supplier/detail/?supplierId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -72,7 +64,7 @@ const RowOptions = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -82,7 +74,7 @@ const RowOptions = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -226,10 +218,15 @@ const SuppliersTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/supplier/add' color='primary'> <Button
href='/master-data/supplier/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Supplier Tambah
</Button> </Button>
</div> </div>
@@ -1,41 +1,44 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
export const SupplierFormSchema = Yup.object({ export const SupplierFormSchema = Yup.object({
name: Yup.string().required('Nama wajib diisi!'), name: Yup.string().required('Nama wajib diisi!'),
alias: Yup.string() alias: Yup.string()
.matches(/^[A-Za-z0-9]+$/, 'Alias hanya boleh berisi huruf dan angka tanpa spasi atau simbol!') .matches(
/^[A-Za-z0-9]+$/,
'Alias hanya boleh berisi huruf dan angka tanpa spasi atau simbol!'
)
.max(5, 'Alias maksimal 5 karakter!') .max(5, 'Alias maksimal 5 karakter!')
.required('Alias wajib diisi!'), .required('Alias wajib diisi!'),
pic: Yup.string().required('PIC wajib diisi!'), pic: Yup.string().required('PIC wajib diisi!'),
type: Yup.object({ type: Yup.object({
value: Yup.string().required(), value: Yup.string().required(),
label: Yup.string().required(), label: Yup.string().required(),
}) }).required('Tipe wajib diisi!'),
.required('Tipe wajib diisi!'), category: Yup.object({
category: Yup.object({ value: Yup.string().required(),
value: Yup.string().required(), label: Yup.string().required(),
label: Yup.string().required(), }).required('Tipe wajib diisi!'),
}) hatchery: Yup.string().required('Hatchery wajib diisi!'),
.required('Tipe wajib diisi!'), phone: Yup.string()
hatchery: Yup.string().required('Hatchery wajib diisi!'), .matches(/^[0-9]+$/, 'Nomor telepon hanya boleh berisi angka!')
phone: Yup.string() .min(10, 'Nomor telepon minimal 10 digit!')
.matches(/^[0-9]+$/, 'Nomor telepon hanya boleh berisi angka!') .max(12, 'Nomor telepon maksimal 12 digit!')
.min(10, 'Nomor telepon minimal 10 digit!') .required('Nomor telepon wajib diisi!'),
.max(12, 'Nomor telepon maksimal 12 digit!') email: Yup.string()
.required('Nomor telepon wajib diisi!'), .email('Format email tidak valid!')
email: Yup.string() .required('Email wajib diisi!'),
.email('Format email tidak valid!') address: Yup.string().required('Alamat wajib diisi!'),
.required('Email wajib diisi!'), npwp: Yup.string()
address: Yup.string().required('Alamat wajib diisi!'), .matches(/^[0-9]+$/, 'Nomor NPWP hanya boleh berisi angka!')
npwp: Yup.string() .required('Nomor NPWP wajib diisi!'),
.matches(/^[0-9]+$/, 'Nomor NPWP hanya boleh berisi angka!') account_number: Yup.string()
.required('Nomor NPWP wajib diisi!'), .matches(/^[0-9]+$/, 'Nomor rekening hanya boleh berisi angka!')
account_number: Yup.string() .required('Nomor rekening wajib diisi!'),
.matches(/^[0-9]+$/, 'Nomor rekening hanya boleh berisi angka!') due_date: Yup.number()
.required('Nomor rekening wajib diisi!'), .min(1, 'Tanggal jatuh tempo wajib diisi!')
due_date: Yup.number().min(1, 'Tanggal jatuh tempo wajib diisi!').required('Tanggal jatuh tempo wajib diisi!'), .required('Tanggal jatuh tempo wajib diisi!'),
}); });
export const UpdateSupplierFormSchema = SupplierFormSchema; export const UpdateSupplierFormSchema = SupplierFormSchema;
export type SupplierFormValues = Yup.InferType<typeof SupplierFormSchema>; export type SupplierFormValues = Yup.InferType<typeof SupplierFormSchema>;
@@ -41,7 +41,9 @@ const SupplierForm = ({
// Setup State // Setup State
const [supplierFormErrorMessage, setSupplierFormErrorMessage] = useState(''); const [supplierFormErrorMessage, setSupplierFormErrorMessage] = useState('');
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [hatcheryOptionsValues, setHatcheryOptionValues] = useState<OptionType[]>([]); const [hatcheryOptionsValues, setHatcheryOptionValues] = useState<
OptionType[]
>([]);
// -- Options data mapping // -- Options data mapping
const typeOptions = TYPE_OPTIONS; const typeOptions = TYPE_OPTIONS;
@@ -167,7 +169,7 @@ const SupplierForm = ({
// Initialize Formik // Initialize Formik
useEffect(() => { useEffect(() => {
formikSetValues(formikInitialValues); formikSetValues(formikInitialValues);
if(formType != 'add'){ if (formType != 'add') {
const hatcheryArrays = formikInitialValues.hatchery.split(','); const hatcheryArrays = formikInitialValues.hatchery.split(',');
const hatcheryCreatedOptions = hatcheryArrays.map((item) => ({ const hatcheryCreatedOptions = hatcheryArrays.map((item) => ({
value: item, value: item,
@@ -177,11 +179,13 @@ const SupplierForm = ({
} }
}, [formikSetValues, formikInitialValues, setHatcheryOptionValues]); }, [formikSetValues, formikInitialValues, setHatcheryOptionValues]);
useEffect(() => { useEffect(() => {
const commaSeparatedValues = hatcheryOptionsValues.map((item) => item.value).join(','); const commaSeparatedValues = hatcheryOptionsValues
.map((item) => item.value)
.join(',');
formikSetValues({ formikSetValues({
...formik.values, ...formik.values,
hatchery: commaSeparatedValues, hatchery: commaSeparatedValues,
}) });
}, [hatcheryOptionsValues, formikSetValues]); }, [hatcheryOptionsValues, formikSetValues]);
// Option Handler // Option Handler
@@ -305,7 +309,9 @@ const SupplierForm = ({
console.log(val); // pastikan val = array of { value, label } console.log(val); // pastikan val = array of { value, label }
setHatcheryOptionValues(val as OptionType[]); setHatcheryOptionValues(val as OptionType[]);
}} }}
isError={formik.touched.hatchery && Boolean(formik.errors.hatchery)} isError={
formik.touched.hatchery && Boolean(formik.errors.hatchery)
}
errorMessage={formik.errors.hatchery as string} errorMessage={formik.errors.hatchery as string}
isDisabled={formType === 'detail'} isDisabled={formType === 'detail'}
isClearable isClearable
@@ -14,6 +14,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Uom } from '@/types/api/master-data/uom'; import { Uom } from '@/types/api/master-data/uom';
import { UomApi } from '@/services/api/master-data'; import { UomApi } from '@/services/api/master-data';
@@ -32,16 +33,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/uom/detail/?uomId=${props.row.original.id}`} href={`/master-data/uom/detail/?uomId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -66,7 +58,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -76,7 +68,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -150,7 +142,7 @@ const UomsTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -199,10 +191,15 @@ const UomsTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/uom/add' color='primary'> <Button
href='/master-data/uom/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah UOM Tambah
</Button> </Button>
</div> </div>
@@ -19,6 +19,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { Warehouse } from '@/types/api/master-data/warehouse'; import { Warehouse } from '@/types/api/master-data/warehouse';
import { WarehouseApi } from '@/services/api/master-data'; import { WarehouseApi } from '@/services/api/master-data';
@@ -37,16 +38,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/master-data/warehouse/detail/?warehouseId=${props.row.original.id}`} href={`/master-data/warehouse/detail/?warehouseId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -81,7 +73,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -206,7 +198,7 @@ const WarehousesTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -277,10 +269,15 @@ const WarehousesTable = () => {
<div className='w-full p-0 sm:p-4'> <div className='w-full p-0 sm:p-4'>
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href='/master-data/warehouse/add' color='primary'> <Button
href='/master-data/warehouse/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Warehouse Tambah
</Button> </Button>
</div> </div>
@@ -8,6 +8,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import Table from '@/components/Table'; import Table from '@/components/Table';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector'; import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector';
import { ROWS_OPTIONS } from '@/config/constant'; import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseSuccess } from '@/lib/api-helper'; import { isResponseSuccess } from '@/lib/api-helper';
@@ -87,7 +88,9 @@ const ChickinTable = () => {
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
<Button <Button
href='/production/chickin/add?projectFlockId=1' href='/production/chickin/add?projectFlockId=1'
variant='outline'
color='primary' color='primary'
className='w-full sm:w-fit'
> >
<Icon icon='uil:plus' width={24} height={24} /> <Icon icon='uil:plus' width={24} height={24} />
Tambah Tambah
@@ -130,20 +133,20 @@ const ChickinTable = () => {
} else { } else {
return '-'; return '-';
} }
} },
}, },
{ {
accessorFn: (row) => row.chick_in_date, accessorFn: (row) => row.chick_in_date,
header: 'Tanggal Chickin', header: 'Tanggal Chickin',
cell: (props) => { cell: (props) => {
if (props.row.original.chick_in_date) { if (props.row.original.chick_in_date) {
return new Date(props.row.original.chick_in_date).toLocaleDateString( return new Date(
'id-ID' props.row.original.chick_in_date
); ).toLocaleDateString('id-ID');
} else { } else {
return '-'; return '-';
} }
} },
}, },
{ {
accessorFn: (row) => row.note, accessorFn: (row) => row.note,
@@ -166,7 +169,7 @@ const ChickinTable = () => {
deleteModal.openModal(); deleteModal.openModal();
}; };
const editClickHandler = () => { const editClickHandler = () => {
setSelectedChickin(props.row.original); setSelectedChickin(props.row.original);
chickinModal.openModal(); chickinModal.openModal();
}; };
@@ -240,7 +243,9 @@ const ChickinTable = () => {
<Modal ref={chickinModal.ref}> <Modal ref={chickinModal.ref}>
<div className='flex flex-row justify-between items-center'> <div className='flex flex-row justify-between items-center'>
<h1 className='text-xl font-semibold text-center mb-6'> <h1 className='text-xl font-semibold text-center mb-6'>
Chickin Kandang - { selectedChickin?.project_flock_kandang && selectedChickin?.project_flock_kandang.kandang?.name} Chickin Kandang -{' '}
{selectedChickin?.project_flock_kandang &&
selectedChickin?.project_flock_kandang.kandang?.name}
</h1> </h1>
<Button <Button
color='error' color='error'
@@ -255,10 +260,14 @@ const ChickinTable = () => {
/> />
</Button> </Button>
</div> </div>
<ChickinForm initialValues={selectedChickin} formType='edit' afterSubmit={() => { <ChickinForm
refreshChickins() initialValues={selectedChickin}
chickinModal.closeModal() formType='edit'
}}/> afterSubmit={() => {
refreshChickins();
chickinModal.closeModal();
}}
/>
</Modal> </Modal>
</> </>
); );
@@ -276,16 +285,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type == 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/production/chickin/detail?chickinId=${props.row.original.id}`} href={`/production/chickin/detail?chickinId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -308,7 +308,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -318,7 +318,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -3,9 +3,11 @@ import * as Yup from 'yup';
export const ChickinFormSchema = Yup.object({ export const ChickinFormSchema = Yup.object({
chick_in_date: Yup.string().required('Tanggal masuk wajib diisi!'), chick_in_date: Yup.string().required('Tanggal masuk wajib diisi!'),
note: Yup.string().required('Catatan wajib diisi!'), note: Yup.string().required('Catatan wajib diisi!'),
quantity: Yup.number().min(1, 'Jumlah wajib diisi!').required('Jumlah wajib diisi!'), quantity: Yup.number()
}) .min(1, 'Jumlah wajib diisi!')
.required('Jumlah wajib diisi!'),
});
export type ChickinFormValues = Yup.InferType<typeof ChickinFormSchema>; export type ChickinFormValues = Yup.InferType<typeof ChickinFormSchema>;
export const UpdateChickinFormSchema = ChickinFormSchema; export const UpdateChickinFormSchema = ChickinFormSchema;
@@ -9,6 +9,7 @@ import ConfirmationModal from '@/components/modal/ConfirmationModal';
import Table from '@/components/Table'; import Table from '@/components/Table';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { ROWS_OPTIONS } from '@/config/constant'; import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
@@ -37,16 +38,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type == 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/production/project-flock/detail?projectFlockId=${props.row.original.id}`} href={`/production/project-flock/detail?projectFlockId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -82,7 +74,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -92,7 +84,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -259,6 +251,7 @@ const ProjectFlockTable = () => {
<div className='flex flex-col sm:flex-row gap-3 w-full'> <div className='flex flex-col sm:flex-row gap-3 w-full'>
<Button <Button
href='/production/project-flock/add' href='/production/project-flock/add'
variant='outline'
color='primary' color='primary'
className='w-full sm:w-fit' className='w-full sm:w-fit'
> >
@@ -371,10 +364,10 @@ const ProjectFlockTable = () => {
const selectableRows = allRows.filter( const selectableRows = allRows.filter(
(row) => row.original?.approval?.step_number == 1 (row) => row.original?.approval?.step_number == 1
); );
const allSelected = selectableRows.every((row) => const allSelected =
row.getIsSelected() selectableRows.every((row) => row.getIsSelected()) &&
) && selectableRows.length != 0; selectableRows.length != 0;
const someSelected = const someSelected =
selectableRows.some((row) => row.getIsSelected()) && selectableRows.some((row) => row.getIsSelected()) &&
@@ -508,7 +501,7 @@ const ProjectFlockTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
deleteClickHandler={deleteClickHandler} deleteClickHandler={deleteClickHandler}
/> />
@@ -24,7 +24,8 @@ export const ProjectFlockFormSchema = Yup.object({
value: Yup.string().required('Nilai Kategori wajib diisi!'), value: Yup.string().required('Nilai Kategori wajib diisi!'),
label: Yup.string().required('Label Kategori wajib diisi!'), label: Yup.string().required('Label Kategori wajib diisi!'),
}).nullable(), }).nullable(),
category: Yup.string().oneOf(['GROWING', 'LAYING'], 'Kategori wajib diisi!') category: Yup.string()
.oneOf(['GROWING', 'LAYING'], 'Kategori wajib diisi!')
.required('Kategori wajib diisi!'), .required('Kategori wajib diisi!'),
// FCR // FCR
@@ -79,9 +79,8 @@ const ProjectFlockForm = ({
const [isApprovedDisabled, setIsApprovedDisabled] = useState( const [isApprovedDisabled, setIsApprovedDisabled] = useState(
initialValues?.approval.step_name == 'Pengajuan' ? false : true initialValues?.approval.step_name == 'Pengajuan' ? false : true
); );
const [isRejectedDisabled, setIsRejectedDisabled] = useState( const [isRejectedDisabled, setIsRejectedDisabled] =
!isApprovedDisabled useState(!isApprovedDisabled);
);
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>( const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
!isApprovedDisabled ? 'APPROVED' : 'REJECTED' !isApprovedDisabled ? 'APPROVED' : 'REJECTED'
); );
@@ -143,10 +142,11 @@ const ProjectFlockForm = ({
search: '', search: '',
location_id: selectedLocation == '' ? '0' : selectedLocation, location_id: selectedLocation == '' ? '0' : selectedLocation,
}).toString()}`; }).toString()}`;
const { data: kandang, isLoading: isLoadingKandang, mutate: refreshKandang} = useSWR( const {
kandangUrl, data: kandang,
KandangApi.getAllFetcher isLoading: isLoadingKandang,
); mutate: refreshKandang,
} = useSWR(kandangUrl, KandangApi.getAllFetcher);
const getPeriodFlocksUrl = `flocks/${selectedFlock}/periods`; const getPeriodFlocksUrl = `flocks/${selectedFlock}/periods`;
@@ -207,10 +207,7 @@ const ProjectFlockForm = ({
setOpenSelectKandangs(true); setOpenSelectKandangs(true);
const newRowSelection = Object.fromEntries( const newRowSelection = Object.fromEntries(
initialValues.kandangs.map((k: Kandang) => [ initialValues.kandangs.map((k: Kandang) => [k.id.toString(), true])
k.id.toString(),
true,
])
); );
setRowSelection(newRowSelection); setRowSelection(newRowSelection);
} }
@@ -38,10 +38,13 @@ const ProjectFlockKandangTable = ({
const allSelected = const allSelected =
selectableRows.every((row) => row.getIsSelected()) && selectableRows.every((row) => row.getIsSelected()) &&
selectableRows.length != 0 && formType != 'detail'; selectableRows.length != 0 &&
formType != 'detail';
const someSelected = const someSelected =
selectableRows.some((row) => row.getIsSelected()) && !allSelected && formType != 'detail'; selectableRows.some((row) => row.getIsSelected()) &&
!allSelected &&
formType != 'detail';
const toggleSelectableRows = () => { const toggleSelectableRows = () => {
const shouldSelect = !allSelected; const shouldSelect = !allSelected;
@@ -14,6 +14,7 @@ import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector';
import Table from '@/components/Table'; import Table from '@/components/Table';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { type CellContext } from '@tanstack/react-table'; import { type CellContext } from '@tanstack/react-table';
import { type Recording } from '@/types/api/production/recording'; import { type Recording } from '@/types/api/production/recording';
@@ -126,16 +127,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`recording/detail/?recordingId=${props.row.original.id}`} href={`recording/detail/?recordingId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -158,7 +150,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='mdi:delete-outline' icon='mdi:delete-outline'
@@ -168,7 +160,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -255,7 +247,7 @@ const RecordingTable = () => {
<TableToolbar <TableToolbar
addButton={{ addButton={{
href: 'recording/add', href: 'recording/add',
label: 'Tambah Recording', label: 'Tambah',
}} }}
search={{ search={{
value: search, value: search,
File diff suppressed because it is too large Load Diff
@@ -19,6 +19,7 @@ import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import TextInput from '@/components/input/TextInput'; import TextInput from '@/components/input/TextInput';
import CheckboxInput from '@/components/input/CheckboxInput'; import CheckboxInput from '@/components/input/CheckboxInput';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { TransferToLaying } from '@/types/api/production/transfer-to-laying'; import { TransferToLaying } from '@/types/api/production/transfer-to-laying';
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying'; import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
@@ -43,16 +44,7 @@ const RowOptionsMenu = ({
deleteClickHandler: () => void; deleteClickHandler: () => void;
}) => { }) => {
return ( return (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`/production/transfer-to-laying/detail/?transferToLayingId=${props.row.original.id}`} href={`/production/transfer-to-laying/detail/?transferToLayingId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -97,7 +89,7 @@ const RowOptionsMenu = ({
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='material-symbols:delete-outline-rounded' icon='material-symbols:delete-outline-rounded'
@@ -107,7 +99,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</div> </RowOptionsMenuWrapper>
); );
}; };
@@ -291,7 +283,7 @@ const TransferToLayingsTable = () => {
{currentPageSize <= 2 && ( {currentPageSize <= 2 && (
<RowCollapseOptions> <RowCollapseOptions>
<RowOptionsMenu <RowOptionsMenu
type='dropdown' type='collapse'
props={props} props={props}
approveClickHandler={approveClickHandler} approveClickHandler={approveClickHandler}
rejectClickHandler={rejectClickHandler} rejectClickHandler={rejectClickHandler}
@@ -328,9 +320,8 @@ const TransferToLayingsTable = () => {
const confirmationModalApproveClickHandler = async () => { const confirmationModalApproveClickHandler = async () => {
setIsApproveLoading(true); setIsApproveLoading(true);
const bulkApproveResponse = await TransferToLayingApi.bulkApprove( const bulkApproveResponse =
selectedRowIds await TransferToLayingApi.bulkApprove(selectedRowIds);
);
if (isResponseSuccess(bulkApproveResponse)) { if (isResponseSuccess(bulkApproveResponse)) {
refreshTransferToLayings(); refreshTransferToLayings();
@@ -358,9 +349,8 @@ const TransferToLayingsTable = () => {
const confirmationModalRejectClickHandler = async () => { const confirmationModalRejectClickHandler = async () => {
setIsRejectLoading(true); setIsRejectLoading(true);
const bulkRejectResponse = await TransferToLayingApi.bulkReject( const bulkRejectResponse =
selectedRowIds await TransferToLayingApi.bulkReject(selectedRowIds);
);
if (isResponseSuccess(bulkRejectResponse)) { if (isResponseSuccess(bulkRejectResponse)) {
refreshTransferToLayings(); refreshTransferToLayings();
@@ -437,11 +427,12 @@ const TransferToLayingsTable = () => {
<div className='w-full sm:w-fit flex flex-col sm:flex-row self-start gap-2'> <div className='w-full sm:w-fit flex flex-col sm:flex-row self-start gap-2'>
<Button <Button
href='/production/transfer-to-laying/add' href='/production/transfer-to-laying/add'
variant='outline'
color='primary' color='primary'
className='w-full sm:w-fit' className='w-full sm:w-fit'
> >
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Transfer ke Laying Tambah
</Button> </Button>
{selectedRowIds.length > 0 && ( {selectedRowIds.length > 0 && (
@@ -484,7 +475,9 @@ const TransferToLayingsTable = () => {
placeholder='Cari TransferToLaying' placeholder='Cari TransferToLaying'
value={tableFilterState.search} value={tableFilterState.search}
onChange={searchChangeHandler} onChange={searchChangeHandler}
className={{ wrapper: 'sm:max-w-3xs' }} className={{
wrapper: 'sm:max-w-3xs',
}}
/> />
</div> </div>
@@ -497,7 +490,9 @@ const TransferToLayingsTable = () => {
placeholder='Masukkan tanggal transfer' placeholder='Masukkan tanggal transfer'
value={tableFilterState.transferDate} value={tableFilterState.transferDate}
onChange={transferDateChangeHandler} onChange={transferDateChangeHandler}
className={{ wrapper: 'col-span-12 sm:col-span-3' }} className={{
wrapper: 'col-span-12 sm:col-span-3',
}}
/> />
<SelectInput <SelectInput
+1 -1
View File
@@ -16,7 +16,7 @@ const RowCollapseOptions = ({ children }: RowCollapseOptionsProps) => {
<Icon icon='material-symbols:more-vert' width={16} height={16} /> <Icon icon='material-symbols:more-vert' width={16} height={16} />
</Button> </Button>
} }
className='w-fit' className='w-fit min-w-36'
titleClassName='p-0! justify-self-end' titleClassName='p-0! justify-self-end'
> >
{children} {children}
@@ -0,0 +1,29 @@
import { ReactNode } from 'react';
import { cn } from '@/lib/helper';
interface RowOptionsMenuWrapperProps {
children?: ReactNode;
type: 'dropdown' | 'collapse';
}
const RowOptionsMenuWrapper = ({
children,
type,
}: RowOptionsMenuWrapperProps) => {
return (
<div
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content mr-2': type === 'dropdown',
'w-fit ml-auto mt-2': type === 'collapse',
},
'p-2.5 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<div className='flex flex-col gap-1'>{children}</div>
</div>
);
};
export default RowOptionsMenuWrapper;
+4 -13
View File
@@ -1,6 +1,6 @@
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import Button from '../Button'; import Button from '../Button';
import { cn } from '@/lib/helper'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
interface TableRowOptionsProps { interface TableRowOptionsProps {
type?: 'dropdown' | 'collapse'; type?: 'dropdown' | 'collapse';
@@ -21,16 +21,7 @@ export const TableRowOptions = ({
showEdit = true, showEdit = true,
showDelete = true, showDelete = true,
}: TableRowOptionsProps) => ( }: TableRowOptionsProps) => (
<div <RowOptionsMenuWrapper type={type}>
tabIndex={type === 'dropdown' ? 0 : undefined}
className={cn(
{
'dropdown-content': type === 'dropdown',
'mt-2': type === 'collapse',
},
'p-2.5 mr-2 flex flex-col gap-1 bg-base-100 rounded-box z-10 border border-black/10 shadow'
)}
>
<Button <Button
href={`${basePath}/detail/?${queryParam}=${recordId}`} href={`${basePath}/detail/?${queryParam}=${recordId}`}
variant='ghost' variant='ghost'
@@ -56,7 +47,7 @@ export const TableRowOptions = ({
onClick={onDelete} onClick={onDelete}
variant='ghost' variant='ghost'
color='error' color='error'
className='text-error hover:text-inherit justify-start text-sm' className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
> >
<Icon <Icon
icon='mdi:delete-outline' icon='mdi:delete-outline'
@@ -67,5 +58,5 @@ export const TableRowOptions = ({
Delete Delete
</Button> </Button>
)} )}
</div> </RowOptionsMenuWrapper>
); );
+7 -2
View File
@@ -18,8 +18,13 @@ export const TableToolbar = ({ addButton, search }: TableToolbarProps) => {
return ( return (
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'> <div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-2'>
{addButton && ( {addButton && (
<div className='flex flex-row'> <div className='w-full flex flex-row'>
<Button href={addButton.href} color='primary'> <Button
href={addButton.href}
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
{addButton.label} {addButton.label}
</Button> </Button>
+7 -4
View File
@@ -105,10 +105,13 @@ export class BaseApiService<T, CreatePayloadGeneric, UpdatePayloadGeneric> {
const url = options?.params const url = options?.params
? `${urlBase}?${new URLSearchParams( ? `${urlBase}?${new URLSearchParams(
Object.entries(options.params).reduce((acc, [key, value]) => { Object.entries(options.params).reduce(
if (value !== undefined) acc[key] = String(value); (acc, [key, value]) => {
return acc; if (value !== undefined) acc[key] = String(value);
}, {} as Record<string, string>) return acc;
},
{} as Record<string, string>
)
)}` )}`
: urlBase; : urlBase;
+1 -1
View File
@@ -140,4 +140,4 @@ export const FlockApi = new BaseApiService<
Flock, Flock,
CreateFlockPayload, CreateFlockPayload,
UpdateFlockPayload UpdateFlockPayload
>('/master-data/flocks'); >('/master-data/flocks');
+1 -1
View File
@@ -29,4 +29,4 @@ export const ChickinApi = new BaseApiService<
Chickin, Chickin,
CreateChickinPayload, CreateChickinPayload,
UpdateChickinPayload UpdateChickinPayload
>('/production/chickins'); >('/production/chickins');
+20 -20
View File
@@ -1,27 +1,27 @@
import { BaseMetadata, CreatedUser } from "@/types/api/api-general"; import { BaseMetadata, CreatedUser } from '@/types/api/api-general';
export type BaseCustomer = { export type BaseCustomer = {
id: number; id: number;
name: string; name: string;
pic_id: number; pic_id: number;
pic: CreatedUser; pic: CreatedUser;
type: string; type: string;
address: string; address: string;
phone: string; phone: string;
email: string; email: string;
account_number: string; account_number: string;
} };
export type Customer = BaseMetadata & BaseCustomer; export type Customer = BaseMetadata & BaseCustomer;
export type CreateCustomerPayload = { export type CreateCustomerPayload = {
name: string; name: string;
pic_id: number; pic_id: number;
type: string; type: string;
address: string; address: string;
phone: string; phone: string;
email: string; email: string;
account_number: string; account_number: string;
} };
export type UpdateCustomerPayload = CreateCustomerPayload; export type UpdateCustomerPayload = CreateCustomerPayload;
+1 -1
View File
@@ -34,4 +34,4 @@ export type CreateProductPayload = {
flags: string[]; flags: string[];
}; };
export type UpdateProductPayload = CreateProductPayload; export type UpdateProductPayload = CreateProductPayload;
+30 -30
View File
@@ -1,38 +1,38 @@
import { BaseMetadata } from '@/types/api/api-general'; import { BaseMetadata } from '@/types/api/api-general';
export type BaseSupplier = { export type BaseSupplier = {
id: number; id: number;
name: string; name: string;
alias: string; alias: string;
pic: string; pic: string;
type: string; type: string;
category: string; category: string;
hatchery: string; hatchery: string;
phone: string; phone: string;
email: string; email: string;
address: string; address: string;
npwp: string; npwp: string;
account_number: string; account_number: string;
due_date: number; due_date: number;
balance?: number; balance?: number;
} };
export type Supplier = BaseMetadata & BaseSupplier; export type Supplier = BaseMetadata & BaseSupplier;
export type CreateSupplierPayload = { export type CreateSupplierPayload = {
name: string; name: string;
alias: string; alias: string;
pic: string; pic: string;
type: string; type: string;
category: string; category: string;
hatchery: string; hatchery: string;
phone: string; phone: string;
email: string; email: string;
address: string; address: string;
npwp: string; npwp: string;
account_number: string; account_number: string;
due_date: number; due_date: number;
balance?: number; balance?: number;
} };
export type UpdateSupplierPayload = CreateSupplierPayload; export type UpdateSupplierPayload = CreateSupplierPayload;
+5 -5
View File
@@ -1,5 +1,5 @@
import { BaseApproval, BaseMetadata } from "@/types/api/api-general"; import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
import { ProjectFlockKandang } from "@/types/api/production/project-flock-kandang"; import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
export type BaseChickin = { export type BaseChickin = {
id?: number; id?: number;
@@ -8,7 +8,7 @@ export type BaseChickin = {
note?: string; note?: string;
project_flock_kandang?: ProjectFlockKandang; project_flock_kandang?: ProjectFlockKandang;
approval: BaseApproval; approval: BaseApproval;
} };
export type Chickin = BaseMetadata & BaseChickin; export type Chickin = BaseMetadata & BaseChickin;
@@ -17,7 +17,7 @@ export type CreateChickinPayload = {
chick_in_date: string; chick_in_date: string;
note: string; note: string;
quantity?: number; quantity?: number;
} };
export type UpdateChickinPayload = CreateChickinPayload & { export type UpdateChickinPayload = CreateChickinPayload & {
id: number; id: number;
@@ -26,4 +26,4 @@ export type UpdateChickinPayload = CreateChickinPayload & {
export type ChickinApprovalPayload = { export type ChickinApprovalPayload = {
action: 'APPROVED' | 'REJECTED'; action: 'APPROVED' | 'REJECTED';
approvable_ids: number[]; approvable_ids: number[];
}; };
+4 -4
View File
@@ -1,5 +1,5 @@
import { Kandang } from "@/type/master-data/kandang"; import { Kandang } from '@/type/master-data/kandang';
import { ProjectFlock } from "@/types/api/production/project-flock"; import { ProjectFlock } from '@/types/api/production/project-flock';
export type BaseProjectFlockKandang = { export type BaseProjectFlockKandang = {
id: number; id: number;
@@ -8,11 +8,11 @@ export type BaseProjectFlockKandang = {
kandang: Kandang; kandang: Kandang;
project_flock: ProjectFlock; project_flock: ProjectFlock;
available_quantity?: number; available_quantity?: number;
} };
export type ProjectFlockKandang = BaseProjectFlockKandang; export type ProjectFlockKandang = BaseProjectFlockKandang;
export type LookupProjectFlockKandangPayload = { export type LookupProjectFlockKandangPayload = {
project_flock_id: number; project_flock_id: number;
kandang_id: number; kandang_id: number;
} };