feat: Ajouter possibilité de renommer les périodes dans documents comptables (staff)
This commit is contained in:
parent
39fea18d9e
commit
c0142d167e
2 changed files with 178 additions and 20 deletions
|
|
@ -415,6 +415,8 @@ export default function StaffDocumentsPage() {
|
|||
const [expandedPeriods, setExpandedPeriods] = React.useState<Set<string>>(new Set())
|
||||
const [viewDocument, setViewDocument] = React.useState<{ name: string; downloadUrl: string; size?: number } | null>(null)
|
||||
const [deleteConfirm, setDeleteConfirm] = React.useState<{ type: 'general' | 'comptable'; data: any } | null>(null)
|
||||
const [editingPeriod, setEditingPeriod] = React.useState<string | null>(null)
|
||||
const [newPeriodName, setNewPeriodName] = React.useState('')
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
|
|
@ -515,6 +517,49 @@ export default function StaffDocumentsPage() {
|
|||
onError: () => toast.error('Erreur lors de la suppression')
|
||||
})
|
||||
|
||||
// Renommer une période
|
||||
const renamePeriod = useMutation({
|
||||
mutationFn: async ({ oldName, newName }: { oldName: string; newName: string }) => {
|
||||
const response = await fetch('/api/staff/documents/update-period', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
org_id: selectedOrgId,
|
||||
old_period_label: oldName,
|
||||
new_period_label: newName
|
||||
})
|
||||
})
|
||||
if (!response.ok) throw new Error('Erreur mise à jour')
|
||||
return response.json()
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
toast.success(`${data.updated_count} document(s) mis à jour`)
|
||||
queryClient.invalidateQueries({ queryKey: ['documents', 'comptables'] })
|
||||
setEditingPeriod(null)
|
||||
setNewPeriodName('')
|
||||
},
|
||||
onError: () => toast.error('Erreur lors de la mise à jour')
|
||||
})
|
||||
|
||||
const handleStartEditPeriod = (period: string) => {
|
||||
setEditingPeriod(period)
|
||||
setNewPeriodName(period)
|
||||
}
|
||||
|
||||
const handleSavePeriod = (oldName: string) => {
|
||||
if (newPeriodName.trim() && newPeriodName !== oldName) {
|
||||
renamePeriod.mutate({ oldName, newName: newPeriodName.trim() })
|
||||
} else {
|
||||
setEditingPeriod(null)
|
||||
setNewPeriodName('')
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancelEditPeriod = () => {
|
||||
setEditingPeriod(null)
|
||||
setNewPeriodName('')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<header>
|
||||
|
|
@ -739,31 +784,78 @@ export default function StaffDocumentsPage() {
|
|||
<div className="space-y-2">
|
||||
{Array.from(documentsByPeriod.entries()).map(([period, docs]) => {
|
||||
const isExpanded = expandedPeriods.has(period)
|
||||
const isEditing = editingPeriod === period
|
||||
|
||||
return (
|
||||
<Card key={period}>
|
||||
<button
|
||||
onClick={() => {
|
||||
const next = new Set(expandedPeriods)
|
||||
if (next.has(period)) next.delete(period)
|
||||
else next.add(period)
|
||||
setExpandedPeriods(next)
|
||||
}}
|
||||
className="w-full p-4 flex items-center justify-between hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-4 flex items-center justify-between">
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!isEditing) {
|
||||
const next = new Set(expandedPeriods)
|
||||
if (next.has(period)) next.delete(period)
|
||||
else next.add(period)
|
||||
setExpandedPeriods(next)
|
||||
}
|
||||
}}
|
||||
className="flex items-center gap-3 flex-1 hover:bg-gray-50 transition-colors rounded p-2 -m-2"
|
||||
disabled={isEditing}
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
<ChevronDown className="h-4 w-4 flex-shrink-0" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
<ChevronRight className="h-4 w-4 flex-shrink-0" />
|
||||
)}
|
||||
<div className="text-left">
|
||||
<h3 className="font-semibold">{period}</h3>
|
||||
<p className="text-sm text-gray-500">
|
||||
{docs.length} document{docs.length > 1 ? 's' : ''}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{isEditing ? (
|
||||
<div className="flex items-center gap-2 flex-1" onClick={(e) => e.stopPropagation()}>
|
||||
<Input
|
||||
value={newPeriodName}
|
||||
onChange={(e) => setNewPeriodName(e.target.value)}
|
||||
className="max-w-sm"
|
||||
autoFocus
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') handleSavePeriod(period)
|
||||
if (e.key === 'Escape') handleCancelEditPeriod()
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => handleSavePeriod(period)}
|
||||
disabled={renamePeriod.isPending}
|
||||
>
|
||||
<Check className="h-3 w-3" />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={handleCancelEditPeriod}
|
||||
disabled={renamePeriod.isPending}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-left">
|
||||
<h3 className="font-semibold">{period}</h3>
|
||||
<p className="text-sm text-gray-500">
|
||||
{docs.length} document{docs.length > 1 ? 's' : ''}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{!isEditing && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => handleStartEditPeriod(period)}
|
||||
className="ml-2"
|
||||
>
|
||||
<Edit3 className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="border-t p-4 space-y-2">
|
||||
|
|
|
|||
66
app/api/staff/documents/update-period/route.ts
Normal file
66
app/api/staff/documents/update-period/route.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function PUT(request: Request) {
|
||||
try {
|
||||
const supabase = createRouteHandlerClient({ cookies })
|
||||
|
||||
// Vérifier l'authentification et les droits staff
|
||||
const { data: { session } } = await supabase.auth.getSession()
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const { data: staffUser } = await supabase
|
||||
.from('staff_users')
|
||||
.select('is_staff')
|
||||
.eq('user_id', session.user.id)
|
||||
.maybeSingle()
|
||||
|
||||
if (!staffUser?.is_staff) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { org_id, old_period_label, new_period_label } = body
|
||||
|
||||
if (!org_id || !old_period_label || !new_period_label) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing required fields' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
console.log('📝 Update Period - Org:', org_id, 'Old:', old_period_label, 'New:', new_period_label)
|
||||
|
||||
// Mettre à jour tous les documents de cette période
|
||||
const { error, count } = await supabase
|
||||
.from('documents')
|
||||
.update({ period_label: new_period_label })
|
||||
.eq('org_id', org_id)
|
||||
.eq('period_label', old_period_label)
|
||||
.eq('category', 'docs_comptables')
|
||||
|
||||
if (error) {
|
||||
console.error('❌ Error updating period:', error)
|
||||
return NextResponse.json({ error: error.message }, { status: 500 })
|
||||
}
|
||||
|
||||
console.log('✅ Updated', count, 'documents')
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
updated_count: count
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('❌ Update period error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: error.message || 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue