From bb05fb7747ce449992c5931bd245e8bf0f7b74f4 Mon Sep 17 00:00:00 2001 From: kusowl Date: Wed, 25 Feb 2026 19:04:00 +0530 Subject: [PATCH] feature: add product page - add UI - add dialog for preview selected images --- src/app/app.html | 10 ++- src/app/app.routes.ts | 6 ++ .../product/add-product/add-product.css | 0 .../product/add-product/add-product.html | 86 +++++++++++++++++++ .../product/add-product/add-product.spec.ts | 22 +++++ .../product/add-product/add-product.ts | 41 +++++++++ src/app/features/product/product.css | 0 src/app/features/product/product.html | 1 + src/app/features/product/product.routes.ts | 9 ++ src/app/features/product/product.spec.ts | 22 +++++ src/app/features/product/product.ts | 9 ++ .../components/image-input/image-input.css | 0 .../components/image-input/image-input.html | 29 +++++++ .../image-input/image-input.spec.ts | 22 +++++ .../components/image-input/image-input.ts | 26 ++++++ src/styles.css | 26 +++++- 16 files changed, 304 insertions(+), 5 deletions(-) create mode 100644 src/app/features/product/add-product/add-product.css create mode 100644 src/app/features/product/add-product/add-product.html create mode 100644 src/app/features/product/add-product/add-product.spec.ts create mode 100644 src/app/features/product/add-product/add-product.ts create mode 100644 src/app/features/product/product.css create mode 100644 src/app/features/product/product.html create mode 100644 src/app/features/product/product.routes.ts create mode 100644 src/app/features/product/product.spec.ts create mode 100644 src/app/features/product/product.ts create mode 100644 src/app/shared/components/image-input/image-input.css create mode 100644 src/app/shared/components/image-input/image-input.html create mode 100644 src/app/shared/components/image-input/image-input.spec.ts create mode 100644 src/app/shared/components/image-input/image-input.ts diff --git a/src/app/app.html b/src/app/app.html index 47869cd..b6a87ba 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -1,3 +1,7 @@ - - - +
+ +
+ +
+ +
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index d948d53..4e4ce05 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -12,4 +12,10 @@ export const routes: Routes = [ path: "", loadChildren: () => import("./features/auth/auth.routes").then((routes) => routes.AuthRoutes), }, + { + path: "products", + loadChildren: () => + import("./features/product/product.routes").then((routes) => routes.productRoutes), + // canActivate: [authGuard] + }, ]; diff --git a/src/app/features/product/add-product/add-product.css b/src/app/features/product/add-product/add-product.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/product/add-product/add-product.html b/src/app/features/product/add-product/add-product.html new file mode 100644 index 0000000..665ea7b --- /dev/null +++ b/src/app/features/product/add-product/add-product.html @@ -0,0 +1,86 @@ +
+

Add Product

+
+
+
+
+ Title + + +
+ +
+ Description + + +
+ +
+ Select category + + +
+
+
+
+ Image +
+ + + + + +
+
+ + + +
+
+ Product Preview +
+
+
+
+
+ +
+
+
+
diff --git a/src/app/features/product/add-product/add-product.spec.ts b/src/app/features/product/add-product/add-product.spec.ts new file mode 100644 index 0000000..6f894c3 --- /dev/null +++ b/src/app/features/product/add-product/add-product.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { AddProduct } from "./add-product"; + +describe("AddProduct", () => { + let component: AddProduct; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AddProduct], + }).compileComponents(); + + fixture = TestBed.createComponent(AddProduct); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/product/add-product/add-product.ts b/src/app/features/product/add-product/add-product.ts new file mode 100644 index 0000000..2417641 --- /dev/null +++ b/src/app/features/product/add-product/add-product.ts @@ -0,0 +1,41 @@ +import { Component, ElementRef, signal, ViewChild } from "@angular/core"; +import { ReactiveFormsModule } from "@angular/forms"; +import { ImageInput } from "../../../shared/components/image-input/image-input"; + +export interface ImageSelection { + id: string; + url: string; + file: File; +} + +@Component({ + selector: "app-add-product", + imports: [ReactiveFormsModule, ImageInput], + templateUrl: "./add-product.html", + styleUrl: "./add-product.css", +}) +export class AddProduct { + @ViewChild("imageDialog") imageDialog!: ElementRef; + activeImage = signal(null); + selectedImages = signal>({}); + + openPreview(image: ImageSelection) { + this.activeImage.set(image); + this.imageDialog.nativeElement.showModal(); + } + + confirmImage() { + // Add the current image to the selected images + const current = this.activeImage(); + if (current) { + this.selectedImages.update((images) => ({ ...images, [current.id]: current })); + } + console.log(this.selectedImages()); + this.closeDialog(); + } + + closeDialog() { + this.activeImage.set(null); + this.imageDialog.nativeElement.close(); + } +} diff --git a/src/app/features/product/product.css b/src/app/features/product/product.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/product/product.html b/src/app/features/product/product.html new file mode 100644 index 0000000..772b623 --- /dev/null +++ b/src/app/features/product/product.html @@ -0,0 +1 @@ +

product works!

diff --git a/src/app/features/product/product.routes.ts b/src/app/features/product/product.routes.ts new file mode 100644 index 0000000..c11716c --- /dev/null +++ b/src/app/features/product/product.routes.ts @@ -0,0 +1,9 @@ +import { Routes } from "@angular/router"; +import { AddProduct } from "./add-product/add-product"; + +export const productRoutes: Routes = [ + { + path: "create", + component: AddProduct, + }, +]; diff --git a/src/app/features/product/product.spec.ts b/src/app/features/product/product.spec.ts new file mode 100644 index 0000000..8a5b60f --- /dev/null +++ b/src/app/features/product/product.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { Product } from "./product"; + +describe("Product", () => { + let component: Product; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Product], + }).compileComponents(); + + fixture = TestBed.createComponent(Product); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/product/product.ts b/src/app/features/product/product.ts new file mode 100644 index 0000000..9c169a0 --- /dev/null +++ b/src/app/features/product/product.ts @@ -0,0 +1,9 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-product", + imports: [], + templateUrl: "./product.html", + styleUrl: "./product.css", +}) +export class Product {} diff --git a/src/app/shared/components/image-input/image-input.css b/src/app/shared/components/image-input/image-input.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/image-input/image-input.html b/src/app/shared/components/image-input/image-input.html new file mode 100644 index 0000000..5145705 --- /dev/null +++ b/src/app/shared/components/image-input/image-input.html @@ -0,0 +1,29 @@ +
+ @if (bgImageUrl) { + + } @else { + + } + +
diff --git a/src/app/shared/components/image-input/image-input.spec.ts b/src/app/shared/components/image-input/image-input.spec.ts new file mode 100644 index 0000000..14e3aec --- /dev/null +++ b/src/app/shared/components/image-input/image-input.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { ImageInput } from "./image-input"; + +describe("ImageInput", () => { + let component: ImageInput; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ImageInput], + }).compileComponents(); + + fixture = TestBed.createComponent(ImageInput); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/image-input/image-input.ts b/src/app/shared/components/image-input/image-input.ts new file mode 100644 index 0000000..027cab3 --- /dev/null +++ b/src/app/shared/components/image-input/image-input.ts @@ -0,0 +1,26 @@ +import { Component, EventEmitter, Input, Output } from "@angular/core"; +import { Camera, LucideAngularModule } from "lucide-angular"; +import { ImageSelection } from "../../../features/product/add-product/add-product"; + +@Component({ + selector: "app-image-input", + imports: [LucideAngularModule], + templateUrl: "./image-input.html", + styleUrl: "./image-input.css", +}) +export class ImageInput { + cameraIcon = Camera; + + @Output() imageSelected = new EventEmitter(); + @Input() id!: string; + @Input() bgImageUrl: string | undefined; + + handleFileSelect(event: Event) { + const input = event.target as HTMLInputElement; + if (input.files && input.files[0]) { + const file = input.files[0]; + const url = URL.createObjectURL(file); + this.imageSelected.emit({ id: this.id, url: url, file: file }); + } + } +} diff --git a/src/styles.css b/src/styles.css index 9123e1d..b38fa58 100644 --- a/src/styles.css +++ b/src/styles.css @@ -13,7 +13,7 @@ } body { - @apply bg-gray-100; + @apply bg-gray-100 antialiased m-0; } .wrapper { @@ -21,7 +21,7 @@ body { } .btn { - @apply rounded-full transition-all duration-200 font-medium ease-out flex justify-center active:translate-y-px disabled:opacity-50 disabled:cursor-not-allowed; + @apply rounded-xl py-2 transition-all duration-200 font-medium ease-out flex justify-center active:translate-y-px disabled:opacity-50 disabled:cursor-not-allowed; } .btn-ghost { @@ -39,6 +39,7 @@ body { .fieldset { @apply space-y-1; } + .fieldset-legend { @apply text-xs font-bold ml-2 text-gray-800; } @@ -51,6 +52,14 @@ body { @apply p-3 border border-gray-300 rounded-xl text-sm w-full; } +.input-image { + @apply rounded-xl border bg-teal-100 border-teal-500 border-dashed; +} + +.input-image-ghost { + @apply bg-gray-100 border-gray-300; +} + .dropdown { position-area: span-left bottom; @apply p-4 mt-2 border border-gray-300 shadow-lg rounded-xl space-y-2 text-gray-800; @@ -59,3 +68,16 @@ body { .dropdown li { @apply rounded-lg hover:bg-linear-to-r hover:from-teal-300 hover:to-transparent px-5 py-1; } + +h1, +h2, +h3, +h4, +h5, +h6 { + @apply font-space text-gray-800; +} + +h1 { + @apply text-3xl my-4 ml-2; +}