chave-mfe-supplier 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -0
- package/.github/workflows/ci.yml +38 -0
- package/Dockerfile +13 -0
- package/README.md +94 -0
- package/index.html +12 -0
- package/jest.config.cjs +34 -0
- package/package.json +37 -0
- package/src/__tests__/SupplierDetailPage.test.tsx +75 -0
- package/src/__tests__/SupplierFormPage.test.tsx +53 -0
- package/src/__tests__/SupplierListPage.test.tsx +48 -0
- package/src/__tests__/client.test.tsx +22 -0
- package/src/__tests__/dialogs.test.tsx +42 -0
- package/src/__tests__/setup.ts +5 -0
- package/src/api/client.ts +63 -0
- package/src/api/createApi.ts +7 -0
- package/src/api/types.ts +94 -0
- package/src/components/LinkProductDialog.tsx +55 -0
- package/src/components/ReplenishmentDialog.tsx +88 -0
- package/src/main.tsx +9 -0
- package/src/pages/SupplierApp.tsx +50 -0
- package/src/pages/SupplierDetailPage.tsx +210 -0
- package/src/pages/SupplierFormPage.tsx +214 -0
- package/src/pages/SupplierListPage.tsx +162 -0
- package/src/theme.ts +14 -0
- package/src/vite-env.d.ts +9 -0
- package/tsconfig.json +27 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +26 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { FC, useCallback, useEffect, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Alert, Box, Button, Chip, MenuItem, Paper, Stack, TextField, Typography,
|
|
4
|
+
} from "@mui/material";
|
|
5
|
+
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
|
6
|
+
import { SupplierApi, ListParams } from "../api/client";
|
|
7
|
+
import { Supplier } from "../api/types";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
api: SupplierApi;
|
|
11
|
+
onCreate: () => void;
|
|
12
|
+
onOpen: (id: string) => void;
|
|
13
|
+
onEdit: (id: string) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Filters {
|
|
17
|
+
q: string;
|
|
18
|
+
status: string;
|
|
19
|
+
city: string;
|
|
20
|
+
state: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const emptyFilters: Filters = { q: "", status: "", city: "", state: "" };
|
|
24
|
+
|
|
25
|
+
const SupplierListPage: FC<Props> = ({ api, onCreate, onOpen, onEdit }) => {
|
|
26
|
+
const [rows, setRows] = useState<Supplier[]>([]);
|
|
27
|
+
const [total, setTotal] = useState(0);
|
|
28
|
+
const [loading, setLoading] = useState(false);
|
|
29
|
+
const [error, setError] = useState<string | null>(null);
|
|
30
|
+
const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 20 });
|
|
31
|
+
const [draft, setDraft] = useState<Filters>(emptyFilters);
|
|
32
|
+
const [applied, setApplied] = useState<Filters>(emptyFilters);
|
|
33
|
+
|
|
34
|
+
const load = useCallback(async () => {
|
|
35
|
+
setLoading(true);
|
|
36
|
+
setError(null);
|
|
37
|
+
try {
|
|
38
|
+
const params: ListParams = {
|
|
39
|
+
page: paginationModel.page + 1,
|
|
40
|
+
pageSize: paginationModel.pageSize,
|
|
41
|
+
q: applied.q || undefined,
|
|
42
|
+
status: applied.status || undefined,
|
|
43
|
+
city: applied.city || undefined,
|
|
44
|
+
state: applied.state || undefined,
|
|
45
|
+
};
|
|
46
|
+
const res = await api.listSuppliers(params);
|
|
47
|
+
setRows(res.data);
|
|
48
|
+
setTotal(res.total);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
setError(e instanceof Error ? e.message : "Erro ao carregar fornecedores");
|
|
51
|
+
} finally {
|
|
52
|
+
setLoading(false);
|
|
53
|
+
}
|
|
54
|
+
}, [api, paginationModel, applied]);
|
|
55
|
+
|
|
56
|
+
useEffect(() => { void load(); }, [load]);
|
|
57
|
+
|
|
58
|
+
const onSearch = () => {
|
|
59
|
+
setPaginationModel((m) => ({ ...m, page: 0 }));
|
|
60
|
+
setApplied(draft);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleInactivate = async (id: string) => {
|
|
64
|
+
try {
|
|
65
|
+
await api.inactivateSupplier(id);
|
|
66
|
+
await load();
|
|
67
|
+
} catch (e) {
|
|
68
|
+
setError(e instanceof Error ? e.message : "Erro ao inativar");
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const columns: GridColDef<Supplier>[] = [
|
|
73
|
+
{ field: "legalName", headerName: "Razão Social", flex: 1, minWidth: 180 },
|
|
74
|
+
{ field: "tradeName", headerName: "Nome Fantasia", flex: 1, minWidth: 140 },
|
|
75
|
+
{ field: "document", headerName: "Documento", width: 160 },
|
|
76
|
+
{
|
|
77
|
+
field: "status", headerName: "Status", width: 110,
|
|
78
|
+
renderCell: (params) => (
|
|
79
|
+
<Chip
|
|
80
|
+
size="small"
|
|
81
|
+
label={params.row.status === "active" ? "Ativo" : "Inativo"}
|
|
82
|
+
color={params.row.status === "active" ? "success" : "default"}
|
|
83
|
+
/>
|
|
84
|
+
),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
field: "city", headerName: "Cidade", width: 140,
|
|
88
|
+
valueGetter: (_value, row) => row.address?.city ?? "",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
field: "actions", headerName: "Ações", width: 220, sortable: false, filterable: false,
|
|
92
|
+
renderCell: (params) => (
|
|
93
|
+
<Stack direction="row" spacing={1}>
|
|
94
|
+
<Button size="small" variant="text" onClick={() => onOpen(params.row.id)}>Abrir</Button>
|
|
95
|
+
<Button size="small" variant="text" onClick={() => onEdit(params.row.id)}>Editar</Button>
|
|
96
|
+
<Button
|
|
97
|
+
size="small" variant="text" color="error"
|
|
98
|
+
disabled={params.row.status === "inactive"}
|
|
99
|
+
onClick={() => handleInactivate(params.row.id)}
|
|
100
|
+
>
|
|
101
|
+
Inativar
|
|
102
|
+
</Button>
|
|
103
|
+
</Stack>
|
|
104
|
+
),
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<Box sx={{ p: 3 }}>
|
|
110
|
+
<Stack direction="row" sx={{ mb: 2, justifyContent: "space-between", alignItems: "center" }}>
|
|
111
|
+
<Typography variant="h5">Fornecedores</Typography>
|
|
112
|
+
<Button onClick={onCreate}>Novo fornecedor</Button>
|
|
113
|
+
</Stack>
|
|
114
|
+
|
|
115
|
+
<Paper sx={{ p: 2, mb: 2 }}>
|
|
116
|
+
<Stack direction={{ xs: "column", sm: "row" }} spacing={2} sx={{ alignItems: "center" }}>
|
|
117
|
+
<TextField
|
|
118
|
+
label="Buscar" size="small" value={draft.q}
|
|
119
|
+
onChange={(e) => setDraft({ ...draft, q: e.target.value })}
|
|
120
|
+
/>
|
|
121
|
+
<TextField
|
|
122
|
+
select label="Status" size="small" sx={{ minWidth: 140 }} value={draft.status}
|
|
123
|
+
onChange={(e) => setDraft({ ...draft, status: e.target.value })}
|
|
124
|
+
>
|
|
125
|
+
<MenuItem value="">Todos</MenuItem>
|
|
126
|
+
<MenuItem value="active">Ativo</MenuItem>
|
|
127
|
+
<MenuItem value="inactive">Inativo</MenuItem>
|
|
128
|
+
</TextField>
|
|
129
|
+
<TextField
|
|
130
|
+
label="Cidade" size="small" value={draft.city}
|
|
131
|
+
onChange={(e) => setDraft({ ...draft, city: e.target.value })}
|
|
132
|
+
/>
|
|
133
|
+
<TextField
|
|
134
|
+
label="Estado" size="small" value={draft.state}
|
|
135
|
+
onChange={(e) => setDraft({ ...draft, state: e.target.value })}
|
|
136
|
+
/>
|
|
137
|
+
<Button onClick={onSearch}>Buscar</Button>
|
|
138
|
+
</Stack>
|
|
139
|
+
</Paper>
|
|
140
|
+
|
|
141
|
+
{error && <Alert severity="error" sx={{ mb: 2 }}>{error}</Alert>}
|
|
142
|
+
|
|
143
|
+
<Paper>
|
|
144
|
+
<DataGrid
|
|
145
|
+
autoHeight
|
|
146
|
+
rows={rows}
|
|
147
|
+
columns={columns}
|
|
148
|
+
getRowId={(row) => row.id}
|
|
149
|
+
loading={loading}
|
|
150
|
+
rowCount={total}
|
|
151
|
+
paginationMode="server"
|
|
152
|
+
paginationModel={paginationModel}
|
|
153
|
+
onPaginationModelChange={setPaginationModel}
|
|
154
|
+
pageSizeOptions={[10, 20, 50]}
|
|
155
|
+
disableRowSelectionOnClick
|
|
156
|
+
/>
|
|
157
|
+
</Paper>
|
|
158
|
+
</Box>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export default SupplierListPage;
|
package/src/theme.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createTheme } from "@mui/material/styles";
|
|
2
|
+
|
|
3
|
+
export const theme = createTheme({
|
|
4
|
+
palette: {
|
|
5
|
+
primary: { main: "#7b1fa2" },
|
|
6
|
+
secondary: { main: "#ec407a" },
|
|
7
|
+
background: { default: "#faf7fb" },
|
|
8
|
+
},
|
|
9
|
+
shape: { borderRadius: 12 },
|
|
10
|
+
components: {
|
|
11
|
+
MuiButton: { defaultProps: { variant: "contained" }, styleOverrides: { root: { textTransform: "none" } } },
|
|
12
|
+
MuiPaper: { styleOverrides: { root: { borderRadius: 12 } } },
|
|
13
|
+
},
|
|
14
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
|
|
11
|
+
/* Bundler mode */
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
|
|
19
|
+
/* Linting */
|
|
20
|
+
"strict": true,
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"noFallthroughCasesInSwitch": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["src"],
|
|
26
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
27
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import react from "@vitejs/plugin-react";
|
|
3
|
+
import federation from "@originjs/vite-plugin-federation";
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [
|
|
7
|
+
react(),
|
|
8
|
+
federation({
|
|
9
|
+
name: "mfe_supplier",
|
|
10
|
+
filename: "remoteEntry.js",
|
|
11
|
+
exposes: {
|
|
12
|
+
"./SupplierApp": "./src/pages/SupplierApp.tsx",
|
|
13
|
+
"./SupplierListPage": "./src/pages/SupplierListPage.tsx",
|
|
14
|
+
"./SupplierFormPage": "./src/pages/SupplierFormPage.tsx",
|
|
15
|
+
"./SupplierDetailPage": "./src/pages/SupplierDetailPage.tsx",
|
|
16
|
+
},
|
|
17
|
+
shared: {
|
|
18
|
+
react: { singleton: true, requiredVersion: "^18.2.0" },
|
|
19
|
+
"react-dom": { singleton: true, requiredVersion: "^18.2.0" },
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
],
|
|
23
|
+
build: { target: "esnext", minify: false },
|
|
24
|
+
server: { port: 4002, host: true },
|
|
25
|
+
preview: { port: 4002, host: true },
|
|
26
|
+
});
|