The Factus Challenge frontend has four dedicated page views plus a home screen, all wired together by the router inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/tutosrive/factus_challenge/llms.txt
Use this file to discover all available pages before exploring further.
index.mjs. Each view is implemented as an ES module that exports a default class with only static methods — you never call new Factus() or new Clientes(). Calling the constructor throws an explicit error. The entry point for every view is a single .init() static method that fetches data, injects HTML into <main>, and initialises a Tabulator table.
Views
Invoices — Factus class (factus.mjs)
Invoices — Factus class (factus.mjs)
The
The assembled body is sent to
Factus class manages the invoice list and invoice creation form. It is loaded when the user navigates to pages/factus.html.Initialisation
Factus.init() performs the following steps before rendering the table:- Fetches the invoice form HTML partial from
/resources/html/factus.htmlviaHelpers.fetchText. - Loads payment methods from
GET ${urlAPI}/get-data/payment_methodand pre-builds the<option>list string. - Fetches all invoices from
GET ${urlAPI}/factura. - Injects a
<div id="table-container">into<main>and initialises aTabulatorinstance.
Table Columns
| Column | Field | Notes |
|---|---|---|
| (delete button) | — | Renders deleteRowButton; only invoices with status === 0 can actually be deleted |
| ID | id | Numeric, sorted descending by default |
| ESTADO | status | Formatted: 0 → 'En espera', 1 → 'Enviada' |
| DOCUMENTO | document.name | Nested field access |
| NÚMERO FACTURA | number | Invoice number string |
| CLIENTE API | api_client_name | The Factus API client name |
| ID CLIENTE | identification | Customer tax ID |
| NOMBRE CLIENTE | graphic_representation_name | Customer display name |
| FORMA DE PAGO | payment_form.name | Nested field |
| HORA CREACIÓN | created_at | Formatted with Luxon (see below) |
Date Formatting
Dates from the API arrive asdd-MM-yyyy hh:mm:ss a (e.g. 23-01-2025 09:16:27 PM) and are formatted for display using Luxon with the es-419 locale:Creating an Invoice
Clicking the “Nuevo registro” button in the table footer opens aModal containing the factus.html form partial. The form includes:- A customer
<select>populated fromGET ${urlAPI}/get-data/customer - A payment method
<select>pre-loaded duringinit() - An observation
<textarea> - An embedded Tabulator products table (see below)
- A “Nuevo cliente” button that opens
pages/cliente.htmlin a new tab viaCustoms.new_client()
Factus.#getFormData() assembles the request body. It queries the customer record via POST ${urlAPI}/get-join/ with a raw SQL SELECT to retrieve full customer details, then maps fields to the shape expected by the API:POST ${urlAPI}/factura. On success the page reloads after 2 seconds to reflect the new invoice in the table.Embedded Products Table
Inside the invoice creation modal, a second Tabulator instance (#products_table) allows building the line items. Product selection happens via a popover (not a modal):- Clicking “Agregar producto” calls
Customs.popover()to render a<select>of products and a quantity stepper (-/+buttons). - Product options are fetched from
GET ${urlAPI}/get-data/productsand rendered withHelpers.toOptionList. - When the user confirms, the full product detail is fetched via
POST ${urlAPI}/get-joinwithSELECT * FROM products WHERE code_reference = '...'. - The row is added to
#products_tablewithquantityset by the stepper andpricemultiplied by quantity.
code_reference, name, price (money formatter), quantity, tax_rate, discount_rate, unit_measure_id, tribute_id, standard_code_id, is_excluded, withholding_taxes (JSON formatter).Deleting an Invoice
Clicking the delete icon on a row opens a confirmationModal. The actual DELETE request is only sent if data.status === 0:Clients — Clientes class (clientes.mjs)
Clients — Clientes class (clientes.mjs)
The
Customer data is then loaded from
Clientes class manages the full customer lifecycle. It is loaded when the user navigates to pages/cliente.html.Initialisation
Clientes.init() pre-fetches four lookup tables before building the main table, so all dropdown options are ready when a form opens:| Lookup table | Endpoint | Used for |
|---|---|---|
municipality | GET /get-data/municipality | City/municipality select |
identification_document | GET /get-data/identification_document | ID type select (NIT, Cédula, etc.) |
customer_tribute | GET /get-data/customer_tribute | Tribute type select |
legal_organization | GET /get-data/legal_organization | Org type select (Natural / Jurídica) |
GET ${urlAPI}/get-data/customer.Table Columns
| Column | Field | Notes |
|---|---|---|
| (edit button) | — | Opens edit modal |
| (delete button) | — | Opens delete confirmation modal |
| ID | id | Sorted ascending by default |
| NOMBRE | names | |
email | ||
| TIPO ID | type_id | Resolved to name via identification_document lookup |
| COMPAÑÍA | company | Null values shown as '----' |
| DIRECCIÓN | address | |
| TELÉFONO | phone | |
| NOMBRE COMERCIAL | trade_name | Null values shown as '----' |
| MUNICIPIO | municipality_id | Resolved to name via municipality lookup |
| DV | verification_digit | Digit for NIT verification; '----' if null |
| ORG | id_org | Resolved to name via legal_organization lookup |
| TRIBUTO | tribute_id | Resolved to name via customer_tribute lookup |
CRUD Operations
Form Fields
Theclientes.html partial contains inputs for: id (tax identification number), type_id (ID document type), names, address, phone, email, company, id_org (legal organization), tribute_id, trade_name, and municipality_id.The DV (verification digit) field container is hidden by default and only revealed — with required set to true — when the user selects NIT (type ID 6) from the identification type dropdown.View Invoices — FactuSearch class (factusearch.mjs)
View Invoices — FactuSearch class (factusearch.mjs)
The
FactuSearch class provides a search-by-number interface for inspecting a single invoice in detail. It is loaded when the user navigates to pages/factusget.html.How it works
FactuSearch.init() loads the search_factus.html partial into <main> and attaches a click listener to the search button (#buscar-envio).When the user enters an invoice number and submits:GET ${urlAPI}/factura/:numberis called.- On success, a detail panel and a Tabulator table are rendered in
#container-info.
Detail Panel Fields
The detail panel displays (fromresponse[0]):- Document type:
bill.document.name - Observation:
bill.observation - API client company:
company.name,company.nit,company.municipality - Reference code:
bill.reference_code - Customer:
customer.names,customer.legal_organization.name - Any API errors from
bill.errors[]
Search Result Table Columns
| Column | Field |
|---|---|
| ID | bill.id |
| ESTADO | bill.status (0=En espera, 1=Enviada) |
| DOCUMENTO | bill.document.name |
| NÚMERO FACTURA | bill.number |
| CUFE | bill.cufe |
| ID CLIENTE | customer.identification |
| NOMBRE CLIENTE | customer.graphic_representation_name |
| FORMA DE PAGO | bill.payment_form.name |
| PREFIJO RANGO | numbering_range.prefix |
| HORA CREACIÓN | bill.created_at (Luxon formatted) |
| HORA VALIDACIÓN | bill.validated (Luxon formatted) |
PDF Download
A PDF download button (rendered withicons.pdf_icon1) triggers FactuSearch.download_pdf():About — About class (about.mjs)
About — About class (about.mjs)
The
About class renders a static profile and project information page. It is loaded when the user navigates to pages/about.html.About.init() fetches the /resources/html/about.html partial and injects it into <main>, then calls #listenLinks() to wire up interactive elements.Interactive elements
All external links on the page are intercepted and replaced with a redirect confirmation popover (viaCustoms.popover) before the user is taken off-site. This includes:- GitHub, Instagram, YouTube, and email icons in
#social-links - Project links in
#projects - Links in the
#about-meand#laboral-expreiencesections
/resources/assets/images/qrw.webp).Email links open a mailto: URL after confirmation via a dedicated popover.Global Button & Table Conventions
index.mjs registers a set of standardised button HTML strings and a table height constant as window globals. Every page controller uses these, ensuring a consistent look:
