From 2ea5a72b3d850141b733a251cf45ca32bb6466d0 Mon Sep 17 00:00:00 2001 From: Jakob Wennberg <149234542+jakobwennberg@users.noreply.github.com> Date: Mon, 20 Apr 2026 09:47:37 +0200 Subject: [PATCH] feat: dynamic voucher series dropdown in SIE import (#274) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: dynamic voucher series dropdown in SIE import Populate the voucher series picker on the import review step from the company's own data instead of a hardcoded A/B/C/I list. Shows all A–Z series with inline labels for the company default ("standard") and any series that already have a running sequence ("används redan"), and preselects the company default (falling back to B, then the first existing series, then A). Co-Authored-By: Claude Opus 4.7 (1M context) * fix: address voucher series dropdown review feedback - Log non-PGRST116 Supabase errors from company_settings and voucher_sequences fetches instead of silently swallowing them. - Disable the series Select until the async load completes so users don't briefly see the 'B' fallback before it snaps to the real company default. - Replace the seriesInitializedRef guard with a seriesLoaded state that resets on company.id change, so switching companies re-runs the preselection instead of sticking with the prior company's choice. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- components/import/ImportReviewStep.tsx | 75 ++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/components/import/ImportReviewStep.tsx b/components/import/ImportReviewStep.tsx index b66dd39d..5feaf5e4 100644 --- a/components/import/ImportReviewStep.tsx +++ b/components/import/ImportReviewStep.tsx @@ -25,8 +25,12 @@ import { } from 'lucide-react' import { useUnsavedChanges } from '@/lib/hooks/use-unsaved-changes' import { useCanWrite } from '@/lib/hooks/use-can-write' +import { createClient } from '@/lib/supabase/client' +import { useCompany } from '@/contexts/CompanyContext' import type { ImportPreview, AccountMapping } from '@/lib/import/types' +const SERIES_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('') + interface ImportReviewStepProps { preview: ImportPreview mappings: AccountMapping[] @@ -50,15 +54,67 @@ export default function ImportReviewStep({ isLoading, }: ImportReviewStepProps) { const { canWrite } = useCanWrite() + const { company } = useCompany() const [options, setOptions] = useState({ createFiscalPeriod: true, importOpeningBalances: true, importTransactions: true, voucherSeries: 'B', }) + const [defaultSeries, setDefaultSeries] = useState(null) + const [existingSeries, setExistingSeries] = useState>(new Set()) + const [seriesLoaded, setSeriesLoaded] = useState(false) const [elapsed, setElapsed] = useState(0) const intervalRef = useRef | null>(null) + useEffect(() => { + if (!company?.id) return + setSeriesLoaded(false) + const supabase = createClient() + + let cancelled = false + ;(async () => { + const [ + { data: settingsData, error: settingsError }, + { data: sequencesData, error: sequencesError }, + ] = await Promise.all([ + supabase + .from('company_settings') + .select('default_voucher_series') + .eq('company_id', company.id) + .single(), + supabase + .from('voucher_sequences') + .select('voucher_series') + .eq('company_id', company.id), + ]) + + if (cancelled) return + + // PGRST116 = no rows returned from .single(); expected when settings not yet created. + if (settingsError && settingsError.code !== 'PGRST116') { + console.error('Failed to load company settings for voucher series', settingsError) + } + if (sequencesError) { + console.error('Failed to load voucher sequences', sequencesError) + } + + const companyDefault = settingsData?.default_voucher_series || null + const sequences = new Set((sequencesData || []).map((row) => row.voucher_series)) + + setDefaultSeries(companyDefault) + setExistingSeries(sequences) + + const initial = companyDefault || (sequences.has('B') ? 'B' : Array.from(sequences).sort()[0]) || 'A' + setOptions((prev) => ({ ...prev, voucherSeries: initial })) + setSeriesLoaded(true) + })() + + return () => { + cancelled = true + } + }, [company?.id]) + // Block browser close/refresh during import useUnsavedChanges(isLoading) @@ -240,15 +296,26 @@ export default function ImportReviewStep({