feat: integrate react-number-format for price inputs in product create and edit forms

This commit is contained in:
Yoga Pangestu 2026-04-16 13:16:05 +07:00
parent 4b5d5af316
commit bfee01153c
5 changed files with 1932 additions and 62 deletions

1799
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -82,6 +82,7 @@
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-hotkeys-hook": "^5.2.4", "react-hotkeys-hook": "^5.2.4",
"react-number-format": "^5.4.5",
"shadcn": "^4.2.0", "shadcn": "^4.2.0",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.5.0", "tailwind-merge": "^3.5.0",

View File

@ -21,6 +21,7 @@ import {
ComboboxValue, ComboboxValue,
useComboboxAnchor, useComboboxAnchor,
} from "@/components/ui/combobox" } from "@/components/ui/combobox"
import { NumericFormat } from 'react-number-format';
export default function ProductCreate({ categories }: { categories: Category[] }) { export default function ProductCreate({ categories }: { categories: Category[] }) {
const { data, setData, post, processing, errors } = useForm({ const { data, setData, post, processing, errors } = useForm({
@ -122,65 +123,100 @@ export default function ProductCreate({ categories }: { categories: Category[] }
<Field> <Field>
<Label htmlFor="price_purchase">Harga Beli</Label> <Label htmlFor="price_purchase">Harga Beli</Label>
<Input <NumericFormat
id="price_purchase" id="price_purchase"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.purchase} value={data.prices.purchase}
onChange={e => setData('prices', { ...data.prices, purchase: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
purchase: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.purchase'] && <p className="text-xs text-red-500">{errors['prices.purchase']}</p>} {errors['prices.purchase'] && <p className="text-xs text-red-500">{errors['prices.purchase']}</p>}
</Field> </Field>
<Field> <Field>
<Label htmlFor="price_distributor">Harga Distributor</Label> <Label htmlFor="price_distributor">Harga Distributor</Label>
<Input <NumericFormat
id="price_distributor" id="price_distributor"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.distributor} value={data.prices.distributor}
onChange={e => setData('prices', { ...data.prices, distributor: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
distributor: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.distributor'] && <p className="text-xs text-red-500">{errors['prices.distributor']}</p>} {errors['prices.distributor'] && <p className="text-xs text-red-500">{errors['prices.distributor']}</p>}
</Field> </Field>
<Field> <Field>
<Label htmlFor="price_agent">Harga Agen</Label> <Label htmlFor="price_agent">Harga Agen</Label>
<Input <NumericFormat
id="price_agent" id="price_agent"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.agent} value={data.prices.agent}
onChange={e => setData('prices', { ...data.prices, agent: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
agent: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.agent'] && <p className="text-xs text-red-500">{errors['prices.agent']}</p>} {errors['prices.agent'] && <p className="text-xs text-red-500">{errors['prices.agent']}</p>}
</Field> </Field>
<Field> <Field>
<Label htmlFor="price_reseller">Harga Reseller</Label> <Label htmlFor="price_reseller">Harga Reseller</Label>
<Input <NumericFormat
id="price_reseller" id="price_reseller"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.reseller} value={data.prices.reseller}
onChange={e => setData('prices', { ...data.prices, reseller: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
reseller: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.reseller'] && <p className="text-xs text-red-500">{errors['prices.reseller']}</p>} {errors['prices.reseller'] && <p className="text-xs text-red-500">{errors['prices.reseller']}</p>}
</Field> </Field>
<Field> <Field>
<Label htmlFor="price_retail">Harga Retail</Label> <Label htmlFor="price_retail">Harga Retail</Label>
<Input <NumericFormat
id="price_retail" id="price_retail"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.retail} value={data.prices.retail}
onChange={e => setData('prices', { ...data.prices, retail: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
retail: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.retail'] && <p className="text-xs text-red-500">{errors['prices.retail']}</p>} {errors['prices.retail'] && <p className="text-xs text-red-500">{errors['prices.retail']}</p>}
</Field> </Field>

View File

@ -22,6 +22,7 @@ import {
ComboboxValue, ComboboxValue,
useComboboxAnchor, useComboboxAnchor,
} from "@/components/ui/combobox" } from "@/components/ui/combobox"
import { NumericFormat } from 'react-number-format';
export default function ProductEdit({ product, categories }: { product: Product, categories: Category[] }) { export default function ProductEdit({ product, categories }: { product: Product, categories: Category[] }) {
const getPrice = (type: string) => { const getPrice = (type: string) => {
@ -130,65 +131,100 @@ export default function ProductEdit({ product, categories }: { product: Product,
<Field> <Field>
<Label htmlFor="price_purchase">Harga Beli</Label> <Label htmlFor="price_purchase">Harga Beli</Label>
<Input <NumericFormat
id="price_purchase" id="price_purchase"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.purchase} value={data.prices.purchase}
onChange={e => setData('prices', { ...data.prices, purchase: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
purchase: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.purchase'] && <p className="text-xs text-red-500">{errors['prices.purchase']}</p>} {errors['prices.purchase'] && <p className="text-xs text-red-500">{errors['prices.purchase']}</p>}
</Field> </Field>
<Field> <Field>
<Label htmlFor="price_distributor">Harga Distributor</Label> <Label htmlFor="price_distributor">Harga Distributor</Label>
<Input <NumericFormat
id="price_distributor" id="price_distributor"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.distributor} value={data.prices.distributor}
onChange={e => setData('prices', { ...data.prices, distributor: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
distributor: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.distributor'] && <p className="text-xs text-red-500">{errors['prices.distributor']}</p>} {errors['prices.distributor'] && <p className="text-xs text-red-500">{errors['prices.distributor']}</p>}
</Field> </Field>
<Field> <Field>
<Label htmlFor="price_agent">Harga Agen</Label> <Label htmlFor="price_agent">Harga Agen</Label>
<Input <NumericFormat
id="price_agent" id="price_agent"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.agent} value={data.prices.agent}
onChange={e => setData('prices', { ...data.prices, agent: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
agent: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.agent'] && <p className="text-xs text-red-500">{errors['prices.agent']}</p>} {errors['prices.agent'] && <p className="text-xs text-red-500">{errors['prices.agent']}</p>}
</Field> </Field>
<Field> <Field>
<Label htmlFor="price_reseller">Harga Reseller</Label> <Label htmlFor="price_reseller">Harga Reseller</Label>
<Input <NumericFormat
id="price_reseller" id="price_reseller"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.reseller} value={data.prices.reseller}
onChange={e => setData('prices', { ...data.prices, reseller: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
reseller: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.reseller'] && <p className="text-xs text-red-500">{errors['prices.reseller']}</p>} {errors['prices.reseller'] && <p className="text-xs text-red-500">{errors['prices.reseller']}</p>}
</Field> </Field>
<Field> <Field>
<Label htmlFor="price_retail">Harga Retail</Label> <Label htmlFor="price_retail">Harga Retail</Label>
<Input <NumericFormat
id="price_retail" id="price_retail"
type="number" customInput={Input}
min="0" thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.prices.retail} value={data.prices.retail}
onChange={e => setData('prices', { ...data.prices, retail: e.target.value })} onValueChange={(values) => {
placeholder='0' setData('prices', {
...data.prices,
retail: values.value
})
}}
placeholder="Rp 0"
/> />
{errors['prices.retail'] && <p className="text-xs text-red-500">{errors['prices.retail']}</p>} {errors['prices.retail'] && <p className="text-xs text-red-500">{errors['prices.retail']}</p>}
</Field> </Field>

View File

@ -4,6 +4,11 @@ import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import laravel from 'laravel-vite-plugin'; import laravel from 'laravel-vite-plugin';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import { fileURLToPath } from 'url';
import path from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
@ -13,13 +18,24 @@ export default defineConfig({
}), }),
inertia(), inertia(),
react({ react({
babel: { // Babel compiler can sometimes cause issues with third-party hooks in React 19
plugins: ['babel-plugin-react-compiler'], // babel: {
}, // plugins: ['babel-plugin-react-compiler'],
// },
}), }),
tailwindcss(), tailwindcss(),
wayfinder({ wayfinder({
formVariants: true, formVariants: true,
}), }),
], ],
resolve: {
alias: {
'react': path.resolve(__dirname, 'node_modules/react'),
'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),
'@': path.resolve(__dirname, 'resources/js'),
},
},
optimizeDeps: {
include: ['react-number-format'],
},
}); });