---
source_hash: "5390ebef"
title: "Chapter 3: Other Features"
weight: 730
---

# Chapter 3: Other Features

## Geolocation

### Getting the position

```javascript
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

```javascript
import { Gps } from '@cap-rel/smartcommon';

<Gps
    name="location"
    label="Intervention location"
    onChange={(coords) => console.log(coords)}
/>
```

## File upload

### PhotosUploader

```javascript
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

```javascript
import { FilesUploader } from '@cap-rel/smartcommon';

<FilesUploader
    name="documents"
    label="Documents"
    accept=".pdf,.doc,.docx"
    multiple
    maxFiles={10}
/>
```

### Resizing an image

```javascript
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

```javascript
// appConfig.js
export const config = {
    globalState: {
        reducers: {
            settings: {
                theme: 'light'  // 'light' or 'dark'
            }
        }
    }
};
```

### Applying the theme

```javascript
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

```javascript
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

```javascript
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`:

```bash
npm install @zxing/browser @zxing/library
```

```javascript
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

```javascript
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

1. **Geolocation** with navigator.geolocation or the Gps component
2. **Upload** with PhotosUploader, FilesUploader, useFile
3. **Dark theme** via useGlobalStates and CSS classes
4. **Signature** with SignaturePad
5. Integrate **third-party libraries** as needed

[Previous Chapter](/training/module10-fonctionnalites-avancees/i18n) | [Back to Module](/training/module10-fonctionnalites-avancees) | [Next Module: Best Practices ->](/training/module11-bonnes-pratiques)
