feat: add base salary field to user profile management with validation and UI integration
This commit is contained in:
parent
6c21c66e12
commit
8e32df7307
@ -45,6 +45,7 @@ public function store(UserRequest $request): RedirectResponse
|
|||||||
'address' => $validated['address'],
|
'address' => $validated['address'],
|
||||||
'birth_place' => $validated['birth_place'],
|
'birth_place' => $validated['birth_place'],
|
||||||
'birth_date' => $validated['birth_date'],
|
'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'],
|
'address' => $validated['address'],
|
||||||
'birth_place' => $validated['birth_place'],
|
'birth_place' => $validated['birth_place'],
|
||||||
'birth_date' => $validated['birth_date'],
|
'birth_date' => $validated['birth_date'],
|
||||||
|
'base_salary' => $validated['base_salary'],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,7 @@ public function rules(): array
|
|||||||
'address' => ['required', 'string'],
|
'address' => ['required', 'string'],
|
||||||
'birth_place' => ['required', 'string', 'max:100'],
|
'birth_place' => ['required', 'string', 'max:100'],
|
||||||
'birth_date' => ['required', 'date'],
|
'birth_date' => ['required', 'date'],
|
||||||
|
'base_salary' => ['required', 'integer', 'min:0'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,13 @@ class UserProfile extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'base_salary' => 'integer',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function user(): BelongsTo
|
public function user(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ public function up(): void
|
|||||||
$table->text('address');
|
$table->text('address');
|
||||||
$table->string('birth_place', 100);
|
$table->string('birth_place', 100);
|
||||||
$table->date('birth_date');
|
$table->date('birth_date');
|
||||||
|
$table->unsignedInteger('base_salary')->default(0);
|
||||||
$table->timestamp('created_at')->useCurrent();
|
$table->timestamp('created_at')->useCurrent();
|
||||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||||
$table->softDeletes();
|
$table->softDeletes();
|
||||||
|
|||||||
@ -26,6 +26,7 @@ public function run(): void
|
|||||||
'address' => 'Jl. Contoh No. 123',
|
'address' => 'Jl. Contoh No. 123',
|
||||||
'birth_place' => 'Jakarta',
|
'birth_place' => 'Jakarta',
|
||||||
'birth_date' => '1990-01-01',
|
'birth_date' => '1990-01-01',
|
||||||
|
'base_salary' => 1500000,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/components/ui/popover"
|
||||||
|
import { NumericFormat } from 'react-number-format';
|
||||||
|
|
||||||
export default function UserCreate() {
|
export default function UserCreate() {
|
||||||
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
|
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
|
||||||
@ -28,6 +29,7 @@ export default function UserCreate() {
|
|||||||
address: '',
|
address: '',
|
||||||
birth_place: '',
|
birth_place: '',
|
||||||
birth_date: '',
|
birth_date: '',
|
||||||
|
base_salary: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = (e: React.FormEvent) => {
|
const onSubmit = (e: React.FormEvent) => {
|
||||||
@ -62,20 +64,20 @@ export default function UserCreate() {
|
|||||||
<CardTitle>Informasi Profil</CardTitle>
|
<CardTitle>Informasi Profil</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-6">
|
<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">
|
<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>
|
<Field>
|
||||||
<Label htmlFor="nik" required>NIK</Label>
|
<Label htmlFor="nik" required>NIK</Label>
|
||||||
<Input
|
<Input
|
||||||
@ -103,6 +105,24 @@ export default function UserCreate() {
|
|||||||
{errors.phone_number && <p className="text-xs text-red-500">{errors.phone_number}</p>}
|
{errors.phone_number && <p className="text-xs text-red-500">{errors.phone_number}</p>}
|
||||||
</Field>
|
</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>
|
<Field>
|
||||||
<Label htmlFor="birth_place" required>Tempat Lahir</Label>
|
<Label htmlFor="birth_place" required>Tempat Lahir</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/components/ui/popover"
|
||||||
|
import { NumericFormat } from 'react-number-format';
|
||||||
|
|
||||||
export default function UserEdit({ user }: { user: any }) {
|
export default function UserEdit({ user }: { user: any }) {
|
||||||
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
|
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
|
||||||
@ -28,6 +29,7 @@ export default function UserEdit({ user }: { user: any }) {
|
|||||||
address: user.profile?.address || '',
|
address: user.profile?.address || '',
|
||||||
birth_place: user.profile?.birth_place || '',
|
birth_place: user.profile?.birth_place || '',
|
||||||
birth_date: user.profile?.birth_date || '',
|
birth_date: user.profile?.birth_date || '',
|
||||||
|
base_salary: user.profile?.base_salary || 0,
|
||||||
_method: 'PATCH',
|
_method: 'PATCH',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -63,19 +65,6 @@ export default function UserEdit({ user }: { user: any }) {
|
|||||||
<CardTitle>Informasi Profil</CardTitle>
|
<CardTitle>Informasi Profil</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-6">
|
<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">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<Field>
|
<Field>
|
||||||
<Label htmlFor="nik" required>NIK</Label>
|
<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>}
|
{errors.nik && <p className="text-xs text-red-500">{errors.nik}</p>}
|
||||||
</Field>
|
</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>
|
<Field>
|
||||||
<Label htmlFor="phone_number" required>Nomor Telepon</Label>
|
<Label htmlFor="phone_number" required>Nomor Telepon</Label>
|
||||||
<Input
|
<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>}
|
{errors.phone_number && <p className="text-xs text-red-500">{errors.phone_number}</p>}
|
||||||
</Field>
|
</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>
|
<Field>
|
||||||
<Label htmlFor="birth_place" required>Tempat Lahir</Label>
|
<Label htmlFor="birth_place" required>Tempat Lahir</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user