@burdenoff/microfe-movethewheels 2026.1012.2 → 2026.1013.1

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.
@@ -1,5 +1,7 @@
1
1
  import { useState, type ReactNode } from 'react';
2
2
  import { Clock } from 'lucide-react';
3
+ import { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';
4
+ import { useShipmentPermissions } from '../hooks/useShipmentPermissions';
3
5
 
4
6
  import { PageLayout } from '../components/PageLayout';
5
7
  import { Button, Input } from '../components/ui';
@@ -105,11 +107,22 @@ export default function FreeTimePage() {
105
107
  subtitle="Separate sourced commercial obligations, actual events and recorded counterparty decisions."
106
108
  icon={<Clock aria-hidden="true" />}
107
109
  >
108
- <Workbench key={`${workspaceId}:${tenantId}:${currentUser?.id}:${apiGatewayUrl}`} />
110
+ <PermissionProvider
111
+ key={`${workspaceId}:${tenantId}:${currentUser?.id}:${apiGatewayUrl}`}
112
+ scopeId={workspaceId ?? null}
113
+ gateway="workspace"
114
+ >
115
+ <FreeTimeAccess />
116
+ </PermissionProvider>
109
117
  </PageLayout>
110
118
  );
111
119
  }
112
- function Workbench() {
120
+ function FreeTimeAccess() {
121
+ const access = useShipmentPermissions();
122
+ return <Workbench access={access} />;
123
+ }
124
+
125
+ function Workbench({ access }: { access: ReturnType<typeof useShipmentPermissions> }) {
113
126
  const [containerId, setContainerId] = useState('');
114
127
  const [cycleId, setCycleId] = useState('');
115
128
  const [clockId, setClockId] = useState('');
@@ -131,41 +144,56 @@ function Workbench() {
131
144
  const [extensionId, setExtensionId] = useState('');
132
145
  const [decisionKind, setDecisionKind] = useState<'ACCEPTED' | 'REJECTED' | 'EXPIRED'>('ACCEPTED');
133
146
  const mutations = useConditionAction();
134
- const containers = useConditionQuery<FreeContainersQuery>(FreeContainersDocument, {
135
- pagination: { limit: pageSize, offset: containerOffset },
136
- });
147
+ const containers = useConditionQuery<FreeContainersQuery>(
148
+ FreeContainersDocument,
149
+ access.canRead
150
+ ? {
151
+ pagination: { limit: pageSize, offset: containerOffset },
152
+ }
153
+ : null
154
+ );
137
155
  const cycles = useConditionQuery<FreeCyclesQuery>(
138
156
  FreeCyclesDocument,
139
- containerId ? { containerId, pagination: { limit: pageSize, offset: cycleOffset } } : null
157
+ access.canRead && containerId
158
+ ? { containerId, pagination: { limit: pageSize, offset: cycleOffset } }
159
+ : null
140
160
  );
141
161
  const events = useConditionQuery<FreeEventsQuery>(
142
162
  FreeEventsDocument,
143
- cycleId ? { cycleId, pagination: { limit: pageSize, offset: eventOffset } } : null,
163
+ access.canRead && cycleId
164
+ ? { cycleId, pagination: { limit: pageSize, offset: eventOffset } }
165
+ : null,
144
166
  mutations.revision
145
167
  );
146
168
  const clocks = useConditionQuery<FreeClocksQuery>(
147
169
  FreeClocksDocument,
148
- cycleId ? { cycleId, pagination: { limit: pageSize, offset: clockOffset } } : null,
170
+ access.canRead && cycleId
171
+ ? { cycleId, pagination: { limit: pageSize, offset: clockOffset } }
172
+ : null,
149
173
  mutations.revision
150
174
  );
151
175
  const agreements = useConditionQuery<FreeAgreementsQuery>(
152
176
  FreeAgreementsDocument,
153
- clockId ? { clockId, pagination: { limit: pageSize, offset: agreementOffset } } : null,
177
+ access.canRead && clockId
178
+ ? { clockId, pagination: { limit: pageSize, offset: agreementOffset } }
179
+ : null,
154
180
  mutations.revision
155
181
  );
156
182
  const currentAgreement = useConditionQuery<FreeAgreementsQuery>(
157
183
  FreeAgreementsDocument,
158
- clockId ? { clockId, pagination: { limit: 1, offset: 0 } } : null,
184
+ access.canRead && clockId ? { clockId, pagination: { limit: 1, offset: 0 } } : null,
159
185
  mutations.revision
160
186
  );
161
187
  const extensions = useConditionQuery<FreeExtensionsQuery>(
162
188
  FreeExtensionsDocument,
163
- clockId ? { clockId, pagination: { limit: pageSize, offset: extensionOffset } } : null,
189
+ access.canRead && clockId
190
+ ? { clockId, pagination: { limit: pageSize, offset: extensionOffset } }
191
+ : null,
164
192
  mutations.revision
165
193
  );
166
194
  const exposure = useConditionQuery<FreeExposureQuery>(
167
195
  FreeExposureDocument,
168
- clockId ? { clockId, asOf: evaluation } : null,
196
+ access.canRead && clockId ? { clockId, asOf: evaluation } : null,
169
197
  mutations.revision
170
198
  );
171
199
  const clock = clocks.data?.mtwFreeTimeClockPage.items.find((item) => item.id === clockId);
@@ -192,6 +220,16 @@ function Workbench() {
192
220
  if (!evidence.length) throw new Error('Select exact decision/request evidence versions.');
193
221
  return evidence.map(({ fileId, versionId }) => ({ fileId, versionId }));
194
222
  };
223
+ if (!access.canRead)
224
+ return (
225
+ <p role={access.failed ? 'alert' : 'status'}>
226
+ {access.loading
227
+ ? 'Checking shipment permissions…'
228
+ : access.failed
229
+ ? 'Shipment permissions are unavailable. Reload to retry.'
230
+ : 'Shipment read permission is required.'}
231
+ </p>
232
+ );
195
233
  return (
196
234
  <div className="space-y-6">
197
235
  <p className="rounded-lg border p-4 text-sm">
@@ -370,7 +408,7 @@ function Workbench() {
370
408
  </RecordState>
371
409
  </section>
372
410
  )}
373
- {cycleId && (
411
+ {cycleId && access.canUpdate && (
374
412
  <section className={card} aria-label="Record contractual change">
375
413
  <h2 className="text-lg font-semibold">Record contractual change</h2>
376
414
  <label className="block">
@@ -456,6 +494,8 @@ function Workbench() {
456
494
  (action === 'correct' && currentAgreement.loading)
457
495
  }
458
496
  onSubmit={async (agreement) => {
497
+ if (!access.allows('update'))
498
+ throw new Error('Shipment update permission is required.');
459
499
  if (action === 'open') {
460
500
  if (!obligation.trim())
461
501
  throw new Error('Enter a distinct commercial obligation reference.');
@@ -485,6 +525,8 @@ function Workbench() {
485
525
  event.preventDefault();
486
526
  setValidation(null);
487
527
  try {
528
+ if (!access.allows('update'))
529
+ throw new Error('Shipment update permission is required.');
488
530
  if (!clock || !current)
489
531
  throw new Error('Reload the current obligation and agreement.');
490
532
  const values = new FormData(event.currentTarget);