feat: add user status toggle functionality with database support and UI controls
This commit is contained in:
parent
f58f909a4a
commit
7ce9409202
@ -134,4 +134,13 @@ public function bulkDestroy(Request $request): RedirectResponse
|
||||
|
||||
return redirect()->back()->with('success', 'Data terpilih berhasil dihapus');
|
||||
}
|
||||
|
||||
public function toggleStatus(User $user): RedirectResponse
|
||||
{
|
||||
$user->update([
|
||||
'is_active' => ! $user->is_active,
|
||||
]);
|
||||
|
||||
return redirect()->back()->with('success', 'Status berhasil diperbarui');
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
use Illuminate\Database\Eloquent\Attributes\Appends;
|
||||
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
@ -31,9 +32,20 @@ protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'password' => 'hashed',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function scopeActive(Builder $query): void
|
||||
{
|
||||
$query->where('is_active', true);
|
||||
}
|
||||
|
||||
public function scopeInactive(Builder $query): void
|
||||
{
|
||||
$query->where('is_active', false);
|
||||
}
|
||||
|
||||
protected function name(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
@ -79,7 +91,7 @@ public function salaryHistories(): HasMany
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->logOnly(['username'])
|
||||
->logOnly(['username', 'is_active'])
|
||||
->logOnlyDirty()
|
||||
->useLogName('Pegawai');
|
||||
}
|
||||
@ -96,6 +108,7 @@ public function tapActivity(Activity $activity, string $eventName)
|
||||
if (isset($activity->properties['attributes'])) {
|
||||
$attributeMap = [
|
||||
'username' => 'Username',
|
||||
'is_active' => 'Status',
|
||||
];
|
||||
|
||||
$properties = $activity->properties->toArray();
|
||||
@ -104,7 +117,13 @@ public function tapActivity(Activity $activity, string $eventName)
|
||||
$newAttrs = [];
|
||||
foreach ($attrs as $key => $value) {
|
||||
$label = $attributeMap[$key] ?? $key;
|
||||
$newAttrs[$label] = $value;
|
||||
$displayValue = $value;
|
||||
|
||||
if ($key === 'is_active') {
|
||||
$displayValue = $value ? 'Aktif' : 'Tidak Aktif';
|
||||
}
|
||||
|
||||
$newAttrs[$label] = $displayValue;
|
||||
}
|
||||
|
||||
return $newAttrs;
|
||||
|
||||
@ -27,6 +27,7 @@ public function definition(): array
|
||||
'username' => fake()->userName(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'is_active' => fake()->boolean(80),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ public function up(): void
|
||||
$table->string('email', 100)->unique();
|
||||
$table->string('password', 60);
|
||||
$table->rememberToken();
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
|
||||
@ -26,6 +26,7 @@ public function run(): void
|
||||
Permission::firstOrCreate(['name' => 'Delete:User', 'guard_name' => 'web']);
|
||||
Permission::firstOrCreate(['name' => 'DeleteAny:User', 'guard_name' => 'web']);
|
||||
Permission::firstOrCreate(['name' => 'ResetPassword:User', 'guard_name' => 'web']);
|
||||
Permission::firstOrCreate(['name' => 'ToggleStatus:User', 'guard_name' => 'web']);
|
||||
|
||||
// Permissions Category
|
||||
Permission::firstOrCreate(['name' => 'View:Category', 'guard_name' => 'web']);
|
||||
@ -125,6 +126,7 @@ public function run(): void
|
||||
'Delete:User',
|
||||
'DeleteAny:User',
|
||||
'ResetPassword:User',
|
||||
'ToggleStatus:User',
|
||||
|
||||
'Create:Category',
|
||||
'Edit:Category',
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import userRoutes from '@/routes/user';
|
||||
import type { User } from '@/types';
|
||||
import { router } from '@inertiajs/react';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import userRoutes from '@/routes/user';
|
||||
import type { User } from '@/types';
|
||||
|
||||
export function useUserIndex() {
|
||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||
@ -62,6 +62,16 @@ export function useUserIndex() {
|
||||
});
|
||||
};
|
||||
|
||||
const onToggleStatus = (user: User) => {
|
||||
router.patch(userRoutes.toggleStatus(user.id).url, {}, {
|
||||
preserveState: true,
|
||||
preserveScroll: true,
|
||||
onSuccess: (response: any) => {
|
||||
toast.success(response.props.flash.success);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
isDeleteDialogOpen,
|
||||
isBulkDeleteDialogOpen,
|
||||
@ -80,5 +90,6 @@ export function useUserIndex() {
|
||||
onDelete,
|
||||
confirmDelete,
|
||||
confirmBulkDelete,
|
||||
onToggleStatus,
|
||||
};
|
||||
}
|
||||
|
||||
@ -42,9 +42,10 @@ export default function UserIndex({ users, defaultPassword }: { users: User[], d
|
||||
onDelete,
|
||||
confirmDelete,
|
||||
confirmBulkDelete,
|
||||
onToggleStatus,
|
||||
} = useUserIndex();
|
||||
|
||||
const columns = getColumns({ onResetPassword, onDelete }).filter(
|
||||
const columns = getColumns({ onResetPassword, onDelete, onToggleStatus }).filter(
|
||||
(col) => col.id !== 'actions' || hasPermission('Edit:User') || hasPermission('Delete:User') || hasPermission('ResetPassword:User')
|
||||
);
|
||||
|
||||
@ -74,6 +75,16 @@ export default function UserIndex({ users, defaultPassword }: { users: User[], d
|
||||
showSelection={hasPermission('DeleteAny:User')}
|
||||
rowSelection={rowSelection}
|
||||
onRowSelectionChange={setRowSelection}
|
||||
filters={[
|
||||
{
|
||||
columnId: 'is_active',
|
||||
title: 'Status',
|
||||
options: [
|
||||
{ label: 'Aktif', value: 'true' },
|
||||
{ label: 'Tidak Aktif', value: 'false' },
|
||||
]
|
||||
}
|
||||
]}
|
||||
bulkActions={
|
||||
hasPermission('DeleteAny:User')
|
||||
? [
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { DataTableColumnHeader } from '@/components/data-table-column-header';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { UserInfo } from '@/components/user-info';
|
||||
import { usePermission } from '@/hooks/use-permission';
|
||||
@ -12,9 +13,10 @@ import { KeyRound, Pencil, Trash2 } from 'lucide-react';
|
||||
interface ColumnProps {
|
||||
onResetPassword: (user: User) => void;
|
||||
onDelete: (user: User) => void;
|
||||
onToggleStatus: (user: User) => void;
|
||||
}
|
||||
|
||||
export const getColumns = ({ onResetPassword, onDelete }: ColumnProps): ColumnDef<User>[] => [
|
||||
export const getColumns = ({ onResetPassword, onDelete, onToggleStatus }: ColumnProps): ColumnDef<User>[] => [
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: ({ column }) => (
|
||||
@ -46,6 +48,23 @@ export const getColumns = ({ onResetPassword, onDelete }: ColumnProps): ColumnDe
|
||||
),
|
||||
meta: { title: "Nomor Telepon" },
|
||||
},
|
||||
{
|
||||
accessorKey: "is_active",
|
||||
header: "Status",
|
||||
meta: { title: "Status" },
|
||||
cell: ({ row }) => {
|
||||
const user = row.original;
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
return (
|
||||
<Switch
|
||||
checked={user.is_active}
|
||||
onCheckedChange={() => onToggleStatus(user)}
|
||||
disabled={!hasPermission('ToggleStatus:User')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "Aksi",
|
||||
|
||||
@ -21,6 +21,7 @@ export type User = {
|
||||
two_factor_enabled?: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
is_active: boolean;
|
||||
profile?: UserProfile;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
@ -31,5 +31,6 @@
|
||||
Route::patch('user/reset-password/{user}', [UserController::class, 'resetPassword'])->name('user.resetPassword')->middleware('can:ResetPassword:User');
|
||||
Route::delete('user/destroy/{user}', [UserController::class, 'destroy'])->name('user.destroy')->middleware('can:Delete:User');
|
||||
Route::delete('user/bulk-destroy', [UserController::class, 'bulkDestroy'])->name('user.bulkDestroy')->middleware('can:DeleteAny:User');
|
||||
Route::patch('user/toggle-status/{user}', [UserController::class, 'toggleStatus'])->name('user.toggleStatus')->middleware('can:ToggleStatus:User');
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user