dstpabuaran.com/resources/js/pages/settings/permissions.tsx
Yoga Pangestu aecd8eaf1f Refactor security settings page and update password functionality
- Translated UI elements to Indonesian for better localization.
- Improved layout by introducing Card components for better visual structure.
- Removed unnecessary imports and props related to passkeys and two-factor authentication.
- Updated tests to cover new password update scenarios and validation rules.
- Ensured proper redirection and error handling for unauthorized access to security settings.
- Enhanced user experience by adding success flash messages on password updates.
2026-07-30 22:08:24 +07:00

189 lines
7.6 KiB
TypeScript

import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Switch } from '@/components/ui/switch';
import { edit } from '@/routes/permissions';
import { Head } from '@inertiajs/react';
import { Bell, Camera, MapPin } from 'lucide-react';
import { useEffect, useState } from 'react';
type PermissionStatus = 'granted' | 'denied' | 'prompt' | 'unknown';
export default function Permissions() {
const [notifications, setNotifications] = useState<PermissionStatus>('unknown');
const [camera, setCamera] = useState<PermissionStatus>('unknown');
const [location, setLocation] = useState<PermissionStatus>('unknown');
useEffect(() => {
if ('Notification' in window) {
if (Notification.permission === 'granted') setNotifications('granted');
else if (Notification.permission === 'denied') setNotifications('denied');
else setNotifications('prompt');
}
if (navigator.mediaDevices) {
navigator.mediaDevices.getUserMedia({ video: true }).then((stream) => {
stream.getTracks().forEach((track) => track.stop());
setCamera('granted');
}).catch(() => {
if ('permissions' in navigator) {
navigator.permissions.query({ name: 'camera' as PermissionName }).then((result) => {
setCamera(result.state as PermissionStatus);
}).catch(() => setCamera('denied'));
} else {
setCamera('denied');
}
});
} else {
setCamera('denied');
}
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(
() => setLocation('granted'),
() => {
if ('permissions' in navigator) {
navigator.permissions.query({ name: 'geolocation' }).then((result) => {
setLocation(result.state as PermissionStatus);
}).catch(() => setLocation('denied'));
} else {
setLocation('denied');
}
},
);
} else {
setLocation('denied');
}
}, []);
const handleNotifications = async (checked: boolean) => {
if (checked) {
if (!('Notification' in window)) return;
if (Notification.permission === 'denied') {
setNotifications('denied');
return;
}
const result = await Notification.requestPermission();
setNotifications(result === 'granted' ? 'granted' : result === 'denied' ? 'denied' : 'prompt');
} else {
setNotifications('prompt');
}
};
const handleCamera = async (checked: boolean) => {
if (checked) {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
stream.getTracks().forEach((track) => track.stop());
setCamera('granted');
} catch {
setCamera('denied');
}
} else {
setCamera('prompt');
}
};
const handleLocation = async (checked: boolean) => {
if (checked) {
navigator.geolocation.getCurrentPosition(
() => setLocation('granted'),
() => setLocation('denied'),
);
} else {
setLocation('prompt');
}
};
const isDenied = (status: PermissionStatus) => status === 'denied';
return (
<>
<Head title="Izin" />
<h1 className="sr-only">Izin</h1>
<div className="flex h-full flex-1 flex-col gap-4 overflow-x-auto px-4 md:px-6">
<Alert variant={isDenied(notifications) ? 'destructive' : 'default'}>
<Bell className="h-4 w-4" />
<div className="flex items-center justify-between">
<div className="flex-1">
<AlertTitle>Notifikasi</AlertTitle>
<AlertDescription>
Izinkan aplikasi mengirimkan notifikasi push ke perangkat Anda.
</AlertDescription>
{isDenied(notifications) && (
<p className="mt-1 text-sm text-destructive">
Izin ditolak. Klik ikon gembok di address bar untuk mengaktifkannya.
</p>
)}
</div>
<div className="flex items-center gap-2">
<Switch
checked={notifications === 'granted'}
onCheckedChange={handleNotifications}
disabled={isDenied(notifications)}
/>
</div>
</div>
</Alert>
<Alert variant={isDenied(camera) ? 'destructive' : 'default'}>
<Camera className="h-4 w-4" />
<div className="flex items-center justify-between">
<div className="flex-1">
<AlertTitle>Kamera</AlertTitle>
<AlertDescription>
Izinkan aplikasi mengakses kamera perangkat Anda untuk mengambil foto atau video.
</AlertDescription>
{isDenied(camera) && (
<p className="mt-1 text-sm text-destructive">
Izin ditolak. Klik ikon gembok di address bar untuk mengaktifkannya.
</p>
)}
</div>
<div className="flex items-center gap-2">
<Switch
checked={camera === 'granted'}
onCheckedChange={handleCamera}
disabled={isDenied(camera)}
/>
</div>
</div>
</Alert>
<Alert variant={isDenied(location) ? 'destructive' : 'default'}>
<MapPin className="h-4 w-4" />
<div className="flex items-center justify-between">
<div className="flex-1">
<AlertTitle>Lokasi</AlertTitle>
<AlertDescription>
Izinkan aplikasi mengakses lokasi perangkat Anda untuk menyediakan layanan berbasis lokasi.
</AlertDescription>
{isDenied(location) && (
<p className="mt-1 text-sm text-destructive">
Izin ditolak. Klik ikon gembok di address bar untuk mengaktifkannya.
</p>
)}
</div>
<div className="flex items-center gap-2">
<Switch
checked={location === 'granted'}
onCheckedChange={handleLocation}
disabled={isDenied(location)}
/>
</div>
</div>
</Alert>
</div>
</>
);
}
Permissions.layout = {
breadcrumbs: [
{
title: 'Izin',
href: edit(),
},
],
};