Fix jwt-token
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import { useSocket } from '../contexts/SocketContext';
|
import { useSocket } from '../contexts/SocketContext';
|
||||||
|
import { useTranslation } from '../utils/tempTranslations';
|
||||||
import {
|
import {
|
||||||
ExclamationTriangleIcon,
|
ExclamationTriangleIcon,
|
||||||
InformationCircleIcon,
|
InformationCircleIcon,
|
||||||
@@ -13,6 +14,7 @@ import {
|
|||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
const MovementAlertsPanel = () => {
|
const MovementAlertsPanel = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { movementAlerts, clearMovementAlerts } = useSocket();
|
const { movementAlerts, clearMovementAlerts } = useSocket();
|
||||||
const [expandedAlert, setExpandedAlert] = useState(null);
|
const [expandedAlert, setExpandedAlert] = useState(null);
|
||||||
const [filter, setFilter] = useState('all'); // all, critical, high, medium
|
const [filter, setFilter] = useState('all'); // all, critical, high, medium
|
||||||
@@ -55,12 +57,12 @@ const MovementAlertsPanel = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const droneTypes = {
|
const droneTypes = {
|
||||||
1: "DJI Mavic",
|
1: t('movementAlerts.droneTypes.djiMavic'),
|
||||||
2: "Racing Drone",
|
2: t('movementAlerts.droneTypes.racingDrone'),
|
||||||
3: "DJI Phantom",
|
3: t('movementAlerts.droneTypes.djiPhantom'),
|
||||||
4: "Fixed Wing",
|
4: t('movementAlerts.droneTypes.fixedWing'),
|
||||||
5: "Surveillance",
|
5: t('movementAlerts.droneTypes.surveillance'),
|
||||||
0: "Unknown"
|
0: t('movementAlerts.droneTypes.unknown')
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -68,7 +70,7 @@ const MovementAlertsPanel = () => {
|
|||||||
<div className="px-6 py-4 border-b border-gray-200">
|
<div className="px-6 py-4 border-b border-gray-200">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<h3 className="text-lg font-medium text-gray-900">Movement Alerts</h3>
|
<h3 className="text-lg font-medium text-gray-900">{t('movementAlerts.title')}</h3>
|
||||||
{movementAlerts.length > 0 && (
|
{movementAlerts.length > 0 && (
|
||||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
|
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
|
||||||
{movementAlerts.length}
|
{movementAlerts.length}
|
||||||
@@ -82,10 +84,10 @@ const MovementAlertsPanel = () => {
|
|||||||
onChange={(e) => setFilter(e.target.value)}
|
onChange={(e) => setFilter(e.target.value)}
|
||||||
className="text-sm border border-gray-300 rounded px-2 py-1"
|
className="text-sm border border-gray-300 rounded px-2 py-1"
|
||||||
>
|
>
|
||||||
<option value="all">All Alerts</option>
|
<option value="all">{t('movementAlerts.allAlerts')}</option>
|
||||||
<option value="critical">Critical</option>
|
<option value="critical">{t('movementAlerts.critical')}</option>
|
||||||
<option value="high">High Priority</option>
|
<option value="high">{t('movementAlerts.highPriority')}</option>
|
||||||
<option value="medium">Medium Priority</option>
|
<option value="medium">{t('movementAlerts.mediumPriority')}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{movementAlerts.length > 0 && (
|
{movementAlerts.length > 0 && (
|
||||||
@@ -93,7 +95,7 @@ const MovementAlertsPanel = () => {
|
|||||||
onClick={clearMovementAlerts}
|
onClick={clearMovementAlerts}
|
||||||
className="text-sm text-gray-600 hover:text-gray-800"
|
className="text-sm text-gray-600 hover:text-gray-800"
|
||||||
>
|
>
|
||||||
Clear All
|
{t('movementAlerts.clearAll')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ const MapView = () => {
|
|||||||
onChange={(e) => setShowDroneDetections(e.target.checked)}
|
onChange={(e) => setShowDroneDetections(e.target.checked)}
|
||||||
className="rounded border-gray-300 text-primary-600 focus:ring-primary-500"
|
className="rounded border-gray-300 text-primary-600 focus:ring-primary-500"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-gray-700">Show Drone Detections</span>
|
<span className="text-sm text-gray-700">{t('map.showDroneDetections')}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{droneDetectionHistory.length > 0 && (
|
{droneDetectionHistory.length > 0 && (
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Navigate, Link } from 'react-router-dom';
|
import { Navigate, Link } from 'react-router-dom';
|
||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
|
import { useTranslation } from '../utils/tempTranslations';
|
||||||
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
|
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import api from '../services/api';
|
import api from '../services/api';
|
||||||
|
|
||||||
const Register = () => {
|
const Register = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
username: '',
|
username: '',
|
||||||
email: '',
|
email: '',
|
||||||
@@ -31,11 +33,11 @@ const Register = () => {
|
|||||||
|
|
||||||
// Security check: If registration is not enabled, show error
|
// Security check: If registration is not enabled, show error
|
||||||
if (!response.data.data?.features?.registration) {
|
if (!response.data.data?.features?.registration) {
|
||||||
toast.error('Registration is not enabled for this tenant');
|
toast.error(t('register.registrationDisabled'));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch tenant config:', error);
|
console.error('Failed to fetch tenant config:', error);
|
||||||
toast.error('Failed to load authentication configuration');
|
toast.error(t('register.configLoadFailed'));
|
||||||
} finally {
|
} finally {
|
||||||
setConfigLoading(false);
|
setConfigLoading(false);
|
||||||
}
|
}
|
||||||
@@ -124,38 +126,38 @@ const Register = () => {
|
|||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
if (!formData.username || !formData.email || !formData.password) {
|
if (!formData.username || !formData.email || !formData.password) {
|
||||||
toast.error('Please fill in all required fields');
|
toast.error(t('register.fillAllFields'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formData.password !== formData.confirmPassword) {
|
if (formData.password !== formData.confirmPassword) {
|
||||||
toast.error('Passwords do not match');
|
toast.error(t('register.passwordsMismatch'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formData.password.length < 8) {
|
if (formData.password.length < 8) {
|
||||||
toast.error('Password must be at least 8 characters long');
|
toast.error(t('register.passwordTooShort'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strong password validation
|
// Strong password validation
|
||||||
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/;
|
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/;
|
||||||
if (!passwordRegex.test(formData.password)) {
|
if (!passwordRegex.test(formData.password)) {
|
||||||
toast.error('Password must contain at least one lowercase letter, one uppercase letter, and one number');
|
toast.error(t('register.passwordRequirements'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Username validation
|
// Username validation
|
||||||
const usernameRegex = /^[a-zA-Z0-9._-]+$/;
|
const usernameRegex = /^[a-zA-Z0-9._-]+$/;
|
||||||
if (!usernameRegex.test(formData.username)) {
|
if (!usernameRegex.test(formData.username)) {
|
||||||
toast.error('Username can only contain letters, numbers, dots, underscores, and hyphens');
|
toast.error(t('register.usernameInvalid'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Email validation
|
// Email validation
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
if (!emailRegex.test(formData.email)) {
|
if (!emailRegex.test(formData.email)) {
|
||||||
toast.error('Please enter a valid email address');
|
toast.error(t('register.emailInvalid'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +165,7 @@ const Register = () => {
|
|||||||
if (formData.phone_number) {
|
if (formData.phone_number) {
|
||||||
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
|
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
|
||||||
if (!phoneRegex.test(formData.phone_number.replace(/[\s\-\(\)]/g, ''))) {
|
if (!phoneRegex.test(formData.phone_number.replace(/[\s\-\(\)]/g, ''))) {
|
||||||
toast.error('Please enter a valid phone number');
|
toast.error(t('register.phoneInvalid'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,7 +313,35 @@ const translations = {
|
|||||||
differentPatterns: 'Different dash patterns',
|
differentPatterns: 'Different dash patterns',
|
||||||
droneLabels: 'Drone ID labels shown',
|
droneLabels: 'Drone ID labels shown',
|
||||||
positionOffsets: 'Slight position offsets for visibility',
|
positionOffsets: 'Slight position offsets for visibility',
|
||||||
ringSize: 'Ring size = estimated distance from detector'
|
ringSize: 'Ring size = estimated distance from detector',
|
||||||
|
showDroneDetections: 'Show Drone Detections'
|
||||||
|
},
|
||||||
|
movementAlerts: {
|
||||||
|
title: 'Movement Alerts',
|
||||||
|
allAlerts: 'All Alerts',
|
||||||
|
critical: 'Critical',
|
||||||
|
highPriority: 'High Priority',
|
||||||
|
mediumPriority: 'Medium Priority',
|
||||||
|
clearAll: 'Clear All',
|
||||||
|
droneTypes: {
|
||||||
|
djiMavic: 'DJI Mavic',
|
||||||
|
racingDrone: 'Racing Drone',
|
||||||
|
djiPhantom: 'DJI Phantom',
|
||||||
|
fixedWing: 'Fixed Wing',
|
||||||
|
surveillance: 'Surveillance',
|
||||||
|
unknown: 'Unknown'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
register: {
|
||||||
|
registrationDisabled: 'Registration is not enabled for this tenant',
|
||||||
|
configLoadFailed: 'Failed to load authentication configuration',
|
||||||
|
fillAllFields: 'Please fill in all required fields',
|
||||||
|
passwordsMismatch: 'Passwords do not match',
|
||||||
|
passwordTooShort: 'Password must be at least 8 characters long',
|
||||||
|
passwordRequirements: 'Password must contain at least one lowercase letter, one uppercase letter, and one number',
|
||||||
|
usernameInvalid: 'Username can only contain letters, numbers, dots, underscores, and hyphens',
|
||||||
|
emailInvalid: 'Please enter a valid email address',
|
||||||
|
phoneInvalid: 'Please enter a valid phone number'
|
||||||
},
|
},
|
||||||
app: {
|
app: {
|
||||||
title: 'UAM-ILS Drone Detection System',
|
title: 'UAM-ILS Drone Detection System',
|
||||||
@@ -684,7 +712,35 @@ const translations = {
|
|||||||
differentPatterns: 'Olika streckmönster',
|
differentPatterns: 'Olika streckmönster',
|
||||||
droneLabels: 'Drönar-ID-etiketter visas',
|
droneLabels: 'Drönar-ID-etiketter visas',
|
||||||
positionOffsets: 'Lätta positionsförskjutningar för synlighet',
|
positionOffsets: 'Lätta positionsförskjutningar för synlighet',
|
||||||
ringSize: 'Ringstorlek = uppskattad distans från detektor'
|
ringSize: 'Ringstorlek = uppskattad distans från detektor',
|
||||||
|
showDroneDetections: 'Visa drönardetekteringar'
|
||||||
|
},
|
||||||
|
movementAlerts: {
|
||||||
|
title: 'Rörelselarm',
|
||||||
|
allAlerts: 'Alla larm',
|
||||||
|
critical: 'Kritisk',
|
||||||
|
highPriority: 'Hög prioritet',
|
||||||
|
mediumPriority: 'Medel prioritet',
|
||||||
|
clearAll: 'Rensa alla',
|
||||||
|
droneTypes: {
|
||||||
|
djiMavic: 'DJI Mavic',
|
||||||
|
racingDrone: 'Racingdrönare',
|
||||||
|
djiPhantom: 'DJI Phantom',
|
||||||
|
fixedWing: 'Fast vinge',
|
||||||
|
surveillance: 'Övervakning',
|
||||||
|
unknown: 'Okänd'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
register: {
|
||||||
|
registrationDisabled: 'Registrering är inte aktiverad för denna klient',
|
||||||
|
configLoadFailed: 'Misslyckades med att ladda autentiseringskonfiguration',
|
||||||
|
fillAllFields: 'Vänligen fyll i alla obligatoriska fält',
|
||||||
|
passwordsMismatch: 'Lösenorden stämmer inte överens',
|
||||||
|
passwordTooShort: 'Lösenordet måste vara minst 8 tecken långt',
|
||||||
|
passwordRequirements: 'Lösenordet måste innehålla minst en liten bokstav, en stor bokstav och en siffra',
|
||||||
|
usernameInvalid: 'Användarnamn kan bara innehålla bokstäver, siffror, punkter, understreck och bindestreck',
|
||||||
|
emailInvalid: 'Vänligen ange en giltig e-postadress',
|
||||||
|
phoneInvalid: 'Vänligen ange ett giltigt telefonnummer'
|
||||||
},
|
},
|
||||||
app: {
|
app: {
|
||||||
title: 'UAM-ILS Drönardetekteringssystem',
|
title: 'UAM-ILS Drönardetekteringssystem',
|
||||||
|
|||||||
Reference in New Issue
Block a user