Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Edwin950821/BodegaX/llms.txt

Use this file to discover all available pages before exploring further.

BodegaX operates on a straightforward two-role model — admin and user — where the role value is established at login, persisted in the browser’s session storage, and then propagated reactively throughout every component via AppService. Route guards prevent unauthenticated access entirely, while *ngIf bindings driven by the live role$ observable ensure each role sees only the UI elements relevant to their responsibilities in the warehouse.

How Roles Are Stored

When a user authenticates, the Spring Boot backend returns a session object that includes a role field. AppService.logIn() serialises this object into sessionStorage under the key "bodegax":
logIn(user: any) {
  sessionStorage.setItem('bodegax', JSON.stringify(user));
  this.isLogged.next(true);
  this.role.next(user.role);   // 'admin' | 'user'
  this.router.navigate(['/']);
}
On a hard page refresh the constructor re-reads sessionStorage['bodegax'] and restores both the logged-in state and the role before any component renders:
constructor(private router: Router) {
  var session = sessionStorage.getItem("bodegax");
  if (session) {
    this.isLogged.next(true);
    this.role.next(JSON.parse(session).role);
  } else {
    this.isLogged.next(false);
  }
}

The AppService Reactive State Pattern

AppService uses RxJS BehaviorSubject to hold the pieces of global state that all components share. Components and guards never read sessionStorage directly — they subscribe to the public observable getters instead.
@Injectable({ providedIn: 'root' })
export class AppService {

  // --- Private subjects (single source of truth) ---
  private isLogged    = new BehaviorSubject<boolean>(true);
  private role        = new BehaviorSubject<string>('client');
  private showSidebar = new BehaviorSubject<boolean>(true);

  // --- Public observables (read-only to consumers) ---
  get isLogged$(): Observable<boolean> {
    return this.isLogged.asObservable();
  }

  get role$(): Observable<string> {
    return this.role.asObservable();
  }

  get showSidebar$(): Observable<boolean> {
    return this.showSidebar.asObservable();
  }

  // --- Mutators ---
  logIn(user: any) {
    sessionStorage.setItem('bodegax', JSON.stringify(user));
    this.isLogged.next(true);
    this.role.next(user.role);
    this.router.navigate(['/']);
  }

  logOut() {
    sessionStorage.removeItem('bodegax');
    this.isLogged.next(false);
    this.role.next('client');
    this.router.navigate(['/login']);
  }
}
The isLogged BehaviorSubject is initialised to true at declaration time, but the AppService constructor immediately overrides it based on the actual contents of sessionStorage. The initial true value is never visible to subscribers because the constructor runs synchronously before any subscription can occur.

Route Guards

BodegaX ships two Angular route guards that use isLogged$ to protect the application’s route tree.

CheckLoginGuard

Applied to protected routes such as / and /settings. If isLogged$ emits false, the guard navigates to /login and returns false, blocking the route. Authenticated users pass through and receive true.

CheckNotLoginGuard

Applied to /login. If isLogged$ emits true (user is already authenticated), the guard redirects to / and returns false. Unauthenticated visitors receive true and can access the login page normally.

CheckLoginGuard Implementation

@Injectable({ providedIn: 'root' })
export class CheckLoginGuard implements CanActivate {
  constructor(private appSvc: AppService, private router: Router) {}

  canActivate(): Observable<boolean> {
    return new Observable<boolean>((o) => {
      this.appSvc.isLogged$.subscribe(r => {
        if (r == false) {
          this.router.navigate(['/login']);
          o.next(false);
          o.complete();
        } else {
          o.next(true);
          o.complete();
        }
      });
    });
  }
}

CheckNotLoginGuard Implementation

@Injectable({ providedIn: 'root' })
export class CheckNotLoginGuard implements CanActivate {
  constructor(private appSvc: AppService, private router: Router) {}

  canActivate(): Observable<boolean> {
    return new Observable<boolean>((o) => {
      this.appSvc.isLogged$.subscribe(r => {
        if (r == true) {
          this.router.navigate(['']);
          o.next(false);
          o.complete();
        } else {
          o.next(true);
          o.complete();
        }
      });
    });
  }
}

Role-Based Feature Matrix

Components subscribe to role$ and use Angular’s *ngIf directive to conditionally render UI elements. The table below maps each major feature to the role that can access it.
FeatureAdmin (admin)User (user)
View Settings page (/settings)
Create / edit / delete users
Terminar Jornada (close the workday)
View all clients’ order history
Solicitar Caja (request a crate)
View own history
Self-register at /register
View inventory / product listing

Example Conditional Rendering

A typical admin-only button in a BodegaX template looks like:
<!-- Only rendered when the active role is 'admin' -->
<button *ngIf="role == 'admin'" mat-raised-button color="primary" (click)="terminar()">
  <img src="/assets/terminar.png"> Terminar Jornada
</button>

<!-- Only rendered for regular users -->
<button *ngIf="role == 'user'" mat-raised-button color="primary" (click)="solicitarCaja()">
  Solicitar Caja
</button>
The component class subscribes to role$ during initialisation and stores the emitted value in a local property:
role = '';

ngOnInit(): void {
  this.appSvc.role$.subscribe(r => {
    this.role = r;  // 'admin' | 'user'
  });
}

Role Assignment Rules

There is no in-app UI to elevate a user account to admin. The role must be set directly in the PostgreSQL database (by updating the role column on the relevant row in the users table) or via a backend-level script or migration. BodegaX does not expose an admin-promotion endpoint in the current version.
Every account created through /register or the Settings page dialog receives role: 'user' — it is hard-coded in the POST /admin/create request body on the frontend. There is no way to self-register as an admin.
Because sessionStorage is client-side, a technically sophisticated user could manually edit the role field in sessionStorage['bodegax']. However, all sensitive backend endpoints should validate the role from the JWT or server-side session — the Angular role check is a UI convenience, not a security boundary. Ensure your Spring Boot controllers enforce role-based access control independently.
New users created via /register always receive role='user' by default. To grant admin privileges, the role field must be updated directly in the PostgreSQL database or through a dedicated backend operation — there is no in-app promotion workflow in the current version of BodegaX.

Session Lifecycle Summary

Browser opens BodegaX


AppService constructor reads sessionStorage['bodegax']

  ┌────┴────┐
  │ Found?  │
  └────┬────┘
       │ Yes → isLogged.next(true), role.next(session.role)
       │ No  → isLogged.next(false)


Route guard reads isLogged$ observable

  ┌────┴──────────────┐
  │ Accessing /login? │
  └────┬──────────────┘
       │ Logged in  → redirect to /
       │ Not logged → allow /login

  ┌────┴──────────────┐
  │ Accessing /home?  │
  └────┬──────────────┘
       │ Logged in  → allow /home
       │ Not logged → redirect to /login

Build docs developers (and LLMs) love