Driver Drawer
Comprehensive drawer component for creating, viewing, and editing drivers with project access management
Live Example
Create New Driver
Open the drawer to create a new driver with name, username, password, and organization code.
View & Edit Existing Driver
Select a driver to view their details. In view mode, you can click Edit to modify the driver's information, manage project access, or change their password.
Select a driver to view and edit:
Features Overview
The DriverDrawer component supports multiple modes and features.
Modes:
- Create Mode: Create new drivers with credentials
- View Mode: Read-only display with Edit and Change Password buttons
- Edit Mode: Modify driver info and manage project access
Features:
- Project access management (participation-type only)
- Password change with validation
- Username splitting (editable@readonly)
- Loading indicators for all operations
- Full localization support (EN/JA)
- Dual-API pattern for data consistency
Driver Drawer
A full-featured drawer component for driver management. Supports creating new drivers, viewing driver details, editing driver information, managing project access, and changing passwords.
Features
- Three Modes: Create, View, and Edit modes with appropriate UI states
- Driver Management: Create new drivers or edit existing ones (name, username, password)
- Project Access Control: Assign and remove participation-type projects for drivers
- Password Management: Change password functionality with validation
- Username Handling: Smart username display splitting organization code from editable username
- Dual-API Pattern: Writes to Tastypie API, refreshes from GET API for consistency
- Collection Integration: Automatically updates driver and projectmember collections
- Loading States: Comprehensive loading indicators for all operations
- Error Handling: Clear error messages for validation and API failures
- Localization: Full English and Japanese translation support
Usage
Create New Driver
import { DriverDrawer } from '@/components/sgerp/driver-drawer';
import { useSGERP } from 'sgerp-frontend-lib';
import { useState } from 'react';
function MyComponent() {
const api = useSGERP();
const [drawerOpen, setDrawerOpen] = useState(false);
return (
<>
<button onClick={() => setDrawerOpen(true)}>
Create Driver
</button>
<DriverDrawer
open={drawerOpen}
onOpenChange={setDrawerOpen}
api={api}
onSuccess={(result) => {
console.log('Driver created:', result);
setDrawerOpen(false);
}}
/>
</>
);
}
View Existing Driver
import { DriverDrawer } from '@/components/sgerp/driver-drawer';
import { useSGERP } from 'sgerp-frontend-lib';
import { useState } from 'react';
function MyComponent() {
const api = useSGERP();
const [selectedDriverId, setSelectedDriverId] = useState<number | undefined>();
const [drawerOpen, setDrawerOpen] = useState(false);
const handleRowClick = (driverId: number) => {
setSelectedDriverId(driverId);
setDrawerOpen(true);
};
return (
<>
{/* Your driver table/list here */}
<DriverDrawer
open={drawerOpen}
onOpenChange={(open) => {
if (!open) setSelectedDriverId(undefined);
setDrawerOpen(open);
}}
driverId={selectedDriverId}
api={api}
onSuccess={(result) => {
console.log('Driver updated:', result);
}}
/>
</>
);
}
With DriverTable Integration
import { DriverTable } from '@/components/sgerp/tables/driver-table';
import { DriverDrawer } from '@/components/sgerp/driver-drawer';
import { useSGERP } from 'sgerp-frontend-lib';
import { useState } from 'react';
function DriversPage() {
const api = useSGERP();
const [drawerOpen, setDrawerOpen] = useState(false);
const [selectedDriverId, setSelectedDriverId] = useState<number | undefined>();
const handleRowClick = (driver: Driver) => {
setSelectedDriverId(driver.id);
setDrawerOpen(true);
};
const handleDrawerOpenChange = (open: boolean) => {
if (!open) setSelectedDriverId(undefined);
setDrawerOpen(open);
};
return (
<div>
<button onClick={() => {
setSelectedDriverId(undefined);
setDrawerOpen(true);
}}>
Create Driver
</button>
<DriverTable
collection={api?.collections.driver ?? null}
onRowClick={handleRowClick}
/>
<DriverDrawer
open={drawerOpen}
onOpenChange={handleDrawerOpenChange}
driverId={selectedDriverId}
api={api}
onSuccess={(result) => {
// Refresh driver list if needed
api?.collections.driver.fetch({});
}}
/>
</div>
);
}
Props
| Prop | Type | Required | Description |
|---|---|---|---|
open | boolean | Yes | Controls drawer open state |
onOpenChange | (open: boolean) => void | Yes | Callback when drawer open state changes |
driverId | number | No | ID of driver to view/edit (omit for create mode) |
initialData | Driver | No | Pre-loaded driver data to avoid fetching |
api | ReturnType<typeof useSGERP> | No | Shared API instance (highly recommended) |
onSuccess | (result: { user_id: number; driver_id: number }) => void | No | Callback when create/update succeeds |
Modes
Create Mode
- Triggered when
driverIdis not provided - Shows form for creating new driver with name, username, password
- Organization code required for username format
View Mode
- Triggered when
driverIdis provided - Shows read-only driver information
- Displays assigned projects
- Shows Edit button to switch to Edit mode
- Shows Change Password button
Edit Mode
- Triggered by clicking Edit button in View mode
- Allows editing name and username (organization code read-only)
- Manages project access (add/remove projects)
- Shows Save and Close buttons
Form Fields
Create Mode Fields
- Driver Name: Full name of the driver (required)
- Username: Username part before @ (required)
- Password: Initial password (required, min 8 characters)
- Organization Code: Organization identifier (required)
Edit Mode Fields
- Driver ID: Non-editable reference ID
- User ID: Non-editable user reference ID
- Driver Name: Editable full name
- Username: Editable username part (organization code read-only)
- Projects: List of assigned projects with add/remove capabilities
Project Management
The driver drawer includes comprehensive project access management:
Features
- Loads only participation-type projects from driver's organization
- Displays currently assigned projects with organization info
- Add projects via dropdown (shows "already granted" for assigned projects)
- Remove project access with confirmation
- Loading indicators for add/remove operations
- Automatic collection updates
Project Filters
Only shows participation-type projects: project__organizations__type=participation
Example Project Management Flow
// User opens driver in view mode
// Clicks "Edit" button
// In "Projects" section:
// - See list of assigned projects
// - Click "Remove" to revoke access
// - Use dropdown to add new projects
// Click "Save" to persist changes
Password Management
Change Password Feature
- Available in View and Edit modes
- Expandable form with password and confirm password fields
- Validation: Password required, passwords must match
- Updates via PATCH to
/api/v2/user/{user_id}withset_password - Clears form on success
Example
// In view/edit mode:
// Click "Change Password" button
// Form expands with:
// - New Password input
// - Confirm New Password input
// - Update Password button (with spinner)
// - Cancel button
Username Display Logic
Usernames follow the format username@organization_code:
Create Mode
- User enters username and organization code separately
- Combined on save:
${username}@${organizationCode}
View/Edit Mode
- Username split on
@character - Editable part:
username(before @) - Read-only part:
organization_code(after @) - Reconstructed on save
Dual-API Implementation
The drawer follows the dual-API pattern:
Create Driver
- POST to
/api/v2/driver/(Tastypie API) with user and driver data - Refresh from GET API to get consistent format
- Update driver collection using
add()
Edit Driver
- PATCH to
/api/v2/user/{user_id}for name and username - PATCH to
/api/v2/driver/{driver_id}for driver name - GET fresh data from microservices API
- Merge with existing nested data (user, projectMembers)
- Update collection using
add()to preserve other models
Project Access
- Add: POST to
/api/v2/projectmember/with resource URIs - Remove: DELETE to
/api/v2/projectmember/{id}/ - Refresh from GET API
- Update both projectmember and driver collections
Loading States
The drawer provides loading indicators for:
- Initial driver data fetch
- Projects data fetch
- Save operation (Create/Update)
- Add project operation
- Remove project operation (per-project spinner)
- Password change operation
Error Handling
Comprehensive error handling for:
- Network errors with API messages
- Validation errors (required fields, password mismatch)
- Loading errors (driver not found)
- Project operation errors
Data Preservation
IMPORTANT: The drawer uses collection.add() instead of collection.fetch() after updates to preserve all rows in parent tables and maintain nested data structures.
Why This Matters
fetch({ id })callsreset()which clears all other models from collectionadd([data])merges by ID without clearing other models- Nested data (user, projectMembers) is preserved from existing driver
Localization
Fully localized with keys in sgerplib/locales/:
driver.create_driver,driver.edit_driver,driver.view_driverdriver.projects,driver.add_project_placeholder,driver.no_projectsdriver.change_password,driver.new_password,driver.confirm_new_password- All error messages and form labels
Styling
Built with:
- Radix UI Sheet component for drawer
- Tailwind CSS for styling
- Lucide React icons
- Theme-aware colors
Related
- Driver Table - Table component for listing drivers
- Driver Collection - API reference for drivers
- Dual API Architecture - Understanding the dual-API pattern
- Project Management - Similar pattern for projects