Phase 8: Visitor Management - Visitor/VisitorLog/AccessZone models - Check-in/out with badge generation - Zone occupancy tracking - Kiosk and management pages Phase 9: Messaging & Communication - Announcements with priority levels - Acknowledgement tracking - Shift notes for team handoffs - AnnouncementBanner component Phase 10: Compliance & Audit Trail - Immutable AuditLog model - Document versioning and approval workflow - Acknowledgement tracking for SOPs - CSV export for audit logs Phase 11: Accessibility & i18n - WCAG 2.1 AA compliance utilities - react-i18next with EN/ES translations - User preferences context (theme, font size, etc) - High contrast and reduced motion support Phase 12: Hardware Integration - QR code generation for batches/plants/visitors - Printable label system - Visitor badge printing Phase 13: Advanced Features - Environmental monitoring (sensors, readings, alerts) - Financial tracking (transactions, P&L reports) - AI/ML insights (yield predictions, anomaly detection)
60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
// Haptic feedback utilities for mobile devices
|
|
|
|
type HapticStyle = 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error';
|
|
|
|
export function hapticFeedback(style: HapticStyle = 'light') {
|
|
// Check if vibration API is available
|
|
if (!navigator.vibrate) return;
|
|
|
|
// Only trigger on touch devices
|
|
if (!('ontouchstart' in window)) return;
|
|
|
|
const patterns: Record<HapticStyle, number | number[]> = {
|
|
light: 10,
|
|
medium: 20,
|
|
heavy: 40,
|
|
success: [20, 50, 20], // Short-pause-short
|
|
warning: [30, 30, 30], // Three quick pulses
|
|
error: [50, 50, 50, 50, 100], // Intense pattern
|
|
};
|
|
|
|
try {
|
|
navigator.vibrate(patterns[style]);
|
|
} catch (e) {
|
|
// Silently fail - vibration not critical
|
|
}
|
|
}
|
|
|
|
// iOS haptic using AudioContext (for devices that support it)
|
|
export function iosHaptic() {
|
|
if (typeof AudioContext === 'undefined') return;
|
|
|
|
try {
|
|
const audioCtx = new AudioContext();
|
|
const oscillator = audioCtx.createOscillator();
|
|
const gainNode = audioCtx.createGain();
|
|
|
|
oscillator.connect(gainNode);
|
|
gainNode.connect(audioCtx.destination);
|
|
|
|
oscillator.type = 'sine';
|
|
oscillator.frequency.value = 200;
|
|
gainNode.gain.value = 0;
|
|
|
|
oscillator.start();
|
|
oscillator.stop(audioCtx.currentTime + 0.01);
|
|
} catch (e) {
|
|
// Silently fail
|
|
}
|
|
}
|
|
|
|
// Hook for haptic feedback
|
|
import { useCallback } from 'react';
|
|
|
|
export function useHaptic() {
|
|
const trigger = useCallback((style: HapticStyle = 'light') => {
|
|
hapticFeedback(style);
|
|
}, []);
|
|
|
|
return { trigger };
|
|
}
|