FlotillasGPS - Sistema completo de monitoreo de flotillas GPS

Sistema completo para monitoreo y gestion de flotas de vehiculos con:
- Backend FastAPI con PostgreSQL/TimescaleDB
- Frontend React con TypeScript y TailwindCSS
- App movil React Native con Expo
- Soporte para dispositivos GPS, Meshtastic y celulares
- Video streaming en vivo con MediaMTX
- Geocercas, alertas, viajes y reportes
- Autenticacion JWT y WebSockets en tiempo real

Documentacion completa y guias de usuario incluidas.
This commit is contained in:
FlotillasGPS Developer
2026-01-21 08:18:00 +00:00
commit 51d78bacf4
248 changed files with 50171 additions and 0 deletions

View File

@@ -0,0 +1,171 @@
import { forwardRef, InputHTMLAttributes, ReactNode } from 'react'
import clsx from 'clsx'
export interface CheckboxProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
label?: string | ReactNode
description?: string
error?: string
}
const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
({ className, label, description, error, id, ...props }, ref) => {
const checkboxId = id || (typeof label === 'string' ? label.toLowerCase().replace(/\s+/g, '-') : undefined)
return (
<div className="relative flex items-start">
<div className="flex items-center h-5">
<input
ref={ref}
type="checkbox"
id={checkboxId}
className={clsx(
'h-4 w-4 rounded border-slate-600 bg-background-800',
'text-accent-500 focus:ring-accent-500 focus:ring-2 focus:ring-offset-2 focus:ring-offset-background-900',
'transition-colors duration-200 cursor-pointer',
error && 'border-error-500',
className
)}
{...props}
/>
</div>
{(label || description) && (
<div className="ml-3">
{label && (
<label
htmlFor={checkboxId}
className="text-sm font-medium text-slate-300 cursor-pointer"
>
{label}
</label>
)}
{description && (
<p className="text-sm text-slate-500">{description}</p>
)}
{error && <p className="text-sm text-error-500 mt-1">{error}</p>}
</div>
)}
</div>
)
}
)
Checkbox.displayName = 'Checkbox'
export default Checkbox
// Switch component
export interface SwitchProps {
checked: boolean
onChange: (checked: boolean) => void
label?: string
description?: string
disabled?: boolean
size?: 'sm' | 'md' | 'lg'
}
export function Switch({
checked,
onChange,
label,
description,
disabled = false,
size = 'md',
}: SwitchProps) {
const sizeStyles = {
sm: {
track: 'w-8 h-4',
thumb: 'w-3 h-3',
translate: 'translate-x-4',
},
md: {
track: 'w-11 h-6',
thumb: 'w-5 h-5',
translate: 'translate-x-5',
},
lg: {
track: 'w-14 h-7',
thumb: 'w-6 h-6',
translate: 'translate-x-7',
},
}
return (
<div className="flex items-center justify-between">
{(label || description) && (
<div className="mr-4">
{label && (
<span className="text-sm font-medium text-slate-300">{label}</span>
)}
{description && (
<p className="text-sm text-slate-500">{description}</p>
)}
</div>
)}
<button
type="button"
role="switch"
aria-checked={checked}
onClick={() => !disabled && onChange(!checked)}
className={clsx(
'relative inline-flex flex-shrink-0 rounded-full cursor-pointer',
'transition-colors duration-200 ease-in-out',
'focus:outline-none focus:ring-2 focus:ring-accent-500 focus:ring-offset-2 focus:ring-offset-background-900',
sizeStyles[size].track,
checked ? 'bg-accent-500' : 'bg-slate-600',
disabled && 'opacity-50 cursor-not-allowed'
)}
disabled={disabled}
>
<span
className={clsx(
'pointer-events-none inline-block rounded-full bg-white shadow-lg',
'transform ring-0 transition duration-200 ease-in-out',
sizeStyles[size].thumb,
checked ? sizeStyles[size].translate : 'translate-x-0.5',
'mt-0.5 ml-0.5'
)}
/>
</button>
</div>
)
}
// Radio button component
export interface RadioProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
label?: string
}
export const Radio = forwardRef<HTMLInputElement, RadioProps>(
({ className, label, id, ...props }, ref) => {
const radioId = id || label?.toLowerCase().replace(/\s+/g, '-')
return (
<div className="flex items-center">
<input
ref={ref}
type="radio"
id={radioId}
className={clsx(
'h-4 w-4 border-slate-600 bg-background-800',
'text-accent-500 focus:ring-accent-500 focus:ring-2 focus:ring-offset-2 focus:ring-offset-background-900',
'cursor-pointer',
className
)}
{...props}
/>
{label && (
<label
htmlFor={radioId}
className="ml-3 text-sm font-medium text-slate-300 cursor-pointer"
>
{label}
</label>
)}
</div>
)
}
)
Radio.displayName = 'Radio'