@postxl/generators 1.3.0 → 1.4.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 (77) hide show
  1. package/dist/backend-actions/actions.generator.d.ts +3 -3
  2. package/dist/backend-actions/index.d.ts +2 -2
  3. package/dist/backend-authentication/authentication.generator.d.ts +1 -1
  4. package/dist/backend-authentication/authentication.generator.js +1 -1
  5. package/dist/backend-authentication/index.d.ts +1 -1
  6. package/dist/backend-authentication/template/src/authentication.mock.service.ts +2 -0
  7. package/dist/backend-core/backend.generator.d.ts +1 -1
  8. package/dist/backend-core/backend.generator.js +2 -2
  9. package/dist/backend-core/backend.generator.js.map +1 -1
  10. package/dist/backend-core/generators/api-config.generator.js +2 -2
  11. package/dist/backend-core/modules/backend-module-utils.generator.d.ts +1 -1
  12. package/dist/backend-core/modules/backend-module-xlport.generator.d.ts +1 -1
  13. package/dist/backend-core/template/.gitignore.template +40 -0
  14. package/dist/backend-data-management/data-management.generator.d.ts +1 -1
  15. package/dist/backend-database-prisma/index.d.ts +2 -2
  16. package/dist/backend-database-prisma/prisma.generator.d.ts +2 -2
  17. package/dist/backend-e2e/backend-e2e.generator.d.ts +1 -1
  18. package/dist/backend-e2e/index.d.ts +1 -1
  19. package/dist/backend-import/import.generator.d.ts +1 -1
  20. package/dist/backend-import/index.d.ts +1 -1
  21. package/dist/backend-repositories/index.d.ts +1 -1
  22. package/dist/backend-repositories/model.types.d.ts +14 -16
  23. package/dist/backend-repositories/repositories.generator.d.ts +1 -1
  24. package/dist/backend-rest-api/index.d.ts +1 -1
  25. package/dist/backend-rest-api/rest-api.generator.d.ts +1 -1
  26. package/dist/backend-router-trpc/index.d.ts +1 -1
  27. package/dist/backend-router-trpc/router-trpc.generator.d.ts +1 -1
  28. package/dist/backend-s3/index.d.ts +1 -1
  29. package/dist/backend-s3/s3.generator.d.ts +1 -1
  30. package/dist/backend-s3/s3.generator.js +2 -2
  31. package/dist/backend-seed/index.d.ts +1 -1
  32. package/dist/backend-seed/seed.generator.d.ts +1 -1
  33. package/dist/backend-update/index.d.ts +1 -1
  34. package/dist/backend-update/update-actions.decoders.d.ts +27 -9
  35. package/dist/backend-update/update-actions.decoders.js +1 -1
  36. package/dist/backend-update/update-actions.decoders.js.map +1 -1
  37. package/dist/backend-update/update.generator.d.ts +1 -1
  38. package/dist/backend-view/index.d.ts +1 -1
  39. package/dist/backend-view/view.generator.d.ts +1 -1
  40. package/dist/base/base.generator.d.ts +1 -1
  41. package/dist/base/template/.gitignore.template +471 -0
  42. package/dist/base/template/sonar-project.properties +68 -30
  43. package/dist/decoders/decoders.generator.d.ts +1 -1
  44. package/dist/decoders/index.d.ts +1 -1
  45. package/dist/devops/devops.generator.d.ts +1 -1
  46. package/dist/devops/index.d.ts +1 -1
  47. package/dist/e2e/e2e.generator.d.ts +1 -1
  48. package/dist/e2e/generators/lib/test-id-collector.d.ts +1 -1
  49. package/dist/e2e/index.d.ts +1 -1
  50. package/dist/e2e/template/e2e/.gitignore.template +11 -0
  51. package/dist/e2e/template/e2e/package.json +1 -1
  52. package/dist/frontend-admin/admin.generator.d.ts +1 -1
  53. package/dist/frontend-admin/generators/admin-sidebar.generator.js +118 -110
  54. package/dist/frontend-admin/generators/admin-sidebar.generator.js.map +1 -1
  55. package/dist/frontend-admin/generators/audit-log-sidebar.generator.js +161 -131
  56. package/dist/frontend-admin/generators/audit-log-sidebar.generator.js.map +1 -1
  57. package/dist/frontend-admin/generators/data-management-page.generator.js +5 -4
  58. package/dist/frontend-admin/generators/data-management-page.generator.js.map +1 -1
  59. package/dist/frontend-admin/generators/model-admin-page.generator.js +34 -18
  60. package/dist/frontend-admin/generators/model-admin-page.generator.js.map +1 -1
  61. package/dist/frontend-admin/index.d.ts +1 -1
  62. package/dist/frontend-core/frontend.generator.d.ts +1 -1
  63. package/dist/frontend-core/frontend.generator.js +2 -2
  64. package/dist/frontend-core/frontend.generator.js.map +1 -1
  65. package/dist/frontend-core/template/.gitignore.template +27 -0
  66. package/dist/frontend-forms/forms.generator.d.ts +1 -1
  67. package/dist/frontend-forms/index.d.ts +1 -1
  68. package/dist/frontend-tables/index.d.ts +1 -1
  69. package/dist/frontend-tables/tables.generator.d.ts +1 -1
  70. package/dist/frontend-trpc-client/index.d.ts +1 -1
  71. package/dist/frontend-trpc-client/trpc-client.generator.d.ts +1 -1
  72. package/dist/mock-data/index.d.ts +1 -1
  73. package/dist/mock-data/mock-data.generator.d.ts +1 -1
  74. package/dist/seed-data/index.d.ts +1 -1
  75. package/dist/seed-data/seed-data.generator.d.ts +1 -1
  76. package/dist/types/types.generator.d.ts +1 -1
  77. package/package.json +5 -5
@@ -45,145 +45,153 @@ function generateAdminSidebar({ context }) {
45
45
  .from(Generator.toBackendModuleLocation('@ui/admin/AdminSidebar'))
46
46
  .addImport({
47
47
  from: Generator.toPackageName('react'),
48
- items: [Generator.toFunctionName('useState')],
48
+ items: [Generator.toFunctionName('ComponentProps')],
49
49
  })
50
50
  .addImport({
51
- from: Generator.toPackageName('@radix-ui/react-icons'),
52
- items: [
53
- Generator.toFunctionName('ChevronRightIcon'),
54
- Generator.toFunctionName('ChevronDownIcon'),
55
- Generator.toFunctionName('ChevronLeftIcon'),
56
- ],
51
+ from: Generator.toPackageName('lucide-react'),
52
+ items: [Generator.toFunctionName('ChevronRight'), Generator.toFunctionName('Home')],
57
53
  });
58
54
  const { schemas: sortedSchemas, modelsBySchema } = (0, utils_1.groupAndSortModelsBySchema)(context);
55
+ // Generate schema keys for collapse state (lowercase for valid JS identifiers)
56
+ const schemaKeys = sortedSchemas.map((schema) => schema.toLowerCase());
57
+ // Generate the CollapseState type properties
58
+ const collapseStateTypeProps = [...schemaKeys, 'advanced'].map((key) => ` ${key}: boolean`).join('\n');
59
+ // Generate the defaultCollapseState properties
60
+ const defaultCollapseStateProps = [...schemaKeys, 'advanced'].map((key) => ` ${key}: true,`).join('\n');
59
61
  // Generate the schema sections
60
62
  const schemaSections = sortedSchemas
61
63
  .map((schema) => {
62
64
  const models = modelsBySchema.get(schema);
63
65
  const modelLinks = models
64
66
  .map((model) => `
65
- <li key="${model.name}">
66
- <Link
67
- to="${model.route}"
68
- className="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded transition-colors"
69
- >
70
- ${model.userFriendlyName}
71
- </Link>
72
- </li>`)
67
+ <SidebarMenuSubItem key="${model.name}">
68
+ <SidebarMenuSubButton asChild>
69
+ <Link
70
+ to="${model.route}"
71
+ className="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded transition-colors"
72
+ >
73
+ ${model.userFriendlyName}
74
+ </Link>
75
+ </SidebarMenuSubButton>
76
+ </SidebarMenuSubItem>
77
+ `)
73
78
  .join('');
79
+ const schemaKey = schema.toLowerCase();
74
80
  return `
75
- <div key="${schema}" className="mb-4">
76
- <button
77
- onClick={() => toggleSchema('${schema}')}
78
- className="flex items-center justify-between w-full px-4 py-2 font-semibold text-left hover:bg-gray-100 dark:hover:bg-gray-800 rounded transition-colors"
79
- >
80
- <span>${schema}</span>
81
- {expandedSchemas['${schema}'] ? (
82
- <ChevronDownIcon className="w-4 h-4" />
83
- ) : (
84
- <ChevronRightIcon className="w-4 h-4" />
85
- )}
86
- </button>
87
- {expandedSchemas['${schema}'] && (
88
- <ul className="mt-2 ml-2 space-y-1">
89
- ${modelLinks}
90
- </ul>
91
- )}
92
- </div>`;
81
+ <Collapse open={collapseState.${schemaKey}} onOpenChange={setOpen('${schemaKey}')} className="group/collapsible-${schemaKey}" key="${schemaKey}">
82
+ <SidebarGroup>
83
+ <SidebarGroupLabel asChild>
84
+ <CollapseTrigger asChild>
85
+ <SidebarMenuButton>
86
+ ${schema}
87
+ <ChevronRight className="ml-auto transition-transform group-data-[state=open]/collapsible-${schemaKey}:rotate-90" />
88
+ </SidebarMenuButton>
89
+ </CollapseTrigger>
90
+ </SidebarGroupLabel>
91
+
92
+ <CollapseContent>
93
+ <SidebarGroupContent>
94
+ <SidebarMenuSub>
95
+ ${modelLinks}
96
+ </SidebarMenuSub>
97
+ </SidebarGroupContent>
98
+ </CollapseContent>
99
+ </SidebarGroup>
100
+ </Collapse>`;
93
101
  })
94
102
  .join('');
95
103
  return `
96
104
  import { Link } from '@tanstack/react-router'
105
+ import useSessionStorageState from 'use-session-storage-state'
106
+ import {
107
+ Collapse,
108
+ CollapseContent,
109
+ CollapseTrigger,
110
+ Sidebar,
111
+ SidebarContent,
112
+ SidebarGroup,
113
+ SidebarGroupContent,
114
+ SidebarGroupLabel,
115
+ SidebarHeader,
116
+ SidebarMenu,
117
+ SidebarMenuButton,
118
+ SidebarMenuItem,
119
+ SidebarMenuSub,
120
+ SidebarMenuSubButton,
121
+ SidebarMenuSubItem,
122
+ } from '@postxl/ui-components'
97
123
  ${imports.generate()}
98
124
 
99
- export function AdminSidebar() {
100
- const [isOpen, setIsOpen] = useState(false)
101
- const [isCollapsed, setIsCollapsed] = useState(false)
102
- const [expandedSchemas, setExpandedSchemas] = useState<Record<string, boolean>>({
103
- ${sortedSchemas.map((schema) => `'${schema}': true`).join(',\n ')},
104
- 'Advanced': true
125
+ const STORAGE_KEY = 'admin-sidebar-collapse-state'
126
+
127
+ type CollapseState = {
128
+ ${collapseStateTypeProps}
129
+ }
130
+
131
+ const defaultCollapseState: CollapseState = {
132
+ ${defaultCollapseStateProps}
133
+ }
134
+
135
+ export function AdminSidebar({ ...props }: ComponentProps<typeof Sidebar>) {
136
+ const [collapseState, setCollapseState] = useSessionStorageState<CollapseState>(STORAGE_KEY, {
137
+ defaultValue: defaultCollapseState,
105
138
  })
106
139
 
107
- const toggleSchema = (schema: string) => {
108
- setExpandedSchemas((prev) => ({
109
- ...prev,
110
- [schema]: !prev[schema],
111
- }))
140
+ const setOpen = (key: keyof CollapseState) => (open: boolean) => {
141
+ setCollapseState((prev) => ({ ...prev, [key]: open }))
112
142
  }
113
-
143
+
114
144
  return (
115
- <>
116
- {/* Mobile toggle button */}
117
- <button
118
- onClick={() => setIsOpen(!isOpen)}
119
- className="fixed top-4 left-4 z-50 md:hidden p-2 bg-sidebar rounded shadow-lg border border-sidebar-border"
120
- aria-label="Toggle admin menu"
121
- >
122
- {isOpen ? <ChevronDownIcon className="w-6 h-6" /> : <ChevronRightIcon className="w-6 h-6" />}
123
- </button>
124
-
125
- {/* Sidebar */}
126
- <aside
127
- className={\`
128
- fixed md:sticky top-0 left-0 h-screen bg-sidebar border-r border-sidebar-border
129
- overflow-y-auto transition-all duration-300 z-40
130
- \${isOpen ? 'translate-x-0' : '-translate-x-full md:translate-x-0'}
131
- \${isCollapsed ? 'w-16 min-w-16' : 'w-64 min-w-64'}
132
- \`}
133
- >
134
- <div className="p-4">
135
- <div className="flex items-center justify-between mb-4">
136
- {!isCollapsed && (
137
- <Link to="/admin" className="text-lg font-bold hover:text-sidebar-primary transition-colors">
138
- Admin
145
+ <Sidebar className="h-svh border-r" {...props}>
146
+ <SidebarHeader>
147
+ <SidebarMenu>
148
+ <SidebarMenuItem>
149
+ <SidebarMenuButton asChild>
150
+ <Link to="/admin" className="hover:text-sidebar-primary transition-colors">
151
+ <Home size={16} className="mr-2" />
152
+ <span className="text-lg font-bold">Admin</span>
139
153
  </Link>
140
- )}
141
- <button
142
- onClick={() => setIsCollapsed(!isCollapsed)}
143
- className="hidden md:block p-1 hover:bg-sidebar-accent rounded transition-colors ml-auto"
144
- aria-label={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
145
- >
146
- {isCollapsed ? <ChevronRightIcon className="w-5 h-5" /> : <ChevronLeftIcon className="w-5 h-5" />}
147
- </button>
148
- </div>
149
- {!isCollapsed && (
150
- <nav>
151
- ${schemaSections}
152
- {import.meta.env.NEXT_PUBLIC_ALLOW_ADMIN === 'true' && (
153
- <div key="Advanced" className="mb-4">
154
- <button
155
- onClick={() => toggleSchema('Advanced')}
156
- className="flex items-center justify-between w-full px-4 py-2 font-semibold text-left hover:bg-sidebar-accent rounded transition-colors"
157
- >
158
- <span>Advanced</span>
159
- {expandedSchemas['Advanced'] ? (
160
- <ChevronDownIcon className="w-4 h-4" />
161
- ) : (
162
- <ChevronRightIcon className="w-4 h-4" />
163
- )}
164
- </button>
165
- {expandedSchemas['Advanced'] && (
166
- <ul className="mt-2 ml-2 space-y-1">
167
- <li key="DataManagement">
154
+ </SidebarMenuButton>
155
+ </SidebarMenuItem>
156
+ </SidebarMenu>
157
+ </SidebarHeader>
158
+
159
+ <SidebarContent>
160
+ ${schemaSections}
161
+
162
+ {import.meta.env.VITE_PUBLIC_ALLOW_ADMIN === 'true' && (
163
+ <Collapse open={collapseState.advanced} onOpenChange={setOpen('advanced')} className="group/collapsible-advanced">
164
+ <SidebarGroup>
165
+ <SidebarGroupLabel asChild>
166
+ <CollapseTrigger asChild>
167
+ <SidebarMenuButton>
168
+ Advanced
169
+ <ChevronRight className="ml-auto transition-transform group-data-[state=open]/collapsible-advanced:rotate-90" />
170
+ </SidebarMenuButton>
171
+ </CollapseTrigger>
172
+ </SidebarGroupLabel>
173
+
174
+ <CollapseContent>
175
+ <SidebarGroupContent>
176
+ <SidebarMenuSub>
177
+ <SidebarMenuSubItem key="DataManagement">
178
+ <SidebarMenuSubButton asChild>
168
179
  <Link
169
180
  to="/admin/data-management"
170
181
  className="block px-4 py-2 hover:bg-sidebar-accent rounded transition-colors"
171
182
  >
172
183
  Data Management
173
184
  </Link>
174
- </li>
175
- </ul>
176
- )}
177
- </div>
178
- )}
179
- </nav>
180
- )}
181
- </div>
182
- </aside>
183
-
184
- {/* Overlay for mobile */}
185
- {isOpen && <div role="button" className="fixed inset-0 bg-sidebar/20 z-30 md:hidden h-full" onClick={() => setIsOpen(false)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { setIsOpen(false) } }} tabIndex={-1} />}
186
- </>
185
+ </SidebarMenuSubButton>
186
+ </SidebarMenuSubItem>
187
+ </SidebarMenuSub>
188
+ </SidebarGroupContent>
189
+ </CollapseContent>
190
+ </SidebarGroup>
191
+ </Collapse>
192
+ )}
193
+ </SidebarContent>
194
+ </Sidebar>
187
195
  )
188
196
  }
189
197
  `;
@@ -1 +1 @@
1
- {"version":3,"file":"admin-sidebar.generator.js","sourceRoot":"","sources":["../../../src/frontend-admin/generators/admin-sidebar.generator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,oDA0JC;AAlKD,6DAA8C;AAG9C,oCAAqD;AAErD;;GAEG;AACH,SAAgB,oBAAoB,CAAC,EAAE,OAAO,EAA8B;IAC1E,MAAM,OAAO,GAAG,SAAS,CAAC,eAAe;QACvC,EAAE;SACD,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,wBAAwB,CAAC,CAAC;SACjE,SAAS,CAAC;QACT,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC;QACtC,KAAK,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;KAC9C,CAAC;SACD,SAAS,CAAC;QACT,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,uBAAuB,CAAC;QACtD,KAAK,EAAE;YACL,SAAS,CAAC,cAAc,CAAC,kBAAkB,CAAC;YAC5C,SAAS,CAAC,cAAc,CAAC,iBAAiB,CAAC;YAC3C,SAAS,CAAC,cAAc,CAAC,iBAAiB,CAAC;SAC5C;KACF,CAAC,CAAA;IAEJ,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,IAAA,kCAA0B,EAAC,OAAO,CAAC,CAAA;IAEtF,+BAA+B;IAC/B,MAAM,cAAc,GAAG,aAAa;SACjC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACd,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAE,CAAA;QAC1C,MAAM,UAAU,GAAG,MAAM;aACtB,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CAAC;qBACA,KAAK,CAAC,IAAI;;oBAEX,KAAK,CAAC,KAAK;;;gBAGf,KAAK,CAAC,gBAAgB;;gBAEtB,CACP;aACA,IAAI,CAAC,EAAE,CAAC,CAAA;QAEX,OAAO;gBACG,MAAM;;uCAEiB,MAAM;;;gBAG7B,MAAM;4BACM,MAAM;;;;;;0BAMR,MAAM;;YAEpB,UAAU;;;WAGX,CAAA;IACP,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAA;IAEX,OAAO;;EAEP,OAAO,CAAC,QAAQ,EAAE;;;;;;MAMd,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAgDxD,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsC7B,CAAA;AACD,CAAC"}
1
+ {"version":3,"file":"admin-sidebar.generator.js","sourceRoot":"","sources":["../../../src/frontend-admin/generators/admin-sidebar.generator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,oDAqKC;AA7KD,6DAA8C;AAG9C,oCAAqD;AAErD;;GAEG;AACH,SAAgB,oBAAoB,CAAC,EAAE,OAAO,EAA8B;IAC1E,MAAM,OAAO,GAAG,SAAS,CAAC,eAAe;QACvC,EAAE;SACD,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,wBAAwB,CAAC,CAAC;SACjE,SAAS,CAAC;QACT,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC;QACtC,KAAK,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;KACpD,CAAC;SACD,SAAS,CAAC;QACT,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,cAAc,CAAC;QAC7C,KAAK,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;KACpF,CAAC,CAAA;IAEJ,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,IAAA,kCAA0B,EAAC,OAAO,CAAC,CAAA;IAEtF,+EAA+E;IAC/E,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;IAEtE,6CAA6C;IAC7C,MAAM,sBAAsB,GAAG,CAAC,GAAG,UAAU,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEvG,+CAA+C;IAC/C,MAAM,yBAAyB,GAAG,CAAC,GAAG,UAAU,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAExG,+BAA+B;IAC/B,MAAM,cAAc,GAAG,aAAa;SACjC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACd,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAE,CAAA;QAC1C,MAAM,UAAU,GAAG,MAAM;aACtB,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CAAC;uCACkB,KAAK,CAAC,IAAI;;;wBAGzB,KAAK,CAAC,KAAK;;;oBAGf,KAAK,CAAC,gBAAgB;;;;WAI/B,CACF;aACA,IAAI,CAAC,EAAE,CAAC,CAAA;QAEX,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,EAAE,CAAA;QACtC,OAAO;wCAC2B,SAAS,4BAA4B,SAAS,oCAAoC,SAAS,UAAU,SAAS;;;;;oBAKlI,MAAM;8GACoF,SAAS;;;;;;;;sBAQjG,UAAU;;;;;oBAKZ,CAAA;IAChB,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAA;IAEX,OAAO;;;;;;;;;;;;;;;;;;;;EAoBP,OAAO,CAAC,QAAQ,EAAE;;;;;EAKlB,sBAAsB;;;;EAItB,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4BjB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCvB,CAAA;AACD,CAAC"}
@@ -46,6 +46,7 @@ function generateAuditLogSidebar({ context }) {
46
46
  .addImport({
47
47
  from: Generator.toPackageName('react'),
48
48
  items: [
49
+ Generator.toFunctionName('ComponentProps'),
49
50
  Generator.toFunctionName('useState'),
50
51
  Generator.toFunctionName('useMemo'),
51
52
  Generator.toFunctionName('useCallback'),
@@ -55,10 +56,9 @@ function generateAuditLogSidebar({ context }) {
55
56
  from: Generator.toPackageName('@radix-ui/react-icons'),
56
57
  items: [
57
58
  Generator.toFunctionName('ChevronRightIcon'),
58
- Generator.toFunctionName('ChevronLeftIcon'),
59
- Generator.toFunctionName('ReloadIcon'),
60
- Generator.toFunctionName('PersonIcon'),
61
59
  Generator.toFunctionName('ClockIcon'),
60
+ Generator.toFunctionName('PersonIcon'),
61
+ Generator.toFunctionName('ReloadIcon'),
62
62
  ],
63
63
  })
64
64
  .addImport({
@@ -80,6 +80,25 @@ function generateAuditLogSidebar({ context }) {
80
80
  }
81
81
  return `
82
82
  ${imports.generate()}
83
+ import {
84
+ Breadcrumb,
85
+ BreadcrumbItem,
86
+ BreadcrumbLink,
87
+ BreadcrumbList,
88
+ BreadcrumbPage,
89
+ BreadcrumbSeparator,
90
+ Select,
91
+ SelectContent,
92
+ SelectItem,
93
+ SelectTrigger,
94
+ SelectValue,
95
+ Sidebar,
96
+ SidebarContent,
97
+ SidebarGroup,
98
+ SidebarGroupContent,
99
+ SidebarSeparator,
100
+ useSidebar,
101
+ } from '@postxl/ui-components'
83
102
 
84
103
  /**
85
104
  * Map of custom action labels from the schema
@@ -371,39 +390,60 @@ function BreadcrumbNavigation({
371
390
  const entityCountText = getEntityCountText(entityCount)
372
391
 
373
392
  return (
374
- <div className="px-4 py-2 border-b border-sidebar-border flex items-center gap-1 text-xs">
375
- <button onClick={onClearEntity} className="text-sidebar-primary hover:underline">
376
- All {model}s
377
- </button>
378
- {hasEntityFilter && (
379
- <>
380
- <ChevronRightIcon className="w-3 h-3 text-muted-foreground" />
381
- {hasFieldFilter ? (
382
- <button onClick={onClearField} className="text-sidebar-primary hover:underline">
383
- {entityCountText}
393
+ <Breadcrumb>
394
+ <BreadcrumbList className="text-xs">
395
+ <BreadcrumbItem>
396
+ <BreadcrumbLink asChild>
397
+ <button onClick={onClearEntity} className="hover:underline">
398
+ All {model}s
384
399
  </button>
385
- ) : (
386
- <span className="text-sidebar-accent-foreground">{entityCountText}</span>
387
- )}
388
- </>
389
- )}
390
- {hasFieldFilter && (
391
- <>
392
- <ChevronRightIcon className="w-3 h-3 text-muted-foreground" />
393
- <span className="text-sidebar-accent-foreground">{field}</span>
394
- </>
395
- )}
396
- </div>
400
+ </BreadcrumbLink>
401
+ </BreadcrumbItem>
402
+ {hasEntityFilter && (
403
+ <>
404
+ <BreadcrumbSeparator />
405
+ <BreadcrumbItem>
406
+ {hasFieldFilter ? (
407
+ <BreadcrumbLink asChild>
408
+ <button onClick={onClearField} className="hover:underline">
409
+ {entityCountText}
410
+ </button>
411
+ </BreadcrumbLink>
412
+ ) : (
413
+ <BreadcrumbPage>{entityCountText}</BreadcrumbPage>
414
+ )}
415
+ </BreadcrumbItem>
416
+ </>
417
+ )}
418
+ {hasFieldFilter && (
419
+ <>
420
+ <BreadcrumbSeparator />
421
+ <BreadcrumbItem>
422
+ <BreadcrumbPage>{field}</BreadcrumbPage>
423
+ </BreadcrumbItem>
424
+ </>
425
+ )}
426
+ </BreadcrumbList>
427
+ </Breadcrumb>
397
428
  )
398
429
  }
399
430
 
400
431
  /**
401
432
  * Audit Log Sidebar component for admin pages
402
433
  */
403
- export function AuditLogSidebar({ model, entityId, entityIds, field, labelField: _labelField = 'name', onClearField, onClearEntity }: AuditLogSidebarProps) {
404
- const [isOpen, setIsOpen] = useState(false)
434
+ export function AuditLogSidebar({
435
+ model,
436
+ entityId,
437
+ entityIds,
438
+ field,
439
+ labelField: _labelField = 'name',
440
+ onClearField,
441
+ onClearEntity,
442
+ ...props
443
+ }: AuditLogSidebarProps & ComponentProps<typeof Sidebar>) {
405
444
  const [limit, setLimit] = useState(20)
406
445
  const [expandedEntries, setExpandedEntries] = useState<Set<string>>(new Set())
446
+ const { open: isOpen } = useSidebar()
407
447
 
408
448
  const trpc = ${context.trpcClient.name}()
409
449
  const queryClient = useQueryClient()
@@ -484,127 +524,117 @@ export function AuditLogSidebar({ model, entityId, entityIds, field, labelField:
484
524
  const showBreadcrumbs = hasEntityFilter || hasFieldFilter
485
525
 
486
526
  return (
487
- <>
488
- {/* Toggle button - fixed on right edge */}
489
- <button
490
- onClick={() => setIsOpen(!isOpen)}
491
- className={\`
492
- fixed right-0 top-1/2 -translate-y-1/2 z-40
493
- flex items-center justify-center
494
- w-6 h-24 bg-sidebar
495
- border border-r-0 border-sidebar-border
496
- rounded-l-lg shadow-lg
497
- hover:bg-sidebar-accent
498
- transition-colors
499
- \${isOpen ? 'right-80' : 'right-0'}
500
- \`}
501
- aria-label={isOpen ? 'Close audit log' : 'Open audit log'}
502
- >
503
- {isOpen ? (
504
- <ChevronRightIcon className="w-4 h-4 text-sidebar-accent-foreground" />
505
- ) : (
506
- <ChevronLeftIcon className="w-4 h-4 text-sidebar-accent-foreground" />
507
- )}
508
- </button>
509
-
510
- {/* Sidebar */}
511
- <aside
512
- className={\`
513
- fixed right-0 top-0 h-screen w-80 bg-sidebar
514
- border-l border-sidebar-border
515
- overflow-hidden transition-transform duration-300 z-30
516
- \${isOpen ? 'translate-x-0' : 'translate-x-full'}
517
- \`}
518
- >
519
- <div className="flex flex-col h-full">
520
- {/* Header */}
521
- <div className="p-4 border-b border-sidebar-border">
527
+ <Sidebar {...props}>
528
+ <SidebarContent>
529
+ <SidebarGroup>
530
+ <SidebarGroupContent className="p-2">
522
531
  <div className="flex items-center justify-between">
523
532
  <h3 className="text-lg font-semibold text-sidebar-foreground">Audit Log</h3>
524
533
  <button
525
534
  onClick={() => void refetch()}
526
535
  disabled={isFetching}
527
- className="p-1 hover:bg-sidebar-accent rounded transition-colors disabled:opacity-50"
536
+ className="hover:bg-sidebar-accent rounded transition-colors disabled:opacity-50"
528
537
  aria-label="Refresh"
529
538
  >
530
539
  <ReloadIcon className={\`w-4 h-4 \${isFetching ? 'animate-spin' : ''}\`} />
531
540
  </button>
532
541
  </div>
542
+
533
543
  <p className="mt-1 text-sm text-muted-foreground">{filterDescription}</p>
534
544
  {data && (
535
545
  <p className="mt-1 text-xs text-muted-foreground">
536
546
  {data.total} {data.total === 1 ? 'change' : 'changes'} found
537
547
  </p>
538
548
  )}
539
- </div>
540
- {/* Breadcrumb navigation */}
541
- {showBreadcrumbs && (
542
- <BreadcrumbNavigation
543
- model={model}
544
- hasEntityFilter={hasEntityFilter}
545
- hasFieldFilter={hasFieldFilter}
546
- entityCount={entityCount}
547
- field={field}
548
- onClearEntity={handleClearEntity}
549
- onClearField={handleClearField}
550
- />
551
- )}
552
-
553
- {/* Limit selector */}
554
- <div className="px-4 py-2 border-b border-sidebar-border">
555
- <label className="text-xs text-muted-foreground">
556
- Show:{' '}
557
- <select
558
- value={limit}
559
- onChange={(e) => setLimit(Number(e.target.value))}
560
- className="ml-1 px-2 py-1 text-xs border border-sidebar-border rounded bg-background"
561
- >
562
- <option value={10}>10</option>
563
- <option value={20}>20</option>
564
- <option value={50}>50</option>
565
- <option value={100}>100</option>
566
- </select>
567
- </label>
568
- </div>
569
- {/* Content */}
570
- <div className="flex-1 overflow-y-auto">
571
- {isLoading && (
572
- <div className="flex items-center justify-center h-32">
573
- <ReloadIcon className="w-6 h-6 animate-spin text-muted-foreground" />
574
- </div>
549
+ </SidebarGroupContent>
550
+ </SidebarGroup>
551
+
552
+ <SidebarSeparator className="mx-0" />
553
+
554
+ <SidebarGroup>
555
+ <SidebarGroupContent className="p-2">
556
+ <p className="font-bold mb-2">Breadcrumb navigation</p>
557
+ {showBreadcrumbs && (
558
+ <BreadcrumbNavigation
559
+ model={model}
560
+ hasEntityFilter={hasEntityFilter}
561
+ hasFieldFilter={hasFieldFilter}
562
+ entityCount={entityCount}
563
+ field={field}
564
+ onClearEntity={handleClearEntity}
565
+ onClearField={handleClearField}
566
+ />
575
567
  )}
576
- {!isLoading && data?.entries.length === 0 && (
577
- <div className="flex flex-col items-center justify-center h-32 text-muted-foreground">
578
- <p className="text-sm">No changes found</p>
579
- <p className="text-xs mt-1">Changes will appear here when entities are modified</p>
580
- </div>
581
- )}
582
- {!isLoading && data && data.entries.length > 0 && (
583
- <div>
584
- {data.entries.map((entry: AuditLogEntry) => (
585
- <AuditLogEntryCard
586
- key={entry.id}
587
- entry={entry}
588
- isExpanded={expandedEntries.has(entry.id)}
589
- onToggle={() => toggleEntry(entry.id)}
590
- />
591
- ))}
592
- {data.hasMore && (
593
- <div className="p-3">
594
- <button
595
- onClick={loadMore}
596
- className="w-full py-2 text-sm text-primary hover:bg-sidebar-accent rounded transition-colors"
597
- >
598
- Load more...
599
- </button>
600
- </div>
601
- )}
602
- </div>
603
- )}
604
- </div>
605
- </div>
606
- </aside>
607
- </>
568
+ </SidebarGroupContent>
569
+ </SidebarGroup>
570
+
571
+ <SidebarSeparator className="mx-0" />
572
+
573
+ <SidebarGroup>
574
+ <SidebarGroupContent className="p-2">
575
+ <p className="font-bold mb-2">Limit selector</p>
576
+ <label className="flex gap-2 items-center text-xs text-muted-foreground">
577
+ <span>Show:</span>
578
+ <Select defaultValue={limit.toString()}>
579
+ <SelectTrigger className="w-[70px]" variant="simple" size="xs">
580
+ <SelectValue placeholder="Limit" />
581
+ </SelectTrigger>
582
+ <SelectContent>
583
+ {[10, 20, 50, 100].map((option) => (
584
+ <SelectItem key={option} value={option.toString()}>
585
+ {option}
586
+ </SelectItem>
587
+ ))}
588
+ </SelectContent>
589
+ </Select>
590
+ </label>
591
+ </SidebarGroupContent>
592
+ </SidebarGroup>
593
+
594
+ <SidebarSeparator className="mx-0" />
595
+
596
+ <SidebarGroup>
597
+ <SidebarGroupContent className="p-2">
598
+ <p className="font-bold mb-2">Content</p>
599
+ <div className="flex-1 overflow-y-auto">
600
+ {isLoading && (
601
+ <div className="flex items-center justify-center h-32">
602
+ <ReloadIcon className="w-6 h-6 animate-spin text-muted-foreground" />
603
+ </div>
604
+ )}
605
+ {!isLoading && data?.entries.length === 0 && (
606
+ <div className="flex flex-col text-muted-foreground">
607
+ <p className="text-sm font-bold">No changes found</p>
608
+ <p className="text-sm mt-1">Changes will appear here when entities are modified</p>
609
+ </div>
610
+ )}
611
+ {!isLoading && data && data.entries.length > 0 && (
612
+ <div>
613
+ {data.entries.map((entry: AuditLogEntry) => (
614
+ <AuditLogEntryCard
615
+ key={entry.id}
616
+ entry={entry}
617
+ isExpanded={expandedEntries.has(entry.id)}
618
+ onToggle={() => toggleEntry(entry.id)}
619
+ />
620
+ ))}
621
+ {data.hasMore && (
622
+ <div className="p-3">
623
+ <button
624
+ onClick={loadMore}
625
+ className="w-full py-2 text-sm text-primary hover:bg-sidebar-accent rounded transition-colors"
626
+ >
627
+ Load more...
628
+ </button>
629
+ </div>
630
+ )}
631
+ </div>
632
+ )}
633
+ </div>
634
+ </SidebarGroupContent>
635
+ </SidebarGroup>
636
+ </SidebarContent>
637
+ </Sidebar>
608
638
  )
609
639
  }
610
640
  `;
@@ -1 +1 @@
1
- {"version":3,"file":"audit-log-sidebar.generator.js","sourceRoot":"","sources":["../../../src/frontend-admin/generators/audit-log-sidebar.generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit-log-sidebar.generator.js","sourceRoot":"","sources":["../../../src/frontend-admin/generators/audit-log-sidebar.generator.ts"],"names":[],"mappings":"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"}