@fayz-ai/plugin-registry 0.11.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/LICENSE +21 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +339 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/entities.d.ts +12 -0
- package/dist/lib/entities.d.ts.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Faya Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { PluginManifest, PluginScope, VerticalId, EntityDef } from '@fayz-ai/core';
|
|
2
|
+
/** Cada tela de cadastro, na ORDEM em que o menu as lê por padrão. */
|
|
3
|
+
declare const SCREENS: readonly [{
|
|
4
|
+
readonly id: "services";
|
|
5
|
+
readonly path: "/registry/services";
|
|
6
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
7
|
+
readonly icon: "Briefcase";
|
|
8
|
+
readonly feature: "services";
|
|
9
|
+
}, {
|
|
10
|
+
readonly id: "serviceCategories";
|
|
11
|
+
readonly path: "/registry/categories";
|
|
12
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
13
|
+
readonly icon: "Tag";
|
|
14
|
+
readonly feature: "services";
|
|
15
|
+
}, {
|
|
16
|
+
readonly id: "locations";
|
|
17
|
+
readonly path: "/registry/locations";
|
|
18
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
19
|
+
readonly icon: "MapPin";
|
|
20
|
+
readonly feature: "services";
|
|
21
|
+
}, {
|
|
22
|
+
readonly id: "locationGroups";
|
|
23
|
+
readonly path: "/registry/location-groups";
|
|
24
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
25
|
+
readonly icon: "Map";
|
|
26
|
+
readonly feature: "services";
|
|
27
|
+
}, {
|
|
28
|
+
readonly id: "staff";
|
|
29
|
+
readonly path: "/registry/staff";
|
|
30
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
31
|
+
readonly icon: "UserCog";
|
|
32
|
+
}, {
|
|
33
|
+
readonly id: "contacts";
|
|
34
|
+
readonly path: "/registry/contacts";
|
|
35
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
36
|
+
readonly icon: "Contact";
|
|
37
|
+
}, {
|
|
38
|
+
readonly id: "suppliers";
|
|
39
|
+
readonly path: "/registry/suppliers";
|
|
40
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
41
|
+
readonly icon: "Building2";
|
|
42
|
+
}, {
|
|
43
|
+
readonly id: "partnerships";
|
|
44
|
+
readonly path: "/registry/partnerships";
|
|
45
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
46
|
+
readonly icon: "Handshake";
|
|
47
|
+
}, {
|
|
48
|
+
readonly id: "equipment";
|
|
49
|
+
readonly path: "/registry/equipment";
|
|
50
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
51
|
+
readonly icon: "Wrench";
|
|
52
|
+
}, {
|
|
53
|
+
readonly id: "origins";
|
|
54
|
+
readonly path: "/registry/origins";
|
|
55
|
+
readonly entity: EntityDef<Record<string, unknown>>;
|
|
56
|
+
readonly icon: "Globe";
|
|
57
|
+
}];
|
|
58
|
+
export type RegistryScreenId = (typeof SCREENS)[number]['id'];
|
|
59
|
+
export interface RegistryPluginOptions {
|
|
60
|
+
scope?: PluginScope;
|
|
61
|
+
verticalId?: VerticalId;
|
|
62
|
+
/** Onde a primeira linha cai no rail. As demais vêm em degraus de 0,1. */
|
|
63
|
+
navPosition?: number;
|
|
64
|
+
navSection?: 'main' | 'secondary' | 'settings';
|
|
65
|
+
/**
|
|
66
|
+
* Quais telas esta casa tem. Ausente = todas.
|
|
67
|
+
*
|
|
68
|
+
* `false` é a única forma de negar: uma chave que ninguém respondeu cai no
|
|
69
|
+
* padrão, não em "não" — do contrário, publicar uma ficha nova seria o dia
|
|
70
|
+
* em que ela some de toda conta que não a listou.
|
|
71
|
+
*/
|
|
72
|
+
modules?: Partial<Record<RegistryScreenId, boolean>>;
|
|
73
|
+
/**
|
|
74
|
+
* Troca a definição de uma ficha sem trocar a tela.
|
|
75
|
+
*
|
|
76
|
+
* A casa que precisa de um campo a mais no fornecedor passa o EntityDef dela
|
|
77
|
+
* aqui em vez de reimplementar a rota — que foi exatamente como as dez
|
|
78
|
+
* acabaram dentro de um aplicativo.
|
|
79
|
+
*/
|
|
80
|
+
entities?: Partial<Record<RegistryScreenId, EntityDef>>;
|
|
81
|
+
}
|
|
82
|
+
export declare function createRegistryPlugin(options?: RegistryPluginOptions): PluginManifest;
|
|
83
|
+
export { staffEntity, supplierEntity, contactEntity, partnershipEntity, equipmentEntity, originEntity, serviceCategoryEntity, serviceLocationEntity, locationGroupEntity, serviceEntity, } from './lib/entities';
|
|
84
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAmCvF,sEAAsE;AACtE,QAAA,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWH,CAAA;AAEV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAA;AAE7D,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAA;IACpD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAA;CACxD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,cAAc,CA+CpF;AAED,OAAO,EACL,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAC9E,YAAY,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,mBAAmB,EAC/E,aAAa,GACd,MAAM,gBAAgB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { createCrudPage } from '@fayz-ai/admin';
|
|
2
|
+
import { tl } from '@fayz-ai/core';
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
var staffEntity = {
|
|
6
|
+
name: tl("Staff", "Profissional"),
|
|
7
|
+
namePlural: tl("Staff", "Profissionais"),
|
|
8
|
+
icon: "UserCog",
|
|
9
|
+
// staff_members is an extension table (no name column) — useless to the
|
|
10
|
+
// agent's generic reader. The agent sees professionals through the
|
|
11
|
+
// beauty:professionals read-model (people kind='staff') instead.
|
|
12
|
+
agentHidden: true,
|
|
13
|
+
layout: "person",
|
|
14
|
+
displayField: "name",
|
|
15
|
+
defaultSort: "name",
|
|
16
|
+
fieldGroups: [
|
|
17
|
+
{ id: "professional", label: tl("Professional Info", "Informa\xE7\xF5es Profissionais"), columns: 2 }
|
|
18
|
+
],
|
|
19
|
+
fields: [
|
|
20
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
21
|
+
{ key: "email", label: "E-mail", type: "email", showInTable: true, searchable: true },
|
|
22
|
+
{ key: "phone", label: tl("Phone", "Telefone"), type: "phone", showInTable: true },
|
|
23
|
+
{ key: "profession", label: tl("Profession", "Profiss\xE3o"), type: "text", showInTable: true, group: "professional" },
|
|
24
|
+
{ key: "commissionRate", label: tl("Commission (%)", "Comiss\xE3o (%)"), type: "number", showInTable: true, group: "professional", defaultValue: 0 },
|
|
25
|
+
// Trabalhar aqui não é atender cliente: a recepção e o financeiro entram
|
|
26
|
+
// por esta mesma tela. Sem a escolha no cadastro, todo mundo nascia com
|
|
27
|
+
// coluna na grade da semana e horário livre para marcarem em cima.
|
|
28
|
+
{
|
|
29
|
+
key: "isBookable",
|
|
30
|
+
label: tl("Appears in the calendar", "Aparece na agenda"),
|
|
31
|
+
type: "boolean",
|
|
32
|
+
showInTable: true,
|
|
33
|
+
defaultValue: true,
|
|
34
|
+
inlineToggle: true,
|
|
35
|
+
group: "professional",
|
|
36
|
+
hint: tl(
|
|
37
|
+
"Clients can be booked with this person.",
|
|
38
|
+
"Clientes podem ser marcados com esta pessoa."
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
42
|
+
],
|
|
43
|
+
// No custom documents tab: the `person` archetype supplies the shared
|
|
44
|
+
// custom_forms documents widget (same as clients / school-saas).
|
|
45
|
+
data: {
|
|
46
|
+
// Pessoa pura, como no resto-saas: quem é da equipe é `people.kind='staff'`.
|
|
47
|
+
// A extensão `staff_members` e a view `v_staff` não existem mais.
|
|
48
|
+
table: "people",
|
|
49
|
+
tenantScoped: true,
|
|
50
|
+
archetype: "person",
|
|
51
|
+
archetypeKind: "staff",
|
|
52
|
+
// `profession` e `commissionRate` eram colunas da extensão. A comissão de
|
|
53
|
+
// verdade é a do motor de regras do Financeiro (plg_financial_commission_*),
|
|
54
|
+
// que sabe dividir por serviço e por profissional; este número aqui é o
|
|
55
|
+
// resquício de quando havia um só. Fica em custom_fields para não sumir.
|
|
56
|
+
customFields: ["profession", "commissionRate"],
|
|
57
|
+
searchColumns: ["name", "email", "phone"]
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var supplierEntity = {
|
|
61
|
+
name: tl("Supplier", "Fornecedor"),
|
|
62
|
+
namePlural: tl("Suppliers", "Fornecedores"),
|
|
63
|
+
icon: "Building2",
|
|
64
|
+
layout: "person",
|
|
65
|
+
displayField: "name",
|
|
66
|
+
defaultSort: "name",
|
|
67
|
+
fields: [
|
|
68
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
69
|
+
{ key: "email", label: "E-mail", type: "email", showInTable: true },
|
|
70
|
+
{ key: "phone", label: tl("Phone", "Telefone"), type: "phone", showInTable: true },
|
|
71
|
+
{ key: "notes", label: tl("Notes", "Observa\xE7\xF5es"), type: "textarea", showInTable: false },
|
|
72
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
73
|
+
],
|
|
74
|
+
data: {
|
|
75
|
+
table: "people",
|
|
76
|
+
schema: "public",
|
|
77
|
+
tenantScoped: true,
|
|
78
|
+
archetype: "person",
|
|
79
|
+
archetypeKind: "supplier",
|
|
80
|
+
filters: { kind: "supplier" },
|
|
81
|
+
defaults: { kind: "supplier" }
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var contactEntity = {
|
|
85
|
+
name: tl("Contact", "Contato"),
|
|
86
|
+
namePlural: tl("Contacts", "Contatos"),
|
|
87
|
+
icon: "Contact",
|
|
88
|
+
layout: "person",
|
|
89
|
+
displayField: "name",
|
|
90
|
+
defaultSort: "name",
|
|
91
|
+
fields: [
|
|
92
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
93
|
+
{ key: "email", label: "E-mail", type: "email", showInTable: true, searchable: true },
|
|
94
|
+
{ key: "phone", label: tl("Phone", "Telefone"), type: "phone", showInTable: true, searchable: true },
|
|
95
|
+
{ key: "notes", label: tl("Notes", "Observa\xE7\xF5es"), type: "textarea", showInTable: false },
|
|
96
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
97
|
+
],
|
|
98
|
+
data: {
|
|
99
|
+
table: "people",
|
|
100
|
+
schema: "public",
|
|
101
|
+
tenantScoped: true,
|
|
102
|
+
archetype: "person",
|
|
103
|
+
archetypeKind: "contact",
|
|
104
|
+
filters: { kind: "contact" },
|
|
105
|
+
defaults: { kind: "contact" },
|
|
106
|
+
searchColumns: ["name", "email", "phone"]
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var partnershipEntity = {
|
|
110
|
+
name: tl("Partnership", "Parceria"),
|
|
111
|
+
namePlural: tl("Partnerships", "Parcerias"),
|
|
112
|
+
icon: "Handshake",
|
|
113
|
+
layout: "person",
|
|
114
|
+
displayField: "name",
|
|
115
|
+
defaultSort: "name",
|
|
116
|
+
fields: [
|
|
117
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
118
|
+
{ key: "phone", label: tl("Phone", "Telefone"), type: "phone", showInTable: true },
|
|
119
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
120
|
+
],
|
|
121
|
+
data: {
|
|
122
|
+
table: "people",
|
|
123
|
+
schema: "public",
|
|
124
|
+
tenantScoped: true,
|
|
125
|
+
archetype: "person",
|
|
126
|
+
archetypeKind: "partner",
|
|
127
|
+
filters: { kind: "partner" },
|
|
128
|
+
defaults: { kind: "partner" }
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var equipmentEntity = {
|
|
132
|
+
name: tl("Equipment", "Equipamento"),
|
|
133
|
+
namePlural: tl("Equipment", "Equipamentos"),
|
|
134
|
+
icon: "Wrench",
|
|
135
|
+
layout: "product",
|
|
136
|
+
displayField: "name",
|
|
137
|
+
defaultSort: "name",
|
|
138
|
+
fields: [
|
|
139
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
140
|
+
{ key: "description", label: tl("Model", "Modelo"), type: "text", showInTable: true },
|
|
141
|
+
{ key: "sku", label: tl("Serial Number", "N\xFAmero de S\xE9rie"), type: "text", showInTable: true },
|
|
142
|
+
{ key: "status", label: "Status", type: "select", options: ["active", "maintenance", "retired"], showInTable: true, defaultValue: "active" }
|
|
143
|
+
],
|
|
144
|
+
data: {
|
|
145
|
+
table: "products",
|
|
146
|
+
schema: "public",
|
|
147
|
+
tenantScoped: true,
|
|
148
|
+
filters: { kind: "asset" },
|
|
149
|
+
defaults: { kind: "asset" }
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var originEntity = {
|
|
153
|
+
name: tl("Origin", "Origem"),
|
|
154
|
+
namePlural: tl("Origins", "Origens"),
|
|
155
|
+
icon: "Globe",
|
|
156
|
+
displayField: "name",
|
|
157
|
+
defaultSort: "name",
|
|
158
|
+
fields: [
|
|
159
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
160
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
161
|
+
],
|
|
162
|
+
data: {
|
|
163
|
+
table: "categories",
|
|
164
|
+
schema: "public",
|
|
165
|
+
tenantScoped: true,
|
|
166
|
+
filters: { kind: "origin" },
|
|
167
|
+
defaults: { kind: "origin" }
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var serviceCategoryEntity = {
|
|
171
|
+
name: tl("Service Category", "Categoria de Servi\xE7o"),
|
|
172
|
+
namePlural: tl("Service Categories", "Categorias de Servi\xE7o"),
|
|
173
|
+
icon: "Tag",
|
|
174
|
+
displayField: "name",
|
|
175
|
+
defaultSort: "sortOrder",
|
|
176
|
+
fields: [
|
|
177
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
178
|
+
{ key: "sortOrder", label: tl("Order", "Ordem"), type: "number", showInTable: true, defaultValue: 0 },
|
|
179
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
180
|
+
],
|
|
181
|
+
data: {
|
|
182
|
+
table: "categories",
|
|
183
|
+
schema: "public",
|
|
184
|
+
tenantScoped: true,
|
|
185
|
+
filters: { kind: "service_category" },
|
|
186
|
+
defaults: { kind: "service_category" }
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
var serviceLocationEntity = {
|
|
190
|
+
name: tl("Service Location", "Local de Atendimento"),
|
|
191
|
+
namePlural: tl("Service Locations", "Locais de Atendimento"),
|
|
192
|
+
icon: "MapPin",
|
|
193
|
+
layout: "location",
|
|
194
|
+
displayField: "name",
|
|
195
|
+
subtitleField: "city",
|
|
196
|
+
defaultSort: "name",
|
|
197
|
+
fields: [
|
|
198
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
199
|
+
{ key: "phone", label: tl("Phone", "Telefone"), type: "phone", showInTable: true },
|
|
200
|
+
{ key: "city", label: tl("City", "Cidade"), type: "text", showInTable: true, searchable: true },
|
|
201
|
+
{ key: "state", label: tl("State", "Estado"), type: "text", showInTable: true },
|
|
202
|
+
{ key: "address", label: tl("Address", "Endere\xE7o"), type: "text" },
|
|
203
|
+
{ key: "country", label: tl("Country", "Pa\xEDs"), type: "text", defaultValue: "BR" },
|
|
204
|
+
{ key: "postalCode", label: tl("Postal Code", "CEP"), type: "text" },
|
|
205
|
+
{ key: "email", label: "E-mail", type: "email" },
|
|
206
|
+
{ key: "isHeadquarters", label: tl("Headquarters", "Matriz"), type: "boolean", showInTable: true, defaultValue: false },
|
|
207
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true },
|
|
208
|
+
{ key: "notes", label: tl("Notes", "Observa\xE7\xF5es"), type: "textarea" }
|
|
209
|
+
],
|
|
210
|
+
data: {
|
|
211
|
+
// A filial É a unidade. `v_units` é a ponte pública para o schema `app`, e
|
|
212
|
+
// traz exatamente os campos desta tela (nome, contato, endereço, matriz,
|
|
213
|
+
// ativo, observações). A tabela `locations` não existe mais.
|
|
214
|
+
table: "v_units",
|
|
215
|
+
tenantScoped: true,
|
|
216
|
+
archetype: "location",
|
|
217
|
+
archetypeKind: "branch",
|
|
218
|
+
searchColumns: ["name", "city", "state"]
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
var locationGroupEntity = {
|
|
222
|
+
name: tl("Location Group", "Grupo de Locais"),
|
|
223
|
+
namePlural: tl("Location Groups", "Grupos de Locais"),
|
|
224
|
+
icon: "Map",
|
|
225
|
+
displayField: "name",
|
|
226
|
+
defaultSort: "sortOrder",
|
|
227
|
+
fields: [
|
|
228
|
+
{ key: "name", label: tl("Name", "Nome"), type: "text", required: true, showInTable: true, searchable: true },
|
|
229
|
+
{ key: "sortOrder", label: tl("Order", "Ordem"), type: "number", showInTable: true, defaultValue: 0 },
|
|
230
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
231
|
+
],
|
|
232
|
+
data: {
|
|
233
|
+
table: "categories",
|
|
234
|
+
schema: "public",
|
|
235
|
+
tenantScoped: true,
|
|
236
|
+
filters: { kind: "location_group" },
|
|
237
|
+
defaults: { kind: "location_group" },
|
|
238
|
+
searchColumns: ["name"]
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
var serviceEntity = {
|
|
242
|
+
name: tl("Service", "Servi\xE7o"),
|
|
243
|
+
icon: "Briefcase",
|
|
244
|
+
layout: "service",
|
|
245
|
+
displayField: "name",
|
|
246
|
+
defaultSort: "name",
|
|
247
|
+
fields: [
|
|
248
|
+
{ key: "name", label: tl("Service Name", "Nome do Servi\xE7o"), type: "text", required: true, searchable: true, showInTable: true },
|
|
249
|
+
{ key: "description", label: tl("Description", "Descri\xE7\xE3o"), type: "textarea", showInTable: false },
|
|
250
|
+
{ key: "durationMinutes", label: tl("Duration (min)", "Dura\xE7\xE3o (min)"), type: "number", required: true, showInTable: true },
|
|
251
|
+
{ key: "price", label: tl("Price", "Pre\xE7o"), type: "currency", required: true, showInTable: true },
|
|
252
|
+
// A pergunta que o BeautySoft responde com `service_kind`: isto se MARCA,
|
|
253
|
+
// ou se VENDE? O tratamento entra num pacote e o que vai para a agenda é o
|
|
254
|
+
// passo — a avaliação, a revisão, a entrega do que foi comprado antes.
|
|
255
|
+
// Ver a migration 120 do spine.
|
|
256
|
+
{
|
|
257
|
+
key: "isBookable",
|
|
258
|
+
label: tl("Appears in the calendar", "Aparece na agenda"),
|
|
259
|
+
type: "boolean",
|
|
260
|
+
showInTable: true,
|
|
261
|
+
defaultValue: true,
|
|
262
|
+
inlineToggle: true,
|
|
263
|
+
hint: tl(
|
|
264
|
+
"Can be picked when booking an appointment. Turn off for what is sold in a package and never booked on its own.",
|
|
265
|
+
"Pode ser escolhido ao marcar um hor\xE1rio. Desligue para o que \xE9 vendido em pacote e nunca se marca sozinho."
|
|
266
|
+
)
|
|
267
|
+
},
|
|
268
|
+
{ key: "isActive", label: tl("Active", "Ativo"), type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
269
|
+
],
|
|
270
|
+
// Services are their own Ring-0 archetype table (public.services), not a
|
|
271
|
+
// kind of product. Direct query by table+schema (no kind filter).
|
|
272
|
+
data: {
|
|
273
|
+
table: "services",
|
|
274
|
+
schema: "public",
|
|
275
|
+
tenantScoped: true,
|
|
276
|
+
searchColumns: ["name"]
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// src/index.ts
|
|
281
|
+
var SCREENS = [
|
|
282
|
+
{ id: "services", path: "/registry/services", entity: serviceEntity, icon: "Briefcase", feature: "services" },
|
|
283
|
+
{ id: "serviceCategories", path: "/registry/categories", entity: serviceCategoryEntity, icon: "Tag", feature: "services" },
|
|
284
|
+
{ id: "locations", path: "/registry/locations", entity: serviceLocationEntity, icon: "MapPin", feature: "services" },
|
|
285
|
+
{ id: "locationGroups", path: "/registry/location-groups", entity: locationGroupEntity, icon: "Map", feature: "services" },
|
|
286
|
+
{ id: "staff", path: "/registry/staff", entity: staffEntity, icon: "UserCog" },
|
|
287
|
+
{ id: "contacts", path: "/registry/contacts", entity: contactEntity, icon: "Contact" },
|
|
288
|
+
{ id: "suppliers", path: "/registry/suppliers", entity: supplierEntity, icon: "Building2" },
|
|
289
|
+
{ id: "partnerships", path: "/registry/partnerships", entity: partnershipEntity, icon: "Handshake" },
|
|
290
|
+
{ id: "equipment", path: "/registry/equipment", entity: equipmentEntity, icon: "Wrench" },
|
|
291
|
+
{ id: "origins", path: "/registry/origins", entity: originEntity, icon: "Globe" }
|
|
292
|
+
];
|
|
293
|
+
function createRegistryPlugin(options) {
|
|
294
|
+
const section = options?.navSection ?? "main";
|
|
295
|
+
const base = options?.navPosition ?? 80;
|
|
296
|
+
const active = SCREENS.filter((screen) => options?.modules?.[screen.id] !== false);
|
|
297
|
+
const pages = active.map((screen, index) => {
|
|
298
|
+
const entity = options?.entities?.[screen.id] ?? screen.entity;
|
|
299
|
+
const feature = screen.feature;
|
|
300
|
+
return {
|
|
301
|
+
screen,
|
|
302
|
+
entity,
|
|
303
|
+
position: base + index * 0.1,
|
|
304
|
+
component: createCrudPage(entity, feature ? { feature } : void 0),
|
|
305
|
+
permission: feature ? { feature, action: "read" } : void 0
|
|
306
|
+
};
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
id: "registry",
|
|
310
|
+
name: "Cadastros",
|
|
311
|
+
icon: "ClipboardList",
|
|
312
|
+
version: "1.0.0",
|
|
313
|
+
scope: options?.scope ?? "universal",
|
|
314
|
+
verticalId: options?.verticalId,
|
|
315
|
+
defaultEnabled: true,
|
|
316
|
+
dependencies: [],
|
|
317
|
+
// Uma linha por ficha, plana. Quem as junta sob "Cadastros" — e em que
|
|
318
|
+
// ordem, e com que nome — é o `rail` do ofício. Ver packages/admin
|
|
319
|
+
// rail-preset.ts: o ofício AGRUPA, o plugin NOMEIA, e o `labels` do preset
|
|
320
|
+
// é a casa dizendo que ali a ficha se chama outra coisa.
|
|
321
|
+
navigation: pages.map((page) => ({
|
|
322
|
+
section,
|
|
323
|
+
position: page.position,
|
|
324
|
+
label: page.entity.namePlural ?? page.entity.name,
|
|
325
|
+
route: page.screen.path,
|
|
326
|
+
icon: page.screen.icon,
|
|
327
|
+
...page.permission ? { permission: page.permission } : {}
|
|
328
|
+
})),
|
|
329
|
+
routes: pages.map((page) => ({
|
|
330
|
+
path: page.screen.path,
|
|
331
|
+
component: page.component,
|
|
332
|
+
...page.permission ? { permission: page.permission } : {}
|
|
333
|
+
}))
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export { contactEntity, createRegistryPlugin, equipmentEntity, locationGroupEntity, originEntity, partnershipEntity, serviceCategoryEntity, serviceEntity, serviceLocationEntity, staffEntity, supplierEntity };
|
|
338
|
+
//# sourceMappingURL=index.js.map
|
|
339
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/entities.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { tl } from '@fayz-ai/core'\nimport type { EntityDef } from '@fayz-ai/core'\n\n// ---------------------------------------------------------------------------\n// entities — as fichas de referência, iguais em qualquer ofício.\n// ---------------------------------------------------------------------------\n// Estas dez definições estavam escritas à mão dentro de UM aplicativo\n// (beauty-saas/src/types/registry.ts), e por isso os Cadastros existiam só lá:\n// o aplicativo central abria a mesma conta, com os mesmos dados, e o menu não\n// tinha a rubrica — não porque ela estivesse escondida, mas porque as telas\n// não existiam para serem agrupadas.\n//\n// Nada aqui é de salão. São as tabelas da espinha vistas por dez recortes:\n// `people` por papel (equipe, fornecedor, contato, parceiro), `products` como\n// patrimônio, `categories` por tipo (origem, categoria de serviço, grupo de\n// locais), `services`, e `v_units` — a filial. Um restaurante tem fornecedor e\n// equipamento; uma escola tem contato e origem. O que muda por ofício é o\n// NOME de cada linha no menu, e isso o preset do banco já resolve.\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Staff — person archetype + extension table\n// ---------------------------------------------------------------------------\nexport const staffEntity: EntityDef = {\n name: tl('Staff', 'Profissional'),\n namePlural: tl('Staff', 'Profissionais'),\n icon: 'UserCog',\n // staff_members is an extension table (no name column) — useless to the\n // agent's generic reader. The agent sees professionals through the\n // beauty:professionals read-model (people kind='staff') instead.\n agentHidden: true,\n layout: 'person',\n displayField: 'name',\n defaultSort: 'name',\n fieldGroups: [\n { id: 'professional', label: tl('Professional Info', 'Informações Profissionais'), columns: 2 },\n ],\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'email', label: 'E-mail', type: 'email', showInTable: true, searchable: true },\n { key: 'phone', label: tl('Phone', 'Telefone'), type: 'phone', showInTable: true },\n { key: 'profession', label: tl('Profession', 'Profissão'), type: 'text', showInTable: true, group: 'professional' },\n { key: 'commissionRate', label: tl('Commission (%)', 'Comissão (%)'), type: 'number', showInTable: true, group: 'professional', defaultValue: 0 },\n // Trabalhar aqui não é atender cliente: a recepção e o financeiro entram\n // por esta mesma tela. Sem a escolha no cadastro, todo mundo nascia com\n // coluna na grade da semana e horário livre para marcarem em cima.\n {\n key: 'isBookable', label: tl('Appears in the calendar', 'Aparece na agenda'), type: 'boolean',\n showInTable: true, defaultValue: true, inlineToggle: true, group: 'professional',\n hint: tl(\n 'Clients can be booked with this person.',\n 'Clientes podem ser marcados com esta pessoa.',\n ),\n },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n ],\n // No custom documents tab: the `person` archetype supplies the shared\n // custom_forms documents widget (same as clients / school-saas).\n data: {\n // Pessoa pura, como no resto-saas: quem é da equipe é `people.kind='staff'`.\n // A extensão `staff_members` e a view `v_staff` não existem mais.\n table: 'people',\n tenantScoped: true,\n archetype: 'person',\n archetypeKind: 'staff',\n // `profession` e `commissionRate` eram colunas da extensão. A comissão de\n // verdade é a do motor de regras do Financeiro (plg_financial_commission_*),\n // que sabe dividir por serviço e por profissional; este número aqui é o\n // resquício de quando havia um só. Fica em custom_fields para não sumir.\n customFields: ['profession', 'commissionRate'],\n searchColumns: ['name', 'email', 'phone'],\n },\n}\n\n// ---------------------------------------------------------------------------\n// Suppliers — person archetype, direct query\n// ---------------------------------------------------------------------------\nexport const supplierEntity: EntityDef = {\n name: tl('Supplier', 'Fornecedor'),\n namePlural: tl('Suppliers', 'Fornecedores'),\n icon: 'Building2',\n layout: 'person',\n displayField: 'name',\n defaultSort: 'name',\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'email', label: 'E-mail', type: 'email', showInTable: true },\n { key: 'phone', label: tl('Phone', 'Telefone'), type: 'phone', showInTable: true },\n { key: 'notes', label: tl('Notes', 'Observações'), type: 'textarea', showInTable: false },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n ],\n data: {\n table: 'people',\n schema: 'public',\n tenantScoped: true,\n archetype: 'person',\n archetypeKind: 'supplier',\n filters: { kind: 'supplier' },\n defaults: { kind: 'supplier' },\n },\n}\n\n// ---------------------------------------------------------------------------\n// Contacts — person archetype, direct query\n// ---------------------------------------------------------------------------\nexport const contactEntity: EntityDef = {\n name: tl('Contact', 'Contato'),\n namePlural: tl('Contacts', 'Contatos'),\n icon: 'Contact',\n layout: 'person',\n displayField: 'name',\n defaultSort: 'name',\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'email', label: 'E-mail', type: 'email', showInTable: true, searchable: true },\n { key: 'phone', label: tl('Phone', 'Telefone'), type: 'phone', showInTable: true, searchable: true },\n { key: 'notes', label: tl('Notes', 'Observações'), type: 'textarea', showInTable: false },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n ],\n data: {\n table: 'people',\n schema: 'public',\n tenantScoped: true,\n archetype: 'person',\n archetypeKind: 'contact',\n filters: { kind: 'contact' },\n defaults: { kind: 'contact' },\n searchColumns: ['name', 'email', 'phone'],\n },\n}\n\n// ---------------------------------------------------------------------------\n// Partnerships — person archetype, direct query\n// ---------------------------------------------------------------------------\nexport const partnershipEntity: EntityDef = {\n name: tl('Partnership', 'Parceria'),\n namePlural: tl('Partnerships', 'Parcerias'),\n icon: 'Handshake',\n layout: 'person',\n displayField: 'name',\n defaultSort: 'name',\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'phone', label: tl('Phone', 'Telefone'), type: 'phone', showInTable: true },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n ],\n data: {\n table: 'people',\n schema: 'public',\n tenantScoped: true,\n archetype: 'person',\n archetypeKind: 'partner',\n filters: { kind: 'partner' },\n defaults: { kind: 'partner' },\n },\n}\n\n// ---------------------------------------------------------------------------\n// Equipment — product archetype, direct query\n// ---------------------------------------------------------------------------\nexport const equipmentEntity: EntityDef = {\n name: tl('Equipment', 'Equipamento'),\n namePlural: tl('Equipment', 'Equipamentos'),\n icon: 'Wrench',\n layout: 'product',\n displayField: 'name',\n defaultSort: 'name',\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'description', label: tl('Model', 'Modelo'), type: 'text', showInTable: true },\n { key: 'sku', label: tl('Serial Number', 'Número de Série'), type: 'text', showInTable: true },\n { key: 'status', label: 'Status', type: 'select', options: ['active', 'maintenance', 'retired'], showInTable: true, defaultValue: 'active' },\n ],\n data: {\n table: 'products',\n schema: 'public',\n tenantScoped: true,\n filters: { kind: 'asset' },\n defaults: { kind: 'asset' },\n },\n}\n\n// ---------------------------------------------------------------------------\n// Origins — category archetype, direct query\n// ---------------------------------------------------------------------------\nexport const originEntity: EntityDef = {\n name: tl('Origin', 'Origem'),\n namePlural: tl('Origins', 'Origens'),\n icon: 'Globe',\n displayField: 'name',\n defaultSort: 'name',\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n ],\n data: {\n table: 'categories',\n schema: 'public',\n tenantScoped: true,\n filters: { kind: 'origin' },\n defaults: { kind: 'origin' },\n },\n}\n\n// ---------------------------------------------------------------------------\n// Service Categories — category archetype, direct query\n// ---------------------------------------------------------------------------\nexport const serviceCategoryEntity: EntityDef = {\n name: tl('Service Category', 'Categoria de Serviço'),\n namePlural: tl('Service Categories', 'Categorias de Serviço'),\n icon: 'Tag',\n displayField: 'name',\n defaultSort: 'sortOrder',\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'sortOrder', label: tl('Order', 'Ordem'), type: 'number', showInTable: true, defaultValue: 0 },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n ],\n data: {\n table: 'categories',\n schema: 'public',\n tenantScoped: true,\n filters: { kind: 'service_category' },\n defaults: { kind: 'service_category' },\n },\n}\n\n// ---------------------------------------------------------------------------\n// Service Locations — location archetype, direct query\n// ---------------------------------------------------------------------------\nexport const serviceLocationEntity: EntityDef = {\n name: tl('Service Location', 'Local de Atendimento'),\n namePlural: tl('Service Locations', 'Locais de Atendimento'),\n icon: 'MapPin',\n layout: 'location',\n displayField: 'name',\n subtitleField: 'city',\n defaultSort: 'name',\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'phone', label: tl('Phone', 'Telefone'), type: 'phone', showInTable: true },\n { key: 'city', label: tl('City', 'Cidade'), type: 'text', showInTable: true, searchable: true },\n { key: 'state', label: tl('State', 'Estado'), type: 'text', showInTable: true },\n { key: 'address', label: tl('Address', 'Endereço'), type: 'text' },\n { key: 'country', label: tl('Country', 'País'), type: 'text', defaultValue: 'BR' },\n { key: 'postalCode', label: tl('Postal Code', 'CEP'), type: 'text' },\n { key: 'email', label: 'E-mail', type: 'email' },\n { key: 'isHeadquarters', label: tl('Headquarters', 'Matriz'), type: 'boolean', showInTable: true, defaultValue: false },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n { key: 'notes', label: tl('Notes', 'Observações'), type: 'textarea' },\n ],\n data: {\n // A filial É a unidade. `v_units` é a ponte pública para o schema `app`, e\n // traz exatamente os campos desta tela (nome, contato, endereço, matriz,\n // ativo, observações). A tabela `locations` não existe mais.\n table: 'v_units',\n tenantScoped: true,\n archetype: 'location',\n archetypeKind: 'branch',\n searchColumns: ['name', 'city', 'state'],\n },\n}\n\n// ---------------------------------------------------------------------------\n// Location Groups — category archetype, direct query\n// ---------------------------------------------------------------------------\nexport const locationGroupEntity: EntityDef = {\n name: tl('Location Group', 'Grupo de Locais'),\n namePlural: tl('Location Groups', 'Grupos de Locais'),\n icon: 'Map',\n displayField: 'name',\n defaultSort: 'sortOrder',\n fields: [\n { key: 'name', label: tl('Name', 'Nome'), type: 'text', required: true, showInTable: true, searchable: true },\n { key: 'sortOrder', label: tl('Order', 'Ordem'), type: 'number', showInTable: true, defaultValue: 0 },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n ],\n data: {\n table: 'categories',\n schema: 'public',\n tenantScoped: true,\n filters: { kind: 'location_group' },\n defaults: { kind: 'location_group' },\n searchColumns: ['name'],\n },\n}\n\n// ---------------------------------------------------------------------------\n// Services — service archetype, direct query on public.services\n// ---------------------------------------------------------------------------\nexport const serviceEntity: EntityDef = {\n name: tl('Service', 'Serviço'),\n icon: 'Briefcase',\n layout: 'service',\n displayField: 'name',\n defaultSort: 'name',\n fields: [\n { key: 'name', label: tl('Service Name', 'Nome do Serviço'), type: 'text', required: true, searchable: true, showInTable: true },\n { key: 'description', label: tl('Description', 'Descrição'), type: 'textarea', showInTable: false },\n { key: 'durationMinutes', label: tl('Duration (min)', 'Duração (min)'), type: 'number', required: true, showInTable: true },\n { key: 'price', label: tl('Price', 'Preço'), type: 'currency', required: true, showInTable: true },\n // A pergunta que o BeautySoft responde com `service_kind`: isto se MARCA,\n // ou se VENDE? O tratamento entra num pacote e o que vai para a agenda é o\n // passo — a avaliação, a revisão, a entrega do que foi comprado antes.\n // Ver a migration 120 do spine.\n {\n key: 'isBookable', label: tl('Appears in the calendar', 'Aparece na agenda'), type: 'boolean',\n showInTable: true, defaultValue: true, inlineToggle: true,\n hint: tl(\n 'Can be picked when booking an appointment. Turn off for what is sold in a package and never booked on its own.',\n 'Pode ser escolhido ao marcar um horário. Desligue para o que é vendido em pacote e nunca se marca sozinho.',\n ),\n },\n { key: 'isActive', label: tl('Active', 'Ativo'), type: 'boolean', showInTable: true, defaultValue: true, inlineToggle: true },\n ],\n // Services are their own Ring-0 archetype table (public.services), not a\n // kind of product. Direct query by table+schema (no kind filter).\n data: {\n table: 'services',\n schema: 'public',\n tenantScoped: true,\n searchColumns: ['name'],\n },\n}\n","import type { PluginManifest, PluginScope, VerticalId, EntityDef } from '@fayz-ai/core'\nimport { createCrudPage } from '@fayz-ai/admin'\n\nimport {\n staffEntity, supplierEntity, contactEntity, partnershipEntity, equipmentEntity,\n originEntity, serviceCategoryEntity, serviceLocationEntity, locationGroupEntity,\n serviceEntity,\n} from './lib/entities'\n\n// ---------------------------------------------------------------------------\n// plugin-registry — os Cadastros.\n// ---------------------------------------------------------------------------\n// ── O buraco que ele fecha ─────────────────────────────────────────────────\n// O aplicativo central carrega o ofício de quem entra, e para o Espaço Facial\n// ele carregava tudo MENOS os Cadastros: a rubrica não desenhava porque as dez\n// telas dela não existiam ali. Elas estavam escritas dentro do beauty-saas, em\n// `src/config/pages.tsx` — um aplicativo só, e portanto uma conta só.\n//\n// Era o mesmo defeito que o preset resolveu para a ORDEM e a 118 para o NOME,\n// um degrau abaixo: não adianta o ofício saber agrupar e nomear se ninguém\n// serve a tela. Quem serve tela é plugin.\n//\n// ── Por que um plugin, e não mais uma pasta no aplicativo ──────────────────\n// Nenhuma destas fichas é de salão. Fornecedor, contato, equipamento, origem e\n// filial são o que QUALQUER casa cadastra; o restaurante compra de fornecedor\n// e a escola registra origem de matrícula. O que muda por ofício é o nome da\n// linha no menu — \"Funcionários\" no BeautySoft, \"Equipe\" no resto — e disso\n// cuida o `labels` do preset, no banco.\n//\n// ── O ofício liga o que usa ────────────────────────────────────────────────\n// `modules` desliga tela a tela: uma loja sem atendimento não quer Serviços\n// nem Categorias de Serviço, e uma rubrica cujos filhos saíram todos não\n// desenha. É o mesmo contrato dos outros plugins — oferecer, não impor.\n// ---------------------------------------------------------------------------\n\n/** Cada tela de cadastro, na ORDEM em que o menu as lê por padrão. */\nconst SCREENS = [\n { id: 'services', path: '/registry/services', entity: serviceEntity, icon: 'Briefcase', feature: 'services' },\n { id: 'serviceCategories', path: '/registry/categories', entity: serviceCategoryEntity, icon: 'Tag', feature: 'services' },\n { id: 'locations', path: '/registry/locations', entity: serviceLocationEntity, icon: 'MapPin', feature: 'services' },\n { id: 'locationGroups', path: '/registry/location-groups', entity: locationGroupEntity, icon: 'Map', feature: 'services' },\n { id: 'staff', path: '/registry/staff', entity: staffEntity, icon: 'UserCog' },\n { id: 'contacts', path: '/registry/contacts', entity: contactEntity, icon: 'Contact' },\n { id: 'suppliers', path: '/registry/suppliers', entity: supplierEntity, icon: 'Building2' },\n { id: 'partnerships', path: '/registry/partnerships', entity: partnershipEntity, icon: 'Handshake' },\n { id: 'equipment', path: '/registry/equipment', entity: equipmentEntity, icon: 'Wrench' },\n { id: 'origins', path: '/registry/origins', entity: originEntity, icon: 'Globe' },\n] as const\n\nexport type RegistryScreenId = (typeof SCREENS)[number]['id']\n\nexport interface RegistryPluginOptions {\n scope?: PluginScope\n verticalId?: VerticalId\n /** Onde a primeira linha cai no rail. As demais vêm em degraus de 0,1. */\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n /**\n * Quais telas esta casa tem. Ausente = todas.\n *\n * `false` é a única forma de negar: uma chave que ninguém respondeu cai no\n * padrão, não em \"não\" — do contrário, publicar uma ficha nova seria o dia\n * em que ela some de toda conta que não a listou.\n */\n modules?: Partial<Record<RegistryScreenId, boolean>>\n /**\n * Troca a definição de uma ficha sem trocar a tela.\n *\n * A casa que precisa de um campo a mais no fornecedor passa o EntityDef dela\n * aqui em vez de reimplementar a rota — que foi exatamente como as dez\n * acabaram dentro de um aplicativo.\n */\n entities?: Partial<Record<RegistryScreenId, EntityDef>>\n}\n\nexport function createRegistryPlugin(options?: RegistryPluginOptions): PluginManifest {\n const section = options?.navSection ?? 'main'\n const base = options?.navPosition ?? 80\n\n const active = SCREENS.filter((screen) => options?.modules?.[screen.id] !== false)\n\n const pages = active.map((screen, index) => {\n const entity = options?.entities?.[screen.id] ?? (screen.entity as EntityDef)\n const feature = (screen as { feature?: string }).feature\n return {\n screen,\n entity,\n position: base + index * 0.1,\n component: createCrudPage(entity, feature ? { feature } : undefined),\n permission: feature ? { feature, action: 'read' as const } : undefined,\n }\n })\n\n return {\n id: 'registry',\n name: 'Cadastros',\n icon: 'ClipboardList',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n\n // Uma linha por ficha, plana. Quem as junta sob \"Cadastros\" — e em que\n // ordem, e com que nome — é o `rail` do ofício. Ver packages/admin\n // rail-preset.ts: o ofício AGRUPA, o plugin NOMEIA, e o `labels` do preset\n // é a casa dizendo que ali a ficha se chama outra coisa.\n navigation: pages.map((page) => ({\n section,\n position: page.position,\n label: page.entity.namePlural ?? page.entity.name,\n route: page.screen.path,\n icon: page.screen.icon,\n ...(page.permission ? { permission: page.permission } : {}),\n })),\n\n routes: pages.map((page) => ({\n path: page.screen.path,\n component: page.component as React.ComponentType<unknown>,\n ...(page.permission ? { permission: page.permission } : {}),\n })),\n }\n}\n\nexport {\n staffEntity, supplierEntity, contactEntity, partnershipEntity, equipmentEntity,\n originEntity, serviceCategoryEntity, serviceLocationEntity, locationGroupEntity,\n serviceEntity,\n} from './lib/entities'\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EntityDef } from '@fayz-ai/core';
|
|
2
|
+
export declare const staffEntity: EntityDef;
|
|
3
|
+
export declare const supplierEntity: EntityDef;
|
|
4
|
+
export declare const contactEntity: EntityDef;
|
|
5
|
+
export declare const partnershipEntity: EntityDef;
|
|
6
|
+
export declare const equipmentEntity: EntityDef;
|
|
7
|
+
export declare const originEntity: EntityDef;
|
|
8
|
+
export declare const serviceCategoryEntity: EntityDef;
|
|
9
|
+
export declare const serviceLocationEntity: EntityDef;
|
|
10
|
+
export declare const locationGroupEntity: EntityDef;
|
|
11
|
+
export declare const serviceEntity: EntityDef;
|
|
12
|
+
//# sourceMappingURL=entities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../src/lib/entities.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAsB9C,eAAO,MAAM,WAAW,EAAE,SAiDzB,CAAA;AAKD,eAAO,MAAM,cAAc,EAAE,SAuB5B,CAAA;AAKD,eAAO,MAAM,aAAa,EAAE,SAwB3B,CAAA;AAKD,eAAO,MAAM,iBAAiB,EAAE,SAqB/B,CAAA;AAKD,eAAO,MAAM,eAAe,EAAE,SAoB7B,CAAA;AAKD,eAAO,MAAM,YAAY,EAAE,SAiB1B,CAAA;AAKD,eAAO,MAAM,qBAAqB,EAAE,SAkBnC,CAAA;AAKD,eAAO,MAAM,qBAAqB,EAAE,SA+BnC,CAAA;AAKD,eAAO,MAAM,mBAAmB,EAAE,SAmBjC,CAAA;AAKD,eAAO,MAAM,aAAa,EAAE,SAiC3B,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fayz-ai/plugin-registry",
|
|
3
|
+
"fayz": {
|
|
4
|
+
"status": "beta",
|
|
5
|
+
"dependencies": []
|
|
6
|
+
},
|
|
7
|
+
"version": "0.11.0",
|
|
8
|
+
"description": "Fayz SDK — os Cadastros: as telas de referência que todo ofício tem",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
25
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
26
|
+
"zustand": "^4.5.0 || ^5.0.0",
|
|
27
|
+
"lucide-react": ">=0.400.0 <1.0.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@fayz-ai/core": "^0.30.0",
|
|
31
|
+
"@fayz-ai/admin": "^0.30.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/react": "^18.3.0",
|
|
35
|
+
"react": "^18.3.0",
|
|
36
|
+
"tsup": "^8.2.0",
|
|
37
|
+
"typescript": "^5.5.0",
|
|
38
|
+
"vitest": "^4.1.5",
|
|
39
|
+
"zustand": "^4.5.0",
|
|
40
|
+
"lucide-react": "^0.400.0"
|
|
41
|
+
},
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"keywords": [
|
|
44
|
+
"fayz",
|
|
45
|
+
"fayz-plugin",
|
|
46
|
+
"fayz-sdk"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup && tsc -b --force",
|
|
53
|
+
"dev": "tsup --watch",
|
|
54
|
+
"typecheck": "tsc -b tsconfig.test.json",
|
|
55
|
+
"test": "vitest run --passWithNoTests",
|
|
56
|
+
"clean": "rm -rf dist .tsbuildinfo"
|
|
57
|
+
}
|
|
58
|
+
}
|