Proiect TAP
Proiect TAP
IUGA DANIEL
2 Tehnologii folosite 5
2.1 [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 MUI - Material UI . . . . . . . . . . . . . . . . . . . . . . 5
2.3 CSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.4 Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3 Arhitectura Frontend-ului 7
3.1 Structura proiectului React . . . . . . . . . . . . . . . . . 7
7 Comunicarea cu Backend-ul 23
7.1 Configurat, ia API . . . . . . . . . . . . . . . . . . . . . . . 23
7.2 Exemple de fetch/axios calls din React . . . . . . . . . . . 23
7.3 Integrarea cu componenta SearchBar . . . . . . . . . . . . 24
7.4 Concluzie . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
7.5 Gestionarea erorilor s, i ı̂ncărcarea datelor . . . . . . . . . . 24
1
7.5.1 Exemplu de autentificare s, i tratare erori . . . . . . 25
7.5.2 Încărcare date utilizator după autentificare . . . . . 25
7.5.3 Validare date s, i sincronizare cu backend-ul . . . . . 25
7.5.4 Notificări s, i feedback vizual pentru erori . . . . . . 26
7.5.5 Feedback vizual: afis, area unui indicator de ı̂ncărcare 26
7.5.6 Concluzie . . . . . . . . . . . . . . . . . . . . . . . 27
8 Concluzii s, i perspective 28
8.1 Sumar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
8.2 Posibile ı̂mbunătăt, iri . . . . . . . . . . . . . . . . . . . . . 29
2
Chapter 1
Introducere
1.1 Prezentare generală proiect
Proiectul realizat constă ı̂ntr-o aplicat, ie web pentru vânzarea s, i cumpărarea
de automobile, similară cu platformele populare precum OLX sau Mo-
[Link]. Scopul acestei aplicat, ii este de a facilita interact, iunea dintre vânzători
s, i cumpărători, oferind un mediu intuitiv s, i performant pentru publicarea,
căutarea s, i gestionarea anunt, urilor auto.
Frontend-ul aplicat, iei este dezvoltat folosind [Link], o bibliotecă JavaScript
populară pentru construirea de interfet, e de utilizator dinamice s, i reactive.
Pentru componenta de stilizare s, i interfat, ă, am utilizat biblioteca MUI
(Material-UI), care oferă un set bogat de componente predefinite con-
form principiilor Material Design, ı̂mpreună cu CSS personalizat pentru
adaptarea vizuală specifică a aplicat, iei.
Partea de backend este implementată cu ajutorul framework-ului Java
Spring Boot, care asigură o arhitectură robustă s, i scalabilă pentru ges-
tionarea cererilor HTTP, logica de afaceri s, i interact, iunea cu baza de date.
Pentru stocarea datelor despre utilizatori, anunt, uri s, i alte entităt, i, am ales
MongoDB, o bază de date NoSQL ce oferă flexibilitate ı̂n modelarea doc-
umentelor JSON s, i performant, ă la scară largă.
Această combinat, ie tehnologică permite dezvoltarea unei aplicat, ii mod-
erne full stack, cu o experient, ă utilizator fluidă s, i o arhitectură backend
eficientă.
3
1.2 Obiectivele documentat, iei
Această documentat, ie are ca scop prezentarea detaliată a dezvoltării frontend-
ului aplicat, iei folosind [Link], cu accent pe arhitectura componentelor,
gestionarea stării s, i comunicarea cu backend-ul. De asemenea, documentat, ia
explică modul ı̂n care tehnologia MUI (Material-UI) s, i CSS-ul personalizat
sunt utilizate pentru a crea o interfat, ă modernă, responsivă s, i prietenoasă
cu utilizatorul.
Se pune accent pe integrarea frontend-ului cu backend-ul dezvoltat ı̂n
Java Spring Boot, ilustrând fluxul de date s, i modul ı̂n care aplicat, ia comu-
nică cu baza de date MongoDB. Documentat, ia include descrieri tehnice,
exemple de cod, bune practici s, i explicat, ii care să faciliteze ı̂nt, elegerea s, i
ı̂ntret, inerea proiectului.
În plus, documentat, ia oferă perspective asupra testării, stilizării s, i posi-
bilelor extensii ale aplicat, iei, astfel ı̂ncât să servească drept ghid complet
pentru dezvoltatori s, i colaboratori.
4
Chapter 2
Tehnologii folosite
2.1 [Link]
[Link] este o bibliotecă JavaScript open-source dezvoltată de Facebook,
folosită pentru construirea interfet, elor de utilizator dinamice s, i reactive.
React se bazează pe conceptul de componente reutilizabile, care permit
dezvoltatorilor să creeze UI-uri modulare s, i us, or de ı̂ntret, inut. Unul dintre
avantajele majore ale React este utilizarea Virtual DOM, o reprezentare
virtuală a DOM-ului real, care optimizează actualizările s, i redarea ele-
mentelor ı̂n pagină, crescând performant, a aplicat, iei.
În acest proiect, [Link] a fost ales datorită flexibilităt, ii sale, a eco-
sistemului bogat s, i a capacităt, ii de a crea o experient, ă utilizator fluidă s, i
interactivă, esent, ială pentru o platformă de vânzări auto unde utilizatorii
au nevoie de răspunsuri rapide s, i interfat, ă prietenoasă.
5
2.3 CSS
Pe lângă componentele oferite de MUI, stilizarea a fost completată cu CSS
personalizat pentru a adapta anumite elemente vizuale conform cerint, elor
specifice ale proiectului. Acest lucru a permis o personalizare mai fină a
interfet, ei, inclusiv ajustări de culori, margini, fonturi s, i layout, pentru a
crea o experient, ă unică s, i plăcută utilizatorului final.
2.4 Backend
Backend-ul aplicat, iei este construit folosind Java Spring Boot, un frame-
work puternic s, i popular pentru dezvoltarea rapidă a aplicat, iilor enterprise.
Spring Boot oferă un mediu configurabil s, i extensibil pentru crearea API-
urilor REST, gestionarea securităt, ii, s, i integrarea cu baze de date.
Pentru stocarea datelor, aplicat, ia utilizează MongoDB, o bază de date
NoSQL orientată pe documente, care permite stocarea flexibilă a informat, iilor
ı̂n format JSON. MongoDB este ideală pentru aplicat, ii care necesită scal-
abilitate s, i modelare dinamică a datelor, cum este cazul platformei de
vânzări auto unde structura anunt, urilor poate varia.
Această combinat, ie ı̂ntre Spring Boot s, i MongoDB asigură o arhitectură
backend robustă, scalabilă s, i us, or de ı̂ntret, inut.
6
Chapter 3
Arhitectura
Frontend-ului
3.1 Structura proiectului React
Frontend-ul aplicat, iei este organizat ı̂ntr-o structură clară s, i modulară, care
facilitează dezvoltarea, ı̂ntret, inerea s, i scalabilitatea aplicat, iei. Structura
principală a proiectului cont, ine două directoare importante: components
s, i storage.
1. Folderul components
Acest folder cont, ine toate componentele React utilizate ı̂n cadrul aplicat, iei.
Fiecare componentă este organizată ı̂ntr-un subfolder dedicat, iar pentru
fiecare componentă sunt incluse două fis, iere principale:
• un fis, ier .css care cont, ine stilizarea specifică acelei componente.
2. Folderul hooks
În acest folder este implementată logica pentru gestionarea sesiunii utiliza-
torului prin intermediul unui custom hook React numit useSessionStorage.
Acest hook permite stocarea informat, iilor de autentificare ı̂n sessionStorage,
7
astfel ı̂ncât utilizatorul rămâne logat atât timp cât sesiunea browserului
este activă (până la ı̂nchiderea tabului sau a ferestrei).
Această abordare oferă un echilibru ı̂ntre confortul utilizatorului s, i se-
curitate, deoarece sesiunile nu persistă după ı̂nchiderea browserului, dar
nici nu obligă utilizatorul să se relogheze la fiecare refresh al paginii.
8
Chapter 4
Fluxul datelor s, i
comunicarea cu
backend-ul
4.1 Fluxul datelor ı̂n aplicat, ie
În cadrul aplicat, iei, datele circulă ı̂ntre utilizator, interfat, a de utiliza-
tor (frontend), server (backend) s, i baza de date (MongoDB). Acest
flux este gândit pentru a fi eficient, securizat s, i us, or de urmărit.
Procesul general este următorul:
9
4.2 Exemplu: Autentificare s, i sincronizare
utilizator
Mai jos este prezentat un exemplu complet de funct, ie de login care
combină mai multe cereri către backend s, i include logica de validare
a sesiunii:
1 const handleSubmit = async ( event ) = > {
2 event . preventDefault () ;
3 if (! email || ! password ) {
4 setError ( ’ Please fill in all fields . ’) ;
5 return ;
6 }
7
8 setError ( ’ ’) ;
9 try {
10 const response = await axios . post ( ’ http :// localhost :8080/ api /
auth / login ’ , {
11 email ,
12 password
13 }) ;
14
15 if ( response . status === 200) {
16 // Login r e u i t , c o n t i n u m cu fetch - ul datelor
utilizatorului
17 }
18 } catch ( error ) {
19 if ( error . response && error . response . status === 401) {
20 setError ( ’ Wrong email or password ! ’) ;
21 return ;
22 } else {
23 setError ( ’ An error occurred . Please try again . ’) ;
24 return ;
25 }
26 }
27
28 try {
29 const response = await axios . get ( ’ http :// localhost :8080/ api /
users / findByEmail ’ , {
30 params : { email : email }
31 }) ;
32
33 if ( response . status === 200) {
34 const user = response . data ;
35 const validCarIds = new Set () ;
36
37 for ( let elem of user . carIds ) {
38 const response = await axios . get ( ‘ http : // localhost :8080/
api / cars / $ { elem } ‘) ;
39 if ( response . data !== null ) {
40 validCarIds . add ( elem ) ;
41 }
42 }
43
44 const uniqueCarIds = Array . from ( validCarIds ) ;
45 if ( validCarIds !== user . carIds ) {
10
46 await axios . patch ( ‘ http : // localhost :8080/ api / users / $ { user .
id } ‘ , {
47 ... user ,
48 carIds : uniqueCarIds
49 }) ;
50 user . carIds = uniqueCarIds ;
51 }
52
53 setUserData ( user ) ;
54 if ( returning === 1) {
55 navigate ( ’/ account ’) ;
56 } else {
57 navigate ( ’/ ’) ;
58 }
59 }
60 } catch ( error ) {
61 if ( error . response && ( error . response . status === 401 || error .
response . status === 404) ) {
62 setError ( ’ Wrong email or password ! ’) ;
63 } else {
64 setError ( ’ An error occurred . Please try again . ’) ;
65 }
66 }
67 };
Listing 4.1: Funct, ia handleSubmit pentru autentificare s, i sincronizare date
utilizator
Descriere
Această funct, ie reprezintă un exemplu complex de comunicare ı̂ntre
frontend s, i backend:
11
Chapter 5
Componenta React:
Structură s, i
Funct, ionalitate
5.1 Componenta principală: Home
Componenta principală a aplicat, iei este Home, care serves, te ca punct
de intrare pentru afis, area paginii de start. Aceasta combină două
componente majore: SearchBar s, i MainPage, fiind responsabilă de
layout-ul principal.
1 import React from ’ react ’;
2 import { Box } from ’ @mui / material ’;
3 import ’ ./ Home . css ’;
4 import SearchBar from ’ ../ searchbar / SearchBar ’;
5 import MainPage from ’ ../ mainPage / MainPage ’;
6
7 const Home = ({ searchFilters , setSearchFilters , setCarData }) = >
{
8 return (
9 < Box
10 sx ={{
11 display : ’ flex ’ ,
12 flexDirection : " row " ,
13 gap : 1 ,
14 padding : 0 ,
15 backgroundColor : ’ #2 B2B2B ’ ,
16 minHeight : ’ 100 vh ’ ,
17 }}
18 className = " home "
19 >
20 { /* Bara de c u t a r e - partea s t n g */ }
21 < Box sx ={{ width : ’ 18% ’ , minWidth : ’ 200 px ’ }} >
22 < SearchBar
23 searchFilters ={ searchFilters }
12
24 setSearchFilters ={ setSearchFilters }
25 />
26 </ Box >
27
28 { /* Zona p r i n c i p a l cu a n u n u r i */ }
29 < Box sx ={{ borderLeft : ’2 px solid hsl (10 , 70% , 0%) ’ , flex :
1 }} >
30 < MainPage
31 searchFilters ={ searchFilters }
32 setCarData ={ setCarData }
33 />
34 </ Box >
35 </ Box >
36 );
37 };
38
39 export default Home ;
Listing 5.1: Componenta Home - pagina principală
5.2 Explicat, ie
Componenta principală prezentată ı̂n exemplul anterior este o com-
ponentă React funct, ională modernă, care utilizează hooks pentru ges-
tionarea stării s, i a efectelor secundare.
Structura generală
Componenta se numes, te ExampleComponent s, i este definită folosind
funct, ia JavaScript function. În interiorul acesteia sunt utilizate
două hook-uri importante din biblioteca React: useState s, i useEffect.
Hooks folosit, i
– useState – acest hook este folosit pentru a declara variabila de
stare data, care este init, ializată cu o listă goală. Această stare
va cont, ine datele primite de la backend s, i va fi actualizată după
ce răspunsul API este obt, inut.
– useEffect – acest hook este responsabil pentru efectuarea unui
efect secundar”, ı̂n acest caz un apel HTTP către backend. Efec-
tul este apelat o singură dată (deoarece lista de dependent, e este
goală: []) după montarea componentei ı̂n DOM.
13
Comunicarea cu backend-ul
Prin utilizarea funct, iei fetch, componenta trimite un request către
ruta /api/data, iar răspunsul JSON este convertit ı̂n obiect JavaScript
s, i salvat ı̂n starea locală a componentei prin setData. Această metodă
de comunicare este asincronă s, i permite actualizarea UI-ului imediat
ce datele sunt disponibile.
14
21 useEffect (() = > {
22 const ap pl ys ea rc hF il te rs = ( carData ) = > {
23 if (! e xi st Se ar ch Fi lt er s ( searchFilters ) ) {
24 return carData ;
25 }
26
27 const f i l t e r e d S e a r c h F i l t e r s = Object . entries (
searchFilters )
28 . filter (([ key , value ]) = > value )
29 . map (([ key , value ]) = > ({ [ key ]: value }) ) ;
30
31 for ( let elem in f i l t e r e d S e a r c h F i l t e r s ) {
32 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " title " ]) {
33 const keywords = f i l t e r e d S e a r c h F i l t e r s [ elem ][ "
title " ]. split ( " " ) ;
34 carData = Object . values ( carData ) . filter (( item )
=>
35 keywords . every (( keyword ) = > item [ " title " ].
includes ( keyword ) )
36 );
37 }
38 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " brand " ]) {
39 carData = Object . values ( carData )
40 . filter (( item ) = > item [ " brand " ] ===
f i l t e r e d S e a r c h F i l t e r s [ elem ][ " brand " ]) ;
41 }
42 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " model " ]) {
43 carData = Object . values ( carData )
44 . filter (( item ) = > item [ " model " ] ===
f i l t e r e d S e a r c h F i l t e r s [ elem ][ " model " ]) ;
45 }
46 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " body " ]) {
47 carData = Object . values ( carData )
48 . filter (( item ) = > item [ " body " ] ===
f i l t e r e d S e a r c h F i l t e r s [ elem ][ " body " ]) ;
49 }
50 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " kmStart " ]) {
51 carData = Object . values ( carData )
52 . filter (( item ) = > Number ( item [ " mileage " ])
>= Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " kmStart " ]) ) ;
53 }
54 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " kmEnd " ]) {
55 carData = Object . values ( carData )
56 . filter (( item ) = > Number ( item [ " mileage " ])
<= Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " kmEnd " ]) ) ;
57 }
58 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " yearStart " ]) {
59 carData = Object . values ( carData )
60 . filter (( item ) = > Number ( item [ " year " ]) >=
Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " yearStart " ]) ) ;
61 }
62 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " yearEnd " ]) {
63 carData = Object . values ( carData )
64 . filter (( item ) = > Number ( item [ " year " ]) <=
Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " yearEnd " ]) ) ;
65 }
66 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " priceStart " ]) {
67 carData = Object . values ( carData )
15
68 . filter (( item ) = > Number ( item [ " price " ]) >=
Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " priceStart " ]) ) ;
69 }
70 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " priceEnd " ]) {
71 carData = Object . values ( carData )
72 . filter (( item ) = > Number ( item [ " price " ]) <=
Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " priceEnd " ]) ) ;
73 }
74 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " cm3Start " ]) {
75 carData = Object . values ( carData )
76 . filter (( item ) = > Number ( item [ " cm3 " ]) >=
Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " cm3Start " ]) ) ;
77 }
78 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " cm3End " ]) {
79 carData = Object . values ( carData )
80 . filter (( item ) = > Number ( item [ " cm3 " ]) <=
Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " cm3End " ]) ) ;
81 }
82 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " hpStart " ]) {
83 carData = Object . values ( carData )
84 . filter (( item ) = > Number ( item [ " hp " ]) >=
Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " hpStart " ]) ) ;
85 }
86 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " hpEnd " ]) {
87 carData = Object . values ( carData )
88 . filter (( item ) = > Number ( item [ " hp " ]) <=
Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " hpEnd " ]) ) ;
89 }
90 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " fuelType " ]) {
91 carData = Object . values ( carData )
92 . filter (( item ) = > item [ " fuelType " ] ===
f i l t e r e d S e a r c h F i l t e r s [ elem ][ " fuelType " ]) ;
93 }
94 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " transmission " ]) {
95 carData = Object . values ( carData )
96 . filter (( item ) = > item [ " transmission " ] ===
f i l t e r e d S e a r c h F i l t e r s [ elem ][ " transmission " ]) ;
97 }
98 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " color " ]) {
99 carData = Object . values ( carData )
100 . filter (( item ) = > item [ " color " ] ===
f i l t e r e d S e a r c h F i l t e r s [ elem ][ " color " ]) ;
101 }
102 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " condition " ]) {
103 carData = Object . values ( carData )
104 . filter (( item ) = > item [ " condition " ] ===
f i l t e r e d S e a r c h F i l t e r s [ elem ][ " condition " ]) ;
105 }
106 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " steeringwheel " ])
{
107 carData = Object . values ( carData )
108 . filter (( item ) = > item [ " steeringwheel " ]
=== f i l t e r e d S e a r c h F i l t e r s [ elem ][ " steeringwheel " ]) ;
109 }
110 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " numberOfDoors " ])
{
111 carData = Object . values ( carData )
112 . filter (( item ) = > Number ( item [ "
16
numberOfDoors " ]) === Number ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ "
numberOfDoors " ]) ) ;
113 }
114 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " sort " ]) {
115 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " sort " ] === "
YearDescending " ) {
116 carData = Object . values ( carData )
117 . sort (( a , b ) = > Number ( b . year ) -
Number ( a . year ) ) ;
118 }
119 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " sort " ] === "
YearAsscending " ) {
120 carData = Object . values ( carData )
121 . sort (( a , b ) = > Number ( a . year ) -
Number ( b . year ) ) ;
122 }
123 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " sort " ] === "
PriceDescending " ) {
124 carData = Object . values ( carData )
125 . sort (( a , b ) = > Number ( b . price ) -
Number ( a . price ) ) ;
126 }
127 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " sort " ] === "
PriceAscending " ) {
128 carData = Object . values ( carData )
129 . sort (( a , b ) = > Number ( a . price ) -
Number ( b . price ) ) ;
130 }
131 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " sort " ] === "
Mil eageDe scendi ng " ) {
132 carData = Object . values ( carData )
133 . sort (( a , b ) = > Number ( b . mileage ) -
Number ( a . mileage ) ) ;
134 }
135 if ( f i l t e r e d S e a r c h F i l t e r s [ elem ][ " sort " ] === "
MileageAscending " ) {
136 carData = Object . values ( carData )
137 . sort (( a , b ) = > Number ( a . mileage ) -
Number ( b . mileage ) ) ;
138 }
139 }
140 }
141 return carData ;
142 };
143
17
154 } finally {
155 setLoading ( false ) ;
156 }
157 };
158
159 fetchData () ;
160 } , [ searchFilters ]) ;
161
162 if ( loading ) {
163 return (
164 < Box sx ={{ display : ’ flex ’ , justifyContent : ’ center ’ ,
alignItems : ’ center ’ , height : ’ 100 vh ’ }} >
165 < CircularProgress / >
166 </ Box >
167 );
168 }
169
170 const accesCar = ( carId ) = > {
171 const carDetails = cars . find ( car = > car . id === carId ) ;
172 if ( carDetails ) {
173 setCarData ( carDetails ) ;
174 navigate ( " / carDetails " ) ;
175 } else {
176 console . error ( ’ Car not found for the given ID ’) ;
177 }
178 };
179
180 return (
181 < Grid container rowGap ={1} columnGap ={5} sx ={{
backgroundColor : " hsl (0 , 0% , 11%) " , paddingLeft : " 5% " , minHeight :
’ 100% ’}} >
182 { cars . length > 0 ? (
183 cars . map (( car ) = > (
184 < Grid item xs ={12} sm ={6} md ={4} lg ={2} key ={
car . id } sx ={{ height : ’ 360 px ’ , marginTop : " 20 px " }} >
185 < Card onClick ={() = > accesCar ( car . id ) } sx
={{ cursor : ’ pointer ’ , maxWidth : " 250 px " , height : ’ 350 px ’ ,
display : ’ flex ’ , flexDirection : ’ column ’ , border : ’1 px solid
black ’}} >
186 { car . image && (
187 < CardMedia
188 component = " img "
189 sx ={{
190 width : ’ 100% ’ , // S e t e a z
l i m e a imaginii la 100% din containerul s u
191 maxHeight : ’ 300 px ’ , //
Seteaz nlimea maxim
192 height : ’ auto ’ , //
M e n i n e raportul de aspect
193 }}
194 image ={ ‘ http : // localhost :8080/
api$ { car . image } ‘}
195 alt ={ ‘ Car $ { car . title } ‘}
196 loading = " lazy "
197 />
198 )}
199 < CardContent
200 sx ={{
18
201 backgroundColor : ’ rgb (17 , 18 ,
20) ’ ,
202 color : ’ lightgray ’ ,
203 flexGrow : 1 ,
204 display : ’ flex ’ ,
205 flexDirection : ’ column ’ ,
206 }}
207 >
208 < Typography
209 variant = " h6 "
210 noWrap
211 gutterBottom
212 sx ={{
213 textOverflow : " ellipsis " ,
214 overflow : " hidden " ,
215 whiteSpace : " nowrap " ,
216 fontSize : ’ clamp (16 px , 2 vw
, 20 px ) ’ , // Ajustare a u t o m a t a dimensiunii textului
217 }}
218 >
219 { car . title }
220 </ Typography >
221 < Typography variant = " h5 " >
222 { car . price } { car . currency }
223 </ Typography >
224 < Box sx ={{ marginTop : ’ auto ’ ,
marginBottom : ’ -15 px ’}} >
225 { car . year && car . mileage &&
226 < Typography
227 color = " textSecondary "
228 flex ={1}
229 >
230 < SpeedIcon
231 sx ={{ fontSize : ’ 1.2
rem ’ , color : ’ white ’ }}
232 / > { car . year } - { car .
mileage } km
233 </ Typography >}
234 </ Box >
235 </ CardContent >
236 </ Card >
237 </ Grid >
238 ))
239 ) : (
240 < Grid item xs ={12} >
241 < Typography variant = " h6 " color = " textSecondary "
> No cars available . </ Typography >
242 </ Grid >
243 )}
244 </ Grid >
245 );
246 };
247
248 export default MainPage ;
Listing 5.2: Componenta Home - pagina principală
19
Chapter 6
Stilizare s, i UI cu MUI
s, i CSS
6.1 Folosirea componentelor MUI
Pentru realizarea interfet, ei grafice a aplicat, iei, a fost utilizată bib-
lioteca Material UI (MUI), care oferă componente predefinite mod-
erne, responsivitate s, i suport pentru theming. Componentele folosite
ı̂n mod activ ı̂n aplicat, ie includ:
20
9 color : ’ hsl (0 , 3.00% , 73.70%) ’ ,
10 borderColor : ’ hsl (0 , 3.00% , 73.70%) ’
11 }}
12 >
13 Reset
14 </ Button >
Listing 6.1: Exemplu de Button personalizat MUI
21
Separatoarele stilizate ı̂ntre grupurile de filtre folosesc combinat, ia de
Box, Divider s, i Icon:
1 < Box display ={ ’ flex ’} > < PlayArrowIcon sx ={{ color : ’ rgb (160 , 160 ,
160) ’ }}/ >
2 < Divider
3 sx ={{
4 width : " 95% " ,
5 marginTop : ’ 10 px ’ ,
6 marginLeft : ’ -10 px ’ ,
7 height : ’5 px ’ ,
8 backgroundColor : ’ rgb (160 , 160 , 160) ’ ,
9 marginBottom : ’ 10 px ’
10 }}
11 />
12 </ Box >
22
Chapter 7
Comunicarea cu
Backend-ul
7.1 Configurat, ia API
Aplicat, ia comunică cu un backend REST API pentru obt, inerea s, i
filtrarea anunt, urilor auto. Endpoint-urile sunt definite ı̂n fis, iere sep-
arate s, i sunt accesate folosind biblioteca axios.
23
6 };
Listing 7.1: Exemplu de fetch cu axios s, i query string
1 const p o s t F i l t e r e d L i s t i n g s = async ( filters ) = > {
2 const response = await axios . post ( ’/ api / listings / filter ’ ,
filters ) ;
3 return response . data ;
4 };
Listing 7.2: Exemplu de POST cu body JSON
7.4 Concluzie
Combinat, ia dintre MUI, CSS personalizat s, i interact, iunea cu API-ul
backend permite dezvoltarea unei aplicat, ii robuste, moderne s, i us, or
de utilizat. Componenta SearchBar demonstrează clar cum poate
fi integrată logica UI, stilizarea s, i comunicarea cu backend-ul ı̂ntr-o
manieră modulară s, i eficientă.
24
7.5.1 Exemplu de autentificare s, i tratare erori
Componenta [Link] implementează logica de conectare a
unui utilizator. La apăsarea butonului Enter, este declans, at un apel
POST către endpoint-ul /api/auth/login. Codul verifică dacă datele
introduse sunt valide s, i tratează posibilele erori returnate de server,
cum ar fi:
25
1 for ( let elem of user . carIds ) {
2 const response = await axios . get ( ‘ http : // localhost :8080/ api / cars
/ $ { elem } ‘) ;
3 if ( response . data !== null ) {
4 validCarIds . add ( elem ) ;
5 }
6 }
7 await axios . patch ( ‘ http : // localhost :8080/ api / users / $ { user . id } ‘ , {
8 ... user ,
9 carIds : uniqueCarIds
10 }) ;
Listing 7.5: Filtrare ID-uri invalide s, i actualizare utilizator
26
13 }
14 };
15
16 ...
17
18 return (
19 <div >
20 { isLoading ? <p > Loading ... </ p > : (
21 < form onSubmit ={ handleSubmit } >
22 { /* formularul propriu - zis */ }
23 </ form >
24 )}
25 </ div >
26 );
7.5.6 Concluzie
Gestionarea erorilor ı̂n aplicat, ie se face ı̂n mai multe etape: ı̂nainte
de apelul ret, elei (validare input), ı̂n timpul răspunsului (verificare co-
duri HTTP) s, i după apel (verificare date). Feedback-ul vizual ajută
utilizatorul să ı̂nt, eleagă starea curentă a aplicat, iei, fie că este vorba
despre un succes, o eroare sau un proces ı̂n desfăs, urare. Aceste prac-
tici contribuie esent, ial la calitatea experient, ei de utilizare.
27
Chapter 8
Concluzii s, i perspective
8.1 Sumar
Lucrarea de fat, ă a avut ca obiectiv dezvoltarea unei aplicat, ii web
moderne pentru gestionarea utilizatorilor s, i a mas, inilor asociate aces-
tora, utilizând tehnologii actuale precum React pentru partea de fron-
tend s, i Spring Boot pentru backend.
Proiectul s-a concentrat pe următoarele direct, ii principale:
28
8.2 Posibile ı̂mbunătăt, iri
Des, i aplicat, ia este funct, ională s, i acoperă cerint, ele de bază, există mai
multe direct, ii ı̂n care aceasta poate fi ı̂mbunătăt, ită s, i extinsă:
29