Implementare interfață utilizator

1. Curățarea fișierului de stiluri

Mergem în tailwind.config.js și lăsăm doar liniile de configurare pentru tailwind.

// tailwind.config.js

@tailwind base;
@tailwind components;
@tailwind utilities;

2. Crearea fișierului de metode pentru manipularea entry-urilor

O să ne deplasăm în folderul utils și o să creăm un nou fișier numit recordsFunctions.js.

În acest fișier, o să apelăm toate rutele create în pașii anteriori din fișierul records.js.

// /utils/recordsFunctions.js

export const getRecords = async () => {
    try {
        const response = await fetch("/api/records", {
            method: "GET",
        });
    
        const data = await response.json();
    
        if (!data?.data) {
            return [];
        }
    
        return data.data;
    } catch (error) {
        console.error(error);
    }
}

export const getRecordById = async (id) => {
    try {
        const response = await fetch(`/api/records?id=${id}`, {
            method: "GET",
        });
    
        const data = await response.json();
    
        if (!data?.data) {
            return null;
        }
    
        return data.data;
    } catch (error) {
        console.error(error);
    }
}

export const createRecord = async (record) => {
    try {
        delete record._id;

        const response = await fetch("/api/records", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(record),
        });

        const data = await response.json();

        if (!data?.data) {
            return null;
        }

        return data.data;
    } catch (error) {
        console.error(error);
    }
}

export const updateRecord = async (record) => {
    try {
        const response = await fetch("/api/records", {
            method: "PUT",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(record),
        });

        const data = await response.json();

        if (!data?.data) {
            return null;
        }

        return data.data;
    } catch (error) {
        console.error(error);
    }
}

export const deleteRecord = async (id) => {
    try {
        const response = await fetch(`/api/records?id=${id}`, {
            method: "DELETE",
        });

        const data = await response.json();

        if (!data?.data) {
            return null;
        }

        return data.data;
    } catch (error) {
        console.error(error);
    }
}

3. Pagina princpală

O să creăm un nou folder care o să se numească components, și înăuntrul lui o să adăugăm un fișier care se va numi MainPage.jsx.

Dacă folosiți Visual Studio Code și doriți un shortcut pentru un boilerplate de componentă de React, puteți tasta rafce și să apăsați enter și o să primiți boilerplate-ul.

Mergem în fișierul de index.js din folderul pages, ștergem tot conținutul return-ului și chemăm componenta de MainPage.jsx.

După efectuarea tuturor acestor modificări, începem să modificăm pagina MainPage.jsx și să o facem să ceară de la baza de date toate înregistrările din tabela records, și să le mapăm sub formă de carduri, oferind posibilitatea utilizatorilor să apese pe butonul de editare sau de ștergere a unui entry.

Această componentă folosește două hook-uri foarte importante puse la dispoziție de către React:

  1. useState -> care ne ajută să menținem valori pe care să le urmărim și să ne folosim de ele într-un mod dinamic atunci când aceste sunt actualizate.

În cazul nostru, salvăm lista de records pe care o mapăm sub formă de carduri.

!! Metoda de set este async !!

  1. useEffect -> un hook care are rolul de a efectua anumite acțiuni atunci când o variabilă din array-ul de dependențe se schimbă, sau de a efectua o singură acțiune atunci când componenta se montează și array-ul de dependințe este gol.

În cazul nostru, ne-am folosit de useEffect pentr a apela metoda fetchRecords care cere la baza de date lista cu toate entry-urile.

Cel mai important lucru pe care trebuie să îl facem atunci când dorim să mapăm o listă de valori, este să oferim fiecărui item mapat o cheie unică de identificare care să ajute sistemul să identifice în orice secundă ce element se dorește a fi modificat/șters.

4. Crearea unui entry

Pentru crearea unui nou entry, o să ne mutăm în folderul pages și o să creăm un nou folder numit records cu un fișier create.jsx.

Ruta poate fi accesată la http://localhost:3000/records/create

După cum putem vedea, am creat un entry pe baza unui template și o metodă de submit (callback function), pe care le-am pasat către altă componentă sub formă de PROPS.

Un callback function este o funcție pasată ca și prop unei componente copil, cu scopul ca acesta să o poată apela oricând are nevoie.

Pentru mai multe detalii despre callback functions: https://developer.mozilla.org/en-US/docs/Glossary/Callback_function

Pentru a crea respectivul template de entry, o să ne mutăm în folderul utils, și o să creăm fișierul constants.js.

Pentru crearea formularului, o să ne mutăm în folderul components și o să creăm fișierul RecordForm.jsx.

După cum putem observa, componenta RecordForm primește ca și props data și onSubmit, setează un state cu obiectul data primit, și umple valorile input-urilor cu valorile primite, punând la dispoziție metoda onSubmit atunci când se apasă pe butonul Create/Update.

5. Editarea unui entry

Ne mutăm în folderul records din pages și adăugăm o nouă componentă, numită edit.jsx.

Principalul rol al acestei componente este ca atunci când este montată (prin intermediul useEffect-ului) să verifice dacă în path-ul accesat există sau nu un Query Param cu id-ul record-ului care se dorește a fi editat.

  • În cazul în care găsește id-ul, va încerca să tragă de la baza de date record-ul cu id-ul furnizat. În cazul fericit, o să paseze către componenta RecordForm următoarele prop-uri:

    • entry -> entry-ul găsit în baza de date

    • onSubmit -> un callback function care are rolul de a actualiza entry-ul atunci când componenta copil cere acest lucru.

  • În cazul în care nu se găsește id-ul ca și Query Param, utilizatorul va fi redirecționat către HomePage.

A fost implementată și funcționalitatea de loading, astfel încât până când nu se primește de la server un răspuns pozitiv sau nu, un Spinner va fi afișat pe ecran.

Pentru testare;

Pentru crearea Spinner-ului, o să mergem în folderul components și o să creăm un fișier numit Spinner.jsx.

Last updated