make favorite state persistant with api

This commit is contained in:
kusowl 2026-03-05 13:34:37 +05:30
parent b575b42f22
commit 7e1ecf35b9
10 changed files with 50 additions and 39 deletions

1
.rgignore Normal file
View File

@ -0,0 +1 @@
backend/

View File

@ -11,6 +11,7 @@ export interface ProductModel {
category: Category; category: Category;
productImages: string[]; productImages: string[];
updatedAt: string; updatedAt: string;
isFavorite: boolean;
} }
export interface ProductCollection extends PaginatedResponse<ProductModel> {} export interface ProductCollection extends PaginatedResponse<ProductModel> {}

View File

@ -1,8 +1,8 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from "@angular/core/testing";
import { FavoriteService } from './favorite-service'; import { FavoriteService } from "./favorite-service";
describe('FavoriteService', () => { describe("FavoriteService", () => {
let service: FavoriteService; let service: FavoriteService;
beforeEach(() => { beforeEach(() => {
@ -10,7 +10,7 @@ describe('FavoriteService', () => {
service = TestBed.inject(FavoriteService); service = TestBed.inject(FavoriteService);
}); });
it('should be created', () => { it("should be created", () => {
expect(service).toBeTruthy(); expect(service).toBeTruthy();
}); });
}); });

View File

@ -1,6 +1,6 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from "@angular/common/http";
import { inject, Injectable } from '@angular/core'; import { inject, Injectable } from "@angular/core";
import { API_URL } from '../tokens/api-url-tokens'; import { API_URL } from "../tokens/api-url-tokens";
export interface FavoriteResponse { export interface FavoriteResponse {
message: string; message: string;
@ -8,7 +8,7 @@ export interface FavoriteResponse {
} }
@Injectable({ @Injectable({
providedIn: 'root', providedIn: "root",
}) })
export class FavoriteService { export class FavoriteService {
http = inject(HttpClient); http = inject(HttpClient);

View File

@ -1,5 +1,9 @@
<div class="card flex flex-col relative cursor-pointer" (click)="goToProductDetails()"> <div class="card flex flex-col relative cursor-pointer" (click)="goToProductDetails()">
<app-favorite-button [productId]="product.id" class="absolute top-5 right-5" /> <app-favorite-button
[productId]="product.id"
[isFavorite]="product.isFavorite"
class="absolute top-5 right-5"
/>
<!--Product image--> <!--Product image-->
<div class="bg-gray-200 rounded-xl h-40"> <div class="bg-gray-200 rounded-xl h-40">
<img [src]="product.productImages[0]" alt="" class="object-cover rounded-xl w-full h-full" /> <img [src]="product.productImages[0]" alt="" class="object-cover rounded-xl w-full h-full" />

View File

@ -1,13 +1,13 @@
import { Component, inject, Input } from '@angular/core'; import { Component, inject, Input } from "@angular/core";
import { ProductModel } from '../../../../core/models/product.model'; import { ProductModel } from "../../../../core/models/product.model";
import { Router } from '@angular/router'; import { Router } from "@angular/router";
import { FavoriteButton } from '../../../../src/app/shared/components/favorite-button/favorite-button'; import { FavoriteButton } from "../../../../src/app/shared/components/favorite-button/favorite-button";
@Component({ @Component({
selector: 'app-product-card', selector: "app-product-card",
standalone: true, standalone: true,
imports: [FavoriteButton], imports: [FavoriteButton],
templateUrl: './product-card.html', templateUrl: "./product-card.html",
}) })
export class ProductCard { export class ProductCard {
readonly router = inject(Router); readonly router = inject(Router);

View File

@ -1,10 +1,10 @@
import { inject, Injectable } from '@angular/core'; import { inject, Injectable } from "@angular/core";
import { HttpClient } from '@angular/common/http'; import { HttpClient } from "@angular/common/http";
import { API_URL } from '../../../core/tokens/api-url-tokens'; import { API_URL } from "../../../core/tokens/api-url-tokens";
import { ProductCollection, ProductModel } from '../../../core/models/product.model'; import { ProductCollection, ProductModel } from "../../../core/models/product.model";
@Injectable({ @Injectable({
providedIn: 'root', providedIn: "root",
}) })
export class ProductService { export class ProductService {
http = inject(HttpClient); http = inject(HttpClient);

View File

@ -1,7 +1,13 @@
<!--Favorite button --> <!--Favorite button -->
<button <button
(click)="$event.stopPropagation(); toggleFavorite($event)" (click)="$event.stopPropagation(); toggleFavorite($event)"
class="transition-all duration-300 ease active:scale-80 hover:bg-gray-100 p-1 rounded-full" class="transition-all duration-300 ease active:scale-80 hover:bg-gray-100 p-1 rounded-full flex items-center justify-center"
> >
<lucide-angular [img]="HeartIcon" class="w-4 h-4 text-gray-500" /> <lucide-angular
[img]="HeartIcon"
[class.text-gray-500]="!isFavorite"
[class.text-red-400]="isFavorite"
[class]="isFavorite ? 'fill-red-500' : 'fill-none'"
class="w-4 h-4 transition-colors duration-200"
/>
</button> </button>

View File

@ -1,23 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from "@angular/core/testing";
import { FavoriteButton } from './favorite-button'; import { FavoriteButton } from "./favorite-button";
describe('FavoriteButton', () => { describe("FavoriteButton", () => {
let component: FavoriteButton; let component: FavoriteButton;
let fixture: ComponentFixture<FavoriteButton>; let fixture: ComponentFixture<FavoriteButton>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [FavoriteButton] imports: [FavoriteButton],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(FavoriteButton); fixture = TestBed.createComponent(FavoriteButton);
component = fixture.componentInstance; component = fixture.componentInstance;
await fixture.whenStable(); await fixture.whenStable();
}); });
it('should create', () => { it("should create", () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,16 +1,16 @@
import { Component, inject, Input } from '@angular/core'; import { Component, inject, Input } from "@angular/core";
import { HeartIcon, LucideAngularModule } from 'lucide-angular'; import { HeartIcon, LucideAngularModule } from "lucide-angular";
import { FavoriteService } from '../../../../../core/services/favorite-service'; import { FavoriteService } from "../../../../../core/services/favorite-service";
@Component({ @Component({
selector: 'app-favorite-button', selector: "app-favorite-button",
imports: [LucideAngularModule], imports: [LucideAngularModule],
templateUrl: './favorite-button.html', templateUrl: "./favorite-button.html",
styleUrl: './favorite-button.css', styleUrl: "./favorite-button.css",
}) })
export class FavoriteButton { export class FavoriteButton {
@Input({ required: true }) productId!: number; @Input({ required: true }) productId!: number;
@Input() isFavorite = true; @Input() isFavorite = false;
favoriteService = inject(FavoriteService); favoriteService = inject(FavoriteService);