# System Pages Module Implementation Plan

## Context

Implement User Management and Branch Management pages for the BTM Microbanking System. The backend infrastructure (routes, controllers, services, policies, form requests) is already complete. This plan focuses on frontend implementation with modal-based CRUD operations following existing patterns from Members and Savings modules.

**User Requirements:**
- User Management: Create, Edit, Delete/Deactivate, View Activity Log
- Branch Management: Create, Edit, Delete/Close Branch
- Layout Pattern: Modal-based CRUD within index page only
- Filtering: Search field only (no advanced filters)

---

## Implementation Overview

### Backend Status: ✅ Complete
- UserController (7 routes), BranchController (7 routes) - DONE
- UserService, BranchService - DONE
- StoreUserRequest, UpdateUserRequest, StoreBranchRequest, UpdateBranchRequest - DONE
- UserPolicy, BranchPolicy - DONE
- Routes in `routes/web.php` - DONE

### Frontend Status: 🔜 Not Started
- Users/Index.tsx - TO CREATE
- Branches/Index.tsx - TO CREATE
- Type definitions for User, Branch - TO CREATE

---

## Phase 1: Type Definitions

### Files to Create

**1. `resources/js/types/user.ts`**
```typescript
export interface User {
    id: number;
    name: string;
    email: string;
    phone?: string | null;
    branch_id?: number | null;
    role: string; // 'Administrator' | 'Manager' | 'Teller' | 'Collector'
    created_at: string;
    updated_at: string;
    email_verified_at?: string | null;
    branch?: { id: number; name: string };
    roles?: Array<{ id: number; name: string }>;
}

export interface UserFilters {
    search?: string;
    branch_id?: string;
    role?: string;
}
```

**2. `resources/js/types/branch.ts`**
```typescript
export interface Branch {
    id: number;
    code: string;
    name: string;
    address?: string;
    city?: string;
    province?: string;
    phone?: string;
    email?: string;
    is_headquarters: boolean;
    is_active: boolean;
    created_at?: string;
    updated_at?: string;
    members_count?: number;
    savings_count?: number;
    financings_count?: number;
    users_count?: number;
}

export interface BranchFilters {
    search?: string;
    is_active?: string;
    is_headquarters?: string;
}
```

**3. Update `resources/js/types/index.ts`**
```typescript
export type * from './user';
export type * from './branch';
```

---

## Phase 2: Branch Management Page

### File to Create: `resources/js/pages/Branches/Index.tsx`

### Component Structure

```typescript
interface PageProps {
    branches: PaginatedData<Branch>;
    filters: BranchFilters;
}

// Modal states
const [showCreateModal, setShowCreateModal] = useState(false)
const [showEditModal, setShowEditModal] = useState(false)
const [showDeleteModal, setShowDeleteModal] = useState(false)
const [selectedBranch, setSelectedBranch] = useState<Branch | null>(null)

// Forms
const createForm = useForm({
    code: '',
    name: '',
    address: '',
    city: '',
    province: '',
    phone: '',
    email: '',
    is_headquarters: false,
    is_active: true,
})

const editForm = useForm({ ... })
```

### DataTable Columns

```typescript
const columns: ColumnDef<Branch>[] = [
    { accessorKey: 'code', header: 'Kode' },
    { accessorKey: 'name', header: 'Nama Cabang' },
    { accessorKey: 'city', header: 'Kota' },
    { accessorKey: 'phone', header: 'Telepon' },
    {
        accessorKey: 'is_headquarters',
        header: 'Pusat',
        cell: ({ row }) =>
            row.getValue('is_headquarters') ? 'Ya' : '-'
    },
    {
        accessorKey: 'is_active',
        header: 'Status',
        cell: ({ row }) => (
            <StatusBadge type="branch" status={row.getValue('is_active') ? 'active' : 'inactive'} />
        )
    },
    { id: 'actions', cell: ActionsCell }
]
```

### Search Implementation

```typescript
const { updateFilter, updatePage, search } = useFilterParams({
    baseUrl: branchesRoutes.index().url,
});

// Search input (refer Members/Index.tsx lines 263-280)
<InputGroup>
    <InputGroupInput
        id="search"
        placeholder="Cari berdasarkan nama atau kode..."
        defaultValue={filters.search}
        onKeyDown={(e) => { if (e.key === 'Enter') search(e.currentTarget.value) }}
        onBlur={(e) => search(e.currentTarget.value)}
    />
    <InputGroupAddon align="inline-start">
        <SearchIcon />
    </InputGroupAddon>
</InputGroup>
```

### Create Modal

```typescript
<Dialog open={showCreateModal} onOpenChange={setShowCreateModal}>
    <DialogContent>
        <DialogHeader>
            <DialogTitle>Tambah Cabang Baru</DialogTitle>
            <DialogDescription>Isi data cabang yang akan ditambahkan.</DialogDescription>
        </DialogHeader>
        <form onSubmit={handleSubmitCreate}>
            {/* Form fields: code, name, address, city, province, phone, email */}
            {/* is_headquarters, is_active as Checkbox */}
            <DialogFooter>
                <Button type="button" variant="outline" onClick={() => setShowCreateModal(false)}>
                    Batal
                </Button>
                <Button type="submit" disabled={createForm.processing}>
                    Simpan
                </Button>
            </DialogFooter>
        </form>
    </DialogContent>
</Dialog>
```

### Delete Modal

```typescript
<Dialog open={showDeleteModal} onOpenChange={setShowDeleteModal}>
    <DialogContent>
        <DialogHeader>
            <DialogTitle>Hapus Cabang</DialogTitle>
            <DialogDescription>
                Apakah Anda yakin ingin menghapus cabang ini?
                {selectedBranch?.members_count > 0 && (
                    <p className="text-destructive mt-2">
                        Cabang ini memiliki {selectedBranch.members_count} anggota. Tidak dapat dihapus.
                    </p>
                )}
            </DialogDescription>
        </DialogHeader>
        <DialogFooter>
            <Button variant="outline" onClick={() => setShowDeleteModal(false)}>
                Batal
            </Button>
            <Button
                variant="destructive"
                onClick={() => {
                    router.delete(branchesRoutes.destroy({ branch: selectedBranch.id }).url, {
                        onStart: () => toastLoading('Menghapus cabang...'),
                        onSuccess: () => {
                            toastSuccess('Cabang berhasil dihapus')
                            setShowDeleteModal(false)
                        },
                        onError: () => toastError('Gagal menghapus cabang')
                    })
                }}
            >
                Hapus
            </Button>
        </DialogFooter>
    </DialogContent>
</Dialog>
```

### Breadcrumbs

```typescript
const breadcrumbs: BreadcrumbItem[] = [
    { title: 'Cabang', href: branchesRoutes.index().url }
];
```

---

## Phase 3: User Management Page

### File to Create: `resources/js/pages/Users/Index.tsx`

### Component Structure

```typescript
interface PageProps {
    users: PaginatedData<User>;
    branches: Branch[];
    roles: Array<{ id: number; name: string }>;
    filters: UserFilters;
}

// Modal states
const [showCreateModal, setShowCreateModal] = useState(false)
const [showEditModal, setShowEditModal] = useState(false)
const [showDeleteModal, setShowDeleteModal] = useState(false)
const [showActivityModal, setShowActivityModal] = useState(false)
const [selectedUser, setSelectedUser] = useState<User | null>(null)
const [selectedUserForActivity, setSelectedUserForActivity] = useState<User | null>(null)
```

### DataTable Columns

```typescript
const columns: ColumnDef<User>[] = [
    { accessorKey: 'name', header: 'Nama' },
    { accessorKey: 'email', header: 'Email' },
    { accessorKey: 'role', header: 'Role' },
    { accessorKey: 'branch.name', header: 'Cabang', cell: ({ row }) => row.original.branch?.name || '-' },
    { accessorKey: 'phone', header: 'Telepon', cell: ({ row }) => row.getValue('phone') || '-' },
    {
        accessorKey: 'email_verified_at',
        header: 'Status',
        cell: ({ row }) => (
            <StatusBadge type="user" status={row.getValue('email_verified_at') ? 'active' : 'inactive'} />
        )
    },
    { id: 'actions', cell: ActionsCell }
]
```

### Create Modal Fields

- name (required, string)
- email (required, email)
- password (required, min:8)
- password_confirmation (required, match)
- phone (optional, string)
- branch_id (select from branches)
- role (select from roles: Administrator/Manager/Teller/Collector)

### Edit Modal

- Same fields as Create
- password is optional (only update if provided)
- Pre-fill with existing user data
- Check: Cannot edit own user (compare with auth.user.id)

### Delete Modal

- Confirmation dialog
- Check: Cannot delete self (compare with auth.user.id)
- Submit to `users.destroy` route

### Activity Log Modal

⚠️ **Requires backend route addition (out of scope for this plan)**
- New route needed: `GET /users/{user}/audit-logs`
- Display audit logs in table format
- Placeholder modal with "Coming soon" message for now

---

## Phase 4: Implementation Sequence

### Step 1: Type Definitions (5 min)
1. Create `resources/js/types/user.ts`
2. Create `resources/js/types/branch.ts`
3. Update `resources/js/types/index.ts`

### Step 2: Branches Page (45 min)
1. Create `resources/js/pages/Branches/Index.tsx`
2. Implement DataTable with columns (code, name, city, phone, headquarters, status, actions)
3. Add search field with useFilterParams hook
4. Implement Create Modal with form validation
5. Implement Edit Modal with pre-filled data
6. Implement Delete Modal with dependency check
7. Add breadcrumbs
8. Test all CRUD operations

### Step 3: Users Page (60 min)
1. Create `resources/js/pages/Users/Index.tsx`
2. Implement DataTable with columns (name, email, role, branch, phone, status, actions)
3. Add search field
4. Implement Create Modal with all fields including role selection
5. Implement Edit Modal (password optional, self-edit check)
6. Implement Delete Modal (self-deletion prevention)
7. Implement Activity Log modal placeholder
8. Test all operations

### Step 4: Navigation Updates (5 min)
1. Update sidebar to include Users and Branches links
2. Verify permissions-based visibility

---

## Component Reuse

### Existing Components

| Component | Purpose | Import Path |
|-----------|---------|--------------|
| Dialog | Modal dialogs | @/components/ui/dialog |
| Button | Buttons | @/components/ui/button |
| Input | Text inputs | @/components/ui/input |
| InputGroup | Search with icon | @/components/ui/input-group |
| Select | Dropdown selects | @/components/ui/select |
| Label | Form labels | @/components/ui/label |
| Checkbox | Boolean fields | @/components/ui/checkbox |
| DataTable | Data table | @/components/ui/data-table |
| DropdownMenu | Actions menu | @/components/ui/dropdown-menu |
| StatusBadge | Status indicators | @/components/ui/status-badge |
| Pagination | Page navigation | @/components/ui/pagination |

### Existing Utilities

| Utility | Purpose | Import Path |
|---------|---------|--------------|
| toastSuccess, toastError, toastLoading | Notifications | @/lib/toast |
| useFilterParams | URL filter management | @/lib/hooks/useFilterParams |
| usersRoutes | User route functions | @/actions/App/Http/Controllers/UserController |
| branchesRoutes | Branch route functions | @/actions/App/Http/Controllers/BranchController |

### Type Definitions

| Type | Location |
|------|----------|
| PaginatedData | Already exists in @/types/member.ts |
| BreadcrumbItem | Already exists in @/types/member.ts |

---

## Reference Files

### Pattern References
- `resources/js/pages/Members/Index.tsx` - DataTable, search, dropdown actions
- `resources/js/pages/Savings/Index.tsx` - Stats cards, filters pattern
- `resources/js/pages/Savings/Show.tsx` - Modal form implementation

### Type References
- `resources/js/types/member.ts` - Branch interface, PaginatedData
- `resources/js/types/index.ts` - Export structure

### Utility References
- `resources/js/lib/hooks/useFilterParams.ts` - URL filter hook API
- `resources/js/lib/toast.ts` - Toast notification functions

---

## Form Validation

### Server-Side Validation (Primary)

Validation is handled by backend Form Requests:
- `StoreUserRequest` / `UpdateUserRequest` for users
- `StoreBranchRequest` / `UpdateBranchRequest` for branches

Display errors from `form.errors`:
```typescript
{createForm.errors.name && (
    <p className="text-sm text-destructive">{createForm.errors.name}</p>
)}
```

### Client-Side Validation (Optional)

- Password confirmation matching (user create)
- Email format check
- Required field warnings

---

## Toast Notifications

### Success Pattern
```typescript
onSuccess: () => {
    toastSuccess('Data berhasil disimpan')
    setShowModal(false)
    form.reset()
}
```

### Error Pattern
```typescript
onError: () => {
    toastError('Gagal menyimpan data')
}
```

### Loading Pattern
```typescript
onStart: () => {
    toastLoading('Memproses...')
}
```

---

## Permissions

### Display Based on Permissions

```typescript
// Show/hide create button
{auth.permissions.includes('branches.create') && (
    <Button onClick={() => setShowCreateModal(true)}>
        <Plus className="mr-2 h-4 w-4" />
        Tambah Cabang
    </Button>
)}
```

### User Permissions
- `users.view` - View users list
- `users.create` - Create users
- `users.update` - Edit users
- `users.delete` - Delete users
- `users.manage_roles` - Change user roles

### Branch Permissions
- `branches.view` - View branches list
- `branches.create` - Create branches
- `branches.update` - Edit branches
- `branches.delete` - Delete branches

---

## Verification & Testing

### Manual Testing Checklist

#### Branches Page
- [ ] Create branch with all fields
- [ ] Create branch validation errors (code duplicate, name empty)
- [ ] Edit branch (update name, city)
- [ ] Edit branch validation errors
- [ ] Delete branch (success)
- [ ] Delete branch with dependencies (blocked, shows warning)
- [ ] Search by name
- [ ] Search by code
- [ ] Status badge displays correctly (active/inactive)
- [ ] Headquarters badge displays correctly

#### Users Page
- [ ] Create user with all fields including role
- [ ] Create user validation errors
- [ ] Edit user (update name, email, role)
- [ ] Edit user validation errors
- [ ] Delete user (success)
- [ ] Delete self (blocked with warning)
- [ ] Search by name
- [ ] Search by email
- [ ] Role selection works
- [ ] Branch selection works
- [ ] Toast notifications display correctly
- [ ] Modals open/close correctly
- [ ] Forms reset after success

### Browser Testing
1. Open browser DevTools Console
2. Monitor Network tab for API calls
3. Verify correct routes are called:
   - `POST /branches` (create)
   - `PUT /branches/{id}` (update)
   - `DELETE /branches/{id}` (delete)
   - `POST /users` (create)
   - `PUT /users/{id}` (update)
   - `DELETE /users/{id}` (delete)
4. Check for console errors
5. Test form submissions with invalid data
6. Verify server-side error messages display

### Build Verification
```bash
npm run build
```

Ensure no TypeScript or build errors.

---

## File Size Targets

- `resources/js/types/user.ts`: ~30 lines
- `resources/js/types/branch.ts`: ~30 lines
- `resources/js/pages/Branches/Index.tsx`: ~350 lines
- `resources/js/pages/Users/Index.tsx`: ~400 lines

---

## Notes

1. **No backend changes needed** - All routes, controllers, services, and policies exist
2. **Activity Log** - Requires new backend route, implementing placeholder modal for now
3. **Immutability** - Always use spread operator for form data updates
4. **Indonesian UI** - All labels and messages in Indonesian
5. **Wayfinder** - Routes auto-generated, import from @/actions/App/Http/Controllers/*
