@ossy/timesheets 3.10.0 → 3.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/package.json +4 -4
- package/src/TimesheetCard.jsx +2 -0
- package/src/TimesheetPanel.jsx +27 -5
- package/src/TimesheetsFreeTool.jsx +10 -6
- package/src/TimesheetsProductHome.jsx +16 -9
- package/src/create-and-save-timesheet.flow.js +34 -0
- package/src/delete-timesheet.flow.js +41 -0
- package/src/disable-timesheets.flow.js +58 -0
- package/src/enable-timesheets.flow.js +24 -0
- package/src/export-csv.action.js +7 -0
- package/src/export-pdf.action.js +7 -0
- package/src/export-png.action.js +7 -0
- package/src/export-timesheet-csv.flow.js +39 -0
- package/src/export-timesheet-pdf.flow.js +39 -0
- package/src/export-timesheet-png.flow.js +39 -0
- package/src/index.js +5 -0
- package/src/open-delete.action.js +7 -0
- package/src/open-export.action.js +7 -0
- package/src/timesheets.page.jsx +6 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/timesheets",
|
|
3
3
|
"description": "Timesheets — generate, review, save, and export monthly work hours",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.11.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"module": "./src/index.js",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"src": "./src"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@ossy/email": "^3.0
|
|
21
|
-
"@ossy/resources": "^3.
|
|
20
|
+
"@ossy/email": "^3.11.0",
|
|
21
|
+
"@ossy/resources": "^3.11.0",
|
|
22
22
|
"@ossy/schema": "^3.8.0",
|
|
23
23
|
"html-to-image": "^1.11.13",
|
|
24
24
|
"nanoid": "^5.1.11",
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"/src",
|
|
44
44
|
"README.md"
|
|
45
45
|
],
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "53ff8456920e09151c9d88df4a6c3ee958d811eb"
|
|
47
47
|
}
|
package/src/TimesheetCard.jsx
CHANGED
|
@@ -33,6 +33,8 @@ export function TimesheetCard ({ timesheet, onClick, selected = false, actions }
|
|
|
33
33
|
selected={selected}
|
|
34
34
|
onClick={onClick}
|
|
35
35
|
onContextMenu={handleContextMenu}
|
|
36
|
+
data-timesheet-id={timesheet?.id}
|
|
37
|
+
data-timesheet-status={status}
|
|
36
38
|
leading={(
|
|
37
39
|
<Icon size="s" name="calendar" style={{ fill: 'hsl(0, 0%, 80%)' }} />
|
|
38
40
|
)}
|
package/src/TimesheetPanel.jsx
CHANGED
|
@@ -11,11 +11,16 @@ import { useTimesheetParties } from './TimesheetPartyFields.jsx'
|
|
|
11
11
|
import { contractBillingMeta, labelForContract, labelForEmployee } from './timesheet-parties.js'
|
|
12
12
|
import { metadata as GetTimesheet } from './get.action.js'
|
|
13
13
|
import { metadata as SaveTimesheet } from './save.action.js'
|
|
14
|
+
import { metadata as OpenDelete } from './open-delete.action.js'
|
|
15
|
+
import { metadata as OpenExport } from './open-export.action.js'
|
|
16
|
+
import { metadata as ExportCsv } from './export-csv.action.js'
|
|
17
|
+
import { metadata as ExportPng } from './export-png.action.js'
|
|
18
|
+
import { metadata as ExportPdf } from './export-pdf.action.js'
|
|
14
19
|
|
|
15
20
|
/**
|
|
16
21
|
* Storage-style side panel for reviewing and saving a timesheet.
|
|
17
22
|
*/
|
|
18
|
-
export function TimesheetPanel ({ timesheetId, onClose, onSaved, width = '50%' }) {
|
|
23
|
+
export function TimesheetPanel ({ timesheetId, onClose, onSaved, onRequestDelete, width = '50%' }) {
|
|
19
24
|
const { t } = useLocale()
|
|
20
25
|
const sdk = useSdk()
|
|
21
26
|
const captureRef = useRef(null)
|
|
@@ -130,6 +135,8 @@ export function TimesheetPanel ({ timesheetId, onClose, onSaved, width = '50%' }
|
|
|
130
135
|
surface="primary"
|
|
131
136
|
bordered
|
|
132
137
|
roundness="xs"
|
|
138
|
+
data-timesheet-panel={timesheetId || undefined}
|
|
139
|
+
data-timesheet-panel-status={timesheet?.status || (loading ? 'loading' : undefined)}
|
|
133
140
|
style={{ height: '100%', width, minHeight: 0, minWidth: 0, flexShrink: 0 }}
|
|
134
141
|
>
|
|
135
142
|
<View.Item>
|
|
@@ -151,6 +158,8 @@ export function TimesheetPanel ({ timesheetId, onClose, onSaved, width = '50%' }
|
|
|
151
158
|
<Dropdown
|
|
152
159
|
trigger={(
|
|
153
160
|
<Button
|
|
161
|
+
{...OpenExport}
|
|
162
|
+
label={undefined}
|
|
154
163
|
variant="command"
|
|
155
164
|
prefix="software-download"
|
|
156
165
|
disabled={exportDisabled}
|
|
@@ -160,25 +169,26 @@ export function TimesheetPanel ({ timesheetId, onClose, onSaved, width = '50%' }
|
|
|
160
169
|
>
|
|
161
170
|
<ContextMenu roundness="s" surface="primary">
|
|
162
171
|
<ContextMenu.Item
|
|
172
|
+
{...ExportCsv}
|
|
163
173
|
prefix="list"
|
|
164
|
-
label="timesheets.exportCsv"
|
|
165
174
|
onClick={() => runExport('csv')}
|
|
166
175
|
/>
|
|
167
176
|
<ContextMenu.Item
|
|
177
|
+
{...ExportPng}
|
|
168
178
|
prefix="image"
|
|
169
|
-
label="timesheets.exportPng"
|
|
170
179
|
onClick={() => runExport('png')}
|
|
171
180
|
/>
|
|
172
181
|
<ContextMenu.Item
|
|
182
|
+
{...ExportPdf}
|
|
173
183
|
prefix="file-document"
|
|
174
|
-
label="timesheets.exportPdf"
|
|
175
184
|
onClick={() => runExport('pdf')}
|
|
176
185
|
/>
|
|
177
186
|
</ContextMenu>
|
|
178
187
|
</Dropdown>
|
|
179
188
|
|
|
180
189
|
<Button
|
|
181
|
-
|
|
190
|
+
{...SaveTimesheet}
|
|
191
|
+
label={undefined}
|
|
182
192
|
variant="command"
|
|
183
193
|
prefix="check"
|
|
184
194
|
disabled={!timesheet?.id || saving || loading}
|
|
@@ -186,6 +196,18 @@ export function TimesheetPanel ({ timesheetId, onClose, onSaved, width = '50%' }
|
|
|
186
196
|
onClick={handleSave}
|
|
187
197
|
/>
|
|
188
198
|
|
|
199
|
+
{onRequestDelete && (
|
|
200
|
+
<Button
|
|
201
|
+
{...OpenDelete}
|
|
202
|
+
label={undefined}
|
|
203
|
+
variant="command-danger"
|
|
204
|
+
prefix="trash-empty"
|
|
205
|
+
disabled={!timesheet?.id || loading}
|
|
206
|
+
aria-label={t('timesheets.delete')}
|
|
207
|
+
onClick={() => onRequestDelete(timesheet)}
|
|
208
|
+
/>
|
|
209
|
+
)}
|
|
210
|
+
|
|
189
211
|
{onClose && (
|
|
190
212
|
<Button
|
|
191
213
|
prefix="close"
|
|
@@ -17,6 +17,10 @@ import { downloadTimesheetPdf } from './download-timesheet-pdf.js'
|
|
|
17
17
|
import { invokeErrorMessage } from './invoke-error-message.js'
|
|
18
18
|
import { sampleShowcaseTimesheet } from './sample-showcase-timesheet.js'
|
|
19
19
|
import { timesheetsAuthHref } from './timesheets-auth-return.js'
|
|
20
|
+
import { metadata as OpenExport } from './open-export.action.js'
|
|
21
|
+
import { metadata as ExportCsv } from './export-csv.action.js'
|
|
22
|
+
import { metadata as ExportPng } from './export-png.action.js'
|
|
23
|
+
import { metadata as ExportPdf } from './export-pdf.action.js'
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Client-only timesheet for the sales page — prefilled, editable, exportable.
|
|
@@ -230,29 +234,29 @@ export function TimesheetsFreeTool ({
|
|
|
230
234
|
<Dropdown
|
|
231
235
|
trigger={(
|
|
232
236
|
<Button
|
|
237
|
+
{...OpenExport}
|
|
233
238
|
variant="secondary"
|
|
234
239
|
prefix="software-download"
|
|
235
240
|
suffix="chevron-down"
|
|
236
241
|
disabled={exportDisabled}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
</Button>
|
|
242
|
+
label={exporting ? 'timesheets.exporting' : OpenExport.label}
|
|
243
|
+
/>
|
|
240
244
|
)}
|
|
241
245
|
>
|
|
242
246
|
<ContextMenu roundness="s" surface="primary">
|
|
243
247
|
<ContextMenu.Item
|
|
248
|
+
{...ExportCsv}
|
|
244
249
|
prefix="list"
|
|
245
|
-
label="timesheets.exportCsv"
|
|
246
250
|
onClick={() => runExport('csv')}
|
|
247
251
|
/>
|
|
248
252
|
<ContextMenu.Item
|
|
253
|
+
{...ExportPng}
|
|
249
254
|
prefix="image"
|
|
250
|
-
label="timesheets.exportPng"
|
|
251
255
|
onClick={() => runExport('png')}
|
|
252
256
|
/>
|
|
253
257
|
<ContextMenu.Item
|
|
258
|
+
{...ExportPdf}
|
|
254
259
|
prefix="file-document"
|
|
255
|
-
label="timesheets.exportPdf"
|
|
256
260
|
onClick={() => runExport('pdf')}
|
|
257
261
|
/>
|
|
258
262
|
</ContextMenu>
|
|
@@ -16,6 +16,10 @@ import { metadata as ListTimesheets } from './list.action.js'
|
|
|
16
16
|
import { metadata as GenerateTimesheet } from './generate.action.js'
|
|
17
17
|
import { metadata as OpenNewTimesheet } from './open-new.action.js'
|
|
18
18
|
import { metadata as DeleteTimesheet } from './delete.action.js'
|
|
19
|
+
import { metadata as OpenDelete } from './open-delete.action.js'
|
|
20
|
+
import { metadata as ExportCsv } from './export-csv.action.js'
|
|
21
|
+
import { metadata as ExportPng } from './export-png.action.js'
|
|
22
|
+
import { metadata as ExportPdf } from './export-pdf.action.js'
|
|
19
23
|
import { HolidayLocaleSelect } from './HolidayLocaleSelect.jsx'
|
|
20
24
|
import { resolveHolidayLocale } from './holiday-locale.js'
|
|
21
25
|
|
|
@@ -212,6 +216,7 @@ export default function TimesheetsProductHome () {
|
|
|
212
216
|
<>
|
|
213
217
|
<Page
|
|
214
218
|
scroll
|
|
219
|
+
data-timesheets-status="on"
|
|
215
220
|
title="timesheets.home.title"
|
|
216
221
|
near={statusTags.length > 0 ? <Tags tags={statusTags} size="s" /> : undefined}
|
|
217
222
|
description="timesheets.home.description"
|
|
@@ -232,7 +237,8 @@ export default function TimesheetsProductHome () {
|
|
|
232
237
|
<Text as="h2" variant="small" weight="medium" text="timesheets.listTitle" />
|
|
233
238
|
</View.Item>
|
|
234
239
|
<Button
|
|
235
|
-
|
|
240
|
+
{...OpenNewTimesheet}
|
|
241
|
+
label={undefined}
|
|
236
242
|
variant="command"
|
|
237
243
|
prefix="add"
|
|
238
244
|
aria-label={t('timesheets.new')}
|
|
@@ -272,25 +278,26 @@ export default function TimesheetsProductHome () {
|
|
|
272
278
|
/>,
|
|
273
279
|
<ContextMenu.Item
|
|
274
280
|
key="csv"
|
|
281
|
+
{...ExportCsv}
|
|
275
282
|
prefix="list"
|
|
276
|
-
label="timesheets.exportCsv"
|
|
277
283
|
onClick={() => requestExport(sheet, 'csv')}
|
|
278
284
|
/>,
|
|
279
285
|
<ContextMenu.Item
|
|
280
286
|
key="png"
|
|
287
|
+
{...ExportPng}
|
|
281
288
|
prefix="image"
|
|
282
|
-
label="timesheets.exportPng"
|
|
283
289
|
onClick={() => requestExport(sheet, 'png')}
|
|
284
290
|
/>,
|
|
285
291
|
<ContextMenu.Item
|
|
286
292
|
key="pdf"
|
|
293
|
+
{...ExportPdf}
|
|
287
294
|
prefix="file-document"
|
|
288
|
-
label="timesheets.exportPdf"
|
|
289
295
|
onClick={() => requestExport(sheet, 'pdf')}
|
|
290
296
|
/>,
|
|
291
297
|
<ContextMenu.Separator key="separator" />,
|
|
292
298
|
<ContextMenu.Item
|
|
293
299
|
key="delete"
|
|
300
|
+
{...OpenDelete}
|
|
294
301
|
variant="command-danger"
|
|
295
302
|
prefix="trash-empty"
|
|
296
303
|
label="timesheets.delete"
|
|
@@ -309,6 +316,7 @@ export default function TimesheetsProductHome () {
|
|
|
309
316
|
timesheetId={selectedResourceId}
|
|
310
317
|
onClose={clearSelectedResourceId}
|
|
311
318
|
onSaved={refreshList}
|
|
319
|
+
onRequestDelete={setSheetToDelete}
|
|
312
320
|
/>
|
|
313
321
|
</View>
|
|
314
322
|
</Page>
|
|
@@ -328,7 +336,7 @@ export default function TimesheetsProductHome () {
|
|
|
328
336
|
onClick: () => setSheetToDelete(null),
|
|
329
337
|
},
|
|
330
338
|
{
|
|
331
|
-
|
|
339
|
+
...DeleteTimesheet,
|
|
332
340
|
variant: 'command-danger',
|
|
333
341
|
disabled: deleting,
|
|
334
342
|
onClick: confirmDelete,
|
|
@@ -410,13 +418,12 @@ export default function TimesheetsProductHome () {
|
|
|
410
418
|
onClick={closeCreateModal}
|
|
411
419
|
/>
|
|
412
420
|
<Button
|
|
413
|
-
|
|
421
|
+
{...GenerateTimesheet}
|
|
422
|
+
label={generating ? 'timesheets.loading' : GenerateTimesheet.label}
|
|
414
423
|
variant="cta"
|
|
415
424
|
disabled={generating || !selected}
|
|
416
425
|
onClick={handleGenerate}
|
|
417
|
-
|
|
418
|
-
{generating ? t('timesheets.loading') : t('timesheets.generate')}
|
|
419
|
-
</Button>
|
|
426
|
+
/>
|
|
420
427
|
</View>
|
|
421
428
|
</View>
|
|
422
429
|
</View>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import enableTimesheetsFlow from './enable-timesheets.flow.js'
|
|
2
|
+
import { metadata as OpenNewTimesheet } from './open-new.action.js'
|
|
3
|
+
import { metadata as GenerateTimesheet } from './generate.action.js'
|
|
4
|
+
import { metadata as SaveTimesheet } from './save.action.js'
|
|
5
|
+
|
|
6
|
+
export const metadata = {
|
|
7
|
+
id: '@ossy/timesheets/flows/create-and-save-timesheet',
|
|
8
|
+
feature: 'timesheets',
|
|
9
|
+
requires: ['server', 'database'],
|
|
10
|
+
timeout: 150_000,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* After enabling timesheets, open the create modal, generate a draft for the
|
|
15
|
+
* current month, and save it from the side panel.
|
|
16
|
+
*/
|
|
17
|
+
export default {
|
|
18
|
+
title: 'Create and save timesheet',
|
|
19
|
+
description:
|
|
20
|
+
'Signed-in user enables timesheets, generates a draft timesheet, saves it, and sees saved status in the panel and list',
|
|
21
|
+
steps: [
|
|
22
|
+
...enableTimesheetsFlow.steps,
|
|
23
|
+
{ result: { action: OpenNewTimesheet, timeout: 10000 } },
|
|
24
|
+
{ action: OpenNewTimesheet },
|
|
25
|
+
{ result: { action: GenerateTimesheet, timeout: 10000 } },
|
|
26
|
+
{ action: GenerateTimesheet },
|
|
27
|
+
{ result: { action: SaveTimesheet, timeout: 20000 } },
|
|
28
|
+
{ result: { selector: '[data-timesheet-panel-status="draft"]', timeout: 20000 } },
|
|
29
|
+
{ result: { selector: '[data-timesheet-status="draft"]', timeout: 20000 } },
|
|
30
|
+
{ action: SaveTimesheet },
|
|
31
|
+
{ result: { selector: '[data-timesheet-panel-status="saved"]', timeout: 20000 } },
|
|
32
|
+
{ result: { selector: '[data-timesheet-status="saved"]', timeout: 20000 } },
|
|
33
|
+
],
|
|
34
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import createAndSaveTimesheetFlow from './create-and-save-timesheet.flow.js'
|
|
2
|
+
import { metadata as OpenDelete } from './open-delete.action.js'
|
|
3
|
+
import { metadata as DeleteTimesheet } from './delete.action.js'
|
|
4
|
+
|
|
5
|
+
export const metadata = {
|
|
6
|
+
id: '@ossy/timesheets/flows/delete-timesheet',
|
|
7
|
+
feature: 'timesheets',
|
|
8
|
+
requires: ['server', 'database'],
|
|
9
|
+
timeout: 150_000,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* After creating and saving a timesheet, open delete from the panel, confirm,
|
|
14
|
+
* and assert the saved row leaves the list.
|
|
15
|
+
*/
|
|
16
|
+
export default {
|
|
17
|
+
title: 'Delete timesheet',
|
|
18
|
+
description:
|
|
19
|
+
'Signed-in user creates and saves a timesheet, deletes it from the panel confirm dialog, and sees it leave the list',
|
|
20
|
+
steps: [
|
|
21
|
+
...createAndSaveTimesheetFlow.steps,
|
|
22
|
+
{ result: { action: OpenDelete, timeout: 10000 } },
|
|
23
|
+
{ action: OpenDelete },
|
|
24
|
+
{ result: { action: DeleteTimesheet, timeout: 10000 } },
|
|
25
|
+
{ action: DeleteTimesheet },
|
|
26
|
+
{
|
|
27
|
+
result: {
|
|
28
|
+
selector: '[data-timesheet-status="saved"]',
|
|
29
|
+
hidden: true,
|
|
30
|
+
timeout: 20000,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
result: {
|
|
35
|
+
selector: '[data-timesheet-panel]',
|
|
36
|
+
hidden: true,
|
|
37
|
+
timeout: 10000,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import enableTimesheetsFlow from './enable-timesheets.flow.js'
|
|
2
|
+
import { DisableService, EnableService } from '@ossy/workspaces'
|
|
3
|
+
|
|
4
|
+
const TIMESHEETS = '@ossy/timesheets'
|
|
5
|
+
|
|
6
|
+
export const metadata = {
|
|
7
|
+
id: '@ossy/timesheets/flows/disable-timesheets',
|
|
8
|
+
feature: 'timesheets',
|
|
9
|
+
requires: ['server', 'database'],
|
|
10
|
+
timeout: 120_000,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* After enabling timesheets, disable the package from the catalog detail
|
|
15
|
+
* toggle and confirm the timesheets home returns to the sales (off) state.
|
|
16
|
+
*/
|
|
17
|
+
export default {
|
|
18
|
+
title: 'Disable timesheets',
|
|
19
|
+
description:
|
|
20
|
+
'Signed-in user enables timesheets, disables it from the package catalog, and sees the sales home again',
|
|
21
|
+
steps: [
|
|
22
|
+
...enableTimesheetsFlow.steps,
|
|
23
|
+
{ page: '@packages/detail', params: { packageSlug: 'timesheets' } },
|
|
24
|
+
{
|
|
25
|
+
result: {
|
|
26
|
+
action: { ...DisableService, service: TIMESHEETS },
|
|
27
|
+
timeout: 15000,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
result: {
|
|
32
|
+
selector: `[data-package-service="${TIMESHEETS}"][data-service-enabled="true"]`,
|
|
33
|
+
timeout: 10000,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{ action: { ...DisableService, service: TIMESHEETS } },
|
|
37
|
+
{
|
|
38
|
+
result: {
|
|
39
|
+
action: { ...EnableService, service: TIMESHEETS },
|
|
40
|
+
timeout: 20000,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
result: {
|
|
45
|
+
selector: `[data-package-service="${TIMESHEETS}"][data-service-enabled="false"]`,
|
|
46
|
+
timeout: 10000,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{ page: '@timesheets/home' },
|
|
50
|
+
{ result: { selector: '[data-timesheets-status="off"]', timeout: 20000 } },
|
|
51
|
+
{
|
|
52
|
+
result: {
|
|
53
|
+
action: { ...EnableService, service: TIMESHEETS },
|
|
54
|
+
timeout: 10000,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import signUpFlow from '@ossy/authentication/sign-up.flow.js'
|
|
2
|
+
import { EnableService } from '@ossy/workspaces'
|
|
3
|
+
|
|
4
|
+
export const metadata = {
|
|
5
|
+
id: '@ossy/timesheets/flows/enable-timesheets',
|
|
6
|
+
feature: 'timesheets',
|
|
7
|
+
requires: ['server', 'database'],
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* New workspace owner signs up and enables timesheets from the sales home.
|
|
12
|
+
*/
|
|
13
|
+
export default {
|
|
14
|
+
title: 'Enable timesheets',
|
|
15
|
+
description:
|
|
16
|
+
'New user registers, verifies email, and activates timesheets from the timesheets home sales page',
|
|
17
|
+
steps: [
|
|
18
|
+
...signUpFlow.steps,
|
|
19
|
+
{ page: '@timesheets/home' },
|
|
20
|
+
{ result: { action: { ...EnableService, service: '@ossy/timesheets' }, timeout: 15000 } },
|
|
21
|
+
{ action: { ...EnableService, service: '@ossy/timesheets' } },
|
|
22
|
+
{ result: { selector: '[data-timesheets-status="on"]', timeout: 20000 } },
|
|
23
|
+
],
|
|
24
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import createAndSaveTimesheetFlow from './create-and-save-timesheet.flow.js'
|
|
2
|
+
import { monthPeriodBounds } from './billing-period.js'
|
|
3
|
+
import { formatDateKey } from './build-prefilled-lines.js'
|
|
4
|
+
import { metadata as OpenExport } from './open-export.action.js'
|
|
5
|
+
import { metadata as ExportCsv } from './export-csv.action.js'
|
|
6
|
+
|
|
7
|
+
const now = new Date()
|
|
8
|
+
const { periodStart } = monthPeriodBounds(now.getFullYear(), now.getMonth())
|
|
9
|
+
const csvFilename = `timesheet-${formatDateKey(periodStart)}.csv`
|
|
10
|
+
|
|
11
|
+
export const metadata = {
|
|
12
|
+
id: '@ossy/timesheets/flows/export-timesheet-csv',
|
|
13
|
+
feature: 'timesheets',
|
|
14
|
+
requires: ['server', 'database'],
|
|
15
|
+
timeout: 150_000,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* After creating and saving a timesheet, open the panel export menu and
|
|
20
|
+
* download CSV (browser download event).
|
|
21
|
+
*/
|
|
22
|
+
export default {
|
|
23
|
+
title: 'Export timesheet CSV',
|
|
24
|
+
description:
|
|
25
|
+
'Signed-in user creates and saves a timesheet, opens export from the panel, and downloads CSV',
|
|
26
|
+
steps: [
|
|
27
|
+
...createAndSaveTimesheetFlow.steps,
|
|
28
|
+
{ result: { action: OpenExport, timeout: 10000 } },
|
|
29
|
+
{ action: OpenExport },
|
|
30
|
+
{ result: { action: ExportCsv, timeout: 10000 } },
|
|
31
|
+
{
|
|
32
|
+
download: {
|
|
33
|
+
action: ExportCsv,
|
|
34
|
+
filename: csvFilename,
|
|
35
|
+
timeout: 30000,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import createAndSaveTimesheetFlow from './create-and-save-timesheet.flow.js'
|
|
2
|
+
import { monthPeriodBounds } from './billing-period.js'
|
|
3
|
+
import { timesheetExportFilename } from './export-capture.js'
|
|
4
|
+
import { metadata as OpenExport } from './open-export.action.js'
|
|
5
|
+
import { metadata as ExportPdf } from './export-pdf.action.js'
|
|
6
|
+
|
|
7
|
+
const now = new Date()
|
|
8
|
+
const { periodStart } = monthPeriodBounds(now.getFullYear(), now.getMonth())
|
|
9
|
+
const pdfFilename = timesheetExportFilename(periodStart, 'pdf')
|
|
10
|
+
|
|
11
|
+
export const metadata = {
|
|
12
|
+
id: '@ossy/timesheets/flows/export-timesheet-pdf',
|
|
13
|
+
feature: 'timesheets',
|
|
14
|
+
requires: ['server', 'database'],
|
|
15
|
+
timeout: 150_000,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* After creating and saving a timesheet, open the panel export menu and
|
|
20
|
+
* download PDF (browser download event from html-to-image + pdf-lib).
|
|
21
|
+
*/
|
|
22
|
+
export default {
|
|
23
|
+
title: 'Export timesheet PDF',
|
|
24
|
+
description:
|
|
25
|
+
'Signed-in user creates and saves a timesheet, opens export from the panel, and downloads PDF',
|
|
26
|
+
steps: [
|
|
27
|
+
...createAndSaveTimesheetFlow.steps,
|
|
28
|
+
{ result: { action: OpenExport, timeout: 10000 } },
|
|
29
|
+
{ action: OpenExport },
|
|
30
|
+
{ result: { action: ExportPdf, timeout: 10000 } },
|
|
31
|
+
{
|
|
32
|
+
download: {
|
|
33
|
+
action: ExportPdf,
|
|
34
|
+
filename: pdfFilename,
|
|
35
|
+
timeout: 60000,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import createAndSaveTimesheetFlow from './create-and-save-timesheet.flow.js'
|
|
2
|
+
import { monthPeriodBounds } from './billing-period.js'
|
|
3
|
+
import { timesheetExportFilename } from './export-capture.js'
|
|
4
|
+
import { metadata as OpenExport } from './open-export.action.js'
|
|
5
|
+
import { metadata as ExportPng } from './export-png.action.js'
|
|
6
|
+
|
|
7
|
+
const now = new Date()
|
|
8
|
+
const { periodStart } = monthPeriodBounds(now.getFullYear(), now.getMonth())
|
|
9
|
+
const pngFilename = timesheetExportFilename(periodStart, 'png')
|
|
10
|
+
|
|
11
|
+
export const metadata = {
|
|
12
|
+
id: '@ossy/timesheets/flows/export-timesheet-png',
|
|
13
|
+
feature: 'timesheets',
|
|
14
|
+
requires: ['server', 'database'],
|
|
15
|
+
timeout: 150_000,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* After creating and saving a timesheet, open the panel export menu and
|
|
20
|
+
* download PNG (browser download event from html-to-image capture).
|
|
21
|
+
*/
|
|
22
|
+
export default {
|
|
23
|
+
title: 'Export timesheet PNG',
|
|
24
|
+
description:
|
|
25
|
+
'Signed-in user creates and saves a timesheet, opens export from the panel, and downloads PNG',
|
|
26
|
+
steps: [
|
|
27
|
+
...createAndSaveTimesheetFlow.steps,
|
|
28
|
+
{ result: { action: OpenExport, timeout: 10000 } },
|
|
29
|
+
{ action: OpenExport },
|
|
30
|
+
{ result: { action: ExportPng, timeout: 10000 } },
|
|
31
|
+
{
|
|
32
|
+
download: {
|
|
33
|
+
action: ExportPng,
|
|
34
|
+
filename: pngFilename,
|
|
35
|
+
timeout: 60000,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
}
|
package/src/index.js
CHANGED
|
@@ -11,6 +11,11 @@ export { metadata as ListTimesheets } from './list.action.js'
|
|
|
11
11
|
export { metadata as GetTimesheet } from './get.action.js'
|
|
12
12
|
export { metadata as DeleteTimesheet } from './delete.action.js'
|
|
13
13
|
export { metadata as OpenNewTimesheet } from './open-new.action.js'
|
|
14
|
+
export { metadata as OpenDeleteTimesheet } from './open-delete.action.js'
|
|
15
|
+
export { metadata as OpenExportTimesheet } from './open-export.action.js'
|
|
16
|
+
export { metadata as ExportTimesheetCsv } from './export-csv.action.js'
|
|
17
|
+
export { metadata as ExportTimesheetPng } from './export-png.action.js'
|
|
18
|
+
export { metadata as ExportTimesheetPdf } from './export-pdf.action.js'
|
|
14
19
|
export { invokeErrorMessage } from './invoke-error-message.js'
|
|
15
20
|
export { downloadTimesheetCsv } from './download-timesheet-csv.js'
|
|
16
21
|
export { downloadTimesheetImage } from './download-timesheet-image.js'
|
package/src/timesheets.page.jsx
CHANGED
|
@@ -20,11 +20,12 @@ export default function TimesheetsHomePage () {
|
|
|
20
20
|
const app = useApp()
|
|
21
21
|
const router = useRouter()
|
|
22
22
|
const { workspace: shellWorkspace } = useShellWorkspace()
|
|
23
|
-
const { read, invoke
|
|
23
|
+
const { read, invoke } = useSdk()
|
|
24
24
|
const isAuthenticated = !!app?.isAuthenticated
|
|
25
25
|
const {
|
|
26
26
|
data: workspaceFromSdk,
|
|
27
27
|
status: workspaceStatus,
|
|
28
|
+
refetch: refetchWorkspace,
|
|
28
29
|
} = read(GetWorkspace, undefined, { enabled: isAuthenticated })
|
|
29
30
|
const autoEnableStarted = useRef(false)
|
|
30
31
|
|
|
@@ -52,10 +53,12 @@ export default function TimesheetsHomePage () {
|
|
|
52
53
|
&& workspaceStatus !== AsyncStatus.Error
|
|
53
54
|
&& app?.workspaceServices == null
|
|
54
55
|
|
|
56
|
+
// Shared GetWorkspace cache with useShellWorkspace — refetch (not bare invalidate)
|
|
57
|
+
// awaits the post-enable read so home can switch sales → product reliably.
|
|
55
58
|
const enableService = useCallback(() => {
|
|
56
59
|
return invoke(EnableService, { service: TIMESHEETS_SERVICE })
|
|
57
|
-
.then(() =>
|
|
58
|
-
}, [invoke,
|
|
60
|
+
.then(() => refetchWorkspace())
|
|
61
|
+
}, [invoke, refetchWorkspace])
|
|
59
62
|
|
|
60
63
|
useEffect(() => {
|
|
61
64
|
if (!isAuthenticated || !enableRequested || autoEnableStarted.current) return
|