feat: create sync status indicator component
Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
25
__tests__/components/sync-status-indicator.test.tsx
Normal file
25
__tests__/components/sync-status-indicator.test.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import { SyncStatusIndicator } from '@/components/bible/sync-status-indicator'
|
||||||
|
|
||||||
|
describe('SyncStatusIndicator', () => {
|
||||||
|
it('should show synced state', () => {
|
||||||
|
render(<SyncStatusIndicator status="synced" />)
|
||||||
|
expect(screen.getByTestId('sync-status-synced')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should show syncing state with spinner', () => {
|
||||||
|
render(<SyncStatusIndicator status="syncing" />)
|
||||||
|
expect(screen.getByTestId('sync-status-syncing')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should show error state', () => {
|
||||||
|
render(<SyncStatusIndicator status="error" errorMessage="Network error" />)
|
||||||
|
expect(screen.getByTestId('sync-status-error')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('Network error')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should show pending count', () => {
|
||||||
|
render(<SyncStatusIndicator status="pending" pendingCount={3} />)
|
||||||
|
expect(screen.getByText('3 pending')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
85
components/bible/sync-status-indicator.tsx
Normal file
85
components/bible/sync-status-indicator.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
'use client'
|
||||||
|
import { Box, Chip, CircularProgress, Tooltip, Typography } from '@mui/material'
|
||||||
|
import CloudSyncIcon from '@mui/icons-material/CloudSync'
|
||||||
|
import CheckCircleIcon from '@mui/icons-material/CheckCircle'
|
||||||
|
import ErrorIcon from '@mui/icons-material/Error'
|
||||||
|
import ScheduleIcon from '@mui/icons-material/Schedule'
|
||||||
|
|
||||||
|
interface SyncStatusIndicatorProps {
|
||||||
|
status: 'synced' | 'syncing' | 'pending' | 'error'
|
||||||
|
pendingCount?: number
|
||||||
|
errorMessage?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SyncStatusIndicator({
|
||||||
|
status,
|
||||||
|
pendingCount = 0,
|
||||||
|
errorMessage
|
||||||
|
}: SyncStatusIndicatorProps) {
|
||||||
|
if (status === 'synced') {
|
||||||
|
return (
|
||||||
|
<Tooltip title="All changes synced">
|
||||||
|
<Chip
|
||||||
|
data-testid="sync-status-synced"
|
||||||
|
icon={<CheckCircleIcon sx={{ color: 'success.main' }} />}
|
||||||
|
label="Synced"
|
||||||
|
variant="outlined"
|
||||||
|
color="success"
|
||||||
|
size="small"
|
||||||
|
sx={{ fontWeight: 500 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'syncing') {
|
||||||
|
return (
|
||||||
|
<Tooltip title="Syncing with server">
|
||||||
|
<Chip
|
||||||
|
data-testid="sync-status-syncing"
|
||||||
|
icon={<CircularProgress size={16} />}
|
||||||
|
label="Syncing..."
|
||||||
|
variant="filled"
|
||||||
|
color="primary"
|
||||||
|
size="small"
|
||||||
|
sx={{ fontWeight: 500 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'pending') {
|
||||||
|
return (
|
||||||
|
<Tooltip title={`${pendingCount} highlights waiting to sync`}>
|
||||||
|
<Chip
|
||||||
|
data-testid="sync-status-pending"
|
||||||
|
icon={<ScheduleIcon sx={{ color: 'warning.main' }} />}
|
||||||
|
label={`${pendingCount} pending`}
|
||||||
|
variant="outlined"
|
||||||
|
color="warning"
|
||||||
|
size="small"
|
||||||
|
sx={{ fontWeight: 500 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
return (
|
||||||
|
<Tooltip title={errorMessage || 'Sync failed'}>
|
||||||
|
<Box data-testid="sync-status-error" sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<ErrorIcon sx={{ color: 'error.main', fontSize: 20 }} />
|
||||||
|
<Box>
|
||||||
|
<Typography variant="caption" color="error" sx={{ fontWeight: 600 }}>
|
||||||
|
Sync Error
|
||||||
|
</Typography>
|
||||||
|
{errorMessage && (
|
||||||
|
<Typography variant="caption" color="error" sx={{ display: 'block' }}>
|
||||||
|
{errorMessage}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user