refactor: extract helpers from ReminderDetailDrawer + OfferCreationWizard
- ReminderDetailDrawer (340→257 lines): constants + SectionTitle/DateRow/ActivityEntry → reminderDetailHelpers.tsx - OfferCreationWizard (350→276 lines): PDF step JSX → OfferWizardPdfStep.tsx Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,15 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
Box, Button, CircularProgress, Dialog, DialogContent,
|
Box, Button, CircularProgress, Dialog, DialogContent,
|
||||||
IconButton, LinearProgress, Stack, Step, StepLabel, Stepper,
|
IconButton, Stack, Step, StepLabel, Stepper,
|
||||||
TextField, Typography,
|
TextField, Typography,
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { Download, Plus, Send, Trash2, X } from 'lucide-react'
|
import { Plus, Trash2, X } from 'lucide-react'
|
||||||
import type { OfferReportDraft, ViewingAppointmentOption } from '../../domain/offerReport'
|
import type { OfferReportDraft, ViewingAppointmentOption } from '../../domain/offerReport'
|
||||||
import { offerReportService } from '../../services/offerReportService'
|
import { offerReportService } from '../../services/offerReportService'
|
||||||
import { usePropertyById } from '../../hooks/useProperties'
|
import { usePropertyById } from '../../hooks/useProperties'
|
||||||
import { useToastStore } from '../../stores/toastStore'
|
import { useToastStore } from '../../stores/toastStore'
|
||||||
|
import { OfferWizardPdfStep } from './OfferWizardPdfStep'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
inquiryId: string
|
inquiryId: string
|
||||||
@@ -213,94 +214,19 @@ export function OfferCreationWizard({ inquiryId, propertyId, tenantName, onClose
|
|||||||
|
|
||||||
{/* Step 3: PDF */}
|
{/* Step 3: PDF */}
|
||||||
{step === 3 && (
|
{step === 3 && (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 3 }}>
|
<OfferWizardPdfStep
|
||||||
{generating ? (
|
generating={generating}
|
||||||
<>
|
progress={progress}
|
||||||
<CircularProgress size={48} sx={{ color: '#1e3a5f' }} />
|
ready={ready}
|
||||||
<Typography variant="body1" sx={{ fontWeight: 600, color: '#1e3a5f' }}>
|
draft={draft}
|
||||||
PDF wird generiert…
|
property={property}
|
||||||
</Typography>
|
onDownload={() => showToast('PDF wird heruntergeladen…', 'info')}
|
||||||
<Box sx={{ width: '100%', maxWidth: 400 }}>
|
onAttach={() => {
|
||||||
<LinearProgress variant="determinate" value={progress} sx={{ height: 6, borderRadius: 3 }} />
|
const label = `Angebot_${property?.title ?? 'Objekt'}.pdf`
|
||||||
<Typography variant="caption" sx={{ color: '#64748b', display: 'block', textAlign: 'center', mt: 1 }}>
|
onAttach(label)
|
||||||
{Math.round(progress)}%
|
showToast('Angebot als Anhang hinzugefügt', 'success')
|
||||||
</Typography>
|
}}
|
||||||
</Box>
|
/>
|
||||||
</>
|
|
||||||
) : ready ? (
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, width: '100%' }}>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: 520,
|
|
||||||
height: 380,
|
|
||||||
bgcolor: '#f8fafc',
|
|
||||||
border: '1px solid #e2e8f0',
|
|
||||||
borderRadius: 1.5,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ bgcolor: '#1e3a5f', px: 2, py: 1, display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
||||||
<Box sx={{ width: 10, height: 10, borderRadius: '50%', bgcolor: '#ef4444' }} />
|
|
||||||
<Box sx={{ width: 10, height: 10, borderRadius: '50%', bgcolor: '#f59e0b' }} />
|
|
||||||
<Box sx={{ width: 10, height: 10, borderRadius: '50%', bgcolor: '#10b981' }} />
|
|
||||||
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)', ml: 1, fontSize: '0.7rem' }}>
|
|
||||||
Angebot_{draft?.propertyId ?? 'Objekt'}.pdf
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ flex: 1, p: 3 }}>
|
|
||||||
<Typography variant="body2" sx={{ fontWeight: 700, color: '#1e3a5f', mb: 1 }}>
|
|
||||||
Angebotsschreiben
|
|
||||||
</Typography>
|
|
||||||
{draft?.editableFields.slice(0, 3).map(f => (
|
|
||||||
<Box key={f.id} sx={{ mb: 1 }}>
|
|
||||||
<Typography variant="caption" sx={{ color: '#94a3b8', fontSize: '0.65rem', display: 'block' }}>{f.label}</Typography>
|
|
||||||
<Typography variant="caption" sx={{ color: '#374151', fontSize: '0.75rem', display: 'block' }}>
|
|
||||||
{f.value.slice(0, 80)}{f.value.length > 80 ? '…' : ''}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
{(draft?.viewingAppointments.length ?? 0) > 0 && (
|
|
||||||
<Box sx={{ mt: 1.5, p: 1, bgcolor: '#f1f5f9', borderRadius: 1 }}>
|
|
||||||
<Typography variant="caption" sx={{ fontWeight: 600, fontSize: '0.72rem', display: 'block', mb: 0.5 }}>
|
|
||||||
Besichtigungstermine
|
|
||||||
</Typography>
|
|
||||||
{draft!.viewingAppointments.map(a => (
|
|
||||||
<Typography key={a.id} variant="caption" sx={{ fontSize: '0.7rem', display: 'block', color: '#475569' }}>
|
|
||||||
{new Date(a.date).toLocaleDateString('de-CH')} · {a.timeSlot}
|
|
||||||
</Typography>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ display: 'flex', gap: 1.5 }}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
startIcon={<Download size={16} />}
|
|
||||||
onClick={() => showToast('PDF wird heruntergeladen…', 'info')}
|
|
||||||
sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }}
|
|
||||||
>
|
|
||||||
Herunterladen
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
startIcon={<Send size={16} />}
|
|
||||||
onClick={() => {
|
|
||||||
const label = `Angebot_${property?.title ?? 'Objekt'}.pdf`
|
|
||||||
onAttach(label)
|
|
||||||
showToast('Angebot als Anhang hinzugefügt', 'success')
|
|
||||||
}}
|
|
||||||
sx={{ textTransform: 'none' }}
|
|
||||||
>
|
|
||||||
An Nachricht anhängen
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
) : null}
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { Box, Button, CircularProgress, LinearProgress, Typography } from '@mui/material'
|
||||||
|
import { Download, Send } from 'lucide-react'
|
||||||
|
import type { OfferReportDraft } from '../../domain/offerReport'
|
||||||
|
import type { Property } from '../../domain/property'
|
||||||
|
|
||||||
|
interface OfferWizardPdfStepProps {
|
||||||
|
generating: boolean
|
||||||
|
progress: number
|
||||||
|
ready: boolean
|
||||||
|
draft: OfferReportDraft | null
|
||||||
|
property: Property | undefined
|
||||||
|
onDownload: () => void
|
||||||
|
onAttach: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OfferWizardPdfStep({ generating, progress, ready, draft, property, onDownload, onAttach }: OfferWizardPdfStepProps) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 3 }}>
|
||||||
|
{generating ? (
|
||||||
|
<>
|
||||||
|
<CircularProgress size={48} sx={{ color: '#1e3a5f' }} />
|
||||||
|
<Typography variant="body1" sx={{ fontWeight: 600, color: '#1e3a5f' }}>
|
||||||
|
PDF wird generiert…
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ width: '100%', maxWidth: 400 }}>
|
||||||
|
<LinearProgress variant="determinate" value={progress} sx={{ height: 6, borderRadius: 3 }} />
|
||||||
|
<Typography variant="caption" sx={{ color: '#64748b', display: 'block', textAlign: 'center', mt: 1 }}>
|
||||||
|
{Math.round(progress)}%
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
) : ready ? (
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, width: '100%' }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 520,
|
||||||
|
height: 380,
|
||||||
|
bgcolor: '#f8fafc',
|
||||||
|
border: '1px solid #e2e8f0',
|
||||||
|
borderRadius: 1.5,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ bgcolor: '#1e3a5f', px: 2, py: 1, display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<Box sx={{ width: 10, height: 10, borderRadius: '50%', bgcolor: '#ef4444' }} />
|
||||||
|
<Box sx={{ width: 10, height: 10, borderRadius: '50%', bgcolor: '#f59e0b' }} />
|
||||||
|
<Box sx={{ width: 10, height: 10, borderRadius: '50%', bgcolor: '#10b981' }} />
|
||||||
|
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.7)', ml: 1, fontSize: '0.7rem' }}>
|
||||||
|
Angebot_{property?.title ?? 'Objekt'}.pdf
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ flex: 1, p: 3 }}>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 700, color: '#1e3a5f', mb: 1 }}>
|
||||||
|
Angebotsschreiben
|
||||||
|
</Typography>
|
||||||
|
{draft?.editableFields.slice(0, 3).map(f => (
|
||||||
|
<Box key={f.id} sx={{ mb: 1 }}>
|
||||||
|
<Typography variant="caption" sx={{ color: '#94a3b8', fontSize: '0.65rem', display: 'block' }}>{f.label}</Typography>
|
||||||
|
<Typography variant="caption" sx={{ color: '#374151', fontSize: '0.75rem', display: 'block' }}>
|
||||||
|
{f.value.slice(0, 80)}{f.value.length > 80 ? '…' : ''}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
{(draft?.viewingAppointments.length ?? 0) > 0 && (
|
||||||
|
<Box sx={{ mt: 1.5, p: 1, bgcolor: '#f1f5f9', borderRadius: 1 }}>
|
||||||
|
<Typography variant="caption" sx={{ fontWeight: 600, fontSize: '0.72rem', display: 'block', mb: 0.5 }}>
|
||||||
|
Besichtigungstermine
|
||||||
|
</Typography>
|
||||||
|
{draft!.viewingAppointments.map(a => (
|
||||||
|
<Typography key={a.id} variant="caption" sx={{ fontSize: '0.7rem', display: 'block', color: '#475569' }}>
|
||||||
|
{new Date(a.date).toLocaleDateString('de-CH')} · {a.timeSlot}
|
||||||
|
</Typography>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ display: 'flex', gap: 1.5 }}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
startIcon={<Download size={16} />}
|
||||||
|
onClick={onDownload}
|
||||||
|
sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }}
|
||||||
|
>
|
||||||
|
Herunterladen
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
startIcon={<Send size={16} />}
|
||||||
|
onClick={onAttach}
|
||||||
|
sx={{ textTransform: 'none' }}
|
||||||
|
>
|
||||||
|
An Nachricht anhängen
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -18,91 +18,8 @@ import { useReminder, useCompleteReminder, useDismissReminder, useSnoozeReminder
|
|||||||
import { ReminderPriorityBadge } from './ReminderPriorityBadge'
|
import { ReminderPriorityBadge } from './ReminderPriorityBadge'
|
||||||
import { ReminderTypeBadge } from './ReminderTypeBadge'
|
import { ReminderTypeBadge } from './ReminderTypeBadge'
|
||||||
import { ReminderDaysIndicator } from './ReminderDaysIndicator'
|
import { ReminderDaysIndicator } from './ReminderDaysIndicator'
|
||||||
import { ReminderStatus, ShadowMarketRisk } from '../../domain/reminder'
|
import { ReminderStatus } from '../../domain/reminder'
|
||||||
import type { ReminderActivity } from '../../domain/reminder'
|
import { SHADOW_RISK_COLOR, SHADOW_RISK_LABEL, STATUS_CHIP_COLOR, STATUS_LABEL, ACTION_LABEL, SectionTitle, DateRow, ActivityEntry } from './reminderDetailHelpers'
|
||||||
|
|
||||||
const SHADOW_RISK_COLOR: Record<string, string> = {
|
|
||||||
NONE: '#64748b',
|
|
||||||
LOW: '#16a34a',
|
|
||||||
MEDIUM: '#ca8a04',
|
|
||||||
HIGH: '#dc2626',
|
|
||||||
}
|
|
||||||
|
|
||||||
const SHADOW_RISK_LABEL: Record<string, string> = {
|
|
||||||
NONE: 'Kein Risiko',
|
|
||||||
LOW: 'Niedrig',
|
|
||||||
MEDIUM: 'Mittel',
|
|
||||||
HIGH: 'Hoch',
|
|
||||||
}
|
|
||||||
|
|
||||||
const STATUS_CHIP_COLOR: Record<string, 'default' | 'success' | 'warning' | 'error' | 'info'> = {
|
|
||||||
ACTIVE: 'info',
|
|
||||||
SNOOZED: 'warning',
|
|
||||||
COMPLETED: 'success',
|
|
||||||
DISMISSED: 'default',
|
|
||||||
}
|
|
||||||
|
|
||||||
const STATUS_LABEL: Record<string, string> = {
|
|
||||||
ACTIVE: 'Aktiv',
|
|
||||||
SNOOZED: 'Schlummernd',
|
|
||||||
COMPLETED: 'Erledigt',
|
|
||||||
DISMISSED: 'Verworfen',
|
|
||||||
}
|
|
||||||
|
|
||||||
const ACTION_LABEL: Record<string, string> = {
|
|
||||||
CREATED: 'Erstellt',
|
|
||||||
SNOOZED: 'Zurückgestellt',
|
|
||||||
COMPLETED: 'Erledigt',
|
|
||||||
DISMISSED: 'Verworfen',
|
|
||||||
NOTED: 'Notiz',
|
|
||||||
}
|
|
||||||
|
|
||||||
function SectionTitle({ icon, children }: { icon: React.ReactNode; children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1.5 }}>
|
|
||||||
{icon}
|
|
||||||
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: '#1e293b', fontSize: '0.8125rem' }}>
|
|
||||||
{children}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function DateRow({ label, value }: { label: string; value?: string }) {
|
|
||||||
if (!value) return null
|
|
||||||
return (
|
|
||||||
<Box sx={{ display: 'grid', gridTemplateColumns: '120px 1fr', gap: 1, alignItems: 'baseline' }}>
|
|
||||||
<Typography variant="caption" color="text.secondary">{label}</Typography>
|
|
||||||
<Typography variant="body2" sx={{ fontWeight: 500 }}>{new Date(value).toLocaleDateString('de-CH')}</Typography>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ActivityEntry({ entry }: { entry: ReminderActivity }) {
|
|
||||||
return (
|
|
||||||
<Box sx={{ display: 'flex', gap: 1.5 }}>
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flexShrink: 0 }}>
|
|
||||||
<Box sx={{ width: 8, height: 8, borderRadius: '50%', bgcolor: '#94a3b8', mt: '4px' }} />
|
|
||||||
<Box sx={{ width: 1, flex: 1, bgcolor: '#e2e8f0', mt: 0.5 }} />
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ pb: 1.5, minWidth: 0 }}>
|
|
||||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'baseline', flexWrap: 'wrap' }}>
|
|
||||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#1e293b' }}>
|
|
||||||
{ACTION_LABEL[entry.action]}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="caption" color="text.secondary">
|
|
||||||
{entry.by} · {new Date(entry.at).toLocaleDateString('de-CH')}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
{entry.note && (
|
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.25 }}>
|
|
||||||
{entry.note}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ReminderDetailDrawer() {
|
export function ReminderDetailDrawer() {
|
||||||
const { selectedId, drawerOpen, setDrawerOpen, setSelectedId } = useReminderStore()
|
const { selectedId, drawerOpen, setDrawerOpen, setSelectedId } = useReminderStore()
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { Box, Typography } from '@mui/material'
|
||||||
|
import type { ReminderActivity } from '../../domain/reminder'
|
||||||
|
|
||||||
|
export const SHADOW_RISK_COLOR: Record<string, string> = {
|
||||||
|
NONE: '#64748b',
|
||||||
|
LOW: '#16a34a',
|
||||||
|
MEDIUM: '#ca8a04',
|
||||||
|
HIGH: '#dc2626',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SHADOW_RISK_LABEL: Record<string, string> = {
|
||||||
|
NONE: 'Kein Risiko',
|
||||||
|
LOW: 'Niedrig',
|
||||||
|
MEDIUM: 'Mittel',
|
||||||
|
HIGH: 'Hoch',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const STATUS_CHIP_COLOR: Record<string, 'default' | 'success' | 'warning' | 'error' | 'info'> = {
|
||||||
|
ACTIVE: 'info',
|
||||||
|
SNOOZED: 'warning',
|
||||||
|
COMPLETED: 'success',
|
||||||
|
DISMISSED: 'default',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const STATUS_LABEL: Record<string, string> = {
|
||||||
|
ACTIVE: 'Aktiv',
|
||||||
|
SNOOZED: 'Schlummernd',
|
||||||
|
COMPLETED: 'Erledigt',
|
||||||
|
DISMISSED: 'Verworfen',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ACTION_LABEL: Record<string, string> = {
|
||||||
|
CREATED: 'Erstellt',
|
||||||
|
SNOOZED: 'Zurückgestellt',
|
||||||
|
COMPLETED: 'Erledigt',
|
||||||
|
DISMISSED: 'Verworfen',
|
||||||
|
NOTED: 'Notiz',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SectionTitle({ icon, children }: { icon: React.ReactNode; children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1.5 }}>
|
||||||
|
{icon}
|
||||||
|
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: '#1e293b', fontSize: '0.8125rem' }}>
|
||||||
|
{children}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DateRow({ label, value }: { label: string; value?: string }) {
|
||||||
|
if (!value) return null
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'grid', gridTemplateColumns: '120px 1fr', gap: 1, alignItems: 'baseline' }}>
|
||||||
|
<Typography variant="caption" color="text.secondary">{label}</Typography>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 500 }}>{new Date(value).toLocaleDateString('de-CH')}</Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ActivityEntry({ entry }: { entry: ReminderActivity }) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', gap: 1.5 }}>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flexShrink: 0 }}>
|
||||||
|
<Box sx={{ width: 8, height: 8, borderRadius: '50%', bgcolor: '#94a3b8', mt: '4px' }} />
|
||||||
|
<Box sx={{ width: 1, flex: 1, bgcolor: '#e2e8f0', mt: 0.5 }} />
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ pb: 1.5, minWidth: 0 }}>
|
||||||
|
<Box sx={{ display: 'flex', gap: 1, alignItems: 'baseline', flexWrap: 'wrap' }}>
|
||||||
|
<Typography variant="caption" sx={{ fontWeight: 600, color: '#1e293b' }}>
|
||||||
|
{ACTION_LABEL[entry.action]}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
{entry.by} · {new Date(entry.at).toLocaleDateString('de-CH')}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
{entry.note && (
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.25 }}>
|
||||||
|
{entry.note}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user