refactor: GenericBadge + Provider-Isolation
Teil A — GenericBadge:
- Neue src/components/shared/GenericBadge.tsx mit zwei Varianten:
transparent (${color}18 Hintergrund, farbiger Text, opt. Border)
solid (gefüllte Farbe, weisser Text)
- Props: label, color, variant, showBorder, bold, icon, size, tooltip, ariaLabel
- 7 Badges auf GenericBadge refactored (Config-Objekt + 1-Zeiler):
ReviewStatusBadge, ReviewPriorityBadge, ReviewEntityTypeBadge,
AIOutputStatusBadge, AIErrorBadge, MatchStatusBadge, ShortlistStatusBadge
- Barrel-Export in src/components/shared/index.ts
Teil B — Provider-Isolation:
- src/mock-data/propertyStore.ts als neutrales Daten-Modul erstellt
- MockupPropertyProvider: importiert aus mock-data statt selbst zu definieren
- MockupUnitProvider: importiert aus mock-data statt aus MockupPropertyProvider
- matchSyncService: importiert aus mock-data statt aus MockupPropertyProvider
- Kein Provider importiert mehr einen anderen Provider
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { Chip, Tooltip } from '@mui/material'
|
import { GenericBadge } from '../shared/GenericBadge'
|
||||||
import type { AIOutputError } from '../../domain/aiOutput'
|
import type { AIOutputError } from '../../domain/aiOutput'
|
||||||
|
|
||||||
const ERROR_CONFIG: Record<string, { label: string; color: string }> = {
|
const ERROR_CONFIG: Record<string, { label: string; color: string }> = {
|
||||||
@@ -11,13 +11,5 @@ const ERROR_CONFIG: Record<string, { label: string; color: string }> = {
|
|||||||
|
|
||||||
export function AIErrorBadge({ error }: { error: AIOutputError }) {
|
export function AIErrorBadge({ error }: { error: AIOutputError }) {
|
||||||
const { label, color } = ERROR_CONFIG[error.type] ?? { label: error.type, color: '#c0392b' }
|
const { label, color } = ERROR_CONFIG[error.type] ?? { label: error.type, color: '#c0392b' }
|
||||||
return (
|
return <GenericBadge label={label} color={color} bold tooltip={error.message} />
|
||||||
<Tooltip title={error.message} arrow>
|
|
||||||
<Chip
|
|
||||||
size="small"
|
|
||||||
label={label}
|
|
||||||
sx={{ bgcolor: `${color}18`, color, fontWeight: 700, fontSize: '0.65rem', cursor: 'default' }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
import { Chip } from '@mui/material'
|
import { GenericBadge } from '../shared/GenericBadge'
|
||||||
import type { ReviewStatus } from '../../domain/enums'
|
import type { ReviewStatus } from '../../domain/enums'
|
||||||
|
|
||||||
const CONFIG: Record<ReviewStatus, { label: string; color: string }> = {
|
const CONFIG: Record<ReviewStatus, { label: string; color: string }> = {
|
||||||
UNREVIEWED: { label: 'Ungeprüft', color: '#94a3b8' },
|
UNREVIEWED: { label: 'Ungeprüft', color: '#94a3b8' },
|
||||||
IN_REVIEW: { label: 'In Prüfung', color: '#d97706' },
|
IN_REVIEW: { label: 'In Prüfung', color: '#d97706' },
|
||||||
APPROVED: { label: 'Genehmigt', color: '#1a7a4a' },
|
APPROVED: { label: 'Genehmigt', color: '#1a7a4a' },
|
||||||
REJECTED: { label: 'Abgelehnt', color: '#c0392b' },
|
REJECTED: { label: 'Abgelehnt', color: '#c0392b' },
|
||||||
FLAGGED: { label: 'Markiert', color: '#ea580c' },
|
FLAGGED: { label: 'Markiert', color: '#ea580c' },
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AIOutputStatusBadge({ status }: { status: ReviewStatus }) {
|
export function AIOutputStatusBadge({ status }: { status: ReviewStatus }) {
|
||||||
const { label, color } = CONFIG[status] ?? { label: status, color: '#64748b' }
|
const { label, color } = CONFIG[status] ?? { label: status, color: '#64748b' }
|
||||||
return (
|
return <GenericBadge label={label} color={color} />
|
||||||
<Chip
|
|
||||||
size="small"
|
|
||||||
label={label}
|
|
||||||
sx={{ bgcolor: `${color}18`, color, fontWeight: 600, fontSize: '0.7rem' }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Chip } from '@mui/material'
|
import { GenericBadge } from '../shared/GenericBadge'
|
||||||
import type { MatchStatus } from '../../domain/enums'
|
import type { MatchStatus } from '../../domain/enums'
|
||||||
|
|
||||||
const STATUS_META: Record<string, { label: string; color: string }> = {
|
const STATUS_META: Record<string, { label: string; color: string }> = {
|
||||||
@@ -10,9 +10,6 @@ const STATUS_META: Record<string, { label: string; color: string }> = {
|
|||||||
|
|
||||||
export function MatchStatusBadge({ status }: { status?: MatchStatus }) {
|
export function MatchStatusBadge({ status }: { status?: MatchStatus }) {
|
||||||
if (!status) return null
|
if (!status) return null
|
||||||
const meta = STATUS_META[status] ?? { label: status, color: '#64748b' }
|
const { label, color } = STATUS_META[status] ?? { label: status, color: '#64748b' }
|
||||||
return (
|
return <GenericBadge label={label} color={color} variant="solid" />
|
||||||
<Chip label={meta.label} size="small"
|
|
||||||
sx={{ bgcolor: meta.color, color: 'white', fontWeight: 600, fontSize: 11 }} />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import { Chip } from '@mui/material'
|
import { GenericBadge } from '../shared/GenericBadge'
|
||||||
import type { ReviewEntityType } from '../../domain/review'
|
import type { ReviewEntityType } from '../../domain/review'
|
||||||
|
|
||||||
interface ReviewEntityTypeBadgeProps {
|
|
||||||
entityType: ReviewEntityType
|
|
||||||
size?: 'small' | 'medium'
|
|
||||||
}
|
|
||||||
|
|
||||||
const CONFIG: Record<ReviewEntityType, { label: string; color: string }> = {
|
const CONFIG: Record<ReviewEntityType, { label: string; color: string }> = {
|
||||||
FUTURE_SIGNAL: { label: 'Zukunftssignal', color: '#7c3aed' },
|
FUTURE_SIGNAL: { label: 'Zukunftssignal', color: '#7c3aed' },
|
||||||
MATCH_EXPLANATION: { label: 'Match-Begründung', color: '#1e3a5f' },
|
MATCH_EXPLANATION: { label: 'Match-Begründung', color: '#1e3a5f' },
|
||||||
@@ -15,18 +10,12 @@ const CONFIG: Record<ReviewEntityType, { label: string; color: string }> = {
|
|||||||
PROPERTY_DATA_ISSUE: { label: 'Datenfehler', color: '#c0392b' },
|
PROPERTY_DATA_ISSUE: { label: 'Datenfehler', color: '#c0392b' },
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReviewEntityTypeBadge({ entityType, size = 'small' }: ReviewEntityTypeBadgeProps) {
|
interface Props {
|
||||||
const { label, color } = CONFIG[entityType] ?? { label: entityType, color: '#64748b' }
|
entityType: ReviewEntityType
|
||||||
return (
|
size?: 'small' | 'medium'
|
||||||
<Chip
|
}
|
||||||
size={size}
|
|
||||||
label={label}
|
export function ReviewEntityTypeBadge({ entityType, size = 'small' }: Props) {
|
||||||
sx={{
|
const { label, color } = CONFIG[entityType] ?? { label: entityType, color: '#64748b' }
|
||||||
bgcolor: `${color}18`,
|
return <GenericBadge label={label} color={color} size={size} />
|
||||||
color,
|
|
||||||
fontWeight: 600,
|
|
||||||
fontSize: size === 'small' ? '0.7rem' : '0.8125rem',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import { Chip } from '@mui/material'
|
import { GenericBadge } from '../shared/GenericBadge'
|
||||||
import type { ReviewPriority } from '../../domain/review'
|
import type { ReviewPriority } from '../../domain/review'
|
||||||
|
|
||||||
interface ReviewPriorityBadgeProps {
|
|
||||||
priority: ReviewPriority
|
|
||||||
size?: 'small' | 'medium'
|
|
||||||
}
|
|
||||||
|
|
||||||
const CONFIG: Record<ReviewPriority, { label: string; color: string }> = {
|
const CONFIG: Record<ReviewPriority, { label: string; color: string }> = {
|
||||||
LOW: { label: 'Niedrig', color: '#64748b' },
|
LOW: { label: 'Niedrig', color: '#64748b' },
|
||||||
MEDIUM: { label: 'Mittel', color: '#d97706' },
|
MEDIUM: { label: 'Mittel', color: '#d97706' },
|
||||||
@@ -13,19 +8,12 @@ const CONFIG: Record<ReviewPriority, { label: string; color: string }> = {
|
|||||||
CRITICAL: { label: 'Kritisch', color: '#c0392b' },
|
CRITICAL: { label: 'Kritisch', color: '#c0392b' },
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReviewPriorityBadge({ priority, size = 'small' }: ReviewPriorityBadgeProps) {
|
interface Props {
|
||||||
const { label, color } = CONFIG[priority] ?? CONFIG.MEDIUM
|
priority: ReviewPriority
|
||||||
return (
|
size?: 'small' | 'medium'
|
||||||
<Chip
|
}
|
||||||
size={size}
|
|
||||||
label={label}
|
export function ReviewPriorityBadge({ priority, size = 'small' }: Props) {
|
||||||
sx={{
|
const { label, color } = CONFIG[priority] ?? CONFIG.MEDIUM
|
||||||
bgcolor: `${color}18`,
|
return <GenericBadge label={label} color={color} showBorder bold size={size} />
|
||||||
color,
|
|
||||||
fontWeight: 700,
|
|
||||||
fontSize: size === 'small' ? '0.7rem' : '0.8125rem',
|
|
||||||
border: `1px solid ${color}40`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,21 @@
|
|||||||
import { Chip } from '@mui/material'
|
import { GenericBadge } from '../shared/GenericBadge'
|
||||||
import type { ReviewTaskStatus } from '../../domain/review'
|
import type { ReviewTaskStatus } from '../../domain/review'
|
||||||
|
|
||||||
interface ReviewStatusBadgeProps {
|
const CONFIG: Record<ReviewTaskStatus, { label: string; color: string }> = {
|
||||||
|
PENDING: { label: 'Ausstehend', color: '#d97706' },
|
||||||
|
IN_REVIEW: { label: 'In Prüfung', color: '#2563eb' },
|
||||||
|
APPROVED: { label: 'Genehmigt', color: '#1a7a4a' },
|
||||||
|
REJECTED: { label: 'Abgelehnt', color: '#c0392b' },
|
||||||
|
NEEDS_MORE_DATA: { label: 'Mehr Daten nötig', color: '#7c3aed' },
|
||||||
|
ESCALATED: { label: 'Eskaliert', color: '#ea580c' },
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
status: ReviewTaskStatus
|
status: ReviewTaskStatus
|
||||||
size?: 'small' | 'medium'
|
size?: 'small' | 'medium'
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONFIG: Record<ReviewTaskStatus, { label: string; color: string }> = {
|
export function ReviewStatusBadge({ status, size = 'small' }: Props) {
|
||||||
PENDING: { label: 'Ausstehend', color: '#d97706' },
|
|
||||||
IN_REVIEW: { label: 'In Prüfung', color: '#2563eb' },
|
|
||||||
APPROVED: { label: 'Genehmigt', color: '#1a7a4a' },
|
|
||||||
REJECTED: { label: 'Abgelehnt', color: '#c0392b' },
|
|
||||||
NEEDS_MORE_DATA: { label: 'Mehr Daten nötig', color: '#7c3aed' },
|
|
||||||
ESCALATED: { label: 'Eskaliert', color: '#ea580c' },
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ReviewStatusBadge({ status, size = 'small' }: ReviewStatusBadgeProps) {
|
|
||||||
const { label, color } = CONFIG[status] ?? CONFIG.PENDING
|
const { label, color } = CONFIG[status] ?? CONFIG.PENDING
|
||||||
return (
|
return <GenericBadge label={label} color={color} showBorder size={size} />
|
||||||
<Chip
|
|
||||||
size={size}
|
|
||||||
label={label}
|
|
||||||
sx={{
|
|
||||||
bgcolor: `${color}18`,
|
|
||||||
color,
|
|
||||||
fontWeight: 600,
|
|
||||||
fontSize: size === 'small' ? '0.7rem' : '0.8125rem',
|
|
||||||
border: `1px solid ${color}40`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Chip, Tooltip } from '@mui/material'
|
||||||
|
import type { ReactElement } from 'react'
|
||||||
|
|
||||||
|
export interface GenericBadgeProps {
|
||||||
|
label: string
|
||||||
|
color: string
|
||||||
|
/** transparent: semi-opaque bg (color + 18% opacity) | solid: filled bg, white text */
|
||||||
|
variant?: 'transparent' | 'solid'
|
||||||
|
/** Adds a 1px border at color 25% opacity (transparent variant only) */
|
||||||
|
showBorder?: boolean
|
||||||
|
/** fontWeight 700 instead of 600 */
|
||||||
|
bold?: boolean
|
||||||
|
/** Optional lucide / MUI icon element — sized by the caller */
|
||||||
|
icon?: ReactElement
|
||||||
|
size?: 'small' | 'medium'
|
||||||
|
/** Wraps the badge in a Tooltip */
|
||||||
|
tooltip?: string
|
||||||
|
ariaLabel?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GenericBadge({
|
||||||
|
label,
|
||||||
|
color,
|
||||||
|
variant = 'transparent',
|
||||||
|
showBorder = false,
|
||||||
|
bold = false,
|
||||||
|
icon,
|
||||||
|
size = 'small',
|
||||||
|
tooltip,
|
||||||
|
ariaLabel,
|
||||||
|
}: GenericBadgeProps) {
|
||||||
|
const chip = (
|
||||||
|
<Chip
|
||||||
|
size={size}
|
||||||
|
label={label}
|
||||||
|
icon={icon}
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
sx={{
|
||||||
|
fontWeight: bold ? 700 : 600,
|
||||||
|
fontSize: size === 'small' ? '0.7rem' : '0.8125rem',
|
||||||
|
bgcolor: variant === 'solid' ? color : `${color}18`,
|
||||||
|
color: variant === 'solid' ? 'white' : color,
|
||||||
|
border: variant === 'transparent' && showBorder ? `1px solid ${color}40` : 'none',
|
||||||
|
'& .MuiChip-icon': { ml: 0.5 },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
return tooltip ? <Tooltip title={tooltip} arrow>{chip}</Tooltip> : chip
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
export { GenericBadge } from './GenericBadge'
|
||||||
|
export type { GenericBadgeProps } from './GenericBadge'
|
||||||
export { ViewToggle } from './ViewToggle'
|
export { ViewToggle } from './ViewToggle'
|
||||||
export { HeatBadge } from './HeatBadge'
|
export { HeatBadge } from './HeatBadge'
|
||||||
export { LocationPreview } from './LocationPreview'
|
export { LocationPreview } from './LocationPreview'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Chip } from '@mui/material'
|
import { GenericBadge } from '../shared/GenericBadge'
|
||||||
import type { ShortlistStatus } from '../../domain/enums'
|
import type { ShortlistStatus } from '../../domain/enums'
|
||||||
|
|
||||||
const STATUS_META: Record<string, { label: string; color: string }> = {
|
const STATUS_META: Record<string, { label: string; color: string }> = {
|
||||||
@@ -12,12 +12,6 @@ const STATUS_META: Record<string, { label: string; color: string }> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ShortlistStatusBadge({ status }: { status: ShortlistStatus }) {
|
export function ShortlistStatusBadge({ status }: { status: ShortlistStatus }) {
|
||||||
const meta = STATUS_META[status] ?? { label: status, color: '#64748b' }
|
const { label, color } = STATUS_META[status] ?? { label: status, color: '#64748b' }
|
||||||
return (
|
return <GenericBadge label={label} color={color} variant="solid" />
|
||||||
<Chip
|
|
||||||
label={meta.label}
|
|
||||||
size="small"
|
|
||||||
sx={{ bgcolor: meta.color, color: 'white', fontWeight: 600, fontSize: 11 }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import type { Property } from '../domain/property'
|
||||||
|
import { mockProperties } from './properties'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared mutable store for mock Property data.
|
||||||
|
*
|
||||||
|
* Imported by MockupPropertyProvider and MockupUnitProvider.
|
||||||
|
* Real providers (REST, Supabase, …) replace the entire provider file
|
||||||
|
* and never touch this module.
|
||||||
|
*/
|
||||||
|
export const propertyStore: Property[] = [...mockProperties]
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import type { IPropertyProvider, PropertyFilters } from './IPropertyProvider'
|
import type { IPropertyProvider, PropertyFilters } from './IPropertyProvider'
|
||||||
import type { Property, CreatePropertyInput, UpdatePropertyInput } from '../domain/property'
|
import type { Property, CreatePropertyInput, UpdatePropertyInput } from '../domain/property'
|
||||||
import { mockProperties } from '../mock-data/properties'
|
import { propertyStore } from '../mock-data/propertyStore'
|
||||||
|
|
||||||
export const propertyStore: Property[] = [...mockProperties]
|
|
||||||
const store = propertyStore
|
const store = propertyStore
|
||||||
|
|
||||||
export const MockupPropertyProvider: IPropertyProvider = {
|
export const MockupPropertyProvider: IPropertyProvider = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { IUnitProvider } from './IUnitProvider'
|
import type { IUnitProvider } from './IUnitProvider'
|
||||||
import type { PropertyUnit } from '../domain/property'
|
import type { PropertyUnit } from '../domain/property'
|
||||||
import { propertyStore } from './MockupPropertyProvider'
|
import { propertyStore } from '../mock-data/propertyStore'
|
||||||
|
|
||||||
function getAllUnits(): PropertyUnit[] {
|
function getAllUnits(): PropertyUnit[] {
|
||||||
return propertyStore.flatMap(p =>
|
return propertyStore.flatMap(p =>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* another provider directly.
|
* another provider directly.
|
||||||
*/
|
*/
|
||||||
import { matchStore } from '../provider/MockupMatchProvider'
|
import { matchStore } from '../provider/MockupMatchProvider'
|
||||||
import { propertyStore } from '../provider/MockupPropertyProvider'
|
import { propertyStore } from '../mock-data/propertyStore'
|
||||||
import { MatchStrength, MatchStatus, RiskLevel, ResultType } from '../domain/enums'
|
import { MatchStrength, MatchStatus, RiskLevel, ResultType } from '../domain/enums'
|
||||||
import type { Match } from '../domain/match'
|
import type { Match } from '../domain/match'
|
||||||
import type { MatchEngineOutput } from '../domain/scoring'
|
import type { MatchEngineOutput } from '../domain/scoring'
|
||||||
|
|||||||
Reference in New Issue
Block a user