@fayz-ai/plugin-workforce 0.2.0 → 0.2.3
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/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React3 from 'react';
|
|
2
|
-
import { createSafeDataProvider, getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core';
|
|
3
|
-
import { createPluginContext, saveSchedule, registerEntityPath, useModuleNavigation, useModuleNavAccess, parseViewId, EntitlementGate, UpgradePrompt, ScheduleEditor, usePermissionOptional, adminNavigateTo, formatCurrency, PersonLink, createCrudPage } from '@fayz-ai/admin';
|
|
4
|
-
import { ModulePage, FilterRail, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MiniCalendar, toLocalISODate, RailWorkspace, Badge, Button, Skeleton, resolveRangePreset, compareCodes, PageToolbar, DateRangeControl, StatTiles, StatementTable, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Input, ModalFooter, toast, Popover, PopoverTrigger, PopoverContent, Checkbox, Avatar, AvatarFallback, AvatarImage } from '@fayz-ai/ui';
|
|
5
|
-
import { Send, CalendarDays, Plus, ExternalLink, HandCoins, AlertTriangle, Users, CalendarCheck, Unlock, Lock, Clock, LayoutTemplate, ListTree, UserPlus, UserMinus, CakeSlice, Plane, CalendarClock, ShieldAlert, Stethoscope, ClipboardList, Trash2, Pencil, Eye, ImageOff, MapPin, ScanFace } from 'lucide-react';
|
|
2
|
+
import { recordRailFields, RECORD_GROUP, activeFacet, createSafeDataProvider, getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core';
|
|
3
|
+
import { createPluginContext, saveSchedule, registerEntityPath, useModuleNavigation, useModuleNavAccess, parseViewId, EntitlementGate, UpgradePrompt, ScheduleEditor, usePermissionOptional, adminNavigateTo, formatCurrency, PersonLink, createCrudPage, WidgetSlot } from '@fayz-ai/admin';
|
|
4
|
+
import { ModulePage, FilterRail, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MiniCalendar, toLocalISODate, RailWorkspace, Badge, Button, Skeleton, resolveRangePreset, compareCodes, PageToolbar, DateRangeControl, StatTiles, StatementTable, PageViewBar, ViewTabs, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Input, ModalFooter, toast, Popover, PopoverTrigger, PopoverContent, Checkbox, Avatar, AvatarFallback, AvatarImage } from '@fayz-ai/ui';
|
|
5
|
+
import { Send, CalendarDays, Plus, ExternalLink, HandCoins, AlertTriangle, Users, CalendarCheck, Unlock, Lock, Clock, LayoutTemplate, ListTree, UserPlus, UserMinus, CakeSlice, Plane, CalendarClock, ShieldAlert, Stethoscope, ClipboardList, FileText, ChevronRight, Trash2, Pencil, Eye, ImageOff, MapPin, ScanFace } from 'lucide-react';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
import { createStore } from 'zustand/vanilla';
|
|
8
8
|
|
|
@@ -182,7 +182,14 @@ function SchedulesView() {
|
|
|
182
182
|
)
|
|
183
183
|
] }) });
|
|
184
184
|
}
|
|
185
|
-
var WEEKDAYS = ["
|
|
185
|
+
var WEEKDAYS = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "S\xE1b"];
|
|
186
|
+
var WEEKDAY_NAMES = ["Domingo", "Segunda", "Ter\xE7a", "Quarta", "Quinta", "Sexta", "S\xE1bado"];
|
|
187
|
+
function plural(n, one, many) {
|
|
188
|
+
return `${n} ${n === 1 ? one : many}`;
|
|
189
|
+
}
|
|
190
|
+
function seats(shift) {
|
|
191
|
+
return shift.requirements.reduce((sum, r) => sum + (r.requiredCount || 0), 0);
|
|
192
|
+
}
|
|
186
193
|
function ShiftCard({ shift, onChanged }) {
|
|
187
194
|
const provider = useWorkforceProvider();
|
|
188
195
|
const [profession, setProfession] = React3.useState("");
|
|
@@ -220,7 +227,7 @@ function ShiftCard({ shift, onChanged }) {
|
|
|
220
227
|
}
|
|
221
228
|
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-3", children: [
|
|
222
229
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
223
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
230
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
224
231
|
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: shift.label }),
|
|
225
232
|
/* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
226
233
|
shift.startTime,
|
|
@@ -228,10 +235,10 @@ function ShiftCard({ shift, onChanged }) {
|
|
|
228
235
|
shift.endTime,
|
|
229
236
|
shift.crossesMidnight ? " \xB7 vira o dia" : ""
|
|
230
237
|
] }),
|
|
231
|
-
/* @__PURE__ */ jsx("div", { className: "mt-1 flex gap-1", children: WEEKDAYS.map((d, i) => /* @__PURE__ */ jsx(
|
|
238
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap gap-1", children: WEEKDAYS.map((d, i) => /* @__PURE__ */ jsx(
|
|
232
239
|
"span",
|
|
233
240
|
{
|
|
234
|
-
className: `inline-flex h-5
|
|
241
|
+
className: `inline-flex h-5 items-center justify-center rounded px-1.5 text-[10px] ${shift.weekdays.includes(i) ? "bg-primary/15 text-primary" : "bg-muted text-muted-foreground"}`,
|
|
235
242
|
children: d
|
|
236
243
|
},
|
|
237
244
|
i
|
|
@@ -246,8 +253,8 @@ function ShiftCard({ shift, onChanged }) {
|
|
|
246
253
|
")",
|
|
247
254
|
/* @__PURE__ */ jsx("button", { onClick: () => removeRequirement(r.id), "aria-label": `Remover ${r.professionName}`, children: /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3", "aria-hidden": true }) })
|
|
248
255
|
] }, r.id)) }),
|
|
249
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-2", children: [
|
|
250
|
-
/* @__PURE__ */ jsx(
|
|
256
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [
|
|
257
|
+
/* @__PURE__ */ jsx("div", { className: "w-full sm:w-56", children: /* @__PURE__ */ jsx(
|
|
251
258
|
Input,
|
|
252
259
|
{
|
|
253
260
|
value: profession,
|
|
@@ -255,25 +262,24 @@ function ShiftCard({ shift, onChanged }) {
|
|
|
255
262
|
placeholder: "Fun\xE7\xE3o (ex.: Gar\xE7om)",
|
|
256
263
|
className: "h-8"
|
|
257
264
|
}
|
|
258
|
-
),
|
|
259
|
-
/* @__PURE__ */ jsx(
|
|
265
|
+
) }),
|
|
266
|
+
/* @__PURE__ */ jsx("div", { className: "w-20", children: /* @__PURE__ */ jsx(
|
|
260
267
|
Input,
|
|
261
268
|
{
|
|
262
269
|
value: count,
|
|
263
270
|
onChange: (e) => setCount(e.target.value),
|
|
264
271
|
type: "number",
|
|
265
272
|
min: 1,
|
|
266
|
-
className: "h-8
|
|
273
|
+
className: "h-8",
|
|
267
274
|
"aria-label": "Vagas"
|
|
268
275
|
}
|
|
269
|
-
),
|
|
276
|
+
) }),
|
|
270
277
|
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: addRequirement, children: "Adicionar" })
|
|
271
278
|
] })
|
|
272
279
|
] });
|
|
273
280
|
}
|
|
274
|
-
function
|
|
281
|
+
function NewShiftModal({ template, open, onOpenChange, onChanged }) {
|
|
275
282
|
const provider = useWorkforceProvider();
|
|
276
|
-
const [open, setOpen] = React3.useState(false);
|
|
277
283
|
const [label, setLabel] = React3.useState("");
|
|
278
284
|
const [start, setStart] = React3.useState("11:00");
|
|
279
285
|
const [end, setEnd] = React3.useState("15:00");
|
|
@@ -289,64 +295,92 @@ function TemplateCard({ template, onChanged }) {
|
|
|
289
295
|
weekdays,
|
|
290
296
|
sortOrder: template.shifts.length
|
|
291
297
|
});
|
|
292
|
-
|
|
298
|
+
onOpenChange(false);
|
|
293
299
|
setLabel("");
|
|
294
300
|
onChanged();
|
|
295
301
|
} catch (e) {
|
|
296
302
|
toast.error(e?.message ?? "N\xE3o foi poss\xEDvel salvar o turno.");
|
|
297
303
|
}
|
|
298
304
|
}
|
|
305
|
+
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange, children: /* @__PURE__ */ jsxs(ModalContent, { size: "sm", children: [
|
|
306
|
+
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: "Novo turno" }) }),
|
|
307
|
+
/* @__PURE__ */ jsxs(ModalBody, { className: "space-y-3", children: [
|
|
308
|
+
/* @__PURE__ */ jsx(Input, { value: label, onChange: (e) => setLabel(e.target.value), placeholder: "Nome do turno (ex.: Almo\xE7o)" }),
|
|
309
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
310
|
+
/* @__PURE__ */ jsx(Input, { type: "time", value: start, onChange: (e) => setStart(e.target.value), "aria-label": "In\xEDcio" }),
|
|
311
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "at\xE9" }),
|
|
312
|
+
/* @__PURE__ */ jsx(Input, { type: "time", value: end, onChange: (e) => setEnd(e.target.value), "aria-label": "Fim" })
|
|
313
|
+
] }),
|
|
314
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
315
|
+
/* @__PURE__ */ jsx("div", { className: "mb-1 text-xs text-muted-foreground", children: "Dias da semana" }),
|
|
316
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: WEEKDAYS.map((d, i) => /* @__PURE__ */ jsx(
|
|
317
|
+
"button",
|
|
318
|
+
{
|
|
319
|
+
type: "button",
|
|
320
|
+
onClick: () => setWeekdays((w) => w.includes(i) ? w.filter((x) => x !== i) : [...w, i]),
|
|
321
|
+
className: `h-8 rounded px-2 text-xs ${weekdays.includes(i) ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
322
|
+
children: d
|
|
323
|
+
},
|
|
324
|
+
i
|
|
325
|
+
)) })
|
|
326
|
+
] })
|
|
327
|
+
] }),
|
|
328
|
+
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
329
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), children: "Cancelar" }),
|
|
330
|
+
/* @__PURE__ */ jsx(Button, { onClick: addShift, children: "Salvar turno" })
|
|
331
|
+
] })
|
|
332
|
+
] }) });
|
|
333
|
+
}
|
|
334
|
+
function TemplateBlock({ template, shifts, onChanged }) {
|
|
335
|
+
const provider = useWorkforceProvider();
|
|
336
|
+
const [open, setOpen] = React3.useState(false);
|
|
299
337
|
async function removeTemplate() {
|
|
300
338
|
try {
|
|
301
339
|
await provider.deleteTemplate(template.id);
|
|
302
340
|
onChanged();
|
|
303
341
|
} catch (e) {
|
|
304
|
-
toast.error(e?.message ?? "N\xE3o foi poss\xEDvel remover o
|
|
342
|
+
toast.error(e?.message ?? "N\xE3o foi poss\xEDvel remover o template.");
|
|
305
343
|
}
|
|
306
344
|
}
|
|
307
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-
|
|
308
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-
|
|
309
|
-
/* @__PURE__ */
|
|
310
|
-
|
|
311
|
-
/* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
312
|
-
template.shifts.length,
|
|
313
|
-
" turno(s)"
|
|
314
|
-
] })
|
|
315
|
-
] }),
|
|
316
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
345
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border/60 bg-muted/20 p-3", children: [
|
|
346
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
|
|
347
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium", children: template.name }),
|
|
348
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
317
349
|
/* @__PURE__ */ jsxs(Button, { size: "sm", variant: "secondary", onClick: () => setOpen(true), children: [
|
|
318
350
|
/* @__PURE__ */ jsx(Plus, { className: "mr-1 h-4 w-4", "aria-hidden": true }),
|
|
319
351
|
" Turno"
|
|
320
352
|
] }),
|
|
321
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: removeTemplate, "aria-label":
|
|
353
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: removeTemplate, "aria-label": `Remover ${template.name}`, children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4", "aria-hidden": true }) })
|
|
322
354
|
] })
|
|
323
355
|
] }),
|
|
324
|
-
|
|
325
|
-
/* @__PURE__ */ jsx(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
356
|
+
shifts.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Nenhum turno neste template ainda." }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: shifts.map((s) => /* @__PURE__ */ jsx(ShiftCard, { shift: s, onChanged }, s.id)) }),
|
|
357
|
+
/* @__PURE__ */ jsx(NewShiftModal, { template, open, onOpenChange: setOpen, onChanged })
|
|
358
|
+
] });
|
|
359
|
+
}
|
|
360
|
+
function DayRow({ title, summary, empty, open, onToggle, children }) {
|
|
361
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-b border-border last:border-b-0", children: [
|
|
362
|
+
/* @__PURE__ */ jsxs(
|
|
363
|
+
"button",
|
|
364
|
+
{
|
|
365
|
+
type: "button",
|
|
366
|
+
onClick: onToggle,
|
|
367
|
+
"aria-expanded": open,
|
|
368
|
+
disabled: empty,
|
|
369
|
+
className: "flex w-full items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-muted/50 disabled:cursor-default disabled:hover:bg-transparent",
|
|
370
|
+
children: [
|
|
371
|
+
/* @__PURE__ */ jsx(
|
|
372
|
+
ChevronRight,
|
|
373
|
+
{
|
|
374
|
+
className: `h-4 w-4 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-90" : ""} ${empty ? "opacity-0" : ""}`,
|
|
375
|
+
"aria-hidden": true
|
|
376
|
+
}
|
|
377
|
+
),
|
|
378
|
+
/* @__PURE__ */ jsx("span", { className: `w-24 shrink-0 text-sm ${empty ? "text-muted-foreground" : "font-medium"}`, children: title }),
|
|
379
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm text-muted-foreground", children: summary })
|
|
380
|
+
]
|
|
381
|
+
}
|
|
382
|
+
),
|
|
383
|
+
open && /* @__PURE__ */ jsx("div", { className: "space-y-3 px-3 pb-3", children })
|
|
350
384
|
] });
|
|
351
385
|
}
|
|
352
386
|
function TemplatesView() {
|
|
@@ -356,9 +390,22 @@ function TemplatesView() {
|
|
|
356
390
|
const fetchTemplates = useWorkforceStore((s) => s.fetchTemplates);
|
|
357
391
|
const [open, setOpen] = React3.useState(false);
|
|
358
392
|
const [name, setName] = React3.useState("");
|
|
393
|
+
const [openDay, setOpenDay] = React3.useState(null);
|
|
359
394
|
React3.useEffect(() => {
|
|
360
395
|
void fetchTemplates();
|
|
361
396
|
}, [fetchTemplates]);
|
|
397
|
+
const byDay = React3.useMemo(() => {
|
|
398
|
+
return WEEKDAY_NAMES.map((_, day) => {
|
|
399
|
+
const blocks = templates.map((t) => ({ template: t, shifts: t.shifts.filter((s) => s.weekdays.includes(day)) })).filter((b) => b.shifts.length > 0);
|
|
400
|
+
const shiftCount = blocks.reduce((n, b) => n + b.shifts.length, 0);
|
|
401
|
+
const seatCount = blocks.reduce((n, b) => n + b.shifts.reduce((m, s) => m + seats(s), 0), 0);
|
|
402
|
+
return { day, blocks, shiftCount, seatCount };
|
|
403
|
+
});
|
|
404
|
+
}, [templates]);
|
|
405
|
+
const withoutShifts = React3.useMemo(
|
|
406
|
+
() => templates.filter((t) => t.shifts.length === 0),
|
|
407
|
+
[templates]
|
|
408
|
+
);
|
|
362
409
|
async function createTemplate() {
|
|
363
410
|
if (!name.trim()) return;
|
|
364
411
|
try {
|
|
@@ -367,18 +414,18 @@ function TemplatesView() {
|
|
|
367
414
|
setOpen(false);
|
|
368
415
|
await fetchTemplates();
|
|
369
416
|
} catch (e) {
|
|
370
|
-
toast.error(e?.message ?? "N\xE3o foi poss\xEDvel criar o
|
|
417
|
+
toast.error(e?.message ?? "N\xE3o foi poss\xEDvel criar o template.");
|
|
371
418
|
}
|
|
372
419
|
}
|
|
420
|
+
const onChanged = React3.useCallback(() => {
|
|
421
|
+
void fetchTemplates();
|
|
422
|
+
}, [fetchTemplates]);
|
|
373
423
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-6", children: [
|
|
374
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
375
|
-
/* @__PURE__ */
|
|
376
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Templates de escala" }),
|
|
377
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "O molde vem primeiro: sem ele n\xE3o se cria escala nenhuma." })
|
|
378
|
-
] }),
|
|
424
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
|
|
425
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Templates de escala" }),
|
|
379
426
|
/* @__PURE__ */ jsxs(Button, { onClick: () => setOpen(true), children: [
|
|
380
427
|
/* @__PURE__ */ jsx(Plus, { className: "mr-2 h-4 w-4", "aria-hidden": true }),
|
|
381
|
-
" Novo
|
|
428
|
+
" Novo template"
|
|
382
429
|
] })
|
|
383
430
|
] }),
|
|
384
431
|
loading ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
@@ -386,10 +433,27 @@ function TemplatesView() {
|
|
|
386
433
|
/* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full" })
|
|
387
434
|
] }) : templates.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-dashed border-border p-8 text-center", children: [
|
|
388
435
|
/* @__PURE__ */ jsx(LayoutTemplate, { className: "mx-auto mb-3 h-8 w-8 text-muted-foreground", "aria-hidden": true }),
|
|
389
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Nenhum
|
|
390
|
-
] }) : /* @__PURE__ */
|
|
436
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Nenhum template cadastrado." })
|
|
437
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
438
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-xl border border-border", children: byDay.map(({ day, blocks, shiftCount, seatCount }) => /* @__PURE__ */ jsx(
|
|
439
|
+
DayRow,
|
|
440
|
+
{
|
|
441
|
+
title: WEEKDAY_NAMES[day],
|
|
442
|
+
summary: shiftCount === 0 ? "Ningu\xE9m escalado" : `${plural(shiftCount, "turno", "turnos")} \xB7 ${plural(seatCount, "vaga", "vagas")}`,
|
|
443
|
+
empty: shiftCount === 0,
|
|
444
|
+
open: openDay === day,
|
|
445
|
+
onToggle: () => setOpenDay((d) => d === day ? null : day),
|
|
446
|
+
children: blocks.map((b) => /* @__PURE__ */ jsx(TemplateBlock, { template: b.template, shifts: b.shifts, onChanged }, b.template.id))
|
|
447
|
+
},
|
|
448
|
+
day
|
|
449
|
+
)) }),
|
|
450
|
+
withoutShifts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
451
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Sem turno \u2014 n\xE3o entram em dia nenhum at\xE9 ganharem o primeiro." }),
|
|
452
|
+
withoutShifts.map((t) => /* @__PURE__ */ jsx(TemplateBlock, { template: t, shifts: [], onChanged }, t.id))
|
|
453
|
+
] })
|
|
454
|
+
] }),
|
|
391
455
|
/* @__PURE__ */ jsx(Modal, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(ModalContent, { size: "sm", children: [
|
|
392
|
-
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: "Novo
|
|
456
|
+
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: "Novo template" }) }),
|
|
393
457
|
/* @__PURE__ */ jsx(ModalBody, { children: /* @__PURE__ */ jsx(Input, { value: name, onChange: (e) => setName(e.target.value), placeholder: "Nome (ex.: Almo\xE7o Seg. a Sex.)" }) }),
|
|
394
458
|
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
395
459
|
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => setOpen(false), children: "Cancelar" }),
|
|
@@ -1131,39 +1195,169 @@ function PeriodsView() {
|
|
|
1131
1195
|
] }) })
|
|
1132
1196
|
] });
|
|
1133
1197
|
}
|
|
1198
|
+
var PEOPLE_BASE_PATH = "/workforce/people";
|
|
1134
1199
|
var DEFAULT_STAFF_ENTITY = {
|
|
1135
1200
|
name: "Colaborador",
|
|
1136
1201
|
namePlural: "Pessoas",
|
|
1137
1202
|
icon: "UserCog",
|
|
1138
1203
|
layout: "person",
|
|
1139
1204
|
displayField: "name",
|
|
1205
|
+
subtitleField: "documentNumber",
|
|
1140
1206
|
defaultSort: "name",
|
|
1207
|
+
formColumns: 2,
|
|
1208
|
+
facets: [activeFacet({ active: "Ativos", inactive: "Inativos", all: "Todos" })],
|
|
1209
|
+
fieldGroups: [
|
|
1210
|
+
{ id: "identity", label: "Dados pessoais", columns: 2 },
|
|
1211
|
+
{ id: "contact", label: "Contato", columns: 2 },
|
|
1212
|
+
{ id: "where", label: "Endere\xE7o", columns: 1 },
|
|
1213
|
+
{
|
|
1214
|
+
id: "work",
|
|
1215
|
+
label: "V\xEDnculo",
|
|
1216
|
+
columns: 2,
|
|
1217
|
+
description: "O que a folha e o fisco esperam desta pessoa."
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
id: "banking",
|
|
1221
|
+
label: "Dados banc\xE1rios",
|
|
1222
|
+
columns: 1,
|
|
1223
|
+
description: "Para onde o sal\xE1rio SAI \u2014 n\xE3o \xE9 conta da casa."
|
|
1224
|
+
},
|
|
1225
|
+
{ ...RECORD_GROUP }
|
|
1226
|
+
],
|
|
1141
1227
|
fields: [
|
|
1142
|
-
{ key: "name", label: "Nome", type: "text", required: true, showInTable: true, searchable: true },
|
|
1143
|
-
{ key: "
|
|
1144
|
-
{ key: "
|
|
1145
|
-
{ key: "
|
|
1146
|
-
{
|
|
1228
|
+
{ key: "name", label: "Nome completo", type: "text", required: true, showInTable: true, searchable: true, group: "identity", span: 2 },
|
|
1229
|
+
{ key: "documentNumber", label: "CPF", type: "text", showInTable: true, searchable: true, group: "identity" },
|
|
1230
|
+
{ key: "rg", label: "RG", type: "text", showInTable: false, group: "identity" },
|
|
1231
|
+
{ key: "dateOfBirth", label: "Data de nascimento", type: "date", showInTable: false, group: "identity" },
|
|
1232
|
+
{
|
|
1233
|
+
key: "gender",
|
|
1234
|
+
label: "Sexo",
|
|
1235
|
+
type: "select",
|
|
1236
|
+
showInTable: false,
|
|
1237
|
+
group: "identity",
|
|
1238
|
+
options: [
|
|
1239
|
+
{ value: "masculino", label: "Masculino" },
|
|
1240
|
+
{ value: "feminino", label: "Feminino" },
|
|
1241
|
+
{ value: "outro", label: "Outro" }
|
|
1242
|
+
]
|
|
1243
|
+
},
|
|
1244
|
+
{ key: "phone", label: "Telefone", type: "phone", showInTable: true, searchable: true, group: "contact" },
|
|
1245
|
+
{ key: "mobile", label: "Celular", type: "phone", showInTable: true, group: "contact" },
|
|
1246
|
+
{ key: "email", label: "E-mail", type: "email", showInTable: false, searchable: true, group: "contact" },
|
|
1247
|
+
{ key: "address", label: "Endere\xE7o", type: "address", showInTable: false, showInDetail: false, group: "where" },
|
|
1248
|
+
{
|
|
1249
|
+
key: "employmentType",
|
|
1250
|
+
label: "Tipo de v\xEDnculo",
|
|
1251
|
+
type: "select",
|
|
1252
|
+
showInTable: true,
|
|
1253
|
+
group: "work",
|
|
1254
|
+
options: [
|
|
1255
|
+
{ value: "clt", label: "CLT" },
|
|
1256
|
+
{ value: "pj", label: "Pessoa jur\xEDdica" },
|
|
1257
|
+
{ value: "autonomo", label: "Aut\xF4nomo" },
|
|
1258
|
+
{ value: "estagio", label: "Est\xE1gio" },
|
|
1259
|
+
{ value: "socio", label: "S\xF3cio" }
|
|
1260
|
+
]
|
|
1261
|
+
},
|
|
1262
|
+
{ key: "jobTitle", label: "Cargo", type: "text", showInTable: true, group: "work" },
|
|
1263
|
+
{ key: "admissionDate", label: "Admiss\xE3o", type: "date", showInTable: false, group: "work" },
|
|
1264
|
+
// O ACESSO ÀS AGENDAS dos profissionais: o V1 pergunta isto no cadastro de
|
|
1265
|
+
// funcionário porque a recepção marca para os outros, e quem não marca não
|
|
1266
|
+
// precisa ver a semana inteira de ninguém.
|
|
1267
|
+
{
|
|
1268
|
+
key: "isBookable",
|
|
1269
|
+
label: "Aparece na agenda",
|
|
1270
|
+
type: "boolean",
|
|
1271
|
+
showInTable: false,
|
|
1272
|
+
defaultValue: false,
|
|
1273
|
+
group: "work",
|
|
1274
|
+
hint: "Clientes podem ser marcados com esta pessoa."
|
|
1275
|
+
},
|
|
1276
|
+
// Para onde o salário SAI — a mesma linha de `payee_accounts` que o
|
|
1277
|
+
// fornecedor usa. Quem a casa paga é quem a casa paga.
|
|
1278
|
+
{ key: "payeeAccount", label: "Conta banc\xE1ria", type: "bank", showInTable: false, showInDetail: false, group: "banking" },
|
|
1279
|
+
...recordRailFields({ tagsTable: "people", activeLabel: "Ativo" })
|
|
1147
1280
|
],
|
|
1148
1281
|
data: {
|
|
1149
1282
|
table: "people",
|
|
1150
1283
|
tenantScoped: true,
|
|
1151
1284
|
archetype: "person",
|
|
1152
1285
|
archetypeKind: "staff",
|
|
1153
|
-
|
|
1286
|
+
customFields: ["rg", "gender", "mobile", "employmentType", "jobTitle", "admissionDate"],
|
|
1287
|
+
searchColumns: ["name", "email", "phone", "document_number"]
|
|
1154
1288
|
}
|
|
1155
1289
|
};
|
|
1290
|
+
function TeamDocuments() {
|
|
1291
|
+
const provider = useWorkforceProvider();
|
|
1292
|
+
const [team, setTeam] = React3.useState(null);
|
|
1293
|
+
const [person, setPerson] = React3.useState(null);
|
|
1294
|
+
React3.useEffect(() => {
|
|
1295
|
+
let alive = true;
|
|
1296
|
+
provider.getStaff().then((people) => {
|
|
1297
|
+
if (alive) setTeam(people);
|
|
1298
|
+
}).catch(() => {
|
|
1299
|
+
if (alive) setTeam([]);
|
|
1300
|
+
});
|
|
1301
|
+
return () => {
|
|
1302
|
+
alive = false;
|
|
1303
|
+
};
|
|
1304
|
+
}, [provider]);
|
|
1305
|
+
if (!team) return /* @__PURE__ */ jsx(Skeleton, { className: "h-40 w-full" });
|
|
1306
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1307
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1308
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Documentos de" }),
|
|
1309
|
+
/* @__PURE__ */ jsxs(
|
|
1310
|
+
Select,
|
|
1311
|
+
{
|
|
1312
|
+
value: person?.id ?? "",
|
|
1313
|
+
onValueChange: (id) => setPerson(team.find((p) => p.id === id) ?? null),
|
|
1314
|
+
children: [
|
|
1315
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "w-full sm:w-72", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: team.length ? "Escolha algu\xE9m da equipe\u2026" : "Nenhuma pessoa cadastrada" }) }),
|
|
1316
|
+
/* @__PURE__ */ jsx(SelectContent, { children: team.map((p) => /* @__PURE__ */ jsx(SelectItem, { value: p.id, children: p.name || "Sem nome" }, p.id)) })
|
|
1317
|
+
]
|
|
1318
|
+
}
|
|
1319
|
+
)
|
|
1320
|
+
] }),
|
|
1321
|
+
person ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1322
|
+
/* @__PURE__ */ jsx(WidgetSlot, { zone: "person.detail.documents", item: person }),
|
|
1323
|
+
/* @__PURE__ */ jsx(WidgetSlot, { zone: "person.detail.files", item: person })
|
|
1324
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-dashed border-border p-8 text-center", children: [
|
|
1325
|
+
/* @__PURE__ */ jsx(FileText, { className: "mx-auto mb-3 h-8 w-8 text-muted-foreground", "aria-hidden": true }),
|
|
1326
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Documentos, contratos, certificados, RG/CPF, etc. Escolha algu\xE9m da equipe acima." })
|
|
1327
|
+
] })
|
|
1328
|
+
] });
|
|
1329
|
+
}
|
|
1156
1330
|
var cache = /* @__PURE__ */ new Map();
|
|
1157
|
-
function PeopleView({ entity }) {
|
|
1331
|
+
function PeopleView({ entity, mode }) {
|
|
1158
1332
|
const def = entity ?? DEFAULT_STAFF_ENTITY;
|
|
1159
1333
|
const Page = React3.useMemo(() => {
|
|
1160
1334
|
const cached = cache.get(def);
|
|
1161
1335
|
if (cached) return cached;
|
|
1162
1336
|
const created = createCrudPage(def);
|
|
1337
|
+
created.__crudBasePath = PEOPLE_BASE_PATH;
|
|
1163
1338
|
cache.set(def, created);
|
|
1164
1339
|
return created;
|
|
1165
1340
|
}, [def]);
|
|
1166
|
-
|
|
1341
|
+
const [tab, setTab] = React3.useState("people");
|
|
1342
|
+
const onList = !mode || mode === "list";
|
|
1343
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1344
|
+
onList && /* @__PURE__ */ jsxs(PageViewBar, { children: [
|
|
1345
|
+
/* @__PURE__ */ jsx(
|
|
1346
|
+
ViewTabs,
|
|
1347
|
+
{
|
|
1348
|
+
value: tab,
|
|
1349
|
+
onChange: setTab,
|
|
1350
|
+
options: [
|
|
1351
|
+
{ value: "people", label: "Pessoas" },
|
|
1352
|
+
{ value: "documents", label: "Documentos" }
|
|
1353
|
+
],
|
|
1354
|
+
"aria-label": "Pessoas e Documentos"
|
|
1355
|
+
}
|
|
1356
|
+
),
|
|
1357
|
+
/* @__PURE__ */ jsx("span", {})
|
|
1358
|
+
] }),
|
|
1359
|
+
onList && tab === "documents" ? /* @__PURE__ */ jsx(TeamDocuments, {}) : /* @__PURE__ */ jsx(Page, {})
|
|
1360
|
+
] });
|
|
1167
1361
|
}
|
|
1168
1362
|
var KEY = "workforce.payroll_account_codes";
|
|
1169
1363
|
function message(e) {
|
|
@@ -2056,7 +2250,7 @@ function WorkforcePage({ config, provider, store, staffEntity }) {
|
|
|
2056
2250
|
case "periods":
|
|
2057
2251
|
return /* @__PURE__ */ jsx(PeriodsView, {});
|
|
2058
2252
|
case "people":
|
|
2059
|
-
return /* @__PURE__ */ jsx(PeopleView, { entity: staffEntity });
|
|
2253
|
+
return /* @__PURE__ */ jsx(PeopleView, { entity: staffEntity, mode: intent.mode });
|
|
2060
2254
|
case "hr":
|
|
2061
2255
|
return /* @__PURE__ */ jsx(HrView, {});
|
|
2062
2256
|
case "payroll-costs":
|