mirror of
https://github.com/johndoe6345789/workforce-pay-bill-p.git
synced 2026-04-24 13:24:57 +00:00
Generated by Spark: Make timesheets use translations
This commit is contained in:
@@ -32,6 +32,7 @@ import { TimesheetTabs } from '@/components/timesheets/TimesheetTabs'
|
||||
import { useTimeTracking } from '@/hooks/use-time-tracking'
|
||||
import { useTimesheetsCrud } from '@/hooks/use-timesheets-crud'
|
||||
import { usePermissions } from '@/hooks/use-permissions'
|
||||
import { useTranslation } from '@/hooks/use-translation'
|
||||
import { toast } from 'sonner'
|
||||
import type { Timesheet, TimesheetStatus, ShiftEntry } from '@/lib/types'
|
||||
|
||||
@@ -46,6 +47,7 @@ export function TimesheetsView({
|
||||
setSearchQuery,
|
||||
onCreateInvoice
|
||||
}: TimesheetsViewProps) {
|
||||
const { t } = useTranslation()
|
||||
const [statusFilter, setStatusFilter] = useState<'all' | TimesheetStatus>('all')
|
||||
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
||||
const [isBulkImportOpen, setIsBulkImportOpen] = useState(false)
|
||||
@@ -90,13 +92,13 @@ export function TimesheetsView({
|
||||
submittedDate: new Date().toISOString(),
|
||||
shifts: []
|
||||
})
|
||||
toast.success('Timesheet created successfully')
|
||||
toast.success(t('timesheets.createSuccess'))
|
||||
setIsCreateDialogOpen(false)
|
||||
} catch (error) {
|
||||
toast.error('Failed to create timesheet')
|
||||
toast.error(t('timesheets.createError'))
|
||||
console.error('Error creating timesheet:', error)
|
||||
}
|
||||
}, [createTimesheet])
|
||||
}, [createTimesheet, t])
|
||||
|
||||
const handleCreateDetailedTimesheet = useCallback(async (data: {
|
||||
workerName: string
|
||||
@@ -120,13 +122,13 @@ export function TimesheetsView({
|
||||
submittedDate: new Date().toISOString(),
|
||||
shifts: data.shifts
|
||||
})
|
||||
toast.success('Detailed timesheet created successfully')
|
||||
toast.success(t('timesheets.createDetailedSuccess'))
|
||||
setIsCreateDialogOpen(false)
|
||||
} catch (error) {
|
||||
toast.error('Failed to create detailed timesheet')
|
||||
toast.error(t('timesheets.createDetailedError'))
|
||||
console.error('Error creating detailed timesheet:', error)
|
||||
}
|
||||
}, [createTimesheet])
|
||||
}, [createTimesheet, t])
|
||||
|
||||
const handleBulkImport = useCallback(async (csvData: string) => {
|
||||
try {
|
||||
@@ -156,17 +158,17 @@ export function TimesheetsView({
|
||||
})
|
||||
|
||||
await bulkCreateTimesheets(timesheetsData)
|
||||
toast.success(`${timesheetsData.length} timesheets imported successfully`)
|
||||
toast.success(t('timesheets.importSuccess', { count: timesheetsData.length }))
|
||||
setIsBulkImportOpen(false)
|
||||
} catch (error) {
|
||||
toast.error('Failed to import timesheets')
|
||||
toast.error(t('timesheets.importError'))
|
||||
console.error('Error importing timesheets:', error)
|
||||
}
|
||||
}, [bulkCreateTimesheets])
|
||||
}, [bulkCreateTimesheets, t])
|
||||
|
||||
const handleApprove = useCallback(async (id: string) => {
|
||||
if (!hasPermission('timesheets.approve')) {
|
||||
toast.error('You do not have permission to approve timesheets')
|
||||
toast.error(t('timesheets.noPermissionApprove'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -175,16 +177,16 @@ export function TimesheetsView({
|
||||
status: 'approved',
|
||||
approvedDate: new Date().toISOString()
|
||||
})
|
||||
toast.success('Timesheet approved')
|
||||
toast.success(t('timesheets.approveSuccess'))
|
||||
} catch (error) {
|
||||
toast.error('Failed to approve timesheet')
|
||||
toast.error(t('timesheets.approveError'))
|
||||
console.error('Error approving timesheet:', error)
|
||||
}
|
||||
}, [updateTimesheet, hasPermission])
|
||||
}, [updateTimesheet, hasPermission, t])
|
||||
|
||||
const handleReject = useCallback(async (id: string) => {
|
||||
if (!hasPermission('timesheets.approve')) {
|
||||
toast.error('You do not have permission to reject timesheets')
|
||||
toast.error(t('timesheets.noPermissionReject'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -192,28 +194,28 @@ export function TimesheetsView({
|
||||
await updateTimesheet(id, {
|
||||
status: 'rejected'
|
||||
})
|
||||
toast.error('Timesheet rejected')
|
||||
toast.error(t('timesheets.rejectSuccess'))
|
||||
} catch (error) {
|
||||
toast.error('Failed to reject timesheet')
|
||||
toast.error(t('timesheets.rejectError'))
|
||||
console.error('Error rejecting timesheet:', error)
|
||||
}
|
||||
}, [updateTimesheet, hasPermission])
|
||||
}, [updateTimesheet, hasPermission, t])
|
||||
|
||||
const handleAdjust = useCallback(async (timesheetId: string, adjustment: any) => {
|
||||
if (!hasPermission('timesheets.edit')) {
|
||||
toast.error('You do not have permission to adjust timesheets')
|
||||
toast.error(t('timesheets.noPermissionEdit'))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await updateTimesheet(timesheetId, adjustment)
|
||||
toast.success('Timesheet adjusted')
|
||||
toast.success(t('timesheets.adjustSuccess'))
|
||||
setSelectedTimesheet(null)
|
||||
} catch (error) {
|
||||
toast.error('Failed to adjust timesheet')
|
||||
toast.error(t('timesheets.adjustError'))
|
||||
console.error('Error adjusting timesheet:', error)
|
||||
}
|
||||
}, [updateTimesheet, hasPermission])
|
||||
}, [updateTimesheet, hasPermission, t])
|
||||
|
||||
const handleTimeAndRateAdjustment = useCallback(async (adjustment: {
|
||||
timesheetId: string
|
||||
@@ -230,7 +232,7 @@ export function TimesheetsView({
|
||||
notes?: string
|
||||
}) => {
|
||||
if (!hasPermission('timesheets.edit')) {
|
||||
toast.error('You do not have permission to adjust timesheets')
|
||||
toast.error(t('timesheets.noPermissionEdit'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -276,30 +278,30 @@ export function TimesheetsView({
|
||||
await updateTimesheet(adjustment.timesheetId, updates)
|
||||
|
||||
if (adjustment.approvalRequired) {
|
||||
toast.success('Adjustment submitted for approval')
|
||||
toast.success(t('timesheets.adjustmentSubmitted'))
|
||||
} else {
|
||||
toast.success('Adjustment applied successfully')
|
||||
toast.success(t('timesheets.adjustmentApplied'))
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Failed to apply adjustment')
|
||||
toast.error(t('timesheets.adjustmentError'))
|
||||
console.error('Error applying adjustment:', error)
|
||||
}
|
||||
}, [updateTimesheet, hasPermission, timesheets])
|
||||
}, [updateTimesheet, hasPermission, timesheets, t])
|
||||
|
||||
const handleDelete = useCallback(async (id: string) => {
|
||||
if (!hasPermission('timesheets.delete')) {
|
||||
toast.error('You do not have permission to delete timesheets')
|
||||
toast.error(t('timesheets.noPermissionDelete'))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteTimesheet(id)
|
||||
toast.success('Timesheet deleted')
|
||||
toast.success(t('timesheets.deleteSuccess'))
|
||||
} catch (error) {
|
||||
toast.error('Failed to delete timesheet')
|
||||
toast.error(t('timesheets.deleteError'))
|
||||
console.error('Error deleting timesheet:', error)
|
||||
}
|
||||
}, [deleteTimesheet, hasPermission])
|
||||
}, [deleteTimesheet, hasPermission, t])
|
||||
|
||||
const timesheetsToFilter = useMemo(() => {
|
||||
return timesheets.filter(t => {
|
||||
@@ -348,16 +350,16 @@ export function TimesheetsView({
|
||||
const [csvData, setCsvData] = useState('')
|
||||
|
||||
const timesheetFields: FilterField[] = [
|
||||
{ name: 'workerName', label: 'Worker Name', type: 'text' },
|
||||
{ name: 'clientName', label: 'Client Name', type: 'text' },
|
||||
{ name: 'status', label: 'Status', type: 'select', options: [
|
||||
{ value: 'pending', label: 'Pending' },
|
||||
{ value: 'approved', label: 'Approved' },
|
||||
{ value: 'rejected', label: 'Rejected' }
|
||||
{ name: 'workerName', label: t('timesheets.workerName'), type: 'text' },
|
||||
{ name: 'clientName', label: t('timesheets.clientName'), type: 'text' },
|
||||
{ name: 'status', label: t('timesheets.status.all'), type: 'select', options: [
|
||||
{ value: 'pending', label: t('timesheets.status.pending') },
|
||||
{ value: 'approved', label: t('timesheets.status.approved') },
|
||||
{ value: 'rejected', label: t('timesheets.status.rejected') }
|
||||
]},
|
||||
{ name: 'hours', label: 'Hours', type: 'number' },
|
||||
{ name: 'amount', label: 'Amount', type: 'number' },
|
||||
{ name: 'weekEnding', label: 'Week Ending', type: 'date' }
|
||||
{ name: 'hours', label: t('timesheets.hours'), type: 'number' },
|
||||
{ name: 'amount', label: t('timesheets.amount'), type: 'number' },
|
||||
{ name: 'weekEnding', label: t('timesheets.weekEnding'), type: 'date' }
|
||||
]
|
||||
|
||||
const pendingCount = filteredTimesheets.filter(ts => ts.status === 'pending').length
|
||||
@@ -371,8 +373,8 @@ export function TimesheetsView({
|
||||
return (
|
||||
<Stack spacing={6}>
|
||||
<PageHeader
|
||||
title="Timesheets"
|
||||
description="Manage and approve worker timesheets"
|
||||
title={t('timesheets.title')}
|
||||
description={t('timesheets.subtitle')}
|
||||
actions={
|
||||
<Stack direction="horizontal" spacing={2}>
|
||||
<Button
|
||||
@@ -380,7 +382,7 @@ export function TimesheetsView({
|
||||
onClick={() => setShowAnalytics(!showAnalytics)}
|
||||
>
|
||||
<ChartBar size={18} className="mr-2" />
|
||||
{showAnalytics ? 'Hide' : 'Show'} Analytics
|
||||
{showAnalytics ? t('timesheets.hideAnalytics') : t('timesheets.showAnalytics')}
|
||||
</Button>
|
||||
<TimesheetCreateDialogs
|
||||
isCreateDialogOpen={isCreateDialogOpen}
|
||||
@@ -403,28 +405,28 @@ export function TimesheetsView({
|
||||
<>
|
||||
<Grid cols={4} gap={4} responsive>
|
||||
<MetricCard
|
||||
label="Total Timesheets"
|
||||
label={t('timesheets.totalTimesheets')}
|
||||
value={filteredTimesheets.length}
|
||||
icon={<FileText size={24} />}
|
||||
description={`${pendingCount} pending review`}
|
||||
description={t('timesheets.pendingReview', { count: pendingCount })}
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total Hours"
|
||||
label={t('timesheets.totalHours')}
|
||||
value={`${totalHours.toFixed(1)}h`}
|
||||
icon={<Clock size={24} />}
|
||||
description="This period"
|
||||
description={t('timesheets.thisPeriod')}
|
||||
/>
|
||||
<MetricCard
|
||||
label="Validation Issues"
|
||||
label={t('timesheets.validationIssues')}
|
||||
value={validationStats.invalid}
|
||||
icon={<Warning size={24} />}
|
||||
description={validationStats.invalid > 0 ? 'Errors found' : 'All valid'}
|
||||
description={validationStats.invalid > 0 ? t('timesheets.errorsFound') : t('timesheets.allValid')}
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total Value"
|
||||
label={t('timesheets.totalValue')}
|
||||
value={`£${totalValue.toLocaleString()}`}
|
||||
icon={<CurrencyDollar size={24} />}
|
||||
description="Pending invoicing"
|
||||
description={t('timesheets.pendingInvoicing')}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
@@ -432,12 +434,12 @@ export function TimesheetsView({
|
||||
<Card className="border-l-4 border-l-warning">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-base font-medium">Pending</CardTitle>
|
||||
<CardTitle className="text-base font-medium">{t('timesheets.pending')}</CardTitle>
|
||||
<Badge variant="outline" className="text-warning border-warning/30 bg-warning/10">
|
||||
{pendingCount}
|
||||
</Badge>
|
||||
</div>
|
||||
<CardDescription className="text-xs">Awaiting approval</CardDescription>
|
||||
<CardDescription className="text-xs">{t('timesheets.awaitingApproval')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold text-warning">
|
||||
@@ -449,13 +451,13 @@ export function TimesheetsView({
|
||||
<Card className="border-l-4 border-l-success">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-base font-medium">Approved</CardTitle>
|
||||
<CardTitle className="text-base font-medium">{t('timesheets.approved')}</CardTitle>
|
||||
<Badge variant="outline" className="text-success border-success/30 bg-success/10">
|
||||
<CheckCircle size={12} weight="bold" className="mr-1" />
|
||||
{approvedCount}
|
||||
</Badge>
|
||||
</div>
|
||||
<CardDescription className="text-xs">Ready for billing</CardDescription>
|
||||
<CardDescription className="text-xs">{t('timesheets.readyForBilling')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold text-success">
|
||||
@@ -467,13 +469,13 @@ export function TimesheetsView({
|
||||
<Card className="border-l-4 border-l-accent">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-base font-medium">Approval Rate</CardTitle>
|
||||
<CardTitle className="text-base font-medium">{t('timesheets.approvalRate')}</CardTitle>
|
||||
<Badge variant="outline" className="text-accent border-accent/30 bg-accent/10">
|
||||
<TrendUp size={12} weight="bold" className="mr-1" />
|
||||
{approvalRate.toFixed(0)}%
|
||||
</Badge>
|
||||
</div>
|
||||
<CardDescription className="text-xs">This period</CardDescription>
|
||||
<CardDescription className="text-xs">{t('timesheets.thisPeriod')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-3xl font-bold text-accent">
|
||||
@@ -492,13 +494,13 @@ export function TimesheetsView({
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<CardTitle className="text-lg">Search & Filter</CardTitle>
|
||||
<CardTitle className="text-lg">{t('timesheets.searchAndFilter')}</CardTitle>
|
||||
<CardDescription className="text-xs mt-1">
|
||||
Find timesheets using advanced search
|
||||
{t('timesheets.findTimesheetsAdvanced')}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Badge variant="secondary" className="font-mono">
|
||||
{filteredTimesheets.length} results
|
||||
{t('timesheets.results', { count: filteredTimesheets.length })}
|
||||
</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
@@ -507,7 +509,7 @@ export function TimesheetsView({
|
||||
items={timesheetsToFilter}
|
||||
fields={timesheetFields}
|
||||
onResultsChange={handleResultsChange}
|
||||
placeholder="Search by worker, client, or status..."
|
||||
placeholder={t('timesheets.searchPlaceholder')}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -522,24 +524,27 @@ export function TimesheetsView({
|
||||
</div>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All Status</SelectItem>
|
||||
<SelectItem value="pending">Pending</SelectItem>
|
||||
<SelectItem value="approved">Approved</SelectItem>
|
||||
<SelectItem value="rejected">Rejected</SelectItem>
|
||||
<SelectItem value="all">{t('timesheets.status.all')}</SelectItem>
|
||||
<SelectItem value="pending">{t('timesheets.status.pending')}</SelectItem>
|
||||
<SelectItem value="approved">{t('timesheets.status.approved')}</SelectItem>
|
||||
<SelectItem value="rejected">{t('timesheets.status.rejected')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{validationStats.invalid > 0 && (
|
||||
<Badge variant="destructive" className="px-3 py-1.5">
|
||||
<Warning size={14} weight="bold" className="mr-1" />
|
||||
{validationStats.invalid} validation {validationStats.invalid === 1 ? 'error' : 'errors'}
|
||||
{t('timesheets.validationErrors', {
|
||||
count: validationStats.invalid,
|
||||
errors: validationStats.invalid === 1 ? t('timesheets.error') : t('timesheets.errors')
|
||||
})}
|
||||
</Badge>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<Button variant="outline">
|
||||
<Download size={18} className="mr-2" />
|
||||
Export CSV
|
||||
{t('timesheets.exportCsv')}
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
},
|
||||
"timesheets": {
|
||||
"title": "Timesheets",
|
||||
"subtitle": "Manage and approve timesheet entries",
|
||||
"subtitle": "Manage and approve worker timesheets",
|
||||
"addTimesheet": "Add Timesheet",
|
||||
"editTimesheet": "Edit Timesheet",
|
||||
"approveTimesheet": "Approve Timesheet",
|
||||
@@ -133,7 +133,9 @@
|
||||
"submitted": "Submitted",
|
||||
"approved": "Approved",
|
||||
"rejected": "Rejected",
|
||||
"processed": "Processed"
|
||||
"processed": "Processed",
|
||||
"pending": "Pending",
|
||||
"all": "All Status"
|
||||
},
|
||||
"hoursWorked": "Hours Worked",
|
||||
"overtimeHours": "Overtime Hours",
|
||||
@@ -142,7 +144,57 @@
|
||||
"nightShift": "Night Shift",
|
||||
"weekendShift": "Weekend Shift",
|
||||
"adjustments": "Adjustments",
|
||||
"comments": "Comments"
|
||||
"comments": "Comments",
|
||||
"analytics": "Analytics",
|
||||
"showAnalytics": "Show Analytics",
|
||||
"hideAnalytics": "Hide Analytics",
|
||||
"totalTimesheets": "Total Timesheets",
|
||||
"pendingReview": "{{count}} pending review",
|
||||
"thisPeriod": "This period",
|
||||
"validationIssues": "Validation Issues",
|
||||
"errorsFound": "Errors found",
|
||||
"allValid": "All valid",
|
||||
"totalValue": "Total Value",
|
||||
"pendingInvoicing": "Pending invoicing",
|
||||
"pending": "Pending",
|
||||
"awaitingApproval": "Awaiting approval",
|
||||
"approved": "Approved",
|
||||
"readyForBilling": "Ready for billing",
|
||||
"approvalRate": "Approval Rate",
|
||||
"searchAndFilter": "Search & Filter",
|
||||
"findTimesheetsAdvanced": "Find timesheets using advanced search",
|
||||
"results": "{{count}} results",
|
||||
"searchPlaceholder": "Search by worker, client, or status...",
|
||||
"allStatus": "All Status",
|
||||
"validationErrors": "{{count}} validation {{errors}}",
|
||||
"error": "error",
|
||||
"errors": "errors",
|
||||
"exportCsv": "Export CSV",
|
||||
"workerName": "Worker Name",
|
||||
"clientName": "Client Name",
|
||||
"bulkImport": "Bulk Import",
|
||||
"detailedEntry": "Detailed Entry",
|
||||
"createSuccess": "Timesheet created successfully",
|
||||
"createError": "Failed to create timesheet",
|
||||
"createDetailedSuccess": "Detailed timesheet created successfully",
|
||||
"createDetailedError": "Failed to create detailed timesheet",
|
||||
"importSuccess": "{{count}} timesheets imported successfully",
|
||||
"importError": "Failed to import timesheets",
|
||||
"approveSuccess": "Timesheet approved",
|
||||
"approveError": "Failed to approve timesheet",
|
||||
"rejectSuccess": "Timesheet rejected",
|
||||
"rejectError": "Failed to reject timesheet",
|
||||
"adjustSuccess": "Timesheet adjusted",
|
||||
"adjustError": "Failed to adjust timesheet",
|
||||
"deleteSuccess": "Timesheet deleted",
|
||||
"deleteError": "Failed to delete timesheet",
|
||||
"noPermissionApprove": "You do not have permission to approve timesheets",
|
||||
"noPermissionReject": "You do not have permission to reject timesheets",
|
||||
"noPermissionEdit": "You do not have permission to adjust timesheets",
|
||||
"noPermissionDelete": "You do not have permission to delete timesheets",
|
||||
"adjustmentSubmitted": "Adjustment submitted for approval",
|
||||
"adjustmentApplied": "Adjustment applied successfully",
|
||||
"adjustmentError": "Failed to apply adjustment"
|
||||
},
|
||||
"billing": {
|
||||
"title": "Billing",
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
},
|
||||
"timesheets": {
|
||||
"title": "Hojas de Tiempo",
|
||||
"subtitle": "Gestionar y aprobar entradas de hojas de tiempo",
|
||||
"subtitle": "Gestionar y aprobar hojas de tiempo de trabajadores",
|
||||
"addTimesheet": "Agregar Hoja de Tiempo",
|
||||
"editTimesheet": "Editar Hoja de Tiempo",
|
||||
"approveTimesheet": "Aprobar Hoja de Tiempo",
|
||||
@@ -133,7 +133,9 @@
|
||||
"submitted": "Enviado",
|
||||
"approved": "Aprobado",
|
||||
"rejected": "Rechazado",
|
||||
"processed": "Procesado"
|
||||
"processed": "Procesado",
|
||||
"pending": "Pendiente",
|
||||
"all": "Todos los Estados"
|
||||
},
|
||||
"hoursWorked": "Horas Trabajadas",
|
||||
"overtimeHours": "Horas Extras",
|
||||
@@ -142,7 +144,57 @@
|
||||
"nightShift": "Turno Nocturno",
|
||||
"weekendShift": "Turno de Fin de Semana",
|
||||
"adjustments": "Ajustes",
|
||||
"comments": "Comentarios"
|
||||
"comments": "Comentarios",
|
||||
"analytics": "Análisis",
|
||||
"showAnalytics": "Mostrar Análisis",
|
||||
"hideAnalytics": "Ocultar Análisis",
|
||||
"totalTimesheets": "Hojas de Tiempo Totales",
|
||||
"pendingReview": "{{count}} pendientes de revisión",
|
||||
"thisPeriod": "Este período",
|
||||
"validationIssues": "Problemas de Validación",
|
||||
"errorsFound": "Errores encontrados",
|
||||
"allValid": "Todo válido",
|
||||
"totalValue": "Valor Total",
|
||||
"pendingInvoicing": "Facturación pendiente",
|
||||
"pending": "Pendiente",
|
||||
"awaitingApproval": "Esperando aprobación",
|
||||
"approved": "Aprobado",
|
||||
"readyForBilling": "Listo para facturar",
|
||||
"approvalRate": "Tasa de Aprobación",
|
||||
"searchAndFilter": "Buscar y Filtrar",
|
||||
"findTimesheetsAdvanced": "Encontrar hojas de tiempo usando búsqueda avanzada",
|
||||
"results": "{{count}} resultados",
|
||||
"searchPlaceholder": "Buscar por trabajador, cliente o estado...",
|
||||
"allStatus": "Todos los Estados",
|
||||
"validationErrors": "{{count}} {{errors}} de validación",
|
||||
"error": "error",
|
||||
"errors": "errores",
|
||||
"exportCsv": "Exportar CSV",
|
||||
"workerName": "Nombre del Trabajador",
|
||||
"clientName": "Nombre del Cliente",
|
||||
"bulkImport": "Importación Masiva",
|
||||
"detailedEntry": "Entrada Detallada",
|
||||
"createSuccess": "Hoja de tiempo creada exitosamente",
|
||||
"createError": "Fallo al crear hoja de tiempo",
|
||||
"createDetailedSuccess": "Hoja de tiempo detallada creada exitosamente",
|
||||
"createDetailedError": "Fallo al crear hoja de tiempo detallada",
|
||||
"importSuccess": "{{count}} hojas de tiempo importadas exitosamente",
|
||||
"importError": "Fallo al importar hojas de tiempo",
|
||||
"approveSuccess": "Hoja de tiempo aprobada",
|
||||
"approveError": "Fallo al aprobar hoja de tiempo",
|
||||
"rejectSuccess": "Hoja de tiempo rechazada",
|
||||
"rejectError": "Fallo al rechazar hoja de tiempo",
|
||||
"adjustSuccess": "Hoja de tiempo ajustada",
|
||||
"adjustError": "Fallo al ajustar hoja de tiempo",
|
||||
"deleteSuccess": "Hoja de tiempo eliminada",
|
||||
"deleteError": "Fallo al eliminar hoja de tiempo",
|
||||
"noPermissionApprove": "No tienes permiso para aprobar hojas de tiempo",
|
||||
"noPermissionReject": "No tienes permiso para rechazar hojas de tiempo",
|
||||
"noPermissionEdit": "No tienes permiso para ajustar hojas de tiempo",
|
||||
"noPermissionDelete": "No tienes permiso para eliminar hojas de tiempo",
|
||||
"adjustmentSubmitted": "Ajuste enviado para aprobación",
|
||||
"adjustmentApplied": "Ajuste aplicado exitosamente",
|
||||
"adjustmentError": "Fallo al aplicar ajuste"
|
||||
},
|
||||
"billing": {
|
||||
"title": "Facturación",
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
},
|
||||
"timesheets": {
|
||||
"title": "Feuilles de Temps",
|
||||
"subtitle": "Gérer et approuver les entrées de feuilles de temps",
|
||||
"subtitle": "Gérer et approuver les feuilles de temps des travailleurs",
|
||||
"addTimesheet": "Ajouter une Feuille de Temps",
|
||||
"editTimesheet": "Modifier une Feuille de Temps",
|
||||
"approveTimesheet": "Approuver une Feuille de Temps",
|
||||
@@ -133,7 +133,9 @@
|
||||
"submitted": "Soumis",
|
||||
"approved": "Approuvé",
|
||||
"rejected": "Rejeté",
|
||||
"processed": "Traité"
|
||||
"processed": "Traité",
|
||||
"pending": "En Attente",
|
||||
"all": "Tous les Statuts"
|
||||
},
|
||||
"hoursWorked": "Heures Travaillées",
|
||||
"overtimeHours": "Heures Supplémentaires",
|
||||
@@ -142,7 +144,57 @@
|
||||
"nightShift": "Quart de Nuit",
|
||||
"weekendShift": "Quart de Fin de Semaine",
|
||||
"adjustments": "Ajustements",
|
||||
"comments": "Commentaires"
|
||||
"comments": "Commentaires",
|
||||
"analytics": "Analytique",
|
||||
"showAnalytics": "Afficher l'Analytique",
|
||||
"hideAnalytics": "Masquer l'Analytique",
|
||||
"totalTimesheets": "Feuilles de Temps Totales",
|
||||
"pendingReview": "{{count}} en attente de révision",
|
||||
"thisPeriod": "Cette période",
|
||||
"validationIssues": "Problèmes de Validation",
|
||||
"errorsFound": "Erreurs trouvées",
|
||||
"allValid": "Tout est valide",
|
||||
"totalValue": "Valeur Totale",
|
||||
"pendingInvoicing": "Facturation en attente",
|
||||
"pending": "En Attente",
|
||||
"awaitingApproval": "En attente d'approbation",
|
||||
"approved": "Approuvé",
|
||||
"readyForBilling": "Prêt pour la facturation",
|
||||
"approvalRate": "Taux d'Approbation",
|
||||
"searchAndFilter": "Recherche et Filtrage",
|
||||
"findTimesheetsAdvanced": "Rechercher des feuilles de temps avec recherche avancée",
|
||||
"results": "{{count}} résultats",
|
||||
"searchPlaceholder": "Rechercher par travailleur, client ou statut...",
|
||||
"allStatus": "Tous les Statuts",
|
||||
"validationErrors": "{{count}} {{errors}} de validation",
|
||||
"error": "erreur",
|
||||
"errors": "erreurs",
|
||||
"exportCsv": "Exporter CSV",
|
||||
"workerName": "Nom du Travailleur",
|
||||
"clientName": "Nom du Client",
|
||||
"bulkImport": "Importation en Vrac",
|
||||
"detailedEntry": "Entrée Détaillée",
|
||||
"createSuccess": "Feuille de temps créée avec succès",
|
||||
"createError": "Échec de la création de la feuille de temps",
|
||||
"createDetailedSuccess": "Feuille de temps détaillée créée avec succès",
|
||||
"createDetailedError": "Échec de la création de la feuille de temps détaillée",
|
||||
"importSuccess": "{{count}} feuilles de temps importées avec succès",
|
||||
"importError": "Échec de l'importation des feuilles de temps",
|
||||
"approveSuccess": "Feuille de temps approuvée",
|
||||
"approveError": "Échec de l'approbation de la feuille de temps",
|
||||
"rejectSuccess": "Feuille de temps rejetée",
|
||||
"rejectError": "Échec du rejet de la feuille de temps",
|
||||
"adjustSuccess": "Feuille de temps ajustée",
|
||||
"adjustError": "Échec de l'ajustement de la feuille de temps",
|
||||
"deleteSuccess": "Feuille de temps supprimée",
|
||||
"deleteError": "Échec de la suppression de la feuille de temps",
|
||||
"noPermissionApprove": "Vous n'avez pas la permission d'approuver les feuilles de temps",
|
||||
"noPermissionReject": "Vous n'avez pas la permission de rejeter les feuilles de temps",
|
||||
"noPermissionEdit": "Vous n'avez pas la permission d'ajuster les feuilles de temps",
|
||||
"noPermissionDelete": "Vous n'avez pas la permission de supprimer les feuilles de temps",
|
||||
"adjustmentSubmitted": "Ajustement soumis pour approbation",
|
||||
"adjustmentApplied": "Ajustement appliqué avec succès",
|
||||
"adjustmentError": "Échec de l'application de l'ajustement"
|
||||
},
|
||||
"billing": {
|
||||
"title": "Facturation",
|
||||
|
||||
Reference in New Issue
Block a user