diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..250df482 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,15 @@ +{ + "singleQuote": true, + "jsxSingleQuote": true, + "endOfLine": "lf", + "arrowParens": "always", + "bracketSpacing": true, + "embeddedLanguageFormatting": "auto", + "htmlWhitespaceSensitivity": "css", + "printWidth": 80, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "tabWidth": 2, + "trailingComma": "es5" +} diff --git a/src/app/inventory/adjustment/add/page.tsx b/src/app/inventory/adjustment/add/page.tsx new file mode 100644 index 00000000..3bd64573 --- /dev/null +++ b/src/app/inventory/adjustment/add/page.tsx @@ -0,0 +1,11 @@ +import InventoryAdjustmentForm from "@/components/pages/inventory/adjustment/form/InventoryAdjustmentForm"; + +const CreateInventoryAdjustment = () => { + return ( +
+ +
+ ); +} + +export default CreateInventoryAdjustment; \ No newline at end of file diff --git a/src/app/inventory/adjustment/detail/page.tsx b/src/app/inventory/adjustment/detail/page.tsx new file mode 100644 index 00000000..5e96c86a --- /dev/null +++ b/src/app/inventory/adjustment/detail/page.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import InventoryAdjustmentForm from '@/components/pages/inventory/adjustment/form/InventoryAdjustmentForm'; +import type { InventoryAdjustment } from '@/types/api/inventory/adjustment'; + +const DetailInventoryAdjustment = () => { + const router = useRouter(); + const [inventoryAdjustment, setInventoryAdjustment] = useState(null); + + // Ambil data dari router state + useEffect(() => { + console.log("Router State"); + console.log(window.history.state); + const state = window.history.state?.usr as + | { inventoryAdjustment?: InventoryAdjustment } + | undefined; + + if (state?.inventoryAdjustment) { + // jika object dikirim via router.push(state) + setInventoryAdjustment(state.inventoryAdjustment); + } + }, [router]); + + const finalData = inventoryAdjustment; + + console.log("Final Data"); + console.log(finalData); + + if (!finalData) { + return ( +
+ +
+ ); + } + + return ( +
+ +
+ ); +}; + +export default DetailInventoryAdjustment; diff --git a/src/app/inventory/adjustment/page.tsx b/src/app/inventory/adjustment/page.tsx new file mode 100644 index 00000000..518fd0bf --- /dev/null +++ b/src/app/inventory/adjustment/page.tsx @@ -0,0 +1,11 @@ +import InventoryAdjustmentTable from '@/components/pages/inventory/adjustment/InventoryAdjustmentTable'; + +const InventoryAdjustment = () => { + return ( +
+ +
+ ); +}; + +export default InventoryAdjustment; diff --git a/src/components/input/RadioInput.tsx b/src/components/input/RadioInput.tsx new file mode 100644 index 00000000..71a731aa --- /dev/null +++ b/src/components/input/RadioInput.tsx @@ -0,0 +1,113 @@ +'use client'; + +import { ChangeEventHandler, ReactNode } from 'react'; +import { cn } from '@/lib/helper'; + +export interface RadioOption { + label: string; + value: string; +} + +export interface RadioInputProps { + label?: string; + bottomLabel?: string; + name: string; + value?: string; + options: RadioOption[]; + variant?: string; + className?: { + wrapper?: string; + label?: string; + radioWrapper?: string; + radio?: string; + }; + isError?: boolean; + isValid?: boolean; + errorMessage?: string; + required?: boolean; + disabled?: boolean; + startAdornment?: ReactNode; + endAdornment?: ReactNode; + onChange?: ChangeEventHandler; + onBlur?: (e: React.FocusEvent) => void; +} + +const RadioInput = ({ + label, + bottomLabel, + name, + value, + options, + variant = 'radio-primary', + className, + isError, + errorMessage, + required = false, + disabled = false, + onChange, + onBlur, +}: RadioInputProps) => { + return ( +
+ {/* Label atas */} + {label && ( + + )} + + {/* Daftar opsi radio */} +
+ {options.map((option) => ( + + ))} +
+ + {/* Label bawah */} + {!isError && bottomLabel && ( +

{bottomLabel}

+ )} + + {/* Pesan error */} + {isError && errorMessage && ( +

{errorMessage}

+ )} +
+ ); +}; + +export default RadioInput; diff --git a/src/components/input/SelectInput.tsx b/src/components/input/SelectInput.tsx index 930b5ed5..43a3f622 100644 --- a/src/components/input/SelectInput.tsx +++ b/src/components/input/SelectInput.tsx @@ -1,28 +1,38 @@ 'use client'; -import { ComponentType, ReactNode, useEffect, useMemo, useState } from 'react'; -import Select, { OptionProps, GroupBase, InputActionMeta } from 'react-select'; +import { + ComponentType, + ReactNode, + useEffect, + useMemo, + useState, +} from 'react'; +import Select, { + OptionProps, + GroupBase, + InputActionMeta, + MultiValue, + SingleValue, +} from 'react-select'; +import CreatableSelect from 'react-select/creatable'; import makeAnimated from 'react-select/animated'; import { useDebounce } from 'use-debounce'; - import { cn } from '@/lib/helper'; export interface OptionType { value: string | number; label: string; - className?: string; // for multi select - labelClassName?: string; // for multi select + className?: string; + labelClassName?: string; } export type OptionComponent = ComponentType< OptionProps> >; -interface SelectInputProps { +interface SelectInputBaseProps { label?: ReactNode; bottomLabel?: ReactNode; - value?: T | T[]; - onChange?: (val: T | T[] | null) => void; options: T[]; optionComponent?: OptionComponent; isDisabled?: boolean; @@ -46,52 +56,78 @@ interface SelectInputProps { onInputChange?: (search: string) => void; } +interface SelectInputProps extends SelectInputBaseProps { + createables?: boolean; + value?: T | T[] | null; + onChange?: (val: T | T[] | null) => void; +} + const animatedComponents = makeAnimated(); -const SelectInput = ({ - label, - bottomLabel, - value, - onChange, - options, - optionComponent, - isDisabled, - isLoading, - isClearable, - isRtl, - isSearchable = true, - isMulti, - placeholder, - required, - className, - isError, - errorMessage, - isAnimated = true, - openMenu, - delay = 300, - onInputChange, -}: SelectInputProps) => { - const [internalInputValue, setInternalInputValue] = useState(''); +const SelectInput = (props: SelectInputProps) => { + const { + label, + bottomLabel, + value, + onChange, + options, + optionComponent, + isDisabled, + isLoading, + isClearable, + isRtl, + isSearchable = true, + isMulti, + placeholder, + required, + className, + isError, + errorMessage, + isAnimated = true, + openMenu, + delay = 300, + createables = false, + onInputChange, + } = props; - const [debouncedInputValue] = useDebounce(internalInputValue, delay ?? 300); + const [internalInputValue, setInternalInputValue] = useState(''); + const [debouncedInputValue] = useDebounce(internalInputValue, delay); const components = useMemo(() => { const base = isAnimated ? animatedComponents : {}; - - return { - ...base, - IndicatorSeparator: () => null, - }; + return { ...base, IndicatorSeparator: () => null }; }, [isAnimated]); - const internalInputChangeHandler = (value: string, meta: InputActionMeta) => { - if (meta.action === 'input-change') setInternalInputValue(value); + const internalInputChangeHandler = ( + val: string, + meta: InputActionMeta + ) => { + if (meta.action === 'input-change') setInternalInputValue(val); if (meta.action === 'menu-close') setInternalInputValue(''); }; useEffect(() => { onInputChange?.(debouncedInputValue); - }, [debouncedInputValue]); + }, [onInputChange, debouncedInputValue]); + + const SelectComponent = createables ? CreatableSelect : Select; + + /** 🎯 handleChange tanpa any */ + const handleChange = ( + val: MultiValue | SingleValue + ): void => { + if (!val) { + onChange?.(null); + return; + } + + if (isMulti) { + onChange?.(val as T[]); + } else { + onChange?.(val as T); + } + }; + return (
({ {label} {required && ( - <> - {' '} - - * - - + + * + )} )} -