Notification Drawer
Form component for scheduling SMS and push notifications to organizations
Live Example
Create Notification - Send Now
Fill in the form to create a notification that will be sent immediately. Select an organization, enter title and message body.
Schedule for Later
Toggle to 'Schedule for Later' to see timezone, date, and time selectors. The component includes smart time validation that prevents scheduling in the past.
💡 Tip:Switch to "Schedule for Later" to see the date/time pickers. Times are rounded to 15-minute intervals, and you cannot schedule in the past.
With iPhone Preview
Set showPreview={true} to display a live iPhone notification preview that updates in real-time as you type.
Features Overview
The NotificationDrawer component includes many smart features for scheduling notifications.
Send Timing Options:
- Send Now: Immediate delivery
- Schedule for Later: Choose date, time, and timezone
Smart Features:
- Auto-detects browser timezone
- 15-minute time intervals
- Prevents past scheduling
- Automatic project resolution from organization
- UUID schedule ID generation
- Real-time validation
Validation:
- Organization required
- Title and body required (non-empty)
- Date and time required for scheduled delivery
- Project must resolve from organization
Props:
api- SGERP API instance (required)showPreview- Show iPhone preview (default: false)onSuccess- Called when notification is createdonCancel- Called when user cancels
Notification Drawer
A comprehensive form component for creating and scheduling SMS messages and push notifications to organizations. Supports immediate sending or scheduled delivery with timezone awareness and smart time validation.
Features
- Dual Notification Types: Supports both SMS messages and push notifications (despite legacy
smsschedulednaming) - Send Timing Options: Send immediately or schedule for later
- Timezone Support: Auto-detects browser timezone with option to customize
- Smart Time Validation: Prevents scheduling in the past, rounds to 15-minute intervals
- Organization Selection: Autocomplete dropdown for organization selection
- Project Resolution: Automatically finds associated project from organization
- Date/Time Pickers: Calendar and time selection with validation
- UUID Generation: Automatic schedule ID generation
- Dual-API Pattern: Writes to Tastypie API, refreshes from GET API
- Collection Integration: Uses
prepend()to add new notifications at top of list - Full Localization: English and Japanese translation support
- Loading States: Visual feedback during save operation
- Error Handling: Comprehensive validation and error messages
Usage
Basic Usage
import { NotificationDrawer } from '@/components/sgerp/editors/notification-drawer';
import { useSGERP } from 'sgerp-frontend-lib';
function MyComponent() {
const api = useSGERP();
return (
<NotificationDrawer
api={api}
onSuccess={(notification) => {
console.log('Notification created:', notification);
}}
onCancel={() => {
console.log('Cancelled');
}}
/>
);
}
In a Drawer/Sheet
import { NotificationDrawer } from '@/components/sgerp/editors/notification-drawer';
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { useSGERP } from 'sgerp-frontend-lib';
import { useState } from 'react';
function MyComponent() {
const api = useSGERP();
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(true)}>
Create Notification
</button>
<Sheet open={open} onOpenChange={setOpen}>
<SheetContent>
<SheetHeader>
<SheetTitle>Schedule Notification</SheetTitle>
</SheetHeader>
<div className="mt-6">
<NotificationDrawer
api={api}
onSuccess={(notification) => {
console.log('Created:', notification);
setOpen(false);
}}
onCancel={() => setOpen(false)}
/>
</div>
</SheetContent>
</Sheet>
</>
);
}
With Preview Callbacks
The drawer provides callbacks for real-time preview of notification content:
import { NotificationDrawer } from '@/components/sgerp/editors/notification-drawer';
import { useSGERP } from 'sgerp-frontend-lib';
import { useState } from 'react';
function MyComponent() {
const api = useSGERP();
const [previewTitle, setPreviewTitle] = useState('');
const [previewBody, setPreviewBody] = useState('');
const [sendTiming, setSendTiming] = useState<'now' | 'later'>('now');
return (
<div className="grid grid-cols-2 gap-6">
{/* Form */}
<NotificationDrawer
api={api}
onTitleChange={setPreviewTitle}
onBodyChange={setPreviewBody}
onSendTimingChange={setSendTiming}
onSuccess={(notification) => {
console.log('Created:', notification);
}}
/>
{/* Preview */}
<div className="border rounded-lg p-4">
<h3 className="font-medium mb-2">Preview</h3>
<div className="space-y-2">
<div>
<strong>Title:</strong> {previewTitle || '(empty)'}
</div>
<div>
<strong>Body:</strong> {previewBody || '(empty)'}
</div>
<div>
<strong>Send:</strong> {sendTiming === 'now' ? 'Immediately' : 'Scheduled'}
</div>
</div>
</div>
</div>
);
}
Props
| Prop | Type | Required | Description |
|---|---|---|---|
api | ReturnType<typeof useSGERP> | No | Shared API instance (recommended for collection updates) |
onSuccess | (notification: SMSScheduled) => void | No | Callback when notification is created successfully |
onCancel | () => void | No | Callback when user cancels |
onTitleChange | (title: string) => void | No | Callback when title changes (for live preview) |
onBodyChange | (body: string) => void | No | Callback when body changes (for live preview) |
onSendTimingChange | (timing: 'now' | 'later') => void | No | Callback when send timing changes |
onDateChange | (date: Date | undefined) => void | No | Callback when date changes |
onTimeChange | (time: string) => void | No | Callback when time changes |
Form Fields
Required Fields
- Organization: Organization to send notification to (autocomplete dropdown)
- Title: Notification title (string)
- Body: Notification message content (textarea)
- Send Timing: "Send Now" or "Schedule for Later" (toggle)
Optional Fields
- URL: Optional URL link for notification (string)
Conditional Fields (When "Schedule for Later" is selected)
- Timezone: Timezone for scheduling (auto-detected, customizable)
- Date: Date to send notification (calendar picker)
- Time: Time to send notification (15-minute intervals)
Send Timing Options
Send Now
- Sends notification immediately
- Sets
scheduled_atto current timestamp - No date/time selection required
Schedule for Later
- Displays timezone, date, and time selectors
- Smart validation prevents past scheduling
- Time rounded to 15-minute intervals
- Auto-clears invalid times when date changes
Smart Time Validation
The component includes intelligent time validation:
Minimum Time Calculation
- If today is selected: minimum time is current time + 15 minutes (rounded)
- If future date is selected: no minimum time restriction
- Times before minimum are automatically cleared
15-Minute Intervals
- Time picker shows only 15-minute intervals (e.g., 09:00, 09:15, 09:30)
- Ensures consistent scheduling
Example
Current time: 14:37
Minimum time: 14:45 (rounded up to next 15-min interval)
Available times: 14:45, 15:00, 15:15, 15:30, ...
Timezone Handling
Auto-Detection
- Browser timezone automatically detected on component mount
- Uses
Intl.DateTimeFormat().resolvedOptions().timeZone
Customization
- User can select different timezone from dropdown
- Affects how scheduled time is interpreted
Example
Browser timezone: America/New_York
Selected date/time: 2025-10-20 15:00
Scheduled: 2025-10-20T15:00:00 in America/New_York timezone
Project Resolution
The component automatically resolves the project ID from the selected organization:
- User selects organization
- Component fetches
organizationprojectrecords - Finds record with
type: 'main' - Extracts
project_idfor API submission
This happens automatically in the background.
Dual-API Implementation
Follows the dual-API pattern:
-
Write: POST to
/api/v2/smsscheduled/(Tastypie API){ title: "Notification Title", content: "Message body", url: "https://example.com", message_delivery_type: "push", recipient: "organization_code", recipient_type: "organization", project: "/api/v2/project/123/", scheduled_at: "2025-10-20T15:00:00Z", timezone: "America/New_York", schedule_id: "uuid-v4-string" } -
Refresh: GET from
/api/v2/microservices/get?model=smsscheduled&id={id} -
Collection Update: Uses
prepend()to add notification at top of listapi.collections.smsscheduled.prepend([freshData]);
UUID Generation
Each notification gets a unique schedule_id (UUID v4):
- Generated client-side using random number generation
- Format:
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx - Used for tracking and deduplication
Validation
Client-Side Validation
- Organization required
- Title required (non-empty)
- Body required (non-empty)
- If "later": date and time required
- Project ID must resolve from organization
Error Messages
All validation errors display in localized error banner:
- "Please select an organization"
- "Title is required"
- "Message body is required"
- "Please select date and time"
- "Project not found for organization"
Localization
Uses localization keys from sgerplib/locales/:
Form Labels
notification.organization_label/notification.organization_placeholdernotification.title_label/notification.title_placeholdernotification.body_label/notification.body_placeholdernotification.url_label/notification.url_placeholdernotification.send_timing_labelnotification.send_now/notification.send_laternotification.timezone_label/notification.timezone_placeholdernotification.date_label/notification.date_placeholdernotification.time_label/notification.time_placeholder
Buttons and Messages
notification.create_button/notification.creatingcommon.cancel
Error Messages
notification.error_select_organizationnotification.error_title_requirednotification.error_body_requirednotification.error_datetime_requirednotification.error_project_requirednotification.error_create_failednotification.error_refresh_failed
Styling
Built with:
- Tailwind CSS for all styling
- shadcn/ui components (Button, Calendar, Popover, ToggleGroup)
- Lucide React icons (CalendarIcon)
- Theme-aware colors (works with light/dark themes)
Example: Complete Notifications Page
import { useSGERP } from 'sgerp-frontend-lib';
import { SMSScheduledTable } from '@/components/sgerp/tables/smsscheduled-table';
import { NotificationDrawer } from '@/components/sgerp/editors/notification-drawer';
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { Button } from '@/components/ui/button';
import { useState } from 'react';
import { Plus } from 'lucide-react';
export default function NotificationsPage() {
const api = useSGERP();
const [drawerOpen, setDrawerOpen] = useState(false);
return (
<div className="flex-1 flex flex-col gap-6 min-h-0">
<div className="flex items-start justify-between">
<div>
<h1 className="text-3xl font-bold">Notifications</h1>
<p className="text-muted-foreground mt-2">
Schedule SMS and push notifications
</p>
</div>
<Button onClick={() => setDrawerOpen(true)}>
<Plus className="h-4 w-4 mr-2" />
Create Notification
</Button>
</div>
<div className="flex-1 min-h-0">
<SMSScheduledTable
collection={api?.collections.smsscheduled ?? null}
fullscreenHeight
/>
</div>
<Sheet open={drawerOpen} onOpenChange={setDrawerOpen}>
<SheetContent>
<SheetHeader>
<SheetTitle>Schedule Notification</SheetTitle>
</SheetHeader>
<div className="mt-6">
<NotificationDrawer
api={api}
onSuccess={(notification) => {
console.log('Created:', notification);
setDrawerOpen(false);
}}
onCancel={() => setDrawerOpen(false)}
/>
</div>
</SheetContent>
</Sheet>
</div>
);
}
Dependencies
Required packages:
react(18+)sgerp-frontend-lib(collections, hooks, types)lucide-react(icons)
Components used:
@/components/ui/button(shadcn/ui)@/components/ui/calendar(shadcn/ui)@/components/ui/popover(shadcn/ui)@/components/ui/toggle-group(shadcn/ui)SGERPAutocompletefrom sgerp-frontend-libTimezoneSelectfrom sgerp-frontend-libTimeSelectfrom sgerp-frontend-lib
Related
- SMSScheduled Table - Table component for listing notifications
- SMSScheduled API - API reference for notifications
- Organization API - API reference for organizations
- Dual API Architecture - Understanding the dual-API pattern