rl-core-front 0.1.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.
Files changed (112) hide show
  1. package/README.md +129 -0
  2. package/package.json +113 -0
  3. package/src/_services/api/axios.factory.ts +97 -0
  4. package/src/_services/api/list-query.ts +26 -0
  5. package/src/_services/api/schema.d.ts +2527 -0
  6. package/src/_services/auth/index.ts +127 -0
  7. package/src/_utils/advanced-filter.ts +386 -0
  8. package/src/_utils/calendar.ts +103 -0
  9. package/src/_utils/crop-image.ts +65 -0
  10. package/src/_utils/filter.ts +313 -0
  11. package/src/_utils/format.ts +35 -0
  12. package/src/_utils/initials.ts +24 -0
  13. package/src/_utils/password.ts +7 -0
  14. package/src/components/app-shell.tsx +314 -0
  15. package/src/components/breadcrumbs.tsx +55 -0
  16. package/src/components/index.ts +7 -0
  17. package/src/components/language-selector.tsx +51 -0
  18. package/src/components/password-field.tsx +40 -0
  19. package/src/components/theme-toggle.tsx +27 -0
  20. package/src/components/ui/alert.tsx +27 -0
  21. package/src/components/ui/avatar.tsx +50 -0
  22. package/src/components/ui/badge.tsx +36 -0
  23. package/src/components/ui/button.tsx +62 -0
  24. package/src/components/ui/card.tsx +86 -0
  25. package/src/components/ui/checkbox.tsx +30 -0
  26. package/src/components/ui/column-visibility-modal.tsx +110 -0
  27. package/src/components/ui/confirm-dialog.tsx +69 -0
  28. package/src/components/ui/data-table.tsx +401 -0
  29. package/src/components/ui/date-range-picker.tsx +250 -0
  30. package/src/components/ui/dialog.tsx +138 -0
  31. package/src/components/ui/dropdown-menu.tsx +108 -0
  32. package/src/components/ui/field.tsx +20 -0
  33. package/src/components/ui/filter-chips.tsx +88 -0
  34. package/src/components/ui/filter-field.tsx +137 -0
  35. package/src/components/ui/filter-group.tsx +155 -0
  36. package/src/components/ui/filter-rule.tsx +221 -0
  37. package/src/components/ui/filter-sheet.tsx +201 -0
  38. package/src/components/ui/index.ts +30 -0
  39. package/src/components/ui/input.tsx +25 -0
  40. package/src/components/ui/label.tsx +23 -0
  41. package/src/components/ui/row-actions.tsx +32 -0
  42. package/src/components/ui/select.tsx +97 -0
  43. package/src/components/ui/separator.tsx +31 -0
  44. package/src/components/ui/sheet.tsx +119 -0
  45. package/src/components/ui/sonner.tsx +28 -0
  46. package/src/components/ui/spinner.tsx +12 -0
  47. package/src/components/ui/switch.tsx +29 -0
  48. package/src/components/ui/table.tsx +84 -0
  49. package/src/components/ui/tabs.tsx +52 -0
  50. package/src/components/user-avatar.tsx +54 -0
  51. package/src/contexts/auth-context.tsx +90 -0
  52. package/src/contexts/color-mode-context.tsx +105 -0
  53. package/src/contexts/i18n-context.tsx +76 -0
  54. package/src/contexts/index.ts +6 -0
  55. package/src/contexts/socket-context.tsx +153 -0
  56. package/src/contexts/toast-context.tsx +40 -0
  57. package/src/features/audit/audit-screen.tsx +299 -0
  58. package/src/features/audit/hooks/use-audit-trail.ts +68 -0
  59. package/src/features/audit/services/audit.service.ts +24 -0
  60. package/src/features/dashboard/dashboard-screen.tsx +73 -0
  61. package/src/features/login/components/backup-codes-dialog.tsx +66 -0
  62. package/src/features/login/components/index.ts +5 -0
  63. package/src/features/login/components/login-form.tsx +62 -0
  64. package/src/features/login/components/two-factor-setup.tsx +71 -0
  65. package/src/features/login/components/two-factor-verify.tsx +56 -0
  66. package/src/features/login/hooks/use-login-flow.ts +119 -0
  67. package/src/features/login/login-screen.tsx +83 -0
  68. package/src/features/login/validation/schemas.ts +30 -0
  69. package/src/features/logs/hooks/use-error-logs.ts +65 -0
  70. package/src/features/logs/hooks/use-request-logs.ts +65 -0
  71. package/src/features/logs/logs-screen.tsx +31 -0
  72. package/src/features/logs/panels/error-logs-panel.tsx +183 -0
  73. package/src/features/logs/panels/index.ts +3 -0
  74. package/src/features/logs/panels/request-logs-panel.tsx +124 -0
  75. package/src/features/logs/services/logs.service.ts +68 -0
  76. package/src/features/notifications/components/notification-item.tsx +67 -0
  77. package/src/features/notifications/hooks/use-notifications.ts +78 -0
  78. package/src/features/notifications/notifications-center.tsx +119 -0
  79. package/src/features/notifications/services/notifications.service.ts +38 -0
  80. package/src/features/profile/components/avatar-crop-dialog.tsx +133 -0
  81. package/src/features/profile/components/index.ts +2 -0
  82. package/src/features/profile/force-password-change.tsx +114 -0
  83. package/src/features/profile/profile-screen.tsx +385 -0
  84. package/src/features/rbac/panels/actions-panel.tsx +20 -0
  85. package/src/features/rbac/panels/catalog-panel.tsx +243 -0
  86. package/src/features/rbac/panels/groups-panel.tsx +269 -0
  87. package/src/features/rbac/panels/index.ts +8 -0
  88. package/src/features/rbac/panels/permissions-panel.tsx +204 -0
  89. package/src/features/rbac/panels/resources-panel.tsx +20 -0
  90. package/src/features/rbac/panels/roles-panel.tsx +224 -0
  91. package/src/features/rbac/panels/scopes-panel.tsx +20 -0
  92. package/src/features/rbac/rbac-screen.tsx +80 -0
  93. package/src/features/rbac/services/rbac.service.ts +110 -0
  94. package/src/features/recovery/forgot-password-screen.tsx +69 -0
  95. package/src/features/recovery/reset-password-screen.tsx +163 -0
  96. package/src/features/users/components/user-form-dialog.tsx +187 -0
  97. package/src/features/users/hooks/use-users.ts +181 -0
  98. package/src/features/users/services/users.service.ts +83 -0
  99. package/src/features/users/users-screen.tsx +331 -0
  100. package/src/hooks/use-column-visibility.ts +54 -0
  101. package/src/hooks/use-confirm.tsx +70 -0
  102. package/src/hooks/use-filter-schema.ts +85 -0
  103. package/src/hooks/use-filters.ts +148 -0
  104. package/src/hooks/use-list-query.ts +69 -0
  105. package/src/hooks/use-request.ts +114 -0
  106. package/src/i18n/messages/en.ts +296 -0
  107. package/src/i18n/messages/pt.ts +305 -0
  108. package/src/index.ts +60 -0
  109. package/src/lib/utils.ts +6 -0
  110. package/src/middleware.ts +42 -0
  111. package/src/styles/core.css +142 -0
  112. package/tailwind-preset.ts +103 -0
@@ -0,0 +1,243 @@
1
+ "use client";
2
+
3
+ import type { ColumnDef } from "@tanstack/react-table";
4
+ import { Pencil, Plus, Trash2 } from "lucide-react";
5
+ import type { JSX } from "react";
6
+ import { useEffect, useMemo, useState } from "react";
7
+
8
+ import {
9
+ Badge,
10
+ Button,
11
+ DataTable,
12
+ DataTableFeatures,
13
+ Dialog,
14
+ DialogContent,
15
+ DialogFooter,
16
+ DialogForm,
17
+ DialogHeader,
18
+ DialogTitle,
19
+ Field,
20
+ Input,
21
+ RowActions,
22
+ } from "#core/components/ui";
23
+ import { useAuth, useI18n } from "#core/contexts";
24
+ import { RbacCatalogItem } from "#core/features/rbac/services/rbac.service";
25
+ import { useConfirm } from "#core/hooks/use-confirm";
26
+ import { RequestOperation, useRequest } from "#core/hooks/use-request";
27
+
28
+ interface CatalogPanelProps {
29
+ /** Nome do resource RBAC (ex.: 'resources', 'actions', 'scopes') — usado nos codes de permissão. */
30
+ resource: string;
31
+ storageKey: string;
32
+ namePlaceholder: string;
33
+ list: () => Promise<RbacCatalogItem[]>;
34
+ create: (data: {
35
+ name: string;
36
+ description?: string;
37
+ }) => Promise<RbacCatalogItem>;
38
+ update: (
39
+ id: string,
40
+ data: { name?: string; description?: string },
41
+ ) => Promise<RbacCatalogItem>;
42
+ remove: (id: string) => Promise<unknown>;
43
+ }
44
+
45
+ /**
46
+ * Painel genérico de CRUD simples (name + description) — reaproveitado por
47
+ * Resources, Actions e Scopes do catálogo RBAC, que têm a mesma forma.
48
+ */
49
+ export function CatalogPanel({
50
+ resource,
51
+ storageKey,
52
+ namePlaceholder,
53
+ list,
54
+ create,
55
+ update,
56
+ remove,
57
+ }: CatalogPanelProps): JSX.Element {
58
+ const { run, loading } = useRequest();
59
+ const { t } = useI18n();
60
+ const { confirm, confirmDialog } = useConfirm();
61
+ const { hasPermission } = useAuth();
62
+ const [rows, setRows] = useState<RbacCatalogItem[]>([]);
63
+ const [open, setOpen] = useState(false);
64
+ const [editing, setEditing] = useState<RbacCatalogItem | null>(null);
65
+ const [form, setForm] = useState({ name: "", description: "" });
66
+
67
+ const load = async (): Promise<void> => {
68
+ const r = await run(() => list());
69
+ if (r) {setRows(r);}
70
+ };
71
+ useEffect(() => {
72
+ void load();
73
+ // eslint-disable-next-line react-hooks/exhaustive-deps
74
+ }, []);
75
+
76
+ const openNew = (): void => {
77
+ setEditing(null);
78
+ setForm({ name: "", description: "" });
79
+ setOpen(true);
80
+ };
81
+ const openEdit = (item: RbacCatalogItem): void => {
82
+ setEditing(item);
83
+ setForm({ name: item.name, description: item.description ?? "" });
84
+ setOpen(true);
85
+ };
86
+
87
+ const save = async (): Promise<void> => {
88
+ const res = editing
89
+ ? await run(() => update(editing.id, form), {
90
+ success: RequestOperation.Update,
91
+ })
92
+ : await run(() => create(form), { success: RequestOperation.Create });
93
+ if (res) {
94
+ setOpen(false);
95
+ await load();
96
+ }
97
+ };
98
+
99
+ const removeItem = async (item: RbacCatalogItem): Promise<void> => {
100
+ const ok = await confirm({
101
+ title: t("common.delete"),
102
+ description: item.name,
103
+ confirmLabel: t("common.delete"),
104
+ destructive: true,
105
+ });
106
+ if (!ok) {
107
+ return;
108
+ }
109
+ const res = await run(() => remove(item.id), {
110
+ success: RequestOperation.Delete,
111
+ });
112
+ if (res !== null) {
113
+ await load();
114
+ }
115
+ };
116
+
117
+ const canUpdate = hasPermission(`${resource}:update:any`);
118
+ const canDelete = hasPermission(`${resource}:delete:any`);
119
+ const canCreate = hasPermission(`${resource}:create:any`);
120
+ const canManage = canUpdate || canDelete;
121
+
122
+ const columns = useMemo<
123
+ ColumnDef<DataTableFeatures, RbacCatalogItem, unknown>[]
124
+ >(() => {
125
+ const cols: ColumnDef<DataTableFeatures, RbacCatalogItem, unknown>[] = [
126
+ {
127
+ accessorKey: "name",
128
+ header: "Name",
129
+ meta: { label: "Name" },
130
+ cell: ({ getValue }) => (
131
+ <span className="font-mono text-xs">{String(getValue())}</span>
132
+ ),
133
+ },
134
+ {
135
+ accessorKey: "description",
136
+ header: "Descrição",
137
+ meta: { label: "Descrição" },
138
+ },
139
+ {
140
+ accessorKey: "isActive",
141
+ header: t("users.active"),
142
+ meta: { label: t("users.active") },
143
+ cell: ({ row }) => (
144
+ <Badge variant={row.original.isActive ? "success" : "secondary"}>
145
+ {row.original.isActive ? t("users.active") : t("users.inactive")}
146
+ </Badge>
147
+ ),
148
+ },
149
+ ];
150
+ if (canManage) {
151
+ cols.push({
152
+ id: "actions",
153
+ header: () => <div className="text-center">{t("common.actions")}</div>,
154
+ meta: { shrink: true },
155
+ enableHiding: false,
156
+ enableSorting: false,
157
+ cell: ({ row }) => (
158
+ <RowActions>
159
+ {canUpdate && (
160
+ <Button
161
+ variant="ghost"
162
+ size="iconSm"
163
+ onClick={() => openEdit(row.original)}
164
+ >
165
+ <Pencil className="h-4 w-4" />
166
+ </Button>
167
+ )}
168
+ {canDelete && (
169
+ <Button
170
+ variant="ghost"
171
+ size="iconSm"
172
+ className="text-destructive"
173
+ onClick={() => removeItem(row.original)}
174
+ >
175
+ <Trash2 className="h-4 w-4" />
176
+ </Button>
177
+ )}
178
+ </RowActions>
179
+ ),
180
+ });
181
+ }
182
+ return cols;
183
+ // eslint-disable-next-line react-hooks/exhaustive-deps
184
+ }, [canManage, canUpdate, canDelete, t]);
185
+
186
+ return (
187
+ <div>
188
+ <DataTable
189
+ columns={columns}
190
+ data={rows}
191
+ getRowId={(r) => r.id}
192
+ storageKey={storageKey}
193
+ loading={loading}
194
+ pageSizeOptions={[20, 50]}
195
+ toolbar={
196
+ canCreate && (
197
+ <Button onClick={openNew}>
198
+ <Plus className="h-4 w-4" />
199
+ {t("common.create")}
200
+ </Button>
201
+ )
202
+ }
203
+ />
204
+
205
+ <Dialog open={open} onOpenChange={(o) => !o && setOpen(false)}>
206
+ <DialogContent className="max-w-md">
207
+ <DialogHeader>
208
+ <DialogTitle>
209
+ {editing ? t("common.edit") : t("common.create")}
210
+ </DialogTitle>
211
+ </DialogHeader>
212
+ <DialogForm onSubmit={() => void save()}>
213
+ <Field label="Name">
214
+ <Input
215
+ placeholder={namePlaceholder}
216
+ value={form.name}
217
+ onChange={(e) => setForm({ ...form, name: e.target.value })}
218
+ />
219
+ </Field>
220
+ <Field label="Descrição">
221
+ <Input
222
+ value={form.description}
223
+ onChange={(e) =>
224
+ setForm({ ...form, description: e.target.value })
225
+ }
226
+ />
227
+ </Field>
228
+ <DialogFooter className="gap-2">
229
+ <Button type="button" variant="outline" onClick={() => setOpen(false)}>
230
+ {t("common.cancel")}
231
+ </Button>
232
+ <Button type="submit" disabled={loading}>
233
+ {t("common.save")}
234
+ </Button>
235
+ </DialogFooter>
236
+ </DialogForm>
237
+ </DialogContent>
238
+ </Dialog>
239
+
240
+ {confirmDialog}
241
+ </div>
242
+ );
243
+ }
@@ -0,0 +1,269 @@
1
+ "use client";
2
+
3
+ import type { ColumnDef } from "@tanstack/react-table";
4
+ import { Pencil, Plus, Trash2 } from "lucide-react";
5
+ import type { JSX } from "react";
6
+ import { useEffect, useMemo, useState } from "react";
7
+
8
+ import {
9
+ Button,
10
+ Checkbox,
11
+ DataTable,
12
+ DataTableFeatures,
13
+ Dialog,
14
+ DialogContent,
15
+ DialogFooter,
16
+ DialogForm,
17
+ DialogHeader,
18
+ DialogTitle,
19
+ Field,
20
+ Input,
21
+ Label,
22
+ RowActions,
23
+ Select,
24
+ SelectContent,
25
+ SelectItem,
26
+ SelectTrigger,
27
+ SelectValue,
28
+ } from "#core/components/ui";
29
+ import { useAuth, useI18n } from "#core/contexts";
30
+ import {
31
+ Group,
32
+ rbacService,
33
+ Role,
34
+ } from "#core/features/rbac/services/rbac.service";
35
+ import { useConfirm } from "#core/hooks/use-confirm";
36
+ import { RequestOperation, useRequest } from "#core/hooks/use-request";
37
+
38
+ const TYPES = ["company", "department", "team"];
39
+
40
+ export function GroupsPanel(): JSX.Element {
41
+ const { run, loading } = useRequest();
42
+ const { t } = useI18n();
43
+ const { confirm, confirmDialog } = useConfirm();
44
+ const { hasPermission } = useAuth();
45
+ const [rows, setRows] = useState<Group[]>([]);
46
+ const [roles, setRoles] = useState<Role[]>([]);
47
+ const [open, setOpen] = useState(false);
48
+ const [editing, setEditing] = useState<Group | null>(null);
49
+ const [form, setForm] = useState({
50
+ name: "",
51
+ description: "",
52
+ type: "team",
53
+ roleIds: [] as string[],
54
+ });
55
+
56
+ const load = async (): Promise<void> => {
57
+ const [g, r] = await Promise.all([
58
+ run(() => rbacService.listGroups()),
59
+ run(() => rbacService.listRoles()),
60
+ ]);
61
+ if (g) {setRows(g);}
62
+ if (r) {setRoles(r);}
63
+ };
64
+ useEffect(() => {
65
+ void load();
66
+ // eslint-disable-next-line react-hooks/exhaustive-deps
67
+ }, []);
68
+
69
+ const openNew = (): void => {
70
+ setEditing(null);
71
+ setForm({ name: "", description: "", type: "team", roleIds: [] });
72
+ setOpen(true);
73
+ };
74
+ const openEdit = (g: Group): void => {
75
+ setEditing(g);
76
+ setForm({
77
+ name: g.name,
78
+ description: g.description ?? "",
79
+ type: g.type,
80
+ roleIds: g.roles?.map((r) => r.id) ?? [],
81
+ });
82
+ setOpen(true);
83
+ };
84
+
85
+ const save = async (): Promise<void> => {
86
+ const res = editing
87
+ ? await run(() => rbacService.updateGroup(editing.id, form), {
88
+ success: RequestOperation.Update,
89
+ })
90
+ : await run(() => rbacService.createGroup(form), {
91
+ success: RequestOperation.Create,
92
+ });
93
+ if (res) {
94
+ setOpen(false);
95
+ await load();
96
+ }
97
+ };
98
+
99
+ const remove = async (g: Group): Promise<void> => {
100
+ const ok = await confirm({
101
+ title: t("common.delete"),
102
+ description: g.name,
103
+ confirmLabel: t("common.delete"),
104
+ destructive: true,
105
+ });
106
+ if (!ok) {
107
+ return;
108
+ }
109
+ const res = await run(() => rbacService.deleteGroup(g.id), {
110
+ success: RequestOperation.Delete,
111
+ });
112
+ if (res !== null) {
113
+ await load();
114
+ }
115
+ };
116
+
117
+ const canManage =
118
+ hasPermission("groups:update:any") || hasPermission("groups:delete:any");
119
+ const toggleRole = (id: string): void =>
120
+ setForm((f) => ({
121
+ ...f,
122
+ roleIds: f.roleIds.includes(id)
123
+ ? f.roleIds.filter((x) => x !== id)
124
+ : [...f.roleIds, id],
125
+ }));
126
+
127
+ const columns = useMemo<
128
+ ColumnDef<DataTableFeatures, Group, unknown>[]
129
+ >(() => {
130
+ const cols: ColumnDef<DataTableFeatures, Group, unknown>[] = [
131
+ {
132
+ accessorKey: "name",
133
+ header: t("users.name"),
134
+ meta: { label: t("users.name") },
135
+ },
136
+ { accessorKey: "type", header: "Tipo", meta: { label: "Tipo" } },
137
+ {
138
+ accessorKey: "description",
139
+ header: "Descrição",
140
+ meta: { label: "Descrição" },
141
+ },
142
+ ];
143
+ if (canManage) {
144
+ cols.push({
145
+ id: "actions",
146
+ header: () => <div className="text-center">{t("common.actions")}</div>,
147
+ meta: { shrink: true },
148
+ enableHiding: false,
149
+ enableSorting: false,
150
+ cell: ({ row }) => (
151
+ <RowActions>
152
+ {hasPermission("groups:update:any") && (
153
+ <Button
154
+ variant="ghost"
155
+ size="iconSm"
156
+ onClick={() => openEdit(row.original)}
157
+ >
158
+ <Pencil className="h-4 w-4" />
159
+ </Button>
160
+ )}
161
+ {hasPermission("groups:delete:any") && (
162
+ <Button
163
+ variant="ghost"
164
+ size="iconSm"
165
+ className="text-destructive"
166
+ onClick={() => remove(row.original)}
167
+ >
168
+ <Trash2 className="h-4 w-4" />
169
+ </Button>
170
+ )}
171
+ </RowActions>
172
+ ),
173
+ });
174
+ }
175
+ return cols;
176
+ // eslint-disable-next-line react-hooks/exhaustive-deps
177
+ }, [t, canManage]);
178
+
179
+ return (
180
+ <div>
181
+ <DataTable
182
+ columns={columns}
183
+ data={rows}
184
+ getRowId={(r) => r.id}
185
+ storageKey="rbac-groups"
186
+ loading={loading}
187
+ pageSizeOptions={[20, 50]}
188
+ toolbar={
189
+ hasPermission("groups:create:any") && (
190
+ <Button onClick={openNew}>
191
+ <Plus className="h-4 w-4" />
192
+ {t("common.create")}
193
+ </Button>
194
+ )
195
+ }
196
+ />
197
+
198
+ <Dialog open={open} onOpenChange={(o) => !o && setOpen(false)}>
199
+ <DialogContent className="max-w-lg">
200
+ <DialogHeader>
201
+ <DialogTitle>
202
+ {editing ? t("common.edit") : t("common.create")}
203
+ </DialogTitle>
204
+ </DialogHeader>
205
+ <DialogForm onSubmit={() => void save()}>
206
+ <Field label={t("users.name")}>
207
+ <Input
208
+ value={form.name}
209
+ onChange={(e) => setForm({ ...form, name: e.target.value })}
210
+ />
211
+ </Field>
212
+ <Field label="Descrição">
213
+ <Input
214
+ value={form.description}
215
+ onChange={(e) =>
216
+ setForm({ ...form, description: e.target.value })
217
+ }
218
+ />
219
+ </Field>
220
+ <Field label="Tipo">
221
+ <Select
222
+ value={form.type}
223
+ onValueChange={(v) => setForm({ ...form, type: v })}
224
+ >
225
+ <SelectTrigger>
226
+ <SelectValue />
227
+ </SelectTrigger>
228
+ <SelectContent>
229
+ {TYPES.map((ty) => (
230
+ <SelectItem key={ty} value={ty}>
231
+ {ty}
232
+ </SelectItem>
233
+ ))}
234
+ </SelectContent>
235
+ </Select>
236
+ </Field>
237
+ <div className="space-y-2">
238
+ <Label>Papéis</Label>
239
+ <div className="flex max-h-48 flex-col gap-2 overflow-y-auto rounded-md border border-border p-3">
240
+ {roles.map((r) => (
241
+ <label
242
+ key={r.id}
243
+ className="flex cursor-pointer items-center gap-2 text-sm"
244
+ >
245
+ <Checkbox
246
+ checked={form.roleIds.includes(r.id)}
247
+ onCheckedChange={() => toggleRole(r.id)}
248
+ />
249
+ {r.name}
250
+ </label>
251
+ ))}
252
+ </div>
253
+ </div>
254
+ <DialogFooter className="gap-2">
255
+ <Button type="button" variant="outline" onClick={() => setOpen(false)}>
256
+ {t("common.cancel")}
257
+ </Button>
258
+ <Button type="submit" disabled={loading}>
259
+ {t("common.save")}
260
+ </Button>
261
+ </DialogFooter>
262
+ </DialogForm>
263
+ </DialogContent>
264
+ </Dialog>
265
+
266
+ {confirmDialog}
267
+ </div>
268
+ );
269
+ }
@@ -0,0 +1,8 @@
1
+ // Barrel da pasta — importe daqui de fora; dentro da pasta use o caminho direto.
2
+ export * from "./actions-panel";
3
+ export * from "./catalog-panel";
4
+ export * from "./groups-panel";
5
+ export * from "./permissions-panel";
6
+ export * from "./resources-panel";
7
+ export * from "./roles-panel";
8
+ export * from "./scopes-panel";