import { FormEvent, useEffect, useState } from "react"; import { api, SambaShare, User } from "../api"; import { useDirty } from "../DirtyContext"; import Modal from "../components/Modal"; import PathField from "../components/PathField"; const empty: Partial = { name: "", path: "", comment: "", read_only: false, guest_ok: false, valid_users: [], valid_groups: [], invalid_users: [], }; type ChipPickerProps = { label: string; options: User[]; selected: string[]; onChange: (selected: string[]) => void; allowRemove?: boolean; }; function ChipPicker({ label, options, selected, onChange }: ChipPickerProps) { const available = options.filter((u) => !selected.includes(u.username)); return (
{selected.length > 0 && (
{selected.map((username) => { const user = options.find((u) => u.username === username); const isSMB = user?.smb_enabled; return ( {username} {isSMB && *} ); })}
)} {available.length > 0 && (
{available.map((user) => ( ))}
)} {options.length === 0 && (

No hay usuarios del sistema. Créalos primero.

)}
); } export default function Samba() { const [shares, setShares] = useState([]); const [editing, setEditing] = useState | null>(null); const [error, setError] = useState(null); const [systemUsers, setSystemUsers] = useState([]); const { refresh } = useDirty(); async function load() { const res = await api.listShares(); setShares(res.shares ?? []); } async function loadSystemUsers() { try { const res = await api.listUsers(); setSystemUsers(res.users ?? []); } catch { setSystemUsers([]); } } useEffect(() => { load(); }, []); function openEditor(share: Partial | null) { setEditing(share ? { ...share } : { ...empty }); loadSystemUsers(); } async function save(e: FormEvent) { e.preventDefault(); if (!editing) return; setError(null); try { if (editing.id) { await api.updateShare(editing.id, editing); } else { await api.createShare(editing); } setEditing(null); await load(); await refresh(); } catch (err) { setError(err instanceof Error ? err.message : "Error al guardar"); } } async function remove(id: number) { if (!confirm("¿Eliminar este share?")) return; await api.deleteShare(id); await load(); await refresh(); } return (

Shares SMB / Samba

{shares.map((s) => ( ))} {shares.length === 0 && ( )}
Nombre Path Modo Invitado Usuarios
{s.name} {s.path} {s.read_only ? "Solo lectura" : "Lectura/Escritura"} {s.guest_ok ? "Sí" : "No"}
{s.valid_users.map((u) => ( {u} ))} {s.invalid_users.map((u) => ( !{u} ))} {s.valid_users.length === 0 && s.invalid_users.length === 0 && ( )}
No hay shares configurados.
{editing && ( setEditing(null)}>
{error && (
{error}
)}
setEditing({ ...editing, name: e.target.value })} />
setEditing({ ...editing, path: p })} />
setEditing({ ...editing, comment: e.target.value })} />
setEditing({ ...editing, valid_users: selected }) } /> setEditing({ ...editing, invalid_users: selected }) } />
)}
); }