GooDrive/frontend/src/components/drive/dialogs/SpaceSettingsDialog.jsx
Louis Mazin 9834626cf4 nice
2026-10-14 17:57:24 +02:00

98 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useState } from 'react'
import { drive, message } from '@/api/client'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import QuotaField from '@/components/drive/QuotaField'
import {fromQuota,toQuota} from '@/lib/quota-utils'
export default function SpaceSettingsDialog({controller: c}) {
const {spaceDialog, setSpaceDialog, spaces, activeSpaceId, loadSpaces, switchSpace, setError, setNote, setManagedSpaceId} = c
const manage = spaceDialog?.mode === 'manage'
const current = spaces.find(space => space.id === spaceDialog?.spaceId)
const managed = spaces.filter(space => !space.is_private && space.permissions?.includes('create_spaces'))
const [name,setName] = useState('')
const [quota,setQuota] = useState('')
const [parent,setParent] = useState('')
const [busy,setBusy] = useState(false)
const eligibleParents = spaces.filter(space => !space.is_private && space.id !== current?.id && space.permissions?.includes('create_spaces') && !isDescendant(space, current, spaces))
const canMove = manage && !!current?.parent_id && eligibleParents.some(space => space.id !== current.parent_id)
const [localError,setLocalError] = useState('')
useEffect(() => {
setName(manage ? current?.display_name || '' : '')
setQuota(manage ? fromQuota(current?.quota_bytes) : fromQuota(managed.find(sp=>sp.id===(spaceDialog?.parentId || managed[0]?.id))?.quota_bytes))
setParent(String(manage ? current?.parent_id || '' : spaceDialog?.parentId || managed[0]?.id || ''))
setLocalError('')
}, [spaceDialog?.mode,spaceDialog?.spaceId,spaceDialog?.parentId,current?.display_name,current?.quota_bytes,current?.parent_id,managed[0]?.id])
async function save(event) {
event.preventDefault()
setBusy(true);setLocalError('')
try {
if (manage && !current) throw new Error('Sous-espace introuvable. Actualise la page.')
const data={display_name:name.trim()}
// Managing an assigned/shared space must never resubmit its quota.
if (!manage || current?.can_edit_quota) data.quota_bytes=toQuota(quota)
if (manage && canMove && parent && Number(parent) !== current.parent_id) data.parent_space = Number(parent)
const result=manage ? await drive.editSpace(current.id,data) : await drive.createSpace({...data,parent_space:Number(parent)})
await loadSpaces()
if (!manage) switchSpace(result.id)
if (manage && data.parent_space) await c.reloadStats()
setNote(manage ? 'Espace modifié.' : 'Espace créé.')
setSpaceDialog(null)
} catch(exception) {setLocalError(exception?.response ? message(exception) : exception.message || message(exception))}
finally {setBusy(false)}
}
function members() {
if (!current) return
setManagedSpaceId(current.id)
setSpaceDialog(null)
}
return <Dialog open={!!spaceDialog && ['manage','create'].includes(spaceDialog.mode)} onOpenChange={open=>{if(!open&&!busy)setSpaceDialog(null)}}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{manage ? `Gérer l’espace « ${current?.display_name || 'introuvable'} »` : 'Créer un espace partagé'}</DialogTitle>
<DialogDescription className="sr-only">{manage ? 'Paramètres de cet espace' : 'Paramètres du nouvel espace'}</DialogDescription>
</DialogHeader>
<form onSubmit={save} className="space-y-4">
{!manage && <div className="space-y-2"><Label htmlFor="space-parent">Espace parent</Label>
<select id="space-parent" value={parent} onChange={e=>{setParent(e.target.value);setQuota(fromQuota(managed.find(sp=>sp.id===Number(e.target.value))?.quota_bytes))}} required
className="h-10 w-full rounded-md border bg-background px-3 text-sm">
{managed.map(space=><option key={space.id} value={space.id}>{space.display_name}</option>)}
</select></div>}
{canMove && <div className="space-y-2"><Label htmlFor="space-new-parent">Espace parent</Label>
<select id="space-new-parent" value={parent} disabled={busy} onChange={e=>setParent(e.target.value)}
className="h-10 w-full rounded-md border bg-background px-3 text-sm">
{current?.parent_id && !eligibleParents.some(space => space.id === current.parent_id) &&
<option value={current.parent_id}>{spaces.find(space => space.id === current.parent_id)?.display_name || 'Parent actuel'}</option>}
{eligibleParents.map(space=><option key={space.id} value={space.id}>{space.display_name}</option>)}
</select>
</div>}
<div className="space-y-2"><Label htmlFor="space-name">Nom affiché</Label>
<Input id="space-name" value={name} onChange={e=>setName(e.target.value)} maxLength={120} required placeholder="Ex. Équipe projet" /></div>
{(!manage || current?.can_edit_quota) &&
<QuotaField id="space-quota" value={quota} onChange={setQuota} disabled={busy} />}
{localError && <p role="alert" className="text-sm text-destructive">{localError}</p>}
<DialogFooter className="gap-2 sm:justify-between">
{manage && current?.can_invite && <Button type="button" variant="outline" onClick={members}>Gérer les accès</Button>}
<Button disabled={busy||!name.trim()||(manage&&!current)||(!manage&&!parent)} type="submit">{busy ? 'Enregistrement…' : manage ? 'Enregistrer' : 'Créer l’espace'}</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
}
function isDescendant(candidate, ancestor, spaces) {
if (!ancestor) return false
let current = candidate
const visited = new Set()
while (current && !visited.has(current.id)) {
if (current.id === ancestor.id) return true
visited.add(current.id)
current = spaces.find(space => space.id === current.parent_id)
}
return false
}