Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/titobrian97/Prueba-tecnica-ts-node---gestion-de-csv/llms.txt

Use this file to discover all available pages before exploring further.

The frontend is a React 19 single-page application built and served by Vite. It runs on port 4000 during development and communicates with the Express backend over plain HTTP at http://localhost:3000. The entire user interface is driven by a lightweight state machine encoded in the APP_STATUS constant — this single piece of state determines which UI section is visible, whether form controls are disabled, and what label appears on the submit button. TypeScript is used throughout: component props, state variables, service return types, and shared domain types are all explicitly typed.

App State Machine

The APP_STATUS object defines every state the application can occupy:
export const APP_STATUS = {
  IDLE:         "idle",
  ERROR:        "error",
  READY_UPLOAD: "ready_upload",
  UPLOADING:    "uploading",
  READY_USAGE:  "ready_usage",
} as const;
StatusMeaning
idleInitial state — the file input is rendered but no file has been selected yet.
ready_uploadA file has been selected; the upload button becomes visible.
uploadingThe SendCsv call is in-flight; the input and button are disabled and the button label reads “Cargando”.
errorThe upload or parsing failed; the form remains visible so the user can try again.
ready_usageUpload succeeded; the upload form is unmounted and the <Search> component is rendered.
State transitions are managed in App via useState<AppStatusType> and are driven by user interactions and the resolved values of the service functions.

Component Breakdown

App

Root component. Owns all state, handles file selection and form submission, and conditionally renders either the upload form or <Search>.

Search

Receives the initial dataCsv prop, manages a controlled search input, debounces the query at 300 ms, and re-renders the row list on each response.

App Component

App holds four pieces of state: appStatus, file, data, and error. The upload form is rendered whenever appStatus is not ready_usage. The submit button is shown only when appStatus is ready_upload or uploading:
const showButton =
  appStatus === APP_STATUS.READY_UPLOAD || appStatus === APP_STATUS.UPLOADING;
On submission, App calls SendCsv(file) and branches on the returned [data?, Error?] tuple. A non-empty error triggers a sonner toast and transitions to error. A successful response with a non-empty data.data array stores the rows in state, fires a success toast, and transitions to ready_usage, which swaps the form for <Search dataCsv={data} />.

Search Component

Search accepts dataCsv (the initial full dataset from the upload response) and setAppStatus as props. It maintains its own local data state, initialised from dataCsv, and a search string state tied to the controlled input. The raw search value is passed through useDebounce with a 300 ms delay:
const [searchDebounced] = useDebounce(search, 300);
A useEffect fires whenever searchDebounced changes (and is non-empty), calling useSearch({ searchParam: searchDebounced }). The resolved data.data array is written to the local data state, causing React to re-render the result list. Each row is rendered as a <li> containing an <article> whose entries are iterated with Object.entries(user), displaying every column key and value in sequence.

Service Functions

Both service functions live in src/Services/useGetApiData.tsx and return Promise<[Data?, Error?]> tuples, following a Go-style error-first result pattern.
Builds a FormData object, appends the File under the key "file", and POSTs it to the backend upload endpoint:
export const SendCsv = async (
  file: File,
): Promise<[Data?: ApiResponse, Error?]> => {
  const formData = new FormData();
  if (file) formData.append("file", file);
  const request = {
    method: "POST",
    body: formData,
  };

  const [data, error] = await fetch("http://localhost:3000/api/files", request)
    .then((res) => res.json())
    .then((data) => [data])
    .catch((e) => [, e]);

  return [data, error];
};
On success, data is the ApiResponse object { data: Record<string, string>[], message: string }. On network failure the catch branch returns [undefined, e] where e is the thrown Error.
Constructs a query string from searchParam and GETs the search endpoint:
export const useSearch = async ({ searchParam }: { searchParam: string }) => {
  const [data, error] = await fetch(
    `http://localhost:3000/api/users?q=${searchParam}`,
  )
    .then((res) => res.json())
    .then((data) => [data])
    .catch((e) => [e]);

  return [data, error];
};
The resolved data contains { data: Record<string, string>[] } — matching rows from the backend filter. Despite the use prefix, this is not a React hook; it is a plain async function called imperatively inside a useEffect.

TypeScript Types

All shared domain types are defined in src/types.ts:
import { APP_STATUS } from "./App";

export type Data = Array<Record<string, string>> | undefined;

export type ApiResponse = {
  data: Record<string, string>[];
  message: string;
};

export type AppStatusType = (typeof APP_STATUS)[keyof typeof APP_STATUS];
TypeUsage
DataThe parsed CSV rows held in both App state and passed as the dataCsv prop to Search. Can be undefined before a successful upload.
ApiResponseThe JSON shape returned by both backend endpoints — a data array and a message string.
AppStatusTypeA union of all APP_STATUS values derived via typeof … [keyof typeof …], ensuring state can only ever be one of the five valid strings.

Key Dependencies

PackageVersionRole
react^19.2.4Core UI library — component model, hooks, and concurrent rendering.
react-dom^19.2.4DOM renderer for React — required alongside react to mount the app into the browser.
sonner^2.0.7Lightweight toast notification library; used to surface upload success and error messages.
use-debounce^10.1.1Provides useDebounce hook to delay the search query by 300 ms and prevent a fetch on every keystroke.
debounce^3.0.0Utility debounce function included as a direct dependency alongside use-debounce.
next-themes^0.4.6Theme management utility included as a dependency (available for light/dark mode switching).
vite^8.0.4Dev server and bundler — provides HMR and the --port flag used to pin the server to port 4000.

Dev Server

The Vite dev server is started on port 4000 via the dev script:
{
  "scripts": {
    "dev":   "vite --port=4000",
    "build": "tsc -b && vite build",
    "preview": "vite preview"
  }
}
The production build runs tsc -b (type-check and compile) followed by vite build, which outputs optimised static assets to the dist/ directory.
The frontend hardcodes http://localhost:3000 as the API base URL in both SendCsv and useSearch. This works correctly during local development but will break in any deployed environment where the backend is not available at that exact address. For production you would need to replace these literals with an environment variable (for example import.meta.env.VITE_API_URL) and configure the correct value at build time.

Build docs developers (and LLMs) love