@abtnode/ux 1.16.17-beta-d282d63e → 1.16.17-beta-1cab4b42

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.
@@ -4,13 +4,13 @@ import MenuItem from '@mui/material/MenuItem';
4
4
  import TextField from '@mui/material/TextField';
5
5
  import Typography from '@mui/material/Typography';
6
6
  import Breadcrumbs from '@mui/material/Breadcrumbs';
7
+ import InputAdornment from '@mui/material/InputAdornment';
7
8
  import styled from '@emotion/styled';
8
9
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
9
10
  import Box from '@mui/material/Box';
10
11
  import Button from '@arcblock/ux/lib/Button';
11
12
  import DidConnect from '@arcblock/did-connect/lib/Connect';
12
13
  import Toast from '@arcblock/ux/lib/Toast';
13
- import DidAddress from '../../did-address';
14
14
  import { useNodeContext } from '../../contexts/node';
15
15
  import { useBlockletContext } from '../../contexts/blocklet';
16
16
  import { useSessionContext } from '../../contexts/session';
@@ -66,6 +66,14 @@ export default function CreateProject({
66
66
  setOpenConnect(false);
67
67
  };
68
68
  const onCreate = () => {
69
+ if (!blockletDid) {
70
+ Toast.error(t('blocklet.publish.errorTip.noDid'));
71
+ return;
72
+ }
73
+ if (!blockletTitle) {
74
+ Toast.error(t('blocklet.publish.errorTip.noTitle'));
75
+ return;
76
+ }
69
77
  setLoading(true);
70
78
  api.createProject({
71
79
  input: {
@@ -100,26 +108,29 @@ export default function CreateProject({
100
108
  style: {
101
109
  maxWidth: 500
102
110
  },
103
- children: [/*#__PURE__*/_jsxs(Box, {
111
+ children: [/*#__PURE__*/_jsx(TextField, {
112
+ required: true,
104
113
  sx: {
105
- mb: 3
114
+ mb: 4
106
115
  },
107
- display: "flex",
108
- alignItems: "center",
109
- children: [/*#__PURE__*/_jsxs(Typography, {
110
- variant: "h6",
111
- sx: {
112
- mr: 3
113
- },
114
- children: ["Blocklet DID:", ' ']
115
- }), blockletDid ? /*#__PURE__*/_jsx(DidAddress, {
116
- size: 14,
117
- responsive: true,
118
- did: blockletDid
119
- }) : /*#__PURE__*/_jsx(Button, {
120
- onClick: () => setOpenConnect(true),
121
- children: t('blocklet.publish.createBlockletDid')
122
- })]
116
+ label: "Blocklet DID",
117
+ fullWidth: true,
118
+ readonly: true,
119
+ autoComplete: "off",
120
+ variant: "outlined",
121
+ onClick: () => !blockletDid && setOpenConnect(true),
122
+ InputProps: {
123
+ 'data-cy': 'export-blocklet-did',
124
+ endAdornment: /*#__PURE__*/_jsx(InputAdornment, {
125
+ position: "end",
126
+ children: /*#__PURE__*/_jsx(Button, {
127
+ onClick: () => setOpenConnect(true),
128
+ children: t(blockletDid ? 'common.update' : 'common.create')
129
+ })
130
+ })
131
+ },
132
+ helperText: "",
133
+ value: blockletDid || ''
123
134
  }), openConnect && /*#__PURE__*/_jsx(DidConnect, {
124
135
  open: true,
125
136
  popup: true,
@@ -142,10 +153,11 @@ export default function CreateProject({
142
153
  success: t('blocklet.publish.connect.success')
143
154
  }
144
155
  }), /*#__PURE__*/_jsx(TextField, {
156
+ required: true,
145
157
  label: `Blocklet ${t('common.title')}`,
146
158
  autoComplete: "off",
147
159
  variant: "outlined",
148
- inputProps: {
160
+ InputProps: {
149
161
  'data-cy': 'export-blocklet-title'
150
162
  },
151
163
  fullWidth: true,
@@ -173,7 +185,7 @@ export default function CreateProject({
173
185
  })]
174
186
  }), /*#__PURE__*/_jsx(Button, {
175
187
  variant: "contained",
176
- disabled: loading || !blockletDid || !blockletTitle,
188
+ disabled: loading,
177
189
  onClick: onCreate,
178
190
  children: t('common.create')
179
191
  })]
@@ -12,26 +12,65 @@ import Breadcrumbs from '@mui/material/Breadcrumbs';
12
12
  import Box from '@mui/material/Box';
13
13
  import IconButton from '@mui/material/IconButton';
14
14
  import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
15
+ import InputAdornment from '@mui/material/InputAdornment';
15
16
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
16
17
  import Button from '@arcblock/ux/lib/Button';
17
18
  import Toast from '@arcblock/ux/lib/Toast';
18
- import Tabs from '@arcblock/ux/lib/Tabs';
19
19
  import Empty from '@arcblock/ux/lib/Empty';
20
+ import Tabs from '@arcblock/ux/lib/Tabs';
20
21
  import { PROJECT } from '@blocklet/constant';
21
22
  import { WELLKNOWN_SERVICE_PATH_PREFIX } from '@abtnode/constant';
22
23
  import BlockletBundleAvatar from '../bundle-avatar';
23
24
  import { useNodeContext } from '../../contexts/node';
24
25
  import { useBlockletContext } from '../../contexts/blocklet';
25
26
  import EmptySpinner from '../../empty-spinner';
27
+ import { formatError } from '../../util';
26
28
  import Resource from './resource';
27
-
28
- // eslint-disable-next-line import/no-unresolved
29
29
  import { jsx as _jsx } from "react/jsx-runtime";
30
30
  import { jsxs as _jsxs } from "react/jsx-runtime";
31
31
  import { Fragment as _Fragment } from "react/jsx-runtime";
32
+ const allowedFileTypes = ['image/*', '.jpg', '.jpeg', '.png', '.gif', '.svg'];
33
+ const maxFileSize = 1024 * 1024 * 10; // 10 MB
34
+
35
+ // eslint-disable-next-line import/no-unresolved
32
36
  const UploaderComponent = /*#__PURE__*/lazy(() => import('@blocklet/uploader/react').then(res => ({
33
37
  default: res.Uploader
34
38
  })));
39
+ const validateParams = (params, status, t) => {
40
+ const errors = [];
41
+ if (!params.blockletTitle) {
42
+ errors.push({
43
+ param: 'blockletTitle',
44
+ message: t('blocklet.publish.errorTip.noTitle')
45
+ });
46
+ }
47
+ if (!params.blockletVersion) {
48
+ errors.push({
49
+ param: 'blockletVersion',
50
+ message: t('blocklet.publish.errorTip.noVersion')
51
+ });
52
+ } else if (!semver.valid(params.blockletVersion)) {
53
+ errors.push({
54
+ param: 'blockletVersion',
55
+ message: 'Invalid Blocklet Version'
56
+ });
57
+ }
58
+ if (status === PROJECT.RELEASE_STATUS.published) {
59
+ if (!params.blockletDescription) {
60
+ errors.push({
61
+ param: 'blockletDescription',
62
+ message: t('blocklet.publish.errorTip.noDescription')
63
+ });
64
+ }
65
+ if (!params.note) {
66
+ errors.push({
67
+ param: 'note',
68
+ message: t('blocklet.publish.errorTip.noNote')
69
+ });
70
+ }
71
+ }
72
+ return errors.length ? errors : null;
73
+ };
35
74
  const addComponentTabs = ({
36
75
  tabs,
37
76
  blocklet,
@@ -40,7 +79,6 @@ const addComponentTabs = ({
40
79
  const key = blocklet.meta.did;
41
80
  tabs.push({
42
81
  label: /*#__PURE__*/_jsxs(Box, {
43
- ml: -4,
44
82
  display: "flex",
45
83
  alignItems: "center",
46
84
  justifyContent: "flex-start",
@@ -49,7 +87,7 @@ const addComponentTabs = ({
49
87
  blocklet: blocklet,
50
88
  ancestors: ancestors
51
89
  }), /*#__PURE__*/_jsx(Box, {
52
- ml: "16px",
90
+ ml: "4px",
53
91
  className: "text",
54
92
  children: /*#__PURE__*/_jsx(Box, {
55
93
  children: blocklet.meta.title
@@ -59,7 +97,11 @@ const addComponentTabs = ({
59
97
  value: key
60
98
  });
61
99
  };
62
- const pickParams = release => pick(release, ['blockletVersion', 'blockletTitle', 'blockletDescription', 'blockletLogo', 'blockletIntroduction', 'blockletScreenshots', 'note']);
100
+ const pickParams = release => {
101
+ const params = pick(release, ['blockletVersion', 'blockletTitle', 'blockletDescription', 'blockletLogo', 'blockletIntroduction', 'blockletScreenshots', 'note']);
102
+ params.blockletScreenshots ||= [];
103
+ return params;
104
+ };
63
105
  export default function CreateRelease({
64
106
  ...rest
65
107
  }) {
@@ -75,6 +117,7 @@ export default function CreateRelease({
75
117
  blocklet
76
118
  } = useBlockletContext();
77
119
  const [params, setParams] = useSetState(null);
120
+ const [paramsErrTip, setParamsErrTip] = useState({});
78
121
  const [loading, setLoading] = useState(false);
79
122
  const [release, setRelease] = useState(null);
80
123
  const {
@@ -85,7 +128,7 @@ export default function CreateRelease({
85
128
  const releaseId = inputReleaseId === 'create' ? '' : inputReleaseId;
86
129
  const mode = inputReleaseId === 'create' ? 'create' : 'edit';
87
130
  const tabs = [];
88
- const resourceComponents = (blocklet?.children || []).filter(x => x.meta.capabilities.resourceExportPage);
131
+ const resourceComponents = (blocklet?.children || []).filter(x => x.meta.capabilities.resourceExportApi);
89
132
  resourceComponents.forEach(item => {
90
133
  addComponentTabs({
91
134
  tabs,
@@ -114,7 +157,7 @@ export default function CreateRelease({
114
157
  setParams(pickParams(data));
115
158
  }).catch(err => {
116
159
  setLoading(false);
117
- Toast.error(err.message);
160
+ Toast.error(formatError(err));
118
161
  });
119
162
  };
120
163
  const getRelease = () => {
@@ -130,7 +173,7 @@ export default function CreateRelease({
130
173
  setRelease(res?.release);
131
174
  }).catch(err => {
132
175
  setLoading(false);
133
- Toast.error(err.message);
176
+ Toast.error(formatError(err));
134
177
  });
135
178
  };
136
179
  const getData = () => {
@@ -150,30 +193,24 @@ export default function CreateRelease({
150
193
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
151
194
 
152
195
  const onRelease = status => {
153
- if (!params.blockletTitle) {
154
- Toast.error(t('blocklet.publish.errorTip.noTitle'));
155
- return;
156
- }
157
- if (!params.blockletVersion) {
158
- Toast.error(t('blocklet.publish.errorTip.noVersion'));
196
+ const errors = validateParams(params, status, t);
197
+ if (errors) {
198
+ Toast.error(errors.map(x => x.message).join('; '));
199
+ setParamsErrTip(errors.reduce((acc, x) => ({
200
+ ...acc,
201
+ [x.param]: x.message
202
+ }), {}));
159
203
  return;
160
204
  }
161
- if (status === PROJECT.RELEASE_STATUS.published) {
162
- if (!params.blockletDescription) {
163
- Toast.error(t('blocklet.publish.errorTip.noDescription'));
164
- return;
165
- }
166
- if (!params.note) {
167
- Toast.error(t('blocklet.publish.errorTip.noNote'));
168
- return;
169
- }
170
- }
171
205
  const _params = {
172
206
  ...params
173
207
  };
174
208
  _params.blockletVersion = semver.valid(_params.blockletVersion);
175
209
  if (!_params.blockletVersion) {
176
210
  Toast.error('Invalid Blocklet Version');
211
+ setParamsErrTip({
212
+ blockletVersion: 'Invalid Blocklet Version'
213
+ });
177
214
  return;
178
215
  }
179
216
  _params.blockletTitle = (_params.blockletTitle || '').trim();
@@ -203,7 +240,7 @@ export default function CreateRelease({
203
240
  });
204
241
  }
205
242
  }).catch(err => {
206
- Toast.error(err.message);
243
+ Toast.error(formatError(err));
207
244
  });
208
245
  };
209
246
  if (!params && loading) {
@@ -295,56 +332,77 @@ export default function CreateRelease({
295
332
  children: [/*#__PURE__*/_jsx(TextField, {
296
333
  label: `Blocklet ${t('common.title')}`,
297
334
  placeholder: `Blocklet ${t('common.title')}`,
298
- inputProps: {
335
+ InputProps: {
299
336
  'data-cy': 'blocklet-title',
300
337
  readOnly
301
338
  },
302
339
  autoComplete: "off",
303
340
  variant: "outlined",
304
341
  fullWidth: true,
342
+ required: true,
305
343
  value: params.blockletTitle || '',
306
344
  onChange: e => {
345
+ setParamsErrTip({
346
+ blockletTitle: ''
347
+ });
307
348
  setParams({
308
349
  blockletTitle: e.target.value
309
350
  });
310
- }
351
+ },
352
+ error: !!paramsErrTip.blockletTitle,
353
+ helperText: paramsErrTip.blockletTitle || ''
311
354
  }), /*#__PURE__*/_jsx(TextField, {
312
355
  label: `Blocklet ${t('common.description')}`,
313
356
  placeholder: `Blocklet ${t('common.description')}`,
314
- inputProps: {
357
+ InputProps: {
315
358
  'data-cy': 'blocklet-description',
316
359
  readOnly
317
360
  },
361
+ required: true,
318
362
  autoComplete: "off",
319
363
  variant: "outlined",
320
364
  fullWidth: true,
321
365
  value: params.blockletDescription || '',
322
366
  onChange: e => {
367
+ setParamsErrTip({
368
+ blockletDescription: ''
369
+ });
323
370
  setParams({
324
371
  blockletDescription: e.target.value
325
372
  });
326
373
  },
374
+ error: !!paramsErrTip.blockletDescription,
375
+ helperText: paramsErrTip.blockletDescription || '',
327
376
  sx: {
328
377
  mt: 3
329
378
  }
330
379
  }), /*#__PURE__*/_jsx(TextField, {
331
380
  label: `Blocklet ${t('common.introduction')}`,
332
381
  placeholder: `Blocklet ${t('common.introduction')}`,
333
- inputProps: {
382
+ InputProps: {
334
383
  'data-cy': 'export-blocklet-introduction',
335
- readOnly
384
+ readOnly,
385
+ endAdornment: /*#__PURE__*/_jsxs(InputAdornment, {
386
+ position: "end",
387
+ children: [params?.blockletIntroduction?.length || 0, " / ", PROJECT.MAX_INTRO_LENGTH]
388
+ })
336
389
  },
337
390
  autoComplete: "off",
338
391
  variant: "outlined",
339
392
  fullWidth: true,
340
393
  multiline: true,
341
- rows: 4,
394
+ minRows: 4,
342
395
  value: params.blockletIntroduction || '',
343
396
  onChange: e => {
397
+ setParamsErrTip({
398
+ blockletIntroduction: ''
399
+ });
344
400
  setParams({
345
- blockletIntroduction: e.target.value
401
+ blockletIntroduction: e.target.value.slice(0, PROJECT.MAX_INTRO_LENGTH)
346
402
  });
347
403
  },
404
+ error: !!paramsErrTip.blockletIntroduction,
405
+ helperText: paramsErrTip.blockletIntroduction || '',
348
406
  sx: {
349
407
  mt: 3
350
408
  }
@@ -363,9 +421,10 @@ export default function CreateRelease({
363
421
  flex: 1
364
422
  },
365
423
  children: [/*#__PURE__*/_jsx(TextField, {
424
+ required: true,
366
425
  label: `Blocklet ${t('common.version')}`,
367
426
  placeholder: `Blocklet ${t('common.version')}`,
368
- inputProps: {
427
+ InputProps: {
369
428
  'data-cy': 'blocklet-version',
370
429
  readOnly
371
430
  },
@@ -374,27 +433,42 @@ export default function CreateRelease({
374
433
  fullWidth: true,
375
434
  value: params.blockletVersion,
376
435
  onChange: e => {
436
+ setParamsErrTip({
437
+ blockletVersion: ''
438
+ });
377
439
  setParams({
378
440
  blockletVersion: e.target.value
379
441
  });
380
- }
442
+ },
443
+ error: !!paramsErrTip.blockletVersion,
444
+ helperText: paramsErrTip.blockletVersion || ''
381
445
  }), /*#__PURE__*/_jsx(TextField, {
382
446
  label: t('blocklet.publish.releaseNote'),
383
447
  autoComplete: "off",
384
448
  variant: "outlined",
385
- inputProps: {
449
+ InputProps: {
386
450
  'data-cy': 'release-note',
387
- readOnly
451
+ readOnly,
452
+ endAdornment: /*#__PURE__*/_jsxs(InputAdornment, {
453
+ position: "end",
454
+ children: [params?.note?.length || 0, " / ", PROJECT.MAX_NOTE_LENGTH]
455
+ })
388
456
  },
389
457
  fullWidth: true,
390
458
  multiline: true,
391
- rows: 4,
459
+ minRows: 4,
460
+ required: true,
392
461
  value: params.note,
393
462
  onChange: e => {
463
+ setParamsErrTip({
464
+ note: ''
465
+ });
394
466
  setParams({
395
- note: e.target.value
467
+ note: e.target.value.slice(0, PROJECT.MAX_NOTE_LENGTH)
396
468
  });
397
469
  },
470
+ error: !!paramsErrTip.note,
471
+ helperText: paramsErrTip.note || '',
398
472
  sx: {
399
473
  mt: 3
400
474
  }
@@ -494,11 +568,19 @@ export default function CreateRelease({
494
568
  setParams({
495
569
  blockletLogo: result.data.filename
496
570
  });
571
+ uploaderLogoRef.current.close();
497
572
  },
498
- plugins: ['ImageEditor', 'Url', 'Webcam'],
573
+ plugins: [],
499
574
  apiPathProps: {
500
575
  uploader: uploadLogoPrefix,
501
576
  disableMediaKitPrefix: true
577
+ },
578
+ coreProps: {
579
+ restrictions: {
580
+ allowedFileTypes,
581
+ maxFileSize,
582
+ maxNumberOfFiles: 1
583
+ }
502
584
  }
503
585
  }, "uploader-screenshot"), /*#__PURE__*/_jsx(UploaderComponent, {
504
586
  id: "\u200Buploader-screenshot",
@@ -510,38 +592,46 @@ export default function CreateRelease({
510
592
  setParams({
511
593
  blockletScreenshots
512
594
  });
595
+ uploaderScreenshotRef.current.close();
513
596
  },
514
- plugins: ['ImageEditor', 'Url', 'Webcam'],
597
+ plugins: [],
515
598
  apiPathProps: {
516
599
  uploader: uploadScreenshotPrefix,
517
600
  disableMediaKitPrefix: true
601
+ },
602
+ coreProps: {
603
+ restrictions: {
604
+ allowedFileTypes,
605
+ maxFileSize,
606
+ maxNumberOfFiles: 5 - screenshotUrls.length
607
+ }
518
608
  }
519
609
  }, "uploader-screenshot")]
520
610
  }), /*#__PURE__*/_jsxs(Box, {
521
611
  display: "flex",
522
612
  mt: 3,
523
613
  className: "section full-width",
524
- children: [/*#__PURE__*/_jsxs(Box, {
614
+ children: [/*#__PURE__*/_jsx(Box, {
525
615
  className: "left",
526
- children: [/*#__PURE__*/_jsx(Typography, {
616
+ children: /*#__PURE__*/_jsx(Typography, {
527
617
  variant: "h3",
528
618
  children: t('blocklet.publish.resourceTitle')
529
- }), /*#__PURE__*/_jsx(Tabs, {
619
+ })
620
+ }), /*#__PURE__*/_jsxs(Box, {
621
+ sx: {
622
+ flex: 1
623
+ },
624
+ children: [/*#__PURE__*/_jsx(Tabs, {
530
625
  tabs: tabs,
531
626
  current: tab,
532
- orientation: "vertical",
533
627
  onChange: onTabChange,
534
628
  sx: {
535
- width: '100%'
629
+ ml: -2,
630
+ mt: -1.5
536
631
  }
537
- })]
538
- }), /*#__PURE__*/_jsx(Box, {
539
- sx: {
540
- flex: 1
541
- },
542
- children: /*#__PURE__*/_jsx(Box, {
632
+ }), /*#__PURE__*/_jsx(Box, {
543
633
  children: resourceContent
544
- })
634
+ })]
545
635
  })]
546
636
  })]
547
637
  })]
@@ -607,20 +697,6 @@ const Main = styled.div`
607
697
  flex-shrink: 0;
608
698
  width: 140px;
609
699
  }
610
- .MuiTabs-root {
611
- margin-top: 8px;
612
- transform: translateX(-16px);
613
- .MuiButtonBase-root .text {
614
- color: ${({
615
- theme
616
- }) => theme.palette.text.primary};
617
- }
618
- .MuiButtonBase-root.Mui-selected .text {
619
- color: ${({
620
- theme
621
- }) => theme.palette.primary.main};
622
- }
623
- }
624
700
  .MuiTabs-indicator {
625
701
  display: none;
626
702
  }
@@ -640,4 +716,11 @@ const Main = styled.div`
640
716
  color: inherit;
641
717
  }
642
718
  }
719
+ .MuiInputBase-multiline .MuiInputAdornment-positionEnd {
720
+ align-self: flex-end;
721
+ .MuiTypography-root {
722
+ font-size: 0.8rem;
723
+ }
724
+ font-size: 0.8rem;
725
+ }
643
726
  `;
@@ -9,6 +9,8 @@ import DeleteIcon from '@mui/icons-material/Delete';
9
9
  import Datatable from '@arcblock/ux/lib/Datatable';
10
10
  import Button from '@arcblock/ux/lib/Button';
11
11
  import Toast from '@arcblock/ux/lib/Toast';
12
+ import Tag from '@arcblock/ux/lib/Tag';
13
+ import RelativeTime from '@arcblock/ux/lib/RelativeTime';
12
14
  import { useBlockletContext } from '../../contexts/blocklet';
13
15
  import { useNodeContext } from '../../contexts/node';
14
16
  import EmptySpinner from '../../empty-spinner';
@@ -104,7 +106,7 @@ export default function ProjectList() {
104
106
  });
105
107
  }
106
108
  const columns = [{
107
- label: t('common.name'),
109
+ label: 'Blocklet',
108
110
  name: 'blockletTitle',
109
111
  options: {
110
112
  customBodyRenderLite: rawIndex => {
@@ -116,7 +118,7 @@ export default function ProjectList() {
116
118
  }
117
119
  }
118
120
  }, {
119
- label: t('common.address'),
121
+ label: 'DID',
120
122
  name: 'blockletDid',
121
123
  options: {
122
124
  customBodyRender: e => {
@@ -127,6 +129,28 @@ export default function ProjectList() {
127
129
  });
128
130
  }
129
131
  }
132
+ }, {
133
+ label: 'Version',
134
+ name: 'blockletVersion',
135
+ options: {
136
+ customBodyRender: value => {
137
+ return value ? /*#__PURE__*/_jsx(Tag, {
138
+ type: "success",
139
+ children: value
140
+ }) : '';
141
+ }
142
+ }
143
+ }, {
144
+ label: t('common.updatedAt'),
145
+ name: 'updatedAt',
146
+ options: {
147
+ customBodyRender: value => {
148
+ return /*#__PURE__*/_jsx(RelativeTime, {
149
+ value: Number(value),
150
+ locale: locale
151
+ });
152
+ }
153
+ }
130
154
  }, {
131
155
  label: t('common.actions'),
132
156
  name: 'actions',
@@ -16,6 +16,8 @@ import { WELLKNOWN_SERVICE_PATH_PREFIX } from '@abtnode/constant';
16
16
  import Button from '@arcblock/ux/lib/Button';
17
17
  import Toast from '@arcblock/ux/lib/Toast';
18
18
  import Tag from '@arcblock/ux/lib/Tag';
19
+ import RelativeTime from '@arcblock/ux/lib/RelativeTime';
20
+ import { PROJECT } from '@blocklet/constant';
19
21
  import { useBlockletContext } from '../../contexts/blocklet';
20
22
  import { useNodeContext } from '../../contexts/node';
21
23
  import EmptySpinner from '../../empty-spinner';
@@ -187,6 +189,21 @@ export default function ReleaseList() {
187
189
  });
188
190
  }
189
191
  }
192
+ }, {
193
+ label: t('common.lastPublishedAt'),
194
+ name: 'updatedAt',
195
+ options: {
196
+ customBodyRenderLite: rawIndex => {
197
+ const release = releases[rawIndex];
198
+ if (release.status !== PROJECT.RELEASE_STATUS.published) {
199
+ return '';
200
+ }
201
+ return /*#__PURE__*/_jsx(RelativeTime, {
202
+ value: Number(release.updatedAt),
203
+ locale: locale
204
+ });
205
+ }
206
+ }
190
207
  }, {
191
208
  label: t('common.actions'),
192
209
  name: 'actions',
@@ -195,11 +212,15 @@ export default function ReleaseList() {
195
212
  const release = releases[rawIndex];
196
213
  return /*#__PURE__*/_jsxs(Box, {
197
214
  display: "flex",
215
+ alignItems: "center",
198
216
  children: [/*#__PURE__*/_jsx(IconButton, {
199
217
  color: "primary",
200
218
  "aria-label": "download",
201
219
  disabled: release.status !== 'published',
202
220
  children: release.status === 'published' ? /*#__PURE__*/_jsx("a", {
221
+ style: {
222
+ display: 'flex'
223
+ },
203
224
  href: joinUrl(`${WELLKNOWN_SERVICE_PATH_PREFIX}/api/project/${blocklet.meta.did}/${project.id}/${release.id}/download/${(release.files || [])[0]}`),
204
225
  children: /*#__PURE__*/_jsx(DownloadIcon, {
205
226
  sx: {