fix: show Ersätt befintlig import button for duplicate-file SIE uploads (#270)
The duplicate (file-hash) error path returned an importId but the UI only captured it for the duplicate_period branch, so users saw a misleading "ta bort under Bokföring" message with no way to act on it. The replace flow (and its BFL 5:5 audit trail) is identical in both cases, so expose the existing button for both. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
44082ff845
commit
d741c46d4e
@@ -334,7 +334,7 @@ function SIEImportWizard() {
|
|||||||
const [errorType, setErrorType] = useState<'duplicate' | 'duplicate_period' | 'validation' | 'parse' | undefined>()
|
const [errorType, setErrorType] = useState<'duplicate' | 'duplicate_period' | 'validation' | 'parse' | undefined>()
|
||||||
const [validationErrors, setValidationErrors] = useState<string[]>([])
|
const [validationErrors, setValidationErrors] = useState<string[]>([])
|
||||||
const [validationWarnings, setValidationWarnings] = useState<string[]>([])
|
const [validationWarnings, setValidationWarnings] = useState<string[]>([])
|
||||||
const [duplicatePeriodImportId, setDuplicatePeriodImportId] = useState<string | null>(null)
|
const [duplicateImportId, setDuplicateImportId] = useState<string | null>(null)
|
||||||
const [isReplacing, setIsReplacing] = useState(false)
|
const [isReplacing, setIsReplacing] = useState(false)
|
||||||
|
|
||||||
const [file, setFile] = useState<File | null>(null)
|
const [file, setFile] = useState<File | null>(null)
|
||||||
@@ -380,8 +380,8 @@ function SIEImportWizard() {
|
|||||||
if (type === 'duplicate' || type === 'duplicate_period') {
|
if (type === 'duplicate' || type === 'duplicate_period') {
|
||||||
setErrorType(type)
|
setErrorType(type)
|
||||||
setError(data.message)
|
setError(data.message)
|
||||||
if (type === 'duplicate_period' && data.importId) {
|
if (data.importId) {
|
||||||
setDuplicatePeriodImportId(data.importId)
|
setDuplicateImportId(data.importId)
|
||||||
}
|
}
|
||||||
toast({ title: type === 'duplicate' ? 'Filen har redan importerats' : 'Överlappande räkenskapsår', description: data.message, variant: 'destructive' })
|
toast({ title: type === 'duplicate' ? 'Filen har redan importerats' : 'Överlappande räkenskapsår', description: data.message, variant: 'destructive' })
|
||||||
} else if (type === 'validation') {
|
} else if (type === 'validation') {
|
||||||
@@ -459,7 +459,7 @@ function SIEImportWizard() {
|
|||||||
// Clear error state and re-trigger the file upload
|
// Clear error state and re-trigger the file upload
|
||||||
setError(null)
|
setError(null)
|
||||||
setErrorType(undefined)
|
setErrorType(undefined)
|
||||||
setDuplicatePeriodImportId(null)
|
setDuplicateImportId(null)
|
||||||
|
|
||||||
// Small delay so the user sees the success toast before re-upload starts
|
// Small delay so the user sees the success toast before re-upload starts
|
||||||
await new Promise(resolve => setTimeout(resolve, 500))
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
@@ -618,7 +618,7 @@ function SIEImportWizard() {
|
|||||||
const handleNewImport = () => {
|
const handleNewImport = () => {
|
||||||
setStep('upload'); setFile(null); setParsed(null); setMappings([])
|
setStep('upload'); setFile(null); setParsed(null); setMappings([])
|
||||||
setPreview(null); setIssues([]); setImportResult(null); setError(null); setErrorType(undefined)
|
setPreview(null); setIssues([]); setImportResult(null); setError(null); setErrorType(undefined)
|
||||||
setValidationErrors([]); setValidationWarnings([]); setDuplicatePeriodImportId(null)
|
setValidationErrors([]); setValidationWarnings([]); setDuplicateImportId(null)
|
||||||
setSieAccounts([]); setIsCreatingAccounts(false)
|
setSieAccounts([]); setIsCreatingAccounts(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,7 +645,7 @@ function SIEImportWizard() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{step === 'upload' && <SIEUploadStep onFileSelect={handleFileSelect} isLoading={isLoading} error={error} errorType={errorType} validationErrors={validationErrors} validationWarnings={validationWarnings} duplicatePeriodImportId={duplicatePeriodImportId} onReplace={handleReplace} isReplacing={isReplacing} />}
|
{step === 'upload' && <SIEUploadStep onFileSelect={handleFileSelect} isLoading={isLoading} error={error} errorType={errorType} validationErrors={validationErrors} validationWarnings={validationWarnings} duplicateImportId={duplicateImportId} onReplace={handleReplace} isReplacing={isReplacing} />}
|
||||||
{step === 'preview' && preview && (
|
{step === 'preview' && preview && (
|
||||||
<SIEPreviewStep preview={preview} issues={issues} missingAccounts={missingAccounts}
|
<SIEPreviewStep preview={preview} issues={issues} missingAccounts={missingAccounts}
|
||||||
onCreateAccounts={handleCreateAccounts} isCreatingAccounts={isCreatingAccounts}
|
onCreateAccounts={handleCreateAccounts} isCreatingAccounts={isCreatingAccounts}
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ interface SIEUploadStepProps {
|
|||||||
errorType?: 'duplicate' | 'duplicate_period' | 'validation' | 'parse'
|
errorType?: 'duplicate' | 'duplicate_period' | 'validation' | 'parse'
|
||||||
validationErrors?: string[]
|
validationErrors?: string[]
|
||||||
validationWarnings?: string[]
|
validationWarnings?: string[]
|
||||||
duplicatePeriodImportId?: string | null
|
duplicateImportId?: string | null
|
||||||
onReplace?: (importId: string) => Promise<void>
|
onReplace?: (importId: string) => Promise<void>
|
||||||
isReplacing?: boolean
|
isReplacing?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SIEUploadStep({ onFileSelect, isLoading, error, errorType, validationErrors, validationWarnings, duplicatePeriodImportId, onReplace, isReplacing }: SIEUploadStepProps) {
|
export default function SIEUploadStep({ onFileSelect, isLoading, error, errorType, validationErrors, validationWarnings, duplicateImportId, onReplace, isReplacing }: SIEUploadStepProps) {
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||||||
const [loadingPhase, setLoadingPhase] = useState(0)
|
const [loadingPhase, setLoadingPhase] = useState(0)
|
||||||
@@ -192,13 +192,10 @@ export default function SIEUploadStep({ onFileSelect, isLoading, error, errorTyp
|
|||||||
|
|
||||||
{/* Actionable guidance */}
|
{/* Actionable guidance */}
|
||||||
<div className="text-sm text-muted-foreground pt-1 border-t border-border/50 mt-2">
|
<div className="text-sm text-muted-foreground pt-1 border-t border-border/50 mt-2">
|
||||||
{errorType === 'duplicate' && (
|
{(errorType === 'duplicate' || errorType === 'duplicate_period') && (
|
||||||
<p>Om du vill importera om filen, ta först bort den tidigare importen under Bokföring.</p>
|
|
||||||
)}
|
|
||||||
{errorType === 'duplicate_period' && (
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p>Den befintliga importens verifikationer kommer att makuleras (status ändras till "makulerad"). De finns kvar som spårbar historik.</p>
|
<p>Den befintliga importens verifikationer kommer att makuleras (status ändras till "makulerad"). De finns kvar som spårbar historik.</p>
|
||||||
{duplicatePeriodImportId && onReplace && (
|
{duplicateImportId && onReplace && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -206,7 +203,7 @@ export default function SIEUploadStep({ onFileSelect, isLoading, error, errorTyp
|
|||||||
disabled={isReplacing}
|
disabled={isReplacing}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onReplace(duplicatePeriodImportId)
|
onReplace(duplicateImportId)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isReplacing ? (
|
{isReplacing ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user