From e54e0e17c2cc3d257891415f72aa1e3c8ba119c5 Mon Sep 17 00:00:00 2001 From: "Enrique R. (Basi)" <90209339+Basiiii@users.noreply.github.com> Date: Tue, 11 Nov 2025 21:40:46 +0000 Subject: [PATCH] docs: finalize for archive Updated and finalized documentation explaining the project ready to be archived. --- README.md | 89 +++++++++++++-- .../ESI_PDS_ALM_20242025_01.xlsx | Bin 0 -> 80329 bytes backend/README.md | 37 +++++- backend/vizzy-backend/README.md | 105 ++++++++++-------- frontend/README.md | 40 +++++-- frontend/vizzy/README.md | 89 +++++++-------- 6 files changed, 246 insertions(+), 114 deletions(-) create mode 100644 archive-resources/ESI_PDS_ALM_20242025_01.xlsx diff --git a/README.md b/README.md index cbd7646d..c1f62367 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,86 @@ # Vizzy -**Vizzy** is a community-driven platform for buying, selling, trading, and renting items locally, promoting sustainable and affordable resource sharing. +> Archived full-stack marketplace elevating local buying, selling, trading, and renting for a more circular economy. -### Goals +## Project Status -* Encourage conscious consumption -* Reduce waste through reuse and rental -* Foster community cooperation +This repository is now archived. The production deployment has been shut down, but the codebase remains a reference for our team project that combined user-centric design with a scalable architecture. -### Project Resources +## Overview -* πŸ“Œ [Jira Board](https://basigraphics.atlassian.net/jira/software/projects/VIZZY/boards/1/backlog) -* πŸ“Š [Project Plan (Excel)](https://ipcapt-my.sharepoint.com/:x:/g/personal/nfr_ipca_pt/Eb29k3z-aWRGisXGIZopL6IBy5l5QZjY8SjG1iWQDl6c7w) -* πŸ’» [GitHub Repository](https://github.com/Basiiii/Vizzy) +Vizzy connected neighbourhoods through a community marketplace that balanced affordability with sustainability. We designed the experience for fast browsing, secure transactions, and trustworthy user interactions while keeping the platform simple enough to operate in multiple cities. + +## Demo + + + Vizzy product trailer + + +## Architecture at a Glance + +The system is structured as a modern TypeScript monorepo: + +* **Frontend** – Next.js (App Router) with Tailwind CSS, Radix UI, and server-side rendering for SEO-friendly browsing and dynamic, internationalized layouts. +* **Backend** – NestJS service exposing a REST API with Swagger documentation, JWT-based authentication, input validation via Zod, and background processing with queues and schedulers. +* **Data & Auth** – Supabase handled Postgres storage, role-based access, and token verification. Redis was introduced for caching, throttling, and session workflows to keep responses low-latency. +* **Infrastructure** – Deployed as containerized services with environment-driven configuration and centralized logging through Winston. + +## Highlights + +* End-to-end marketplace flows: listings, wishlists, offers, rentals, and messaging. +* Secure auth lifecycle with Supabase-issued JWTs, refresh tokens, and access revocation hooks. +* Redis-backed caching, rate limiting, and job queues to support surges in local demand. +* Media uploads with image processing pipelines for responsive asset delivery. +* City-aware search, filtering, and localization powered by geocoding APIs. +* Accessibility-first UI with component primitives tested across themes and devices. + +## Tech Stack + +| Layer | Technologies | +| ---------- | ------------ | +| Frontend | Next.js 15, React 19, Tailwind CSS 4, Radix UI, React Hook Form, Zod | +| Backend | NestJS 11, Express 5, Supabase SDK, ioredis, Swagger, Winston | +| Tooling | TypeScript 5, ESLint 9, Prettier, TurboPack dev server | +| Data & Ops | Supabase (Postgres + Auth), Redis, Nodemailer, Geocoding APIs | + +## Quality & Testing + +* Automated unit, integration, and end-to-end suites via Jest with coverage tracking. +* Dedicated performance and load testing against key endpoints to validate Redis caching benefits. +* Linting, type checking, and format enforcement wired into CI to keep pull requests healthy. +* GitHub Actions workflows gate pull requests with linting, test runs, and coverage reporting. + +## Getting Started Locally + +> The hosted instance is no longer active. To explore the project, run it locally. + +1. **Requirements** – Node.js 20+, npm, Redis (local or cloud), Supabase project credentials. +2. **Install dependencies**: + ```bash + npm install + ``` +3. **Configure environment** – Copy the variables from [`backend/vizzy-backend/README.md`](backend/vizzy-backend/README.md) into a `.env` file and supply your Supabase, Redis, and SMTP secrets. +4. **Start the stack**: + ```bash + npm run dev + ``` + This runs the Next.js frontend and NestJS backend concurrently. + +## Repository Structure + +``` +β”œβ”€β”€ frontend/ +β”‚ β”œβ”€β”€ vizzy/ # Next.js application +β”‚ └── README.md # Frontend-specific setup notes +β”œβ”€β”€ backend/ +β”‚ β”œβ”€β”€ vizzy-backend/ # NestJS API service +β”‚ └── README.md # Backend environment and API docs +β”œβ”€β”€ package.json # Monorepo scripts to run both services +└── README.md # You are here +``` + +## Core Contributors + +- [Enrique George Rodrigues](https://github.com/Basiiii) +- [JosΓ© AntΓ³nio da Cunha Alves](https://github.com/zealves99) +- [Diogo Araujo Machado](https://github.com/DiogoMachado04) diff --git a/archive-resources/ESI_PDS_ALM_20242025_01.xlsx b/archive-resources/ESI_PDS_ALM_20242025_01.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..301c42812acc38c8cb862b040b05782d763b0bb0 GIT binary patch literal 80329 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⋙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 Service layer responsible for Vizzy's API, authentication, and integrations. -## Project Structure +## Overview -- `vizzy-backend/` - NestJS backend application +The backend folder hosts the NestJS code that powered the Vizzy marketplace. Although the deployment has been shut down, the implementation is preserved for reference during future portfolio reviews or case studies. + +## Structure + +``` +vizzy-backend/ # NestJS 11 service with modules for auth, marketplace, messaging, etc. +└── README.md # Detailed setup instructions, environment variables, and testing guides +``` + +## Highlights + +* Supabase-backed JWT auth with role-aware guards and refresh token flows. +* Redis caching, throttling, and job queues to keep responses fast under load. +* Swagger docs exposed at `/api` for rapid iteration with frontend and QA teams. + +## Getting Started + +To boot the backend locally: + +1. Install dependencies from this directory: + ```bash + npm install --prefix vizzy-backend + ``` +2. Copy the environment template from `vizzy-backend/README.md` and fill in Supabase, Redis, SMTP, and geocoding credentials. +3. Start the service: + ```bash + npm run start:dev --prefix vizzy-backend + ``` + +Refer to [`vizzy-backend/README.md`](vizzy-backend/README.md) for the full breakdown of modules, scripts, and test suites. diff --git a/backend/vizzy-backend/README.md b/backend/vizzy-backend/README.md index 86cc3e7b..0500faa9 100644 --- a/backend/vizzy-backend/README.md +++ b/backend/vizzy-backend/README.md @@ -1,66 +1,81 @@ -# Vizzy Backend API +# Vizzy Backend API (Archived) -

- Nest Logo -

+> NestJS service powering Vizzy's marketplace workflows, authentication, and integrations. -## Description +## Status -Vizzy Backend API is built with [NestJS](https://github.com/nestjs/nest), a progressive Node.js framework for building efficient and scalable server-side applications. +The backend is no longer deployed, but the codebase remains as a reference for the architecture and patterns we implemented during the Vizzy project. -## Installation +## What This Service Handles -```bash -$ npm install -``` +* RESTful API for marketplace features: listings, rentals, messaging, and wishlists. +* JWT authentication pipeline backed by Supabase roles and refresh tokens. +* Redis caching for hot queries, rate limiting, and background job orchestration. +* Media uploads with Sharp transformations and secure storage hand-offs. +* Swagger/OpenAPI docs exposed via `/api` for consumers and QA. -## Running the app +## Tech Stack -```bash -# development -$ npm run start +| Concern | Tools | +| ----------- | ----- | +| Framework | NestJS 11, Express 5 | +| Data & Auth | Supabase (Postgres + Auth), Redis | +| Messaging | Nodemailer SMTP, Geocoding APIs | +| Observability | Winston logger, Nest Throttler | -# watch mode -$ npm run start:dev +## Local Setup -# production mode -$ npm run start:prod +1. Install dependencies: + ```bash + npm install + ``` +2. Use the template below and copy into `.env` with your Supabase, Redis, and SMTP credentials. +3. Run the server: + ```bash + npm run start:dev + ``` +4. Visit `http://localhost:5000/api` for auto-generated Swagger docs. + +### Environment Variables + +``` +PORT= +FRONTEND_URL= +SUPABASE_URL= +SUPABASE_SERVICE_ROLE_KEY= +SUPABASE_ANON_KEY= +JWT_SECRET= +REDIS_HOST= +REDIS_PORT= +REDIS_PASSWORD= +SMTP_USER= +SMTP_PASSWORD= +GEOCODING_BASE_API_URL= +GEOCODING_API_KEY= ``` -## Test +## Testing ```bash # unit tests -$ npm run test +npm run test -# test coverage -$ npm run test:cov -``` - -## Environment Setup - -Create a `.env` file in the root directory with the following variables: +# integration & e2e suites +npm run test:integration +npm run test:e2e +# coverage report +npm run test:cov ``` -PORT=your-port -FRONTEND_URL=your-frontend-url -SUPABASE_URL=your-supabase-url -SUPABASE_SERVICE_ROLE_KEY=your-service-role-key -SUPABASE_ANON_KEY=your-anon-key -JWT_SECRET=your-secret-key -REDIS_HOST=your-redis-host -REDIS_PORT=your-redis-port -REDIS_PASSWORD=your-redis-password -SMTP_USER=your-smtp-username -SMTP_PASSWORD=your-smtp-password -GEOCODING_BASE_API_URL=your-geocoding-api-base-url -GEOCODING_API_KEY=your-geocoding-api-key -``` - -## API Documentation -When running the server, you can access the Swagger API documentation at: +## Project Layout ``` -http://localhost:5000/api +src/ +β”œβ”€β”€ auth/ # JWT guards, strategies, Supabase integration +β”œβ”€β”€ marketplace/ # Listings, rentals, offers modules +β”œβ”€β”€ messaging/ # Conversations, notifications +β”œβ”€β”€ rate-limit/ # Redis-based throttling +β”œβ”€β”€ common/ # DTOs, interceptors, utils +└── main.ts # Nest bootstrap ``` diff --git a/frontend/README.md b/frontend/README.md index 60e9a03d..4e93adbb 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,17 +1,37 @@ -# Vizzy Frontend +# Vizzy Frontend (Archived) -This directory contains the frontend application for Vizzy, built with Next.js. +> Next.js client powering Vizzy's marketplace experience. -## Project Structure +## Overview -The frontend is organized as follows: +This folder contains the source for Vizzy's user-facing web application. The deployment is retired, but the code remains as a reference implementation of our capstone UI. -- `vizzy/` - The main Next.js application - - `app/` - Application routes and pages - - `components/` - Reusable UI components - - `lib/` - Utility functions, API clients, and services - - `public/` - Static assets +## Structure + +``` +vizzy/ # Next.js 15 application +β”œβ”€β”€ app/ # App Router pages, layouts, and server components +β”œβ”€β”€ components/ # Shared UI primitives +β”œβ”€β”€ lib/ # API clients, services, utilities +└── public/ # Static assets +``` + +## Highlights + +* Server-rendered marketplace flows with authenticated dashboards. +* Localization, theming, and accessibility baked into the component system. +* Tight integration with Supabase-backed APIs for listings, messaging, and rentals. ## Getting Started -To work with the frontend application, navigate to the `vizzy` directory and follow the instructions in its README. +1. Install dependencies and run the dev server from the repo root: + ```bash + npm install + npm run dev + ``` +2. Or work directly in this package: + ```bash + npm install --prefix vizzy + npm run dev --prefix vizzy + ``` +3. See [`vizzy/README.md`](vizzy/README.md) for deeper details on scripts, tech stack, and environment configuration. diff --git a/frontend/vizzy/README.md b/frontend/vizzy/README.md index 9088cea7..9e74b9d8 100644 --- a/frontend/vizzy/README.md +++ b/frontend/vizzy/README.md @@ -1,61 +1,58 @@ -# Vizzy - Community Sharing Platform +# Vizzy Frontend (Archived) -Vizzy is a community platform designed to facilitate buying, selling, trading, and renting items between people in the same community, promoting a practical, economical, and sustainable way to share resources. Through Vizzy, users can acquire second-hand products, exchange items, or even rent tools and other equipment. +> Next.js 15 application delivering Vizzy's responsive marketplace experience. -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +## Status -## Objective +The hosted frontend has been sunset. The code remains a reference for the UI/UX we crafted during the Vizzy project. -The platform aims to create a sharing network within a community where members can benefit from more conscious consumption, saving money and resources while promoting sustainability and cooperation. +## Highlights -## Getting Started - -First, install the dependencies: +* App Router architecture with server-side rendering and route-level layouts. +* Authenticated dashboards, wishlists, and messaging driven by Supabase tokens. +* Geolocation-aware browse and search flows, localized with `next-intl`. +* Accessible component system built with Radix primitives and themed via `next-themes`. -```bash -npm install -``` +## Tech Stack -Then, run the development server: +| Concern | Tools | +| ----------- | ----- | +| Framework | Next.js 15, React 19 | +| Styling | Tailwind CSS 4, Radix UI, class-variance-authority | +| Forms | React Hook Form, Zod | +| Data | Supabase SSR helpers, custom API client | -```bash -npm run dev -``` +## Getting Started -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +1. Install dependencies: + ```bash + npm install + ``` +2. Set the required environment variables (see `.env.example`) to point at a running backend and Supabase instance. +3. Run the development server: + ```bash + npm run dev + ``` +4. Visit `http://localhost:3000` to explore the app. ## Project Structure -- `app/` - Application routes and pages using Next.js App Router -- `components/` - Reusable UI components -- `lib/` - Utility functions and services - - `api/` - API client functions - - `constants/` - Application constants - - `services/` - Service functions - - `utils/` - Utility functions - -## Features - -- Authentication and user management -- Dashboard with data visualization -- Contact management -- Internationalization with next-intl -- Theme support with next-themes +``` +app/ # Routes, layouts, server components +components/ # Shared UI building blocks +lib/ +β”œβ”€β”€ api/ # REST client helpers +β”œβ”€β”€ constants/ # App-wide constants +β”œβ”€β”€ services/ # Domain services and side effects +└── utils/ # Misc utilities +public/ # Static assets +``` ## Scripts -- `npm run dev` - Start the development server with Turbopack -- `npm run build` - Build the application for production -- `npm run start` - Start the production server on port 4000 -- `npm run lint` - Run ESLint to check code quality - -## Technologies - -- Next.js 15 -- React 19 -- TypeScript -- Tailwind CSS -- Shadcn UI -- Recharts for data visualization -- Zod for schema validation -- React Hook Form for form handling +```bash +npm run dev # Start dev server with Turbopack +npm run build # Build for production +npm run start # Run production build (default port 4000) +npm run lint # ESLint checks +```