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');
|
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\Appends;
|
||||||
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
||||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
@ -31,9 +32,20 @@ protected function casts(): array
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'password' => 'hashed',
|
'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
|
protected function name(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
@ -79,7 +91,7 @@ public function salaryHistories(): HasMany
|
|||||||
public function getActivitylogOptions(): LogOptions
|
public function getActivitylogOptions(): LogOptions
|
||||||
{
|
{
|
||||||
return LogOptions::defaults()
|
return LogOptions::defaults()
|
||||||
->logOnly(['username'])
|
->logOnly(['username', 'is_active'])
|
||||||
->logOnlyDirty()
|
->logOnlyDirty()
|
||||||
->useLogName('Pegawai');
|
->useLogName('Pegawai');
|
||||||
}
|
}
|
||||||
@ -96,6 +108,7 @@ public function tapActivity(Activity $activity, string $eventName)
|
|||||||
if (isset($activity->properties['attributes'])) {
|
if (isset($activity->properties['attributes'])) {
|
||||||
$attributeMap = [
|
$attributeMap = [
|
||||||
'username' => 'Username',
|
'username' => 'Username',
|
||||||
|
'is_active' => 'Status',
|
||||||
];
|
];
|
||||||
|
|
||||||
$properties = $activity->properties->toArray();
|
$properties = $activity->properties->toArray();
|
||||||
@ -104,7 +117,13 @@ public function tapActivity(Activity $activity, string $eventName)
|
|||||||
$newAttrs = [];
|
$newAttrs = [];
|
||||||
foreach ($attrs as $key => $value) {
|
foreach ($attrs as $key => $value) {
|
||||||
$label = $attributeMap[$key] ?? $key;
|
$label = $attributeMap[$key] ?? $key;
|
||||||
$newAttrs[$label] = $value;
|
$displayValue = $value;
|
||||||
|
|
||||||
|
if ($key === 'is_active') {
|
||||||
|
$displayValue = $value ? 'Aktif' : 'Tidak Aktif';
|
||||||
|
}
|
||||||
|
|
||||||
|
$newAttrs[$label] = $displayValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $newAttrs;
|
return $newAttrs;
|
||||||
|
|||||||
@ -27,6 +27,7 @@ public function definition(): array
|
|||||||
'username' => fake()->userName(),
|
'username' => fake()->userName(),
|
||||||
'email' => fake()->unique()->safeEmail(),
|
'email' => fake()->unique()->safeEmail(),
|
||||||
'password' => static::$password ??= Hash::make('password'),
|
'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('email', 100)->unique();
|
||||||
$table->string('password', 60);
|
$table->string('password', 60);
|
||||||
$table->rememberToken();
|
$table->rememberToken();
|
||||||
|
$table->boolean('is_active')->default(true);
|
||||||
$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
|
|||||||
Permission::firstOrCreate(['name' => 'Delete:User', 'guard_name' => 'web']);
|
Permission::firstOrCreate(['name' => 'Delete:User', 'guard_name' => 'web']);
|
||||||
Permission::firstOrCreate(['name' => 'DeleteAny:User', 'guard_name' => 'web']);
|
Permission::firstOrCreate(['name' => 'DeleteAny:User', 'guard_name' => 'web']);
|
||||||
Permission::firstOrCreate(['name' => 'ResetPassword:User', 'guard_name' => 'web']);
|
Permission::firstOrCreate(['name' => 'ResetPassword:User', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'ToggleStatus:User', 'guard_name' => 'web']);
|
||||||
|
|
||||||
// Permissions Category
|
// Permissions Category
|
||||||
Permission::firstOrCreate(['name' => 'View:Category', 'guard_name' => 'web']);
|
Permission::firstOrCreate(['name' => 'View:Category', 'guard_name' => 'web']);
|
||||||
@ -125,6 +126,7 @@ public function run(): void
|
|||||||
'Delete:User',
|
'Delete:User',
|
||||||
'DeleteAny:User',
|
'DeleteAny:User',
|
||||||
'ResetPassword:User',
|
'ResetPassword:User',
|
||||||
|
'ToggleStatus:User',
|
||||||
|
|
||||||
'Create:Category',
|
'Create:Category',
|
||||||
'Edit:Category',
|
'Edit:Category',
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
|
import userRoutes from '@/routes/user';
|
||||||
|
import type { User } from '@/types';
|
||||||
import { router } from '@inertiajs/react';
|
import { router } from '@inertiajs/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import userRoutes from '@/routes/user';
|
|
||||||
import type { User } from '@/types';
|
|
||||||
|
|
||||||
export function useUserIndex() {
|
export function useUserIndex() {
|
||||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
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 {
|
return {
|
||||||
isDeleteDialogOpen,
|
isDeleteDialogOpen,
|
||||||
isBulkDeleteDialogOpen,
|
isBulkDeleteDialogOpen,
|
||||||
@ -80,5 +90,6 @@ export function useUserIndex() {
|
|||||||
onDelete,
|
onDelete,
|
||||||
confirmDelete,
|
confirmDelete,
|
||||||
confirmBulkDelete,
|
confirmBulkDelete,
|
||||||
|
onToggleStatus,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,9 +42,10 @@ export default function UserIndex({ users, defaultPassword }: { users: User[], d
|
|||||||
onDelete,
|
onDelete,
|
||||||
confirmDelete,
|
confirmDelete,
|
||||||
confirmBulkDelete,
|
confirmBulkDelete,
|
||||||
|
onToggleStatus,
|
||||||
} = useUserIndex();
|
} = 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')
|
(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')}
|
showSelection={hasPermission('DeleteAny:User')}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
onRowSelectionChange={setRowSelection}
|
onRowSelectionChange={setRowSelection}
|
||||||
|
filters={[
|
||||||
|
{
|
||||||
|
columnId: 'is_active',
|
||||||
|
title: 'Status',
|
||||||
|
options: [
|
||||||
|
{ label: 'Aktif', value: 'true' },
|
||||||
|
{ label: 'Tidak Aktif', value: 'false' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]}
|
||||||
bulkActions={
|
bulkActions={
|
||||||
hasPermission('DeleteAny:User')
|
hasPermission('DeleteAny:User')
|
||||||
? [
|
? [
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { DataTableColumnHeader } from '@/components/data-table-column-header';
|
import { DataTableColumnHeader } from '@/components/data-table-column-header';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { UserInfo } from '@/components/user-info';
|
import { UserInfo } from '@/components/user-info';
|
||||||
import { usePermission } from '@/hooks/use-permission';
|
import { usePermission } from '@/hooks/use-permission';
|
||||||
@ -12,9 +13,10 @@ import { KeyRound, Pencil, Trash2 } from 'lucide-react';
|
|||||||
interface ColumnProps {
|
interface ColumnProps {
|
||||||
onResetPassword: (user: User) => void;
|
onResetPassword: (user: User) => void;
|
||||||
onDelete: (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",
|
accessorKey: "name",
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
@ -46,6 +48,23 @@ export const getColumns = ({ onResetPassword, onDelete }: ColumnProps): ColumnDe
|
|||||||
),
|
),
|
||||||
meta: { title: "Nomor Telepon" },
|
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",
|
id: "actions",
|
||||||
header: "Aksi",
|
header: "Aksi",
|
||||||
|
|||||||
@ -21,6 +21,7 @@ export type User = {
|
|||||||
two_factor_enabled?: boolean;
|
two_factor_enabled?: boolean;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
|
is_active: boolean;
|
||||||
profile?: UserProfile;
|
profile?: UserProfile;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -31,5 +31,6 @@
|
|||||||
Route::patch('user/reset-password/{user}', [UserController::class, 'resetPassword'])->name('user.resetPassword')->middleware('can:ResetPassword:User');
|
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/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::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