Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ export const routes: Routes = [
loadComponent: () =>
import('./features/catalog/categories/categories').then((m) => m.CategoriesPageComponent),
},
{
path: 'catalog/manufacturers',
loadComponent: () =>
import('./features/catalog/manufacturer/manufacturers').then(
(m) => m.ManufacturersPageComponent,
),
},
{
path: 'compatibility/:sub',
loadComponent: () => import('./features/placeholder').then((m) => m.PlaceholderPageComponent),
Expand Down
2 changes: 1 addition & 1 deletion src/app/core/models/catetories/categories-default-query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export interface CategoryDefaultQuery {
page: number | null;
name: string | null;
search: string | null;
active: boolean | null;
per_page: number | null;
parent_id: string | null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface ManufacturerRequest {
name: string;
slug: string;
website: string;
active: boolean;
image: string | File | null;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface ManufacturerResponse {
export interface ManufacturerResponse extends Record<string, unknown> {
id: string;
name: string;
slug: string;
Expand Down
23 changes: 21 additions & 2 deletions src/app/core/services/manufacture-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { GeneralOptionQuery } from '../models/generals/general-option-query.mode
import { ManufacturerOption } from '../models/manufactureres/manufaturer-options.model';
import { getAllResponse } from '../models/generals/general-responses-list.model';
import { GeneralOption } from '../models/generals/general-options-response.model';
import { ManufacturerRequest } from '../models/manufactureres/manufacturer-request.model';

@Injectable({
providedIn: 'root',
Expand All @@ -17,8 +18,11 @@ export class ManufacturerService {
private api = environment.apiUrl;
private flag = 'manufacturer';

getAll() {
return this.http.get<PaginatedResponse<ManufacturerResponse>>(`${this.api}/${this.flag}`);
getAll(filters: Partial<GeneralOptionQuery>) {
const params = buildHttpParams(filters);
return this.http.get<PaginatedResponse<ManufacturerResponse>>(`${this.api}/${this.flag}`, {
params,
});
}

getOptions(filters: GeneralOptionQuery) {
Expand All @@ -37,4 +41,19 @@ export class ManufacturerService {
`${this.api}/${this.flag}/product/${productId}`,
);
}

createManufacturer(data: ManufacturerRequest) {
return this.http.post<getAllResponse<ManufacturerResponse>>(`${this.api}/${this.flag}`, data);
}

updateManufacturer(id: string, data: ManufacturerRequest) {
return this.http.put<getAllResponse<ManufacturerResponse>>(
`${this.api}/${this.flag}/${id}`,
data,
);
}

deleteManufacturer(id: string) {
return this.http.delete<getAllResponse<ManufacturerResponse>>(`${this.api}/${this.flag}/${id}`);
}
}
69 changes: 46 additions & 23 deletions src/app/features/catalog/categories/categories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import {
Component,
computed,
inject,
OnDestroy,
OnInit,
signal,
} from '@angular/core';
import { Router } from '@angular/router';
import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';

import { PageHeaderComponent } from '../../../design-system/page-header/page-header';
import { ToolbarComponent } from '../../../design-system/toolbar/toolbar';
import { SearchInputComponent } from '../../../design-system/input/search-input';
Expand Down Expand Up @@ -43,18 +47,22 @@ import { initialValuesPagination } from '../../../design-system/pagination/utils
templateUrl: './categories.html',
styleUrl: './categories.css',
})
export class CategoriesPageComponent implements OnInit {
export class CategoriesPageComponent implements OnInit, OnDestroy {
private router = inject(Router);
private toastService = inject(ToastService);
private readonly categoryService = inject(CategoryService);

private readonly perPage = signal<number>(25);
private readonly perPage = signal<number>(10);
private readonly currentPage = signal<number>(1);

readonly loading = signal<boolean>(false);
readonly searchQuery = signal<string>('');
readonly selectedStatus = signal<string>('');

// Subject para gerenciar o debounce da digitação no campo de busca
private searchSubject = new Subject<string>();
private searchSubscription?: Subscription;

// Category Form Drawer State
readonly isFormOpen = signal<boolean>(false);
readonly formMode = signal<'create' | 'edit' | 'view'>('create');
Expand Down Expand Up @@ -82,15 +90,33 @@ export class CategoriesPageComponent implements OnInit {

readonly totalItens = signal<number>(0);
query: CategoryDefaultQuery = {
name: null,
search: null,
active: null,
parent_id: null,
page: this.currentPage(),
per_page: this.perPage(),
};

ngOnInit(): void {
// 1. Busca inicial
this.getPaginationAllCategories(this.query);

// 2. Inscrição para escutar a busca com debounce de 400ms
this.searchSubscription = this.searchSubject
.pipe(debounceTime(400), distinctUntilChanged())
.subscribe((queryText) => {
this.currentPage.set(1);
this.query = {
...this.query,
search: queryText || null,
page: 1,
};
this.getPaginationAllCategories(this.query);
});
}

ngOnDestroy(): void {
this.searchSubscription?.unsubscribe();
}

getPaginationAllCategories(filters: CategoryDefaultQuery): void {
Expand All @@ -111,30 +137,24 @@ export class CategoriesPageComponent implements OnInit {
});
}

readonly filteredCategories = computed(() => {
const q = this.searchQuery().toLowerCase().trim();
const st = this.selectedStatus();

return this.allCategories().filter((c) => {
const matchesQ =
!q ||
c.name.toLowerCase().includes(q) ||
c.slug.toLowerCase().includes(q) ||
(c.description || '').toLowerCase().includes(q);
const matchesSt = !st || (st === 'active' ? c.active : !c.active);
return matchesQ && matchesSt;
});
});

onSearchChange(query: string): void {
this.query = { ...this.query, name: query };
this.searchQuery.set(query);
this.getPaginationAllCategories(this.query);
this.searchSubject.next(query);
}

onStatusChange(status: string): void {
this.query = { ...this.query, active: status === 'active' };
let activeValue: boolean | null = null;
if (status === 'active') activeValue = true;
if (status === 'inactive') activeValue = false;

this.selectedStatus.set(status);
this.currentPage.set(1);

this.query = {
...this.query,
active: activeValue,
page: 1,
};
this.getPaginationAllCategories(this.query);
}

Expand Down Expand Up @@ -165,13 +185,16 @@ export class CategoriesPageComponent implements OnInit {
resetFilters(): void {
this.searchQuery.set('');
this.selectedStatus.set('');
this.currentPage.set(1);

this.query = {
name: null,
search: null,
active: null,
parent_id: null,
page: this.currentPage(),
page: 1,
per_page: this.perPage(),
};
this.getPaginationAllCategories(this.query);
this.toastService.info('Filtros limpos', 'Todos os parâmetros de busca foram resetados.');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,35 @@ import {
signal,
computed,
effect,
inject
inject,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { ButtonComponent } from '../../../../design-system/button/button';
import { AppIconComponent } from '../../../../design-system/icon/app-icon';
import { DrawerComponent } from '../../../../design-system/drawer/drawer';
import { ManufacturerService } from '../../../../core/services/manufacturer';
import { ToastService } from '../../../../core/services/toast';
import {
Manufacturer,
CreateManufacturerPayload,
UpdateManufacturerPayload
} from '../../../../core/models/manufacturer';
import { ManufacturerRequest } from '../../../../core/models/manufactureres/manufacturer-request.model';
import { ManufacturerService } from '../../../../core/services/manufacture-service';
import { ManufacturerResponse } from '../../../../core/models/manufactureres/manufacturer-response.model';

@Component({
selector: 'app-manufacturer-form',
standalone: true,
imports: [
CommonModule,
ButtonComponent,
AppIconComponent,
DrawerComponent
],
imports: [CommonModule, ButtonComponent, AppIconComponent, DrawerComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './manufacturer-form.html',
styleUrl: './manufacturer-form.css'
styleUrl: './manufacturer-form.css',
})
export class ManufacturerFormComponent {
private manufacturerService = inject(ManufacturerService);
private toastService = inject(ToastService);

readonly isOpen = input<boolean>(false);
readonly mode = input<'create' | 'edit' | 'view'>('create');
readonly manufacturer = input<Manufacturer | null>(null);
readonly manufacturer = input<ManufacturerResponse | null>(null);

readonly closeForm = output<void>();
readonly manufacturerSaved = output<Manufacturer>();
readonly manufacturerSaved = output<ManufacturerResponse>();

// Reactive State Signals
readonly isSubmitting = signal<boolean>(false);
Expand Down Expand Up @@ -230,28 +222,31 @@ export class ManufacturerFormComponent {
}

if (!this.validateForm()) {
this.toastService.error('Formulário Inválido', 'Corrija os campos indicados antes de salvar.');
this.toastService.error(
'Formulário Inválido',
'Corrija os campos indicados antes de salvar.',
);
return;
}

this.isSubmitting.set(true);

const payload: CreateManufacturerPayload | UpdateManufacturerPayload = {
const payload: ManufacturerRequest = {
name: this.name().trim(),
slug: this.slug().trim(),
image: this.image().trim() || null,
website: this.website().trim() || null,
active: this.active()
image: this.image().trim(),
website: this.website().trim(),
active: this.active(),
};

if (this.mode() === 'edit' && this.manufacturer()?.id) {
const id = this.manufacturer()!.id;
this.manufacturerService.update(id, payload).subscribe({
this.manufacturerService.updateManufacturer(id, payload).subscribe({
next: (response) => {
this.isSubmitting.set(false);
this.toastService.success(
'Fabricante Atualizado',
`O fabricante "${response.data.name}" foi salvo com sucesso.`
`O fabricante "${response.data.name}" foi salvo com sucesso.`,
);
this.manufacturerSaved.emit(response.data);
this.close();
Expand All @@ -269,15 +264,15 @@ export class ManufacturerFormComponent {
}
this.formErrors.set(serverErrors);
}
}
},
});
} else {
this.manufacturerService.create(payload as CreateManufacturerPayload).subscribe({
this.manufacturerService.createManufacturer(payload).subscribe({
next: (response) => {
this.isSubmitting.set(false);
this.toastService.success(
'Fabricante Criado',
`O fabricante "${response.data.name}" foi cadastrado com sucesso.`
`O fabricante "${response.data.name}" foi cadastrado com sucesso.`,
);
this.manufacturerSaved.emit(response.data);
this.close();
Expand All @@ -295,7 +290,7 @@ export class ManufacturerFormComponent {
}
this.formErrors.set(serverErrors);
}
}
},
});
}
}
Expand Down
Loading