Skip to content

Commit ae8e05c

Browse files
committed
Add product workspace, create UI & services
Introduce a new product creation/workspace feature: adds standalone product-create and product-workspace components with tabbed subcomponents (general, commercial, inventory, media, compatibility, administration) and UI pieces (sidebar, stepper). Adds models, utilities and compatibility-modules.config to centralize compatibility modules. New services: PartOriginService and UnitSaleService; ProductService gains delete(). Payload transformer and request models updated to match create API contract and validation rules. Routes updated to point to new components and appConfig registers provideHttpClient(withFetch()). Legacy product-form files removed and global scrollbar styles refined.
1 parent bcfa3d9 commit ae8e05c

48 files changed

Lines changed: 4834 additions & 2389 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/app/app.config.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
22
import { provideRouter } from '@angular/router';
3-
3+
import { provideHttpClient, withFetch } from '@angular/common/http';
44
import { routes } from './app.routes';
55

66
export const appConfig: ApplicationConfig = {
7-
providers: [provideBrowserGlobalErrorListeners(), provideRouter(routes)],
7+
providers: [
8+
provideHttpClient(withFetch()),
9+
provideBrowserGlobalErrorListeners(),
10+
provideRouter(routes),
11+
],
812
};

src/app/app.routes.server.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ export const serverRoutes: ServerRoute[] = [
99
path: 'catalog/products/:id/view',
1010
renderMode: RenderMode.Server,
1111
},
12-
{
13-
path: 'catalog/products/:id/duplicate',
14-
renderMode: RenderMode.Server,
15-
},
1612
{
1713
path: 'catalog/products/:id',
1814
renderMode: RenderMode.Server,

src/app/app.routes.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,28 @@ export const routes: Routes = [
1919
{
2020
path: 'catalog/products/new',
2121
loadComponent: () =>
22-
import('./features/catalog/product-form/product-form').then((m) => m.ProductFormComponent),
22+
import('./features/catalog/product-create/product-create').then((m) => m.ProductCreateComponent),
2323
},
2424
{
2525
path: 'catalog/products/:id/edit',
2626
loadComponent: () =>
27-
import('./features/catalog/product-form/product-form').then((m) => m.ProductFormComponent),
27+
import('./features/catalog/product-workspace/product-workspace').then(
28+
(m) => m.ProductWorkspaceComponent,
29+
),
2830
},
2931
{
3032
path: 'catalog/products/:id/view',
3133
loadComponent: () =>
32-
import('./features/catalog/product-form/product-form').then((m) => m.ProductFormComponent),
33-
},
34-
{
35-
path: 'catalog/products/:id/duplicate',
36-
loadComponent: () =>
37-
import('./features/catalog/product-form/product-form').then((m) => m.ProductFormComponent),
34+
import('./features/catalog/product-workspace/product-workspace').then(
35+
(m) => m.ProductWorkspaceComponent,
36+
),
3837
},
3938
{
4039
path: 'catalog/products/:id',
4140
loadComponent: () =>
42-
import('./features/catalog/product-form/product-form').then((m) => m.ProductFormComponent),
41+
import('./features/catalog/product-workspace/product-workspace').then(
42+
(m) => m.ProductWorkspaceComponent,
43+
),
4344
},
4445
{
4546
path: 'catalog/:sub',
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { FormTab } from '../../features/catalog/models/product-workspace.model';
2+
import { SubMenuItem } from '../models/menu-item';
3+
4+
export interface CompatibilityModuleConfig {
5+
menuId: string;
6+
workspaceTab: FormTab;
7+
label: string;
8+
route: string;
9+
icon: string;
10+
headerIcon: string;
11+
headerIconClass: string;
12+
description: string;
13+
countKey:
14+
| 'oemCodesCount'
15+
| 'productCodesCount'
16+
| 'equivalentProductsCount'
17+
| 'vehicleApplicationsCount';
18+
}
19+
20+
/** Módulos de compatibilidade — fonte única alinhada ao menu do shell-state */
21+
export const COMPATIBILITY_MODULES: CompatibilityModuleConfig[] = [
22+
{
23+
menuId: 'oem',
24+
workspaceTab: 'oem',
25+
label: 'OEM Codes',
26+
route: '/compatibility/oem-codes',
27+
icon: 'hash',
28+
headerIcon: 'box',
29+
headerIconClass: 'text-[#4F8A6B]',
30+
description: 'Códigos originais de montadoras de referência cadastrados para esta peça',
31+
countKey: 'oemCodesCount',
32+
},
33+
{
34+
menuId: 'product-codes',
35+
workspaceTab: 'codigos',
36+
label: 'Product Codes',
37+
route: '/compatibility/product-codes',
38+
icon: 'barcode',
39+
headerIcon: 'tag',
40+
headerIconClass: 'text-[#5A8DEE]',
41+
description: 'EAN, DUN, Código do fabricante, Código paralelo e Código interno auxiliar',
42+
countKey: 'productCodesCount',
43+
},
44+
{
45+
menuId: 'equivalents',
46+
workspaceTab: 'equivalentes',
47+
label: 'Produtos Equivalentes',
48+
route: '/compatibility/equivalent-products',
49+
icon: 'copy',
50+
headerIcon: 'copy',
51+
headerIconClass: 'text-purple-500',
52+
description: 'Relacionamento entre produtos equivalentes e marcas cruzadas',
53+
countKey: 'equivalentProductsCount',
54+
},
55+
{
56+
menuId: 'applications',
57+
workspaceTab: 'compatibilidade',
58+
label: 'Aplicações',
59+
route: '/compatibility/applications',
60+
icon: 'layers',
61+
headerIcon: 'car',
62+
headerIconClass: 'text-amber-500',
63+
description: 'Relacionamento entre o produto e suas aplicações veiculares',
64+
countKey: 'vehicleApplicationsCount',
65+
},
66+
];
67+
68+
export function compatibilityMenuChildren(): SubMenuItem[] {
69+
return COMPATIBILITY_MODULES.map(({ menuId, label, route }) => ({
70+
id: menuId,
71+
label,
72+
route,
73+
}));
74+
}
75+
76+
export const COMPATIBILITY_WORKSPACE_TABS: FormTab[] = COMPATIBILITY_MODULES.map(
77+
(module) => module.workspaceTab,
78+
);
79+
80+
export function isCompatibilityWorkspaceTab(tab: FormTab): boolean {
81+
return COMPATIBILITY_WORKSPACE_TABS.includes(tab);
82+
}
83+
84+
export function getCompatibilityModuleByTab(tab: FormTab): CompatibilityModuleConfig | undefined {
85+
return COMPATIBILITY_MODULES.find((module) => module.workspaceTab === tab);
86+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface PartOriginResponse {
2+
id: string;
3+
name: string;
4+
abbreviation: string;
5+
description: string;
6+
display_order: number;
7+
active: boolean;
8+
}

src/app/core/models/catetories/category-options.model.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,3 @@ export interface CategoryOption {
1010
description: string;
1111
icon: string;
1212
}
13-
// 001400_ARARAS_PT1 => http://localhost:4200/#/my-farms/bagaco_03_30/72529
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface getAllResponse<T> {
2+
success: boolean;
3+
message: string;
4+
data: T;
5+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { AutocompleteOption } from '../../../design-system/autocomplete-select/autocomplete-select';
2+
import { DSelectOption } from '../design-system/select-option.model';
3+
4+
/** Estado interno do formulário de criação (campos do POST) */
5+
export interface ProductCreateFormState {
6+
categoryId: string;
7+
manufacturerId: string;
8+
partOriginId: string;
9+
unitId: string;
10+
internalCode: string;
11+
barcode: string;
12+
name: string;
13+
shortDescription: string;
14+
description: string;
15+
weight: number;
16+
height: number;
17+
width: number;
18+
length: number;
19+
featured: boolean;
20+
}
21+
22+
export function defaultProductCreateFormState(): ProductCreateFormState {
23+
return {
24+
categoryId: '',
25+
manufacturerId: '',
26+
partOriginId: '',
27+
unitId: '',
28+
internalCode: '',
29+
barcode: '',
30+
name: '',
31+
shortDescription: '',
32+
description: '',
33+
weight: 0,
34+
height: 0,
35+
width: 0,
36+
length: 0,
37+
featured: false,
38+
};
39+
}
40+
41+
export function createFormToGeneralSource(state: ProductCreateFormState): ProductGeneralFormSource {
42+
return {
43+
...state,
44+
slug: '',
45+
statusId: '',
46+
active: true,
47+
};
48+
}
49+
50+
/** Payload esperado pelo backend no POST /product */
51+
export interface CreateProductPayload {
52+
category_id: string;
53+
manufacturer_id: string;
54+
part_origin_id: string;
55+
unit_id: string;
56+
internal_code: string;
57+
barcode: string;
58+
name: string;
59+
short_description: string;
60+
description: string;
61+
weight: number;
62+
height: number;
63+
width: number;
64+
length: number;
65+
featured: boolean;
66+
}
67+
68+
/** Campos da aba Geral compartilhados entre create e workspace */
69+
export interface ProductGeneralFormData {
70+
name: string;
71+
slug: string;
72+
shortDescription: string;
73+
description: string;
74+
categoryId: string;
75+
manufacturerId: string;
76+
partOriginId: string;
77+
statusId: string;
78+
internalCode: string;
79+
barcode: string;
80+
unitId: string;
81+
weight: number;
82+
height: number;
83+
width: number;
84+
length: number;
85+
featured: boolean;
86+
active: boolean;
87+
}
88+
89+
export interface ProductGeneralFormOptions {
90+
categoryOptions: AutocompleteOption[];
91+
categoryLoading: boolean;
92+
manufacturerOptions: AutocompleteOption[];
93+
manufacturerLoading: boolean;
94+
partOriginOptions: DSelectOption[];
95+
unitOptions: DSelectOption[];
96+
statusOptions: DSelectOption[];
97+
}
98+
99+
export interface ProductGeneralFormSource {
100+
name: string;
101+
slug: string;
102+
shortDescription: string;
103+
description: string;
104+
categoryId: string;
105+
manufacturerId: string;
106+
partOriginId: string;
107+
statusId: string;
108+
internalCode: string;
109+
barcode: string;
110+
unitId: string;
111+
weight: number;
112+
height: number;
113+
width: number;
114+
length: number;
115+
featured: boolean;
116+
active: boolean;
117+
}
118+
119+
export function toProductGeneralFormData(source: ProductGeneralFormSource): ProductGeneralFormData {
120+
return {
121+
name: source.name,
122+
slug: source.slug,
123+
shortDescription: source.shortDescription,
124+
description: source.description,
125+
categoryId: source.categoryId,
126+
manufacturerId: source.manufacturerId,
127+
partOriginId: source.partOriginId,
128+
statusId: source.statusId,
129+
internalCode: source.internalCode,
130+
barcode: source.barcode,
131+
unitId: source.unitId,
132+
weight: source.weight,
133+
height: source.height,
134+
width: source.width,
135+
length: source.length,
136+
featured: source.featured,
137+
active: source.active,
138+
};
139+
}
140+
141+
export function toCreateProductPayload(source: ProductGeneralFormSource): CreateProductPayload {
142+
return {
143+
category_id: source.categoryId,
144+
manufacturer_id: source.manufacturerId,
145+
part_origin_id: source.partOriginId,
146+
unit_id: source.unitId,
147+
internal_code: source.internalCode,
148+
barcode: source.barcode,
149+
name: source.name,
150+
short_description: source.shortDescription,
151+
description: source.description,
152+
weight: source.weight,
153+
height: source.height,
154+
width: source.width,
155+
length: source.length,
156+
featured: source.featured,
157+
};
158+
}

0 commit comments

Comments
 (0)