Style tweaks

This commit is contained in:
2025-02-26 22:14:11 -05:00
parent 3ca72674af
commit 88f1853b09
6 changed files with 56 additions and 81 deletions

View File

@@ -19,8 +19,7 @@ import {
import { useQuery } from "@tanstack/react-query" import { useQuery } from "@tanstack/react-query"
import config from "@/config" import config from "@/config"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { CheckCircle2, AlertCircle, InfoIcon, EyeIcon, EyeOffIcon, ArrowRightIcon, XIcon, FileIcon, LinkIcon } from "lucide-react" import { CheckCircle2, AlertCircle, EyeIcon, EyeOffIcon, ArrowRightIcon, XIcon, FileSpreadsheetIcon, LinkIcon, FileIcon } from "lucide-react"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
@@ -110,7 +109,7 @@ export const MatchColumnsStep = <T extends string>({
onBack, onBack,
initialGlobalSelections initialGlobalSelections
}: MatchColumnsProps<T>) => { }: MatchColumnsProps<T>) => {
const dataExample = useMemo(() => data.slice(0, 3), [data]) // Show 3 sample rows const dataExample = useMemo(() => data.slice(0, 5), [data]) // Show 5 sample rows
const { fields, autoMapHeaders, autoMapSelectValues, autoMapDistance, translations } = useRsi<T>() const { fields, autoMapHeaders, autoMapSelectValues, autoMapDistance, translations } = useRsi<T>()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [columns, setColumns] = useState<Columns<T>>( const [columns, setColumns] = useState<Columns<T>>(
@@ -200,8 +199,6 @@ export const MatchColumnsStep = <T extends string>({
}); });
}, [columns]); }, [columns]);
// Get the first value from the sample data for a column
// Get mapped company value (if company is mapped to a column) // Get mapped company value (if company is mapped to a column)
const mappedCompanyColumn = useMemo(() => findMappedColumnForField('company'), [findMappedColumnForField]); const mappedCompanyColumn = useMemo(() => findMappedColumnForField('company'), [findMappedColumnForField]);
const mappedCompanyValue = useMemo(() => { const mappedCompanyValue = useMemo(() => {
@@ -726,7 +723,7 @@ export const MatchColumnsStep = <T extends string>({
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button variant="ghost" size="icon" className="h-8 w-8"> <Button variant="ghost" size="icon" className="h-8 w-8">
<FileIcon className="h-4 w-4 text-muted-foreground" /> <FileSpreadsheetIcon className="h-4 w-4 text-muted-foreground" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent side="right" align="start" className="w-[250px] p-0"> <PopoverContent side="right" align="start" className="w-[250px] p-0">
@@ -865,13 +862,13 @@ export const MatchColumnsStep = <T extends string>({
{/* Left panel - Global selections & Required fields */} {/* Left panel - Global selections & Required fields */}
<div className="lg:col-span-1 space-y-4"> <div className="lg:col-span-1 space-y-4">
<div> <div>
<h3 className="text-lg font-medium mb-4">Global Settings</h3> <h3 className="text-lg font-medium mb-0">Global Settings</h3>
<p className="text-sm text-muted-foreground mb-4"> <p className="text-sm text-muted-foreground mb-2">
Apply these values to all imported items These values will be applied to all imported items
</p> </p>
<div className="space-y-4"> <div className="space-y-2">
<div className="space-y-2"> <div className="space-y-1">
<label className="text-sm font-medium">Supplier</label> <label className="text-sm font-medium">Supplier</label>
<Select <Select
value={globalSelections.supplier} value={globalSelections.supplier}
@@ -890,7 +887,7 @@ export const MatchColumnsStep = <T extends string>({
</Select> </Select>
</div> </div>
<div className="space-y-2"> <div className="space-y-1">
<label className="text-sm font-medium">Company</label> <label className="text-sm font-medium">Company</label>
<Select <Select
value={globalSelections.company} value={globalSelections.company}
@@ -916,7 +913,7 @@ export const MatchColumnsStep = <T extends string>({
</Select> </Select>
</div> </div>
<div className="space-y-2"> <div className="space-y-1">
<label className="text-sm font-medium">Line</label> <label className="text-sm font-medium">Line</label>
<Select <Select
value={globalSelections.line} value={globalSelections.line}
@@ -942,7 +939,7 @@ export const MatchColumnsStep = <T extends string>({
</Select> </Select>
</div> </div>
<div className="space-y-2"> <div className="space-y-1">
<label className="text-sm font-medium">Sub Line</label> <label className="text-sm font-medium">Sub Line</label>
<Select <Select
value={globalSelections.subline} value={globalSelections.subline}
@@ -964,22 +961,12 @@ export const MatchColumnsStep = <T extends string>({
</div> </div>
</div> </div>
<Separator className="my-6" /> <Separator className="my-8" />
{/* Required Fields Section - Updated to show source column */} {/* Required Fields Section - Updated to show source column */}
<div> <div>
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-4">
<h3 className="text-lg font-medium">Required Fields</h3> <h3 className="text-lg font-medium">Matched Fields</h3>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="h-4 w-4 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent>
<p className="max-w-xs">Map columns to required fields or set them globally</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
@@ -1019,27 +1006,7 @@ export const MatchColumnsStep = <T extends string>({
</div> </div>
</div> </div>
{/* Stats summary for column mapping */}
<div className="mt-6 pt-4">
<div className="flex flex-col gap-1">
<div className="flex justify-between">
<span className="text-sm">Columns total:</span>
<span className="text-sm font-medium">{columns.length}</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Mapped:</span>
<span className="text-sm font-medium">{matchedColumns.length}</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Ignored:</span>
<span className="text-sm font-medium">{ignoredColumns.length}</span>
</div>
<div className="flex justify-between">
<span className="text-sm">Unmapped:</span>
<span className="text-sm font-medium">{unmatchedColumns.length}</span>
</div>
</div>
</div>
</div> </div>
{/* Right panel - Column mapping interface */} {/* Right panel - Column mapping interface */}
@@ -1075,7 +1042,7 @@ export const MatchColumnsStep = <T extends string>({
<TableHeader className="sticky top-0 bg-muted z-10"> <TableHeader className="sticky top-0 bg-muted z-10">
<TableRow> <TableRow>
<TableHead className="w-1/3">Spreadsheet Column</TableHead> <TableHead className="w-1/3">Spreadsheet Column</TableHead>
<TableHead className="w-12 text-center">Data</TableHead> <TableHead className="w-12 text-center">Sample Data</TableHead>
<TableHead className="w-12"></TableHead> <TableHead className="w-12"></TableHead>
<TableHead>Map To Field</TableHead> <TableHead>Map To Field</TableHead>
<TableHead className="w-24 text-right">Action</TableHead> <TableHead className="w-24 text-right">Action</TableHead>

View File

@@ -149,21 +149,24 @@ export const SelectHeaderStep = ({ data, onContinue, onBack }: SelectHeaderProps
return ( return (
<div className="flex flex-col h-[calc(100vh-9.5rem)]"> <div className="flex flex-col h-[calc(100vh-9.5rem)]">
<div className="px-8 py-6 bg-background"> <div className="px-8 py-6 bg-background flex justify-between items-end">
<h2 className="text-2xl font-semibold text-foreground"> <div>
{translations.selectHeaderStep.title} <h2 className="text-2xl font-semibold text-foreground">
</h2> {translations.selectHeaderStep.title}
</h2>
<p className="mt-1 text-sm text-muted-foreground">
Select the row that contains your column headers
</p>
</div>
<Button
variant="default"
size="sm"
onClick={discardEmptyAndDuplicateRows}
>
Remove Empty/Duplicates
</Button>
</div> </div>
<div className="flex-1 flex flex-col min-h-0"> <div className="flex-1 flex flex-col min-h-0">
<div className="px-8 mb-4 flex justify-end">
<Button
variant="default"
size="sm"
onClick={discardEmptyAndDuplicateRows}
>
Remove Empty/Duplicates
</Button>
</div>
<div className="px-8 flex-1 overflow-auto"> <div className="px-8 flex-1 overflow-auto">
<SelectHeaderTable <SelectHeaderTable
data={localData} data={localData}

View File

@@ -40,15 +40,13 @@ export const SelectHeaderTable = ({ data, selectedRows, setSelectedRows }: Props
return ( return (
<div className="rounded-md border p-3"> <div className="rounded-md border p-3">
<p className="mb-2 p-2 text-sm text-muted-foreground">
Select the row that contains your column headers <div className="h-[calc(100vh-23rem)] overflow-auto">
</p>
<div className="h-[calc(100vh-27rem)] overflow-auto">
<Table className="relative w-full" style={{ tableLayout: 'fixed' }}> <Table className="relative w-full" style={{ tableLayout: 'fixed' }}>
<TableHeader> <TableHeader>
<TableRow className="grid" style={{ gridTemplateColumns }}> <TableRow className="grid" style={{ gridTemplateColumns }}>
<TableHead className="sticky top-0 z-20 bg-background overflow-hidden"> <TableHead className="sticky top-0 z-20 bg-background overflow-hidden">
<div className="truncate">Select</div>
</TableHead> </TableHead>
{columns.map((column) => ( {columns.map((column) => (
<TableHead <TableHead

View File

@@ -30,7 +30,14 @@ export const Steps = () => {
const onNext = (v: StepState) => { const onNext = (v: StepState) => {
history.current.push(state) history.current.push(state)
setState(v) setState(v)
v.type !== StepType.selectSheet && setActiveStep(activeStep + 1)
if (v.type === StepType.validateData && 'isFromScratch' in v && v.isFromScratch) {
// If starting from scratch, jump directly to the validation step
const validationStepIndex = steps.indexOf('validationStep')
setActiveStep(validationStepIndex)
} else if (v.type !== StepType.selectSheet) {
setActiveStep(activeStep + 1)
}
} }
return ( return (

View File

@@ -4,15 +4,16 @@ import { useRsi } from "../../hooks/useRsi"
import { DropZone } from "./components/DropZone" import { DropZone } from "./components/DropZone"
import { StepType } from "../UploadFlow" import { StepType } from "../UploadFlow"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"
type UploadProps = { type UploadProps = {
onContinue: (data: XLSX.WorkBook, file: File) => Promise<void> onContinue: (data: XLSX.WorkBook, file: File) => Promise<void>
setInitialState?: (state: { type: StepType; data: any[] }) => void setInitialState?: (state: { type: StepType; data: any[]; isFromScratch?: boolean }) => void
} }
export const UploadStep = ({ onContinue, setInitialState }: UploadProps) => { export const UploadStep = ({ onContinue, setInitialState }: UploadProps) => {
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const { translations, fields } = useRsi() const { translations } = useRsi()
const handleOnContinue = useCallback( const handleOnContinue = useCallback(
async (data: XLSX.WorkBook, file: File) => { async (data: XLSX.WorkBook, file: File) => {
@@ -30,19 +31,18 @@ export const UploadStep = ({ onContinue, setInitialState }: UploadProps) => {
}, [setInitialState]) }, [setInitialState])
return ( return (
<div className="p-8 flex flex-col items-center max-w-xl mx-auto"> <div className="p-8">
<h2 className="text-3xl font-semibold mb-8 text-center">{translations.uploadStep.title}</h2> <h2 className="text-3xl font-semibold mb-8 text-left">{translations.uploadStep.title}</h2>
<div className="w-full space-y-8"> <div className="max-w-xl mx-auto w-full space-y-8">
<div className="border rounded-lg p-6 flex flex-col items-center"> <div className="rounded-lg p-6 flex flex-col items-center">
<h3 className="text-lg font-medium mb-4">Upload spreadsheet file</h3>
<DropZone onContinue={handleOnContinue} isLoading={isLoading} /> <DropZone onContinue={handleOnContinue} isLoading={isLoading} />
</div> </div>
<div className="flex items-center justify-center"> <div className="flex items-center justify-center">
<div className="bg-muted h-px w-16"></div> <Separator className="w-24" />
<span className="px-3 text-muted-foreground text-sm font-medium">OR</span> <span className="px-3 text-muted-foreground text-sm font-medium">OR</span>
<div className="bg-muted h-px w-16"></div> <Separator className="w-24" />
</div> </div>
<div className="flex justify-center"> <div className="flex justify-center">

View File

@@ -2,25 +2,25 @@ import type { DeepPartial } from "ts-essentials"
export const translations = { export const translations = {
uploadStep: { uploadStep: {
title: "Upload file", title: "Upload File",
manifestTitle: "Data that we expect:", manifestTitle: "Data that we expect:",
manifestDescription: "(You will have a chance to rename or remove columns in next steps)", manifestDescription: "(You will have a chance to rename or remove columns in next steps)",
maxRecordsExceeded: (maxRecords: string) => `Too many records. Up to ${maxRecords} allowed`, maxRecordsExceeded: (maxRecords: string) => `Too many records. Up to ${maxRecords} allowed`,
dropzone: { dropzone: {
title: "Upload .xlsx, .xls or .csv file", title: "Drop any .xlsx, .xls or .csv file here or click to select",
errorToastDescription: "upload rejected", errorToastDescription: "upload rejected",
activeDropzoneTitle: "Drop file here...", activeDropzoneTitle: "Drop file here...",
buttonTitle: "Select file", buttonTitle: "Select file",
loadingTitle: "Processing...", loadingTitle: "Processing...",
}, },
selectSheet: { selectSheet: {
title: "Select the sheet to use", title: "Select Which Sheet To Use",
nextButtonTitle: "Next", nextButtonTitle: "Next",
backButtonTitle: "Back", backButtonTitle: "Back",
}, },
}, },
selectHeaderStep: { selectHeaderStep: {
title: "Select header row", title: "Select Header Row",
nextButtonTitle: "Next", nextButtonTitle: "Next",
backButtonTitle: "Back", backButtonTitle: "Back",
}, },
@@ -39,7 +39,7 @@ export const translations = {
duplicateColumnWarningDescription: "Columns cannot duplicate", duplicateColumnWarningDescription: "Columns cannot duplicate",
}, },
validationStep: { validationStep: {
title: "Validate data", title: "Validate Data",
nextButtonTitle: "Next", nextButtonTitle: "Next",
backButtonTitle: "Back", backButtonTitle: "Back",
noRowsMessage: "No data found", noRowsMessage: "No data found",