feat: add base salary field to user profile management with validation and UI integration

This commit is contained in:
Yoga Pangestu 2026-04-18 14:51:49 +07:00
parent 6c21c66e12
commit 8e32df7307
7 changed files with 78 additions and 26 deletions

View File

@ -45,6 +45,7 @@ public function store(UserRequest $request): RedirectResponse
'address' => $validated['address'],
'birth_place' => $validated['birth_place'],
'birth_date' => $validated['birth_date'],
'base_salary' => $validated['base_salary'],
]);
});
@ -77,6 +78,7 @@ public function update(UserRequest $request, User $user): RedirectResponse
'address' => $validated['address'],
'birth_place' => $validated['birth_place'],
'birth_date' => $validated['birth_date'],
'base_salary' => $validated['base_salary'],
]);
});

View File

@ -36,6 +36,7 @@ public function rules(): array
'address' => ['required', 'string'],
'birth_place' => ['required', 'string', 'max:100'],
'birth_date' => ['required', 'date'],
'base_salary' => ['required', 'integer', 'min:0'],
];
}
}

View File

@ -13,6 +13,13 @@ class UserProfile extends Model
{
use HasFactory, SoftDeletes;
protected function casts(): array
{
return [
'base_salary' => 'integer',
];
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);

View File

@ -20,6 +20,7 @@ public function up(): void
$table->text('address');
$table->string('birth_place', 100);
$table->date('birth_date');
$table->unsignedInteger('base_salary')->default(0);
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
$table->softDeletes();

View File

@ -26,6 +26,7 @@ public function run(): void
'address' => 'Jl. Contoh No. 123',
'birth_place' => 'Jakarta',
'birth_date' => '1990-01-01',
'base_salary' => 1500000,
]);
}
}

View File

@ -15,6 +15,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import { NumericFormat } from 'react-number-format';
export default function UserCreate() {
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
@ -28,6 +29,7 @@ export default function UserCreate() {
address: '',
birth_place: '',
birth_date: '',
base_salary: 0,
});
const onSubmit = (e: React.FormEvent) => {
@ -62,20 +64,20 @@ export default function UserCreate() {
<CardTitle>Informasi Profil</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<Field>
<Label htmlFor="full_name" required>Nama Lengkap</Label>
<Input
id="full_name"
name="full_name"
value={data.full_name}
onChange={e => setData('full_name', e.target.value)}
autoComplete='off'
placeholder='Contoh: John Doe'
/>
{errors.full_name && <p className="text-xs text-red-500">{errors.full_name}</p>}
</Field>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Field>
<Label htmlFor="full_name" required>Nama Lengkap</Label>
<Input
id="full_name"
name="full_name"
value={data.full_name}
onChange={e => setData('full_name', e.target.value)}
autoComplete='off'
placeholder='Contoh: John Doe'
/>
{errors.full_name && <p className="text-xs text-red-500">{errors.full_name}</p>}
</Field>
<Field>
<Label htmlFor="nik" required>NIK</Label>
<Input
@ -103,6 +105,24 @@ export default function UserCreate() {
{errors.phone_number && <p className="text-xs text-red-500">{errors.phone_number}</p>}
</Field>
<Field>
<Label htmlFor="base_salary" required>Gaji Pokok</Label>
<NumericFormat
id="base_salary"
customInput={Input}
thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.base_salary}
onValueChange={(values) => {
setData('base_salary', values.floatValue || 0)
}}
placeholder="Rp 0"
autoComplete='off'
/>
{errors.base_salary && <p className="text-xs text-red-500">{errors.base_salary}</p>}
</Field>
<Field>
<Label htmlFor="birth_place" required>Tempat Lahir</Label>
<Input

View File

@ -15,6 +15,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import { NumericFormat } from 'react-number-format';
export default function UserEdit({ user }: { user: any }) {
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
@ -28,6 +29,7 @@ export default function UserEdit({ user }: { user: any }) {
address: user.profile?.address || '',
birth_place: user.profile?.birth_place || '',
birth_date: user.profile?.birth_date || '',
base_salary: user.profile?.base_salary || 0,
_method: 'PATCH',
});
@ -63,19 +65,6 @@ export default function UserEdit({ user }: { user: any }) {
<CardTitle>Informasi Profil</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<Field>
<Label htmlFor="full_name" required>Nama Lengkap</Label>
<Input
id="full_name"
name="full_name"
value={data.full_name}
onChange={e => setData('full_name', e.target.value)}
autoComplete='off'
placeholder='Contoh: John Doe'
/>
{errors.full_name && <p className="text-xs text-red-500">{errors.full_name}</p>}
</Field>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Field>
<Label htmlFor="nik" required>NIK</Label>
@ -91,6 +80,19 @@ export default function UserEdit({ user }: { user: any }) {
{errors.nik && <p className="text-xs text-red-500">{errors.nik}</p>}
</Field>
<Field>
<Label htmlFor="full_name" required>Nama Lengkap</Label>
<Input
id="full_name"
name="full_name"
value={data.full_name}
onChange={e => setData('full_name', e.target.value)}
autoComplete='off'
placeholder='Contoh: John Doe'
/>
{errors.full_name && <p className="text-xs text-red-500">{errors.full_name}</p>}
</Field>
<Field>
<Label htmlFor="phone_number" required>Nomor Telepon</Label>
<Input
@ -104,6 +106,24 @@ export default function UserEdit({ user }: { user: any }) {
{errors.phone_number && <p className="text-xs text-red-500">{errors.phone_number}</p>}
</Field>
<Field>
<Label htmlFor="base_salary" required>Gaji Pokok</Label>
<NumericFormat
id="base_salary"
customInput={Input}
thousandSeparator="."
decimalSeparator=","
prefix="Rp "
value={data.base_salary}
onValueChange={(values) => {
setData('base_salary', values.floatValue || 0)
}}
placeholder="Rp 0"
autoComplete='off'
/>
{errors.base_salary && <p className="text-xs text-red-500">{errors.base_salary}</p>}
</Field>
<Field>
<Label htmlFor="birth_place" required>Tempat Lahir</Label>
<Input