Chapter 3: Other Features
Geolocation
Getting the position
import { useState } from 'react';
import { Button, Block } from '@cap-rel/smartcommon';
function LocationButton({ onLocation }) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const getLocation = () => {
if (!navigator.geolocation) {
setError('Geolocation not supported');
return;
}
setLoading(true);
setError(null);
navigator.geolocation.getCurrentPosition(
(position) => {
setLoading(false);
onLocation({
lat: position.coords.latitude,
lng: position.coords.longitude,
accuracy: position.coords.accuracy
});
},
(err) => {
setLoading(false);
setError(err.message);
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
};
return (
<Block>
<Button onClick={getLocation} loading={loading}>
Get my position
</Button>
{error && <p className="text-red-500 mt-2">{error}</p>}
</Block>
);
}
SmartCommon Gps component
import { Gps } from '@cap-rel/smartcommon';
<Gps
name="location"
label="Intervention location"
onChange={(coords) => console.log(coords)}
/>
File upload
PhotosUploader
import { PhotosUploader } from '@cap-rel/smartcommon';
<PhotosUploader
name="photos"
label="Photos"
maxFiles={5}
maxSize={5 * 1024 * 1024} // 5 MB
onChange={(files) => console.log(files)}
/>
FilesUploader
import { FilesUploader } from '@cap-rel/smartcommon';
<FilesUploader
name="documents"
label="Documents"
accept=".pdf,.doc,.docx"
multiple
maxFiles={10}
/>
Resizing an image
import { useFile } from '@cap-rel/smartcommon';
function ImageUploader() {
const { resizeImage } = useFile();
const handleFile = async (e) => {
const file = e.target.files[0];
const base64 = await resizeImage(file, {
maxWidth: 1200,
maxHeight: 1200,
quality: 80
});
// Send to the server
await api.private.post('files', {
json: {
filename: file.name,
content: base64
}
});
};
return <input type="file" accept="image/*" onChange={handleFile} />;
}
Dark theme
Global configuration
// appConfig.js
export const config = {
globalState: {
reducers: {
settings: {
theme: 'light' // 'light' or 'dark'
}
}
}
};
Applying the theme
import { useEffect } from 'react';
import { useGlobalStates } from '@cap-rel/smartcommon';
function ThemeProvider({ children }) {
const gst = useGlobalStates();
useEffect(() => {
document.documentElement.classList.toggle(
'dark',
gst.get('settings')?.theme === 'dark'
);
}, [gst.get('settings')?.theme]);
return children;
}
Theme toggle
import { useGlobalStates } from '@cap-rel/smartcommon';
import { Boolean } from '@cap-rel/smartcommon';
function ThemeToggle() {
const gst = useGlobalStates();
const settings = gst.get('settings');
const toggleTheme = () => {
gst.local.set('settings', {
...settings,
theme: settings.theme === 'light' ? 'dark' : 'light'
});
};
return (
<Boolean
value={settings?.theme === 'dark'}
onChange={toggleTheme}
label="Dark mode"
/>
);
}
Handwritten signature
import { SignaturePad } from '@cap-rel/smartcommon';
<SignaturePad
name="signature"
label="Customer signature"
width={400}
height={200}
/>
Barcode scanning
Use a library such as @zxing/browser:
npm install @zxing/browser @zxing/library
import { useState, useRef, useEffect } from 'react';
import { BrowserMultiFormatReader } from '@zxing/browser';
function BarcodeScanner({ onScan }) {
const videoRef = useRef(null);
const [scanning, setScanning] = useState(false);
useEffect(() => {
let reader;
if (scanning) {
reader = new BrowserMultiFormatReader();
reader.decodeFromVideoDevice(
undefined,
videoRef.current,
(result, error) => {
if (result) {
onScan(result.getText());
setScanning(false);
}
}
);
}
return () => {
if (reader) {
reader.reset();
}
};
}, [scanning]);
return (
<div>
{scanning ? (
<video ref={videoRef} className="w-full" />
) : (
<button onClick={() => setScanning(true)}>
Scan a code
</button>
)}
</div>
);
}
Local notifications
function requestNotificationPermission() {
if ('Notification' in window) {
Notification.requestPermission().then(permission => {
console.log('Permission:', permission);
});
}
}
function showNotification(title, body) {
if (Notification.permission === 'granted') {
new Notification(title, { body });
}
}
Key takeaways
- Geolocation with navigator.geolocation or the Gps component
- Upload with PhotosUploader, FilesUploader, useFile
- Dark theme via useGlobalStates and CSS classes
- Signature with SignaturePad
- Integrate third-party libraries as needed
Previous Chapter | Back to Module | Next Module: Best Practices ->