feat: replace all hardcoded en-US and $ formatting with locale-aware helpers
This commit is contained in:
@@ -94,6 +94,7 @@ import {
|
||||
import { Clock } from 'lucide-vue-next'
|
||||
import { useEntriesStore } from '../stores/entries'
|
||||
import { useProjectsStore } from '../stores/projects'
|
||||
import { formatDateLong } from '../utils/locale'
|
||||
import type { TimeEntry } from '../stores/entries'
|
||||
|
||||
// Register Chart.js components
|
||||
@@ -117,12 +118,7 @@ const greeting = computed(() => {
|
||||
|
||||
// Formatted date
|
||||
const formattedDate = computed(() => {
|
||||
return new Date().toLocaleDateString('en-US', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
return formatDateLong(new Date().toISOString())
|
||||
})
|
||||
|
||||
// Empty state check
|
||||
|
||||
@@ -231,6 +231,7 @@ import AppSelect from '../components/AppSelect.vue'
|
||||
import AppDatePicker from '../components/AppDatePicker.vue'
|
||||
import { useEntriesStore, type TimeEntry } from '../stores/entries'
|
||||
import { useProjectsStore } from '../stores/projects'
|
||||
import { formatDate } from '../utils/locale'
|
||||
|
||||
const entriesStore = useEntriesStore()
|
||||
const projectsStore = useProjectsStore()
|
||||
@@ -316,16 +317,6 @@ function formatDuration(seconds: number): string {
|
||||
return `${minutes}m`
|
||||
}
|
||||
|
||||
// Format date
|
||||
function formatDate(dateString: string): string {
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
})
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
function applyFilters() {
|
||||
entriesStore.fetchEntries(startDate.value || undefined, endDate.value || undefined)
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
{{ formatDate(invoice.date) }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right text-[0.75rem] font-mono text-accent-text">
|
||||
${{ invoice.total.toFixed(2) }}
|
||||
{{ formatCurrency(invoice.total) }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<span
|
||||
@@ -194,19 +194,19 @@
|
||||
<div class="bg-bg-inset rounded-lg p-4">
|
||||
<div class="flex justify-between text-[0.75rem] text-text-secondary mb-2">
|
||||
<span>Subtotal:</span>
|
||||
<span class="font-mono">${{ calculateSubtotal().toFixed(2) }}</span>
|
||||
<span class="font-mono">{{ formatCurrency(calculateSubtotal()) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-[0.75rem] text-text-secondary mb-2">
|
||||
<span>Tax ({{ createForm.tax_rate }}%):</span>
|
||||
<span class="font-mono">${{ calculateTax().toFixed(2) }}</span>
|
||||
<span class="font-mono">{{ formatCurrency(calculateTax()) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-[0.75rem] text-text-secondary mb-2">
|
||||
<span>Discount:</span>
|
||||
<span class="font-mono">-${{ createForm.discount.toFixed(2) }}</span>
|
||||
<span class="font-mono">-{{ formatCurrency(createForm.discount) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-text-primary font-medium text-[0.8125rem] pt-2 border-t border-border-subtle">
|
||||
<span>Total:</span>
|
||||
<span class="font-mono text-accent-text">${{ calculateTotal().toFixed(2) }}</span>
|
||||
<span class="font-mono text-accent-text">{{ formatCurrency(calculateTotal()) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -266,19 +266,19 @@
|
||||
<div class="border-t border-border-subtle pt-4">
|
||||
<div class="flex justify-between text-[0.75rem] text-text-secondary mb-2">
|
||||
<span>Subtotal:</span>
|
||||
<span class="font-mono">${{ (selectedInvoice?.subtotal || 0).toFixed(2) }}</span>
|
||||
<span class="font-mono">{{ formatCurrency(selectedInvoice?.subtotal || 0) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-[0.75rem] text-text-secondary mb-2">
|
||||
<span>Tax ({{ selectedInvoice?.tax_rate || 0 }}%):</span>
|
||||
<span class="font-mono">${{ (selectedInvoice?.tax_amount || 0).toFixed(2) }}</span>
|
||||
<span class="font-mono">{{ formatCurrency(selectedInvoice?.tax_amount || 0) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-[0.75rem] text-text-secondary mb-2">
|
||||
<span>Discount:</span>
|
||||
<span class="font-mono">-${{ (selectedInvoice?.discount || 0).toFixed(2) }}</span>
|
||||
<span class="font-mono">-{{ formatCurrency(selectedInvoice?.discount || 0) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-text-primary font-medium text-[0.8125rem] pt-2 border-t border-border-subtle">
|
||||
<span>Total:</span>
|
||||
<span class="font-mono text-accent-text">${{ (selectedInvoice?.total || 0).toFixed(2) }}</span>
|
||||
<span class="font-mono text-accent-text">{{ formatCurrency(selectedInvoice?.total || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -339,6 +339,7 @@ import { useInvoicesStore, type Invoice } from '../stores/invoices'
|
||||
import { useClientsStore } from '../stores/clients'
|
||||
import { useToastStore } from '../stores/toast'
|
||||
import { generateInvoicePdf } from '../utils/invoicePdf'
|
||||
import { formatDate, formatCurrency } from '../utils/locale'
|
||||
|
||||
const invoicesStore = useInvoicesStore()
|
||||
const clientsStore = useClientsStore()
|
||||
@@ -369,17 +370,6 @@ function getClientName(clientId: number): string {
|
||||
return client?.name || 'Unknown Client'
|
||||
}
|
||||
|
||||
// Format date
|
||||
function formatDate(dateString: string): string {
|
||||
if (!dateString) return '-'
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
})
|
||||
}
|
||||
|
||||
// Calculate subtotal (simplified - would need line items in a real app)
|
||||
function calculateSubtotal(): number {
|
||||
return 0
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 class="text-[0.8125rem] font-semibold text-text-primary">{{ project.name }}</h3>
|
||||
<p class="text-xs text-text-secondary mt-0.5">{{ getClientName(project.client_id) }} · ${{ project.hourly_rate.toFixed(2) }}/hr</p>
|
||||
<p class="text-xs text-text-secondary mt-0.5">{{ getClientName(project.client_id) }} · {{ formatCurrency(project.hourly_rate) }}/hr</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity duration-100">
|
||||
<button
|
||||
@@ -200,6 +200,7 @@ import AppSelect from '../components/AppSelect.vue'
|
||||
import { useProjectsStore, type Project } from '../stores/projects'
|
||||
import { useClientsStore } from '../stores/clients'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
import { formatCurrency } from '../utils/locale'
|
||||
|
||||
const colorPresets = ['#D97706', '#3B82F6', '#8B5CF6', '#EC4899', '#10B981', '#EF4444', '#06B6D4', '#6B7280']
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1">Earnings</p>
|
||||
<p class="text-[1.25rem] font-[family-name:var(--font-heading)] text-accent-text font-medium">${{ reportData.totalEarnings?.toFixed(2) || '0.00' }}</p>
|
||||
<p class="text-[1.25rem] font-[family-name:var(--font-heading)] text-accent-text font-medium">{{ formatCurrency(reportData.totalEarnings || 0) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[0.6875rem] text-text-tertiary uppercase tracking-[0.08em] mb-1">Projects</p>
|
||||
@@ -80,7 +80,7 @@
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="text-[0.75rem] font-mono text-accent-text">{{ formatHours(projectData.total_seconds) }}</span>
|
||||
<span class="text-[0.75rem] font-mono text-text-secondary">${{ ((projectData.total_seconds / 3600) * getProjectRate(projectData.project_id)).toFixed(2) }}</span>
|
||||
<span class="text-[0.75rem] font-mono text-text-secondary">{{ formatCurrency((projectData.total_seconds / 3600) * getProjectRate(projectData.project_id)) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full bg-bg-elevated rounded-full h-[2px]">
|
||||
@@ -120,6 +120,7 @@ import {
|
||||
} from 'chart.js'
|
||||
import { useEntriesStore } from '../stores/entries'
|
||||
import { useProjectsStore } from '../stores/projects'
|
||||
import { formatCurrency } from '../utils/locale'
|
||||
|
||||
// Register Chart.js components
|
||||
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend)
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<p class="text-[0.75rem] font-mono text-text-primary">{{ formatDuration(entry.duration) }}</p>
|
||||
<p class="text-[0.6875rem] text-text-tertiary">{{ formatDate(entry.start_time) }}</p>
|
||||
<p class="text-[0.6875rem] text-text-tertiary">{{ formatDateTime(entry.start_time) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,6 +110,7 @@ import { useEntriesStore } from '../stores/entries'
|
||||
import { useToastStore } from '../stores/toast'
|
||||
import { Timer as TimerIcon } from 'lucide-vue-next'
|
||||
import AppSelect from '../components/AppSelect.vue'
|
||||
import { formatDateTime } from '../utils/locale'
|
||||
|
||||
const timerStore = useTimerStore()
|
||||
const projectsStore = useProjectsStore()
|
||||
@@ -200,17 +201,6 @@ function formatDuration(seconds: number): string {
|
||||
return `${minutes}m`
|
||||
}
|
||||
|
||||
// Format date
|
||||
function formatDate(dateString: string): string {
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
}
|
||||
|
||||
// Load data on mount
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
|
||||
Reference in New Issue
Block a user