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.
@@ -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
+ });
@@ -0,0 +1,9 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ interface ImportMetaEnv {
4
+ readonly VITE_MS_SUPPLIER_URL?: string;
5
+ }
6
+
7
+ interface ImportMeta {
8
+ readonly env: ImportMetaEnv;
9
+ }
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
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true
8
+ },
9
+ "include": ["vite.config.ts"]
10
+ }
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
+ });