Volunteerish este o aplicație web NextJS, construită folosind Firebase pentru autentificare și baza de date, care conectează direct, fără intermediari, oamenii care au nevoie de ajutor cu cei care sunt dispuși să facă să ajute.
- Comutare mod luminos / mod întunecat
- Suport pentru limbi multiple
- Suport pentru mărimea fontului
- Disponibil pe mai multe platforme
- Mesagerie integrată în aplicație
- Optimizat pentru SEO
- Mobile friendly
Pagina de autentificare oferă comutarea modului întunecat, schimbarea limbii și opțiunea de recuperare a parolei uitate.
Pagina de înregistrare oferă și ea opțiunea de a comuta modul întunecat și de a schimba limba. Atât pagina de autentificare, cât și pagina de înregistrare au un sistem de eroare explicit, care oferă utilizatorului informațiile corecte despre ceea ce este greșit și unde.
Meniul Acasă conține informații precum persoane care au fost ajutate, persoane care sunt în prezent ajutate și anunțuri postate de utilizator. Există, de asemenea, informații despre numărul de puncte (a se vedea mai multe în secțiunea magazinului) și numărul de persoane ajutate.
În secțiunea de anunțuri, utilizatorii pot răspunde la cererile altor persoane de ajutor, precum și să posteze cererile de ajutor. În partea de sus a meniului se află un filtru cu care utilizatorii pot filtra anunțurile în funcție de: țară, stat, oraș, dificultate, data publicării. De asemenea, în colțul din dreapta jos se află butonul cu care utilizatorii pot adăuga anunțuri de ajutor.
Când apăsați butonul, un meniu se va deschide acolo unde se va completa, după cum caz, cu descrierea anunțului, dificultatea acestuia și categoria din care face parte anunțul. Detalii precum țara, statul și orașul sunt introduse automat din datele furnizate de utilizator la înregistrare și pot fi modificate.
În secțiunea magazinului există un mini magazin cu produse personalizate cu logo -ul și sloganul nostru sau cu alte accesorii sau articole vestimentare. Acestea pot fi cumpărate cu punctele obținute atunci când utilizatorul ajută o persoană.
Punctele sunt calculate în funcție de categoria din care aparțin anunțurile și dificultatea acesteia.
În coșul de cumpărături puteți vedea toate produsele adăugate, gestionați -le cantitatea sau le eliminați din coș. Aici puteți vedea câte produse aveți în coș, costul total al acestora, punctele disponibile și dacă le puteți cumpăra sau nu.
În pagina produsului puteți vedea informații despre preț, dacă produsul este în stoc, puteți vizualiza câteva imagini ale produsului și puteți citi descrierea acestuia.
Platforma are, de asemenea, un sistem de achiziții (numai la nivelul bazei de date), unde când ați cumpărat ceva, acesta este salvat în baza de date sub formă de comenzi. Puteți vizualiza ce produse ați cumpărat, cantitatea lor și când au fost făcute cumpărate.
Category | Easy | Medium | Hard |
---|---|---|---|
Cumpărături alimentare | 10 | 25 | 40 |
Meditații școlare | 40 | 50 | 60 |
Cumpărături | 10 | 10 | 25 |
Curățenie | 20 | 35 | 50 |
Plimbat | 15 | 30 | 45 |
Gătit | 25 | 45 | 60 |
Plata facturilor | 15 | 20 | 30 |
Suport emoțional | 20 | 40 | 60 |
Muncă fizică | 40 | 60 | 80 |
Muncă solicitantă | 40 | 60 | 80 |
În secțiunea Mesaje, utilizatorii pot comunica între ei prin mesaje text. Conversațiile pot fi inițiate fie de către persoana care solicită ajutor, fie de către persoana care ajută, din pagina de mesaje.
Mesajele sunt transmise instantaneu prin baza de date în timp real. Oferim caracteristici, cum ar fi defilarea în jos și încărcarea paginată.
Pagina Setări este împărțită în 4 pagini: Setările aplicației, Setări contului, Ajutor și asistență și Achiziții.
În setările aplicației puteți comuta între modul întuneric și lumină, puteți schimba limba aplicației sau puteți schimba dimensiunea fontului, pentru a -l face mai mic sau mai mare.
În această pagină, utilizatorul poate schimba informații precum numele, imaginea de profil, e -mailul, își poate verifica e -mailul sau poate schimba adresa sa.
Pagina de ajutor oferă răspunsuri pentru cele mai frecvente întrebări.
Pentru securitatea bazei de date, Firebase îmi permite să creez anumite reguli personalizate pentru a limita atât accesul, cât și cererile la baza de date.
Nimeni nu are acces la document cu datele personale ale unui utilizator decât dacă deține documentul, el ajută respectivul sau este ajutat de respectivul respectiv. În acest fel, limităm accesul la datele personale. De asemenea, același principiu se aplică mesajelor.
Client: React, NextJS, NextUI
Server: Node, Firebase
- NextJS - Am folosit NextJS datorită performanței sale mult mai bune decât React, prin SSR care îmi permite să fac solicitare la baza de date pe server, reducând timpul pe care utilizatorul îl așteaptă să fie livrate datele.
- NextUI - Biblioteca de componente folosite pentru a face proiectarea și responsivitatea aplicației.
- SwiperJS - Am folosit acest framework JS pentru a crea meniul „Creați cont nou” care presupune utilizatorul să parcurgă 6 diapozitive și pe pagina unui produs, unde îl folosesc să creez caruselul pentru imagini.
- Firebase - Pentru autentificare, stocarea fișierelor, mesajelor și datelor utilizatorilor.
- SASS - Pre-processor pentru CSS.
- Vercel - Pentru deployment.
- Undraw - Toate SVG -urile din aplicație au fost preluate de pe acest site.
- Printful - Am folosit platforma lor pentru a crea produse personalizate pentru magazin. Produsele au fost luate împreună cu pozele și descrierile lor.
- SVGR - Folosit pentru a utiliza direct SVG -uri în React.
- Nookies - Folosit pentru a gestiona cookie-urile pentru autentificare în NextJS.
- React-Icons - Iconițe.
- Country State City - Baza de date pentru gestionarea locației precisă și actualizată a utilizatorilor (țară, stat și oraș).
- Numbers abbreviation - Am folosit acest gist pentru o prelucrearea numerelor mari
export function abbreviateNumber(number) {
var s = ["", "K", "M", "B", "T", "P", "E"];
const tier = (Math.log10(number) / 3) | 0;
if (tier == 0) return number;
const suffix = s[tier];
const scale = Math.pow(10, tier * 3);
const scaled = number / scale;
return scaled.toFixed(1) + suffix;
}
- Loading spinner - Am folosit această postare pentru loading spinner
const Loading = () => {
return (
<section className={styles.wrapper}>
<div className={`${styles.square} ${styles.r0}`}></div>
<div className={`${styles.square} ${styles.r1}`}></div>
<div className={`${styles.square} ${styles.r2}`}></div>
<div className={`${styles.square} ${styles.r3}`}></div>
<div className={`${styles.square} ${styles.r4}`}></div>
<div className={`${styles.square} ${styles.r5}`}></div>
<div className={`${styles.square} ${styles.r6}`}></div>
<div className={`${styles.square} ${styles.r7}`}></div>
<div className={`${styles.square} ${styles.r8}`}></div>
<div className={`${styles.square} ${styles.r9}`}></div>
<div className={`${styles.square} ${styles.r10}`}></div>
<div className={`${styles.square} ${styles.r11}`}></div>
<div className={`${styles.square} ${styles.r12}`}></div>
<div className={`${styles.square} ${styles.r13}`}></div>
<div className={`${styles.square} ${styles.r14}`}></div>
<div className={`${styles.square} ${styles.r15}`}></div>
<div className={`${styles.square} ${styles.r16}`}></div>
<div className={`${styles.square} ${styles.r17}`}></div>
</section>
);
};
.wrapper {
position: absolute;
inset: 0;
background-color: var(--nextui-colors-background);
}
.square {
height: 100vh;
position: absolute;
inset: 0;
animation-duration: 5s;
animation-iteration-count: infinite;
border: 3px solid var(--nextui-colors-red500);
margin: auto;
height: 20rem;
width: 20rem;
&.r0 {
animation-name: r0;
}
&.r1 {
animation-name: r1;
}
&.r2 {
animation-name: r2;
}
&.r3 {
animation-name: r3;
}
&.r4 {
animation-name: r4;
}
&.r5 {
animation-name: r5;
}
&.r6 {
animation-name: r6;
}
&.r7 {
animation-name: r7;
}
&.r8 {
animation-name: r8;
}
&.r9 {
animation-name: r9;
}
&.r10 {
animation-name: r10;
}
&.r11 {
animation-name: r11;
}
&.r12 {
animation-name: r12;
}
&.r13 {
animation-name: r13;
}
&.r14 {
animation-name: r14;
}
&.r15 {
animation-name: r15;
}
&.r16 {
animation-name: r16;
}
&.r17 {
animation-name: r17;
}
@keyframes r0 {
50% {
border-radius: 50%;
-webkit-transform: rotate(270deg);
transform: rotate(270deg);
height: 6rem;
}
}
@keyframes r1 {
50% {
border-radius: 50%;
-webkit-transform: rotate(260deg);
transform: rotate(260deg);
height: 6rem;
}
}
@keyframes r2 {
50% {
border-radius: 50%;
-webkit-transform: rotate(250deg);
transform: rotate(250deg);
height: 6rem;
}
}
@keyframes r3 {
50% {
border-radius: 50%;
-webkit-transform: rotate(240deg);
transform: rotate(240deg);
height: 6rem;
}
}
@keyframes r4 {
50% {
border-radius: 50%;
-webkit-transform: rotate(230deg);
transform: rotate(230deg);
height: 6rem;
}
}
@keyframes r5 {
50% {
border-radius: 50%;
-webkit-transform: rotate(220deg);
transform: rotate(220deg);
height: 6rem;
}
}
@keyframes r6 {
50% {
border-radius: 50%;
-webkit-transform: rotate(210deg);
transform: rotate(210deg);
height: 6rem;
}
}
@keyframes r7 {
50% {
border-radius: 50%;
-webkit-transform: rotate(200deg);
transform: rotate(200deg);
height: 6rem;
}
}
@keyframes r8 {
50% {
border-radius: 50%;
-webkit-transform: rotate(190deg);
transform: rotate(190deg);
height: 6rem;
}
}
@keyframes r9 {
50% {
border-radius: 50%;
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
height: 6rem;
}
}
@keyframes r10 {
50% {
border-radius: 50%;
-webkit-transform: rotate(170deg);
transform: rotate(170deg);
height: 6rem;
}
}
@keyframes r11 {
50% {
border-radius: 50%;
-webkit-transform: rotate(160deg);
transform: rotate(160deg);
height: 6rem;
}
}
@keyframes r12 {
50% {
border-radius: 50%;
-webkit-transform: rotate(150deg);
transform: rotate(150deg);
height: 6rem;
}
}
@keyframes r13 {
50% {
border-radius: 50%;
-webkit-transform: rotate(140deg);
transform: rotate(140deg);
height: 6rem;
}
}
@keyframes r14 {
50% {
border-radius: 50%;
-webkit-transform: rotate(130deg);
transform: rotate(130deg);
height: 6rem;
}
}
@keyframes r15 {
50% {
border-radius: 50%;
-webkit-transform: rotate(120deg);
transform: rotate(120deg);
height: 6rem;
}
}
@keyframes r16 {
50% {
border-radius: 50%;
-webkit-transform: rotate(110deg);
transform: rotate(110deg);
height: 6rem;
}
}
@keyframes r17 {
50% {
border-radius: 50%;
-webkit-transform: rotate(100deg);
transform: rotate(100deg);
height: 6rem;
}
}
}