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
5 changes: 3 additions & 2 deletions src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ export const routes: Routes = [
),
},
{
path: 'catalog/:sub',
loadComponent: () => import('./features/placeholder').then((m) => m.PlaceholderPageComponent),
path: 'catalog/categories',
loadComponent: () =>
import('./features/catalog/categories/categories').then((m) => m.CategoriesPageComponent),
},
{
path: 'compatibility/:sub',
Expand Down
7 changes: 7 additions & 0 deletions src/app/core/models/catetories/categories-default-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface CategoryDefaultQuery {
page: number | null;
name: string | null;
active: boolean | null;
per_page: number | null;
parent_id: string | null;
}
47 changes: 42 additions & 5 deletions src/app/core/models/catetories/categories.model.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,50 @@
export interface CategoryResponse {
// export interface CategoryResponse {
// id: string;
// parent_id: null;
// name: string;
// slug: string;
// description: string;
// display_order: number;
// icon: string;
// image: string;
// active: boolean;
// created_at: string;
// updated_at: string;
// }

export interface CategoryResponse extends Record<string, unknown> {
id: string;
parent_id: null;
parent_id: string | null;
name: string;
slug: string;
description: string;
description: string | null;
display_order: number;
icon: string;
image: string;
icon: string | null;
image: string | null;
active: boolean;
created_at: string;
updated_at: string;
parent_name?: string;
}

export interface CreateCategoryPayload {
parent_id?: string | null;
name: string;
slug: string;
description?: string | null;
display_order?: number;
icon?: string | null;
image?: string | null;
active?: boolean;
}

export interface UpdateCategoryPayload {
parent_id?: string | null;
name?: string;
slug?: string;
description?: string | null;
display_order?: number;
icon?: string | null;
image?: string | null;
active?: boolean;
}
1 change: 1 addition & 0 deletions src/app/core/models/catetories/category-options.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import { GeneralOption } from '../generals/general-options-response.model';

export interface CategoryOption extends GeneralOption {
icon: string;
is_parent: boolean;
description: string;
}
1 change: 1 addition & 0 deletions src/app/core/models/design-system/auto-complete.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import { GeneralOption } from '../generals/general-options-response.model';
export interface AutocompleteOption extends GeneralOption {
icon: string;
disabled: boolean;
is_parent: boolean;
description: string;
}
Empty file.
2 changes: 1 addition & 1 deletion src/app/core/models/list-table/list-table.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface TableAction<T = Record<string, unknown>> {
colorClass?: string;
title?: string;
visible?: (row: T) => boolean;
handler: (row: T) => void;
handler?: (row: T) => void;
}

export interface TablePaginationConfig {
Expand Down
24 changes: 16 additions & 8 deletions src/app/core/services/category-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { PaginatedResponse } from '../models/pagination/pagination.model';
import { environment } from '../../../environments/environment';
import { CategoryResponse } from '../models/catetories/categories.model';
import {
CategoryResponse,
CreateCategoryPayload,
UpdateCategoryPayload,
} from '../models/catetories/categories.model';
import { buildHttpParams } from './build-http-params';
import { CategoryOption } from '../models/catetories/category-options.model';
import { GeneralOptionQuery } from '../models/generals/general-option-query.model';
import { getAllResponse } from '../models/generals/general-responses-list.model';
import { CategoryDefaultQuery } from '../models/catetories/categories-default-query';

@Injectable({
providedIn: 'root',
Expand All @@ -16,8 +21,11 @@ export class CategoryService {
private api = environment.apiUrl;
private flag = 'category';

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

getOptions(filters: GeneralOptionQuery) {
Expand All @@ -31,15 +39,15 @@ export class CategoryService {
return this.http.get<CategoryResponse>(`${this.api}/${this.flag}/${id}`);
}

create(data: FormData) {
return this.http.post<CategoryResponse>(`${this.api}/${this.flag}`, data);
create(data: CreateCategoryPayload) {
return this.http.post<getAllResponse<CategoryResponse>>(`${this.api}/${this.flag}`, data);
}

update(id: string, data: FormData) {
return this.http.post<CategoryResponse>(`${this.api}/${this.flag}/${id}`, data);
update(id: string, data: UpdateCategoryPayload) {
return this.http.put<getAllResponse<CategoryResponse>>(`${this.api}/${this.flag}/${id}`, data);
}

delete(id: string) {
return this.http.delete<void>(`${this.api}/${this.flag}/${id}`);
return this.http.delete<getAllResponse<CategoryResponse>>(`${this.api}/${this.flag}/${id}`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class ManufacturerStore extends OptionCacheStore<
icon: 'chevrons-right',
hasSubOptions: true,
description: option.sublabel,
is_parent: false,
})),
})),
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/design-system/data-table/data-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export class DataTableComponent<T extends Record<string, unknown>> {

onActionClick(action: TableAction<T>, row: T, event: MouseEvent): void {
event.stopPropagation();
if (action.id) {
if (action.handler) {
action.handler(row);
}
console.log('[ON ACTION CLICK]', action, action.id);
Expand Down
26 changes: 13 additions & 13 deletions src/app/design-system/pagination/pagination.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
class="appearance-none pl-2.5 pr-7 py-1 bg-gray-50 dark:bg-slate-700/80 border border-gray-200 dark:border-slate-600 rounded-lg text-xs font-semibold text-gray-800 dark:text-slate-200 focus:outline-none focus:ring-2 focus:ring-[#4F8A6B]/30 hover:border-gray-300 dark:hover:border-slate-500 transition-all cursor-pointer"
title="Itens por página"
>
@for (opt of pageSizeOptions(); track opt) {
@for (opt of optionsSizes(); track opt) {
<option [value]="opt" [selected]="opt === pageSize()">{{ opt }} por pág.</option>
}
</select>
Expand All @@ -46,7 +46,7 @@
<!-- First Page Button -->
<button
type="button"
[disabled]="currentPage() <= 1"
[disabled]="pagination().current_page <= 1"
(click)="goToPage(1)"
class="p-1.5 rounded-lg border border-gray-200 dark:border-slate-700 hover:bg-gray-100 dark:hover:bg-slate-700 text-gray-600 dark:text-slate-300 disabled:opacity-35 disabled:cursor-not-allowed disabled:hover:bg-transparent transition-all"
title="Primeira Página"
Expand All @@ -57,8 +57,8 @@
<!-- Previous Page Button -->
<button
type="button"
[disabled]="currentPage() <= 1"
(click)="goToPage(currentPage() - 1)"
[disabled]="pagination().current_page <= 1"
(click)="goToPage(pagination().from || 0)"
class="p-1.5 rounded-lg border border-gray-200 dark:border-slate-700 hover:bg-gray-100 dark:hover:bg-slate-700 text-gray-600 dark:text-slate-300 disabled:opacity-35 disabled:cursor-not-allowed disabled:hover:bg-transparent transition-all"
title="Página Anterior"
>
Expand All @@ -69,7 +69,7 @@
<div
class="sm:hidden px-3 py-1 bg-[#EAF4EE] dark:bg-slate-700 text-[#4F8A6B] dark:text-[#5BAE6A] font-semibold rounded-lg text-xs"
>
{{ currentPage() }} / {{ totalPages() }}
{{ pagination().current_page }} / {{ pagination().total }}
</div>

<!-- Desktop Page Number Buttons -->
Expand All @@ -78,7 +78,7 @@
<button
type="button"
(click)="goToPage(item.page!)"
[class]="item.page === currentPage()
[class]="item.page === pagination().current_page
? 'bg-[#4F8A6B] text-white font-bold border-[#4F8A6B] shadow-xs'
: 'border border-gray-200 dark:border-slate-700 hover:bg-gray-100 dark:hover:bg-slate-700 text-gray-700 dark:text-slate-200 font-medium'"
class="min-w-[32px] h-[32px] px-2 rounded-lg flex items-center justify-center text-xs transition-all cursor-pointer"
Expand All @@ -93,8 +93,8 @@
<!-- Next Page Button -->
<button
type="button"
[disabled]="currentPage() >= totalPages()"
(click)="goToPage(currentPage() + 1)"
[disabled]="pagination().current_page >= pagination().last_page"
(click)="goToPage(pagination().current_page + 1)"
class="p-1.5 rounded-lg border border-gray-200 dark:border-slate-700 hover:bg-gray-100 dark:hover:bg-slate-700 text-gray-600 dark:text-slate-300 disabled:opacity-35 disabled:cursor-not-allowed disabled:hover:bg-transparent transition-all"
title="Próxima Página"
>
Expand All @@ -104,25 +104,25 @@
<!-- Last Page Button -->
<button
type="button"
[disabled]="currentPage() >= totalPages()"
(click)="goToPage(totalPages())"
[disabled]="pagination().current_page >= pagination().last_page"
(click)="goToPage(pagination().last_page)"
class="p-1.5 rounded-lg border border-gray-200 dark:border-slate-700 hover:bg-gray-100 dark:hover:bg-slate-700 text-gray-600 dark:text-slate-300 disabled:opacity-35 disabled:cursor-not-allowed disabled:hover:bg-transparent transition-all"
title="Última Página"
>
<app-icon name="chevrons-right" size="w-4 h-4" />
</button>

<!-- Quick Jump to Page (Optional Input) -->
@if (totalPages() > 5) {
@if (pagination().last_page > 5) {
<div
class="hidden lg:flex items-center gap-1.5 ml-2 pl-2 border-l border-gray-200 dark:border-slate-700"
>
<span class="text-[11px] text-gray-400 dark:text-slate-500">Ir p/:</span>
<input
type="number"
min="1"
[max]="totalPages()"
[value]="currentPage()"
[max]="pagination().last_page"
[value]="pagination().current_page"
(keydown.enter)="onDirectJump($event)"
(blur)="onDirectJump($event)"
class="w-11 px-1.5 py-1 text-center bg-gray-50 dark:bg-slate-700/80 border border-gray-200 dark:border-slate-600 rounded-lg text-xs font-medium text-gray-800 dark:text-slate-200 focus:outline-none focus:ring-1 focus:ring-[#4F8A6B]"
Expand Down
67 changes: 50 additions & 17 deletions src/app/design-system/pagination/pagination.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Component, ChangeDetectionStrategy, input, output, computed } from '@angular/core';
import { Component, ChangeDetectionStrategy, input, output, computed, signal } from '@angular/core';
import { AppIconComponent } from '../icon/app-icon';
import { PaginationMeta } from '../../core/models/pagination/pagination.model';
import { initialValuesPagination } from './utils/initial-values';

export interface PageItem {
type: 'page' | 'ellipsis';
Expand All @@ -15,44 +17,53 @@ export interface PageItem {
styleUrl: './pagination.css',
})
export class PaginationComponent {
readonly currentPage = input<number>(1);
readonly pageSize = input<number>(10);
readonly totalItems = input<number>(0);
readonly pageSizeOptions = input<number[]>([10, 25, 50, 100]);
readonly showPageSize = input<boolean>(true);
readonly showItemRange = input<boolean>(true);
readonly pagination = input<PaginationMeta>(initialValuesPagination);
readonly optionsSizes = signal<number[]>([5, 10, 25, 50, 100]);

// Evento Unificado para o Pai
readonly paginationChange = output<PaginationMeta>();

//remover depois
readonly currentPage = input<number>();
readonly totalItems = input<number>();

readonly pageChange = output<number>();
readonly pageSizeChange = output<number>();

readonly totalPages = computed(() => {
return Math.max(1, Math.ceil(this.totalItems() / this.pageSize()));
return this.pagination().last_page;
});

readonly pageSize = computed(() => {
return this.pagination().per_page;
});

readonly startItem = computed(() => {
if (this.totalItems() === 0) return 0;
return (this.currentPage() - 1) * this.pageSize() + 1;
return this.pagination().from;
});

readonly endItem = computed(() => {
return Math.min(this.currentPage() * this.pageSize(), this.totalItems());
return this.pagination().to;
});

readonly startItemFormatted = computed(() => {
return this.startItem().toLocaleString('pt-BR');
return this.startItem()?.toLocaleString('pt-BR');
});

readonly endItemFormatted = computed(() => {
return this.endItem().toLocaleString('pt-BR');
return this.endItem()?.toLocaleString('pt-BR');
});

readonly totalItemsFormatted = computed(() => {
return this.totalItems().toLocaleString('pt-BR');
return this.pagination().total.toLocaleString('pt-BR');
});

readonly visiblePages = computed<PageItem[]>(() => {
const total = this.totalPages();
const current = this.currentPage();
const current = this.pagination().current_page;

if (total <= 7) {
const items: PageItem[] = [];
Expand Down Expand Up @@ -90,15 +101,37 @@ export class PaginationComponent {
});

goToPage(page: number): void {
if (page >= 1 && page <= this.totalPages() && page !== this.currentPage()) {
this.pageChange.emit(page);
}
const currentMeta = this.pagination();

// Evita ir para páginas inválidas ou para a página atual
if (page < 1 || page > this.totalPages() || page === currentMeta.current_page) return;

// Emite o evento individual simples
this.pageChange.emit(page);

// Emite o objeto PaginationMeta atualizado mantendo per_page
this.paginationChange.emit({
...currentMeta,
current_page: page,
});
}

onPageSizeChange(event: Event): void {
const size = parseInt((event.target as HTMLSelectElement).value, 10);
if (!isNaN(size)) {
const currentMeta = this.pagination();

if (!isNaN(size) && size !== currentMeta.per_page) {
this.pageSizeChange.emit(size);

if (currentMeta.current_page !== 1) {
this.pageChange.emit(1);
}

this.paginationChange.emit({
...currentMeta,
current_page: 1,
per_page: size,
});
}
}

Expand All @@ -108,7 +141,7 @@ export class PaginationComponent {
if (!isNaN(val) && val >= 1 && val <= this.totalPages()) {
this.goToPage(val);
} else {
inputEl.value = this.currentPage().toString();
inputEl.value = this.pagination().current_page.toString();
}
}
}
10 changes: 10 additions & 0 deletions src/app/design-system/pagination/utils/initial-values.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const initialValuesPagination = {
current_page: 0,
from: 0,
last_page: 0,
links: [],
path: '',
per_page: 0,
to: 0,
total: 0,
};
Loading