@strapi/admin 4.6.2 → 4.7.0-exp.3d6a31eb083e9d44afcf98f68c107fb7567e5720

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 (97) hide show
  1. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +0 -2
  2. package/admin/src/hooks/useRegenerate/index.js +2 -2
  3. package/admin/src/hooks/useSettingsMenu/utils/defaultGlobalLinks.js +7 -0
  4. package/admin/src/pages/HomePage/CloudBox.js +83 -0
  5. package/admin/src/pages/HomePage/ContentBlocks.js +2 -0
  6. package/admin/src/pages/HomePage/assets/strapi-cloud-background.png +0 -0
  7. package/admin/src/pages/HomePage/assets/strapi-cloud-flags.svg +1 -0
  8. package/admin/src/pages/HomePage/assets/strapi-cloud-icon.svg +1 -0
  9. package/admin/src/pages/SettingsPage/{pages/ApiTokens/EditView/components → components/Tokens}/FormHead/index.js +36 -19
  10. package/admin/src/pages/SettingsPage/components/Tokens/FormiTokenContainer/LifeSpanInput.js +95 -0
  11. package/admin/src/pages/SettingsPage/components/Tokens/LifeSpanInput/index.js +97 -0
  12. package/admin/src/pages/SettingsPage/components/Tokens/Regenerate/index.js +73 -0
  13. package/admin/src/pages/SettingsPage/{pages/ApiTokens/ListView/DynamicTable → components/Tokens/Table}/DeleteButton/index.js +19 -6
  14. package/admin/src/pages/SettingsPage/components/Tokens/Table/index.js +145 -0
  15. package/admin/src/pages/SettingsPage/{pages/ApiTokens/EditView/components/ContentBox → components/Tokens/TokenBox}/index.js +19 -16
  16. package/admin/src/pages/SettingsPage/components/Tokens/TokenDescription/index.js +51 -0
  17. package/admin/src/pages/SettingsPage/components/Tokens/TokenName/index.js +46 -0
  18. package/admin/src/pages/SettingsPage/components/Tokens/TokenTypeSelect/index.js +69 -0
  19. package/admin/src/pages/SettingsPage/components/Tokens/constants.js +2 -0
  20. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +3 -1
  21. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +53 -150
  22. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +5 -1
  23. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +46 -17
  24. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +16 -16
  25. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/components/FormTransferTokenContainer/index.js +101 -0
  26. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/components/LoadingView/index.js +48 -0
  27. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/index.js +219 -0
  28. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/utils/getDateOfExpiration.js +16 -0
  29. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/utils/index.js +4 -0
  30. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/utils/schema.js +10 -0
  31. package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/index.js +194 -0
  32. package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/utils/tableHeaders.js +48 -0
  33. package/admin/src/pages/SettingsPage/pages/TransferTokens/ProtectedCreateView/index.js +14 -0
  34. package/admin/src/pages/SettingsPage/pages/TransferTokens/ProtectedEditView/index.js +14 -0
  35. package/admin/src/pages/SettingsPage/pages/TransferTokens/ProtectedListView/index.js +12 -0
  36. package/admin/src/pages/SettingsPage/utils/defaultRoutes.js +33 -0
  37. package/admin/src/permissions/defaultPermissions.js +8 -0
  38. package/admin/src/translations/en.json +15 -0
  39. package/build/27d16aefee06412db90a.png +0 -0
  40. package/build/4049.16583eee.chunk.js +1 -0
  41. package/build/4649.b7e84a29.chunk.js +30 -0
  42. package/build/7259.3f04094f.chunk.js +1 -0
  43. package/build/{Admin-authenticatedApp.dd16edad.chunk.js → Admin-authenticatedApp.368164a1.chunk.js} +6 -6
  44. package/build/Admin_homePage.1f10437f.chunk.js +78 -0
  45. package/build/{Admin_settingsPage.3cd54156.chunk.js → Admin_settingsPage.5a329b58.chunk.js} +25 -25
  46. package/build/{admin-app.3a084127.chunk.js → admin-app.df9adf93.chunk.js} +26 -26
  47. package/build/{api-tokens-create-page.a31c7fba.chunk.js → api-tokens-create-page.4328b852.chunk.js} +1 -1
  48. package/build/{api-tokens-edit-page.64fef287.chunk.js → api-tokens-edit-page.bce5050f.chunk.js} +1 -1
  49. package/build/api-tokens-list-page.149903c8.chunk.js +16 -0
  50. package/build/bb3108f7fd1e6179bde1.svg +1 -0
  51. package/build/bb4d0d527bdfb161bc5a.svg +1 -0
  52. package/build/{content-manager.d04b738f.chunk.js → content-manager.6ed87531.chunk.js} +1 -1
  53. package/build/en-json.8e5451b1.chunk.js +1 -0
  54. package/build/index.html +1 -1
  55. package/build/{main.9c01de7f.js → main.8009bfe8.js} +1 -0
  56. package/build/runtime~main.725b20df.js +2 -0
  57. package/build/transfer-tokens-create-page.a1f14bb1.chunk.js +1 -0
  58. package/build/transfer-tokens-edit-page.00ee1c74.chunk.js +1 -0
  59. package/build/transfer-tokens-list-page.1e15926d.chunk.js +16 -0
  60. package/package.json +9 -9
  61. package/server/bootstrap.js +2 -0
  62. package/server/config/admin-actions.js +48 -0
  63. package/server/content-types/index.js +2 -0
  64. package/server/content-types/transfer-token-permission.js +36 -0
  65. package/server/content-types/transfer-token.js +66 -0
  66. package/server/controllers/api-token.js +4 -5
  67. package/server/controllers/index.js +1 -0
  68. package/server/controllers/transfer/index.js +13 -0
  69. package/server/controllers/transfer/runner.js +24 -0
  70. package/server/controllers/transfer/token.js +131 -0
  71. package/server/register.js +2 -9
  72. package/server/routes/index.js +2 -0
  73. package/server/routes/transfer.js +95 -0
  74. package/server/services/api-token.js +2 -3
  75. package/server/services/constants.js +6 -0
  76. package/server/services/index.js +1 -0
  77. package/server/services/transfer/index.js +6 -0
  78. package/server/services/transfer/permission.js +22 -0
  79. package/server/services/transfer/token.js +409 -0
  80. package/server/strategies/api-token.js +4 -2
  81. package/server/strategies/data-transfer.js +107 -0
  82. package/server/strategies/index.js +1 -0
  83. package/server/utils/index.d.ts +2 -0
  84. package/server/validation/api-tokens.js +1 -6
  85. package/server/validation/transfer/index.js +5 -0
  86. package/server/validation/transfer/token.js +34 -0
  87. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +0 -77
  88. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js +0 -110
  89. package/build/1341.5d48c79b.chunk.js +0 -1
  90. package/build/4318.9b1ac9bc.chunk.js +0 -30
  91. package/build/Admin_homePage.4b878f04.chunk.js +0 -72
  92. package/build/api-tokens-list-page.370459ba.chunk.js +0 -16
  93. package/build/en-json.9cada7f3.chunk.js +0 -1
  94. package/build/runtime~main.bb1389c9.js +0 -2
  95. package/admin/src/pages/SettingsPage/{pages/ApiTokens/ListView/DynamicTable → components/Tokens/Table}/DefaultButton/index.js +1 -1
  96. /package/admin/src/pages/SettingsPage/{pages/ApiTokens/ListView/DynamicTable → components/Tokens/Table}/ReadButton/index.js +0 -0
  97. /package/admin/src/pages/SettingsPage/{pages/ApiTokens/ListView/DynamicTable → components/Tokens/Table}/UpdateButton/index.js +0 -0
@@ -210,8 +210,6 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
210
210
 
211
211
  const onDelete = useCallback(
212
212
  async (trackerProperty) => {
213
- console.log('onDelete');
214
-
215
213
  try {
216
214
  trackUsageRef.current('willDeleteEntry', trackerProperty);
217
215
 
@@ -2,7 +2,7 @@ import { useState } from 'react';
2
2
  import { get } from 'lodash';
3
3
  import { useFetchClient, useNotification } from '@strapi/helper-plugin';
4
4
 
5
- const useRegenerate = (id, onRegenerate) => {
5
+ const useRegenerate = (url, id, onRegenerate) => {
6
6
  const [isLoadingConfirmation, setIsLoadingConfirmation] = useState(false);
7
7
  const toggleNotification = useNotification();
8
8
  const { post } = useFetchClient();
@@ -13,7 +13,7 @@ const useRegenerate = (id, onRegenerate) => {
13
13
  data: {
14
14
  data: { accessKey },
15
15
  },
16
- } = await post(`/admin/api-tokens/${id}/regenerate`);
16
+ } = await post(`${url}${id}/regenerate`);
17
17
  setIsLoadingConfirmation(false);
18
18
  onRegenerate(accessKey);
19
19
  } catch (error) {
@@ -22,6 +22,13 @@ const defaultGlobalLinks = [
22
22
  isDisplayed: false,
23
23
  permissions: adminPermissions.settings['api-tokens'].main,
24
24
  },
25
+ {
26
+ intlLabel: { id: 'Settings.transferTokens.title', defaultMessage: 'Transfer Tokens' },
27
+ to: '/settings/transfer-tokens?sort=name:ASC',
28
+ id: 'transfer-tokens',
29
+ isDisplayed: false,
30
+ permissions: adminPermissions.settings['transfer-tokens'].main,
31
+ },
25
32
  ];
26
33
 
27
34
  export default defaultGlobalLinks;
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { useIntl } from 'react-intl';
4
+ import { useTracking, pxToRem } from '@strapi/helper-plugin';
5
+ import { Box, Flex, Typography } from '@strapi/design-system';
6
+ import cloudIconBackgroundImage from './assets/strapi-cloud-background.png';
7
+ import cloudIcon from './assets/strapi-cloud-icon.svg';
8
+ import cloudFlagsImage from './assets/strapi-cloud-flags.svg';
9
+
10
+ const BlockLink = styled.a`
11
+ text-decoration: none;
12
+ `;
13
+
14
+ const CloudCustomWrapper = styled(Box)`
15
+ background-image: url(${({ backgroundImage }) => backgroundImage});
16
+ `;
17
+
18
+ const CloudIconWrapper = styled(Flex)`
19
+ background: rgba(255, 255, 255, 0.3);
20
+ `;
21
+
22
+ const CloudBox = () => {
23
+ const { formatMessage } = useIntl();
24
+ const { trackUsage } = useTracking();
25
+
26
+ return (
27
+ <BlockLink
28
+ href="https://cloud.strapi.io"
29
+ target="_blank"
30
+ rel="noopener noreferrer nofollow"
31
+ onClick={() => {
32
+ trackUsage('didClickOnTryStrapiCloudSection');
33
+ }}
34
+ >
35
+ <Flex
36
+ shadow="tableShadow"
37
+ hasRadius
38
+ padding={6}
39
+ background="neutral0"
40
+ position="relative"
41
+ gap={6}
42
+ >
43
+ <CloudCustomWrapper backgroundImage={cloudIconBackgroundImage} hasRadius padding={3}>
44
+ <CloudIconWrapper
45
+ width={pxToRem(32)}
46
+ height={pxToRem(32)}
47
+ justifyContent="center"
48
+ hasRadius
49
+ alignItems="center"
50
+ >
51
+ <img
52
+ src={cloudIcon}
53
+ alt={formatMessage({
54
+ id: 'app.components.BlockLink.cloud',
55
+ defaultMessage: 'Strapi Cloud',
56
+ })}
57
+ />
58
+ </CloudIconWrapper>
59
+ </CloudCustomWrapper>
60
+ <Flex gap={1} direction="column" alignItems="start">
61
+ <Flex>
62
+ <Typography fontWeight="semiBold" variant="pi">
63
+ {formatMessage({
64
+ id: 'app.components.BlockLink.cloud',
65
+ defaultMessage: 'Strapi Cloud',
66
+ })}
67
+ </Typography>
68
+ </Flex>
69
+ <Typography textColor="neutral600">
70
+ {formatMessage({
71
+ id: 'app.components.BlockLink.cloud.content',
72
+ defaultMessage:
73
+ 'A fully composable, and collaborative platform to boost your team velocity.',
74
+ })}
75
+ </Typography>
76
+ <Box src={cloudFlagsImage} position="absolute" top={0} right={0} as="img" />
77
+ </Flex>
78
+ </Flex>
79
+ </BlockLink>
80
+ );
81
+ };
82
+
83
+ export default CloudBox;
@@ -4,6 +4,7 @@ import { useIntl } from 'react-intl';
4
4
  import { ContentBox, useTracking } from '@strapi/helper-plugin';
5
5
  import { Stack } from '@strapi/design-system';
6
6
  import { InformationSquare, CodeSquare, PlaySquare, FeatherSquare } from '@strapi/icons';
7
+ import CloudBox from './CloudBox';
7
8
 
8
9
  const BlockLink = styled.a`
9
10
  text-decoration: none;
@@ -19,6 +20,7 @@ const ContentBlocks = () => {
19
20
 
20
21
  return (
21
22
  <Stack spacing={5}>
23
+ <CloudBox />
22
24
  <BlockLink
23
25
  href="https://strapi.io/resource-center"
24
26
  target="_blank"
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="160" fill="none" viewBox="29.41 44 149.09 50.35"><path stroke="#EAEAEF" d="M30.5 44.5c37 27 47.5 38 147.499 32"/><g filter="url(#a)"><path fill="#F29D41" d="m129.467 94.085 8.148-14.142a.598.598 0 0 0-.403-.887 75.099 75.099 0 0 0-3.045-.536c-1.875-.274-2.936-.44-4.837-.505-1.838-.064-2.873.01-4.698.153a64.05 64.05 0 0 0-2.692.293.597.597 0 0 0-.462.851l7.042 14.737c.186.388.732.41.947.036Z"/></g><g filter="url(#b)"><path fill="#F29D41" d="m46.99 75.52 13.813-8.697a.597.597 0 0 0 .058-.971 75.191 75.191 0 0 0-2.442-1.897c-1.53-1.119-2.39-1.761-4.04-2.707-1.595-.915-2.545-1.334-4.226-2.06a64.09 64.09 0 0 0-2.516-.999.597.597 0 0 0-.806.537l-.66 16.319a.535.535 0 0 0 .82.474Z"/></g><g filter="url(#c)"><path fill="#5CB176" d="m29.41 63.51 2.102-16.186a.597.597 0 0 1 .858-.46c.748.371 1.905.954 2.745 1.42 1.657.92 2.602 1.433 4.152 2.534 1.5 1.064 2.278 1.75 3.642 2.972a64.026 64.026 0 0 1 1.962 1.865c.291.286.214.771-.149.957l-14.538 7.443a.535.535 0 0 1-.774-.545Z"/></g><mask id="d" width="17" height="19" x="29" y="46" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#5CB176" d="m29.412 63.51 2.101-16.186a.597.597 0 0 1 .858-.46c.749.371 1.905.954 2.746 1.42 1.657.92 2.601 1.433 4.152 2.534 1.5 1.064 2.278 1.75 3.642 2.972a64.148 64.148 0 0 1 1.962 1.865c.29.286.214.771-.15.957l-14.537 7.443a.535.535 0 0 1-.774-.545Z"/></mask><g stroke="#fff" stroke-linecap="round" stroke-width=".661" mask="url(#d)"><path d="M39.755 51.218c-1.05 1.558-4.606 4.788-10.44 5.247M36.415 47.816c-1.05 1.558-4.606 4.789-10.44 5.247M42.97 53.97c-1.457 1.908-6.341 5.902-14.216 6.619M42.15 58.499c-1.645 1.748-5.29 4.033-13.197 3.939"/></g><g filter="url(#e)"><path fill="#7B79FF" d="m169.813 92.983 5.927-15.208a.598.598 0 0 0-.531-.815 75.213 75.213 0 0 0-3.091-.072c-1.895.01-2.969.007-4.859.228-1.826.214-2.838.442-4.621.859-.815.19-1.893.488-2.617.694a.596.596 0 0 0-.328.91l9.178 13.51a.536.536 0 0 0 .942-.106Z"/></g><g filter="url(#f)"><path fill="#7B79FF" d="m87.001 90.853-4.466-15.699a.597.597 0 0 1 .606-.761c.835.045 2.127.123 3.084.22 1.886.19 2.956.287 4.816.685 1.797.385 2.784.709 4.519 1.291.794.267 1.839.665 2.54.938.38.148.501.624.24.938L87.929 91.048a.535.535 0 0 1-.927-.195Z"/></g><g filter="url(#g)"><path fill="#EE5E52" d="m66.153 85.122 12.355-10.665a.597.597 0 0 0-.087-.97 75.353 75.353 0 0 0-2.698-1.51c-1.68-.877-2.627-1.383-4.4-2.072-1.714-.666-2.716-.938-4.486-1.404a64.083 64.083 0 0 0-2.638-.611.597.597 0 0 0-.716.651l1.788 16.234a.535.535 0 0 0 .882.347Z"/></g><circle cx="70.793" cy="74.511" r=".992" fill="#fff" transform="rotate(8.553 70.793 74.51)"/><circle cx="65.574" cy="78.63" r=".992" fill="#fff" transform="rotate(8.553 65.574 78.63)"/><circle cx="65.408" cy="74.519" r=".992" fill="#fff" transform="rotate(8.553 65.408 74.519)"/><circle cx="67.834" cy="81.943" r=".992" fill="#fff" transform="rotate(8.553 67.834 81.943)"/><circle cx="67.259" cy="69.875" r=".992" fill="#fff" transform="rotate(8.553 67.259 69.875)"/><circle cx="74.691" cy="72.834" r=".992" fill="#fff" transform="rotate(8.553 74.69 72.834)"/><circle cx="71.171" cy="78.968" r=".992" fill="#fff" transform="rotate(8.553 71.171 78.968)"/><g filter="url(#h)"><path fill="#EE5E52" d="m149.911 93.824 6.521-14.963a.598.598 0 0 0-.499-.836 75.255 75.255 0 0 0-3.086-.193c-1.894-.064-2.967-.11-4.863.037-1.833.141-2.854.33-4.651.676-.823.158-1.912.414-2.643.59a.596.596 0 0 0-.364.898l8.64 13.86a.535.535 0 0 0 .945-.07Z"/></g><circle cx="149.5" cy="82.25" r=".992" fill="#fff" transform="rotate(-17.098 149.5 82.25)"/><circle cx="146.579" cy="88.222" r=".992" fill="#fff" transform="rotate(-17.098 146.579 88.222)"/><circle cx="144.65" cy="84.588" r=".992" fill="#fff" transform="rotate(-17.098 144.65 84.588)"/><circle cx="150.049" cy="90.231" r=".992" fill="#fff" transform="rotate(-17.098 150.049 90.231)"/><circle cx="144.306" cy="79.6" r=".992" fill="#fff" transform="rotate(-17.098 144.306 79.6)"/><circle cx="152.287" cy="79.051" r=".992" fill="#fff" transform="rotate(-17.098 152.287 79.05)"/><circle cx="151.77" cy="86.103" r=".992" fill="#fff" transform="rotate(-17.098 151.77 86.103)"/><g filter="url(#i)"><path fill="#5CB176" d="m109.171 93.4 8.86-13.707a.598.598 0 0 0-.357-.906c-.811-.2-2.07-.503-3.013-.69-1.859-.37-2.911-.59-4.806-.753-1.831-.157-2.869-.136-4.699-.086-.837.023-1.953.098-2.703.154a.597.597 0 0 0-.505.827l6.279 15.077c.166.398.71.447.944.084Z"/></g><mask id="j" width="18" height="17" x="101" y="77" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#5CB176" d="m109.17 93.4 8.86-13.707a.598.598 0 0 0-.357-.905c-.811-.201-2.07-.504-3.013-.692-1.859-.37-2.911-.589-4.806-.751-1.831-.158-2.869-.137-4.699-.087-.837.023-1.953.099-2.703.154a.597.597 0 0 0-.505.827l6.279 15.077c.166.398.71.447.944.085Z"/></mask><g stroke="#fff" stroke-linecap="round" stroke-width=".661" mask="url(#j)"><path d="M109.225 77.335c-.207 1.867.418 6.632 4.573 10.753M113.972 76.9c-.207 1.867.418 6.631 4.573 10.753M104.992 77.357c-.121 2.397 1.023 8.602 6.568 14.24M102.69 81.343c.125 2.397 1.429 6.497 7.522 11.537"/></g><defs><filter id="a" width="21.565" height="21.649" x="118.775" y="76.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy=".661"/><feGaussianBlur stdDeviation="1.322"/><feColorMatrix values="0 0 0 0 0.129412 0 0 0 0 0.129412 0 0 0 0 0.203922 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_12_39"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_12_39" result="shape"/></filter><filter id="b" width="20.201" height="22.741" x="43.526" y="56.167" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy=".661"/><feGaussianBlur stdDeviation="1.322"/><feColorMatrix values="0 0 0 0 0.129412 0 0 0 0 0.129412 0 0 0 0 0.203922 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_12_39"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_12_39" result="shape"/></filter><filter id="c" width="20.932" height="22.602" x="26.761" y="44.818" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy=".661"/><feGaussianBlur stdDeviation="1.322"/><feColorMatrix values="0 0 0 0 0.129412 0 0 0 0 0.129412 0 0 0 0 0.203922 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_12_39"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_12_39" result="shape"/></filter><filter id="e" width="21.482" height="21.724" x="156.944" y="74.904" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy=".661"/><feGaussianBlur stdDeviation="1.322"/><feColorMatrix values="0 0 0 0 0.129412 0 0 0 0 0.129412 0 0 0 0 0.203922 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_12_39"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_12_39" result="shape"/></filter><filter id="f" width="21.256" height="22.139" x="79.868" y="72.409" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy=".661"/><feGaussianBlur stdDeviation="1.322"/><feColorMatrix values="0 0 0 0 0.129412 0 0 0 0 0.129412 0 0 0 0 0.203922 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_12_39"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_12_39" result="shape"/></filter><filter id="g" width="20.526" height="22.664" x="60.835" y="65.893" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy=".661"/><feGaussianBlur stdDeviation="1.322"/><feColorMatrix values="0 0 0 0 0.129412 0 0 0 0 0.129412 0 0 0 0 0.203922 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_12_39"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_12_39" result="shape"/></filter><filter id="h" width="21.537" height="21.662" x="137.59" y="75.788" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy=".661"/><feGaussianBlur stdDeviation="1.322"/><feColorMatrix values="0 0 0 0 0.129412 0 0 0 0 0.129412 0 0 0 0 0.203922 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_12_39"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_12_39" result="shape"/></filter><filter id="i" width="21.515" height="21.712" x="99.257" y="75.238" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy=".661"/><feGaussianBlur stdDeviation="1.322"/><feColorMatrix values="0 0 0 0 0.129412 0 0 0 0 0.129412 0 0 0 0 0.203922 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_12_39"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_12_39" result="shape"/></filter></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="15" fill="none"><path fill="#fff" fill-rule="evenodd" d="M4.39453 13.8298C1.93859 13.6455 0 11.468 0 8.80884 0 6.0289 2.11876 3.7753 4.73238 3.7753c.46775 0 .91964.07218 1.34638.20664C7.21234 1.62909 9.66469 0 12.5073 0c2.5102 0 4.7161 1.27036 5.9782 3.18766a4.54297 4.54297 0 0 1 .6132-.04144C21.8056 3.14622 24 5.54066 24 8.49436c0 2.89194-2.1036 5.24784-4.7323 5.34504v.0031l-1.8948.278a38.18054 38.18054 0 0 1-11.08354 0l-1.89483-.278v-.0127Z" clip-rule="evenodd"/></svg>
@@ -1,34 +1,41 @@
1
1
  import React from 'react';
2
2
  import { useIntl } from 'react-intl';
3
- import { Link } from '@strapi/helper-plugin';
4
3
  import PropTypes from 'prop-types';
4
+ import { Link } from '@strapi/helper-plugin';
5
5
  import { ArrowLeft, Check } from '@strapi/icons';
6
6
  import { Button, HeaderLayout, Stack } from '@strapi/design-system';
7
7
  import Regenerate from '../Regenerate';
8
8
 
9
- const FormHead = ({ apiToken, setApiToken, canEditInputs, canRegenerate, isSubmitting }) => {
9
+ const FormHead = ({
10
+ title,
11
+ token,
12
+ setToken,
13
+ canEditInputs,
14
+ canRegenerate,
15
+ isSubmitting,
16
+ backUrl,
17
+ regenerateUrl,
18
+ }) => {
10
19
  const { formatMessage } = useIntl();
11
20
  const handleRegenerate = (newKey) => {
12
- setApiToken({
13
- ...apiToken,
21
+ setToken({
22
+ ...token,
14
23
  accessKey: newKey,
15
24
  });
16
25
  };
17
26
 
18
27
  return (
19
28
  <HeaderLayout
20
- title={
21
- apiToken?.name ||
22
- formatMessage({
23
- id: 'Settings.apiTokens.createPage.title',
24
- defaultMessage: 'Create API Token',
25
- })
26
- }
29
+ title={token?.name || formatMessage(title)}
27
30
  primaryAction={
28
31
  canEditInputs ? (
29
32
  <Stack horizontal spacing={2}>
30
- {canRegenerate && apiToken?.id && (
31
- <Regenerate onRegenerate={handleRegenerate} idToRegenerate={apiToken?.id} />
33
+ {canRegenerate && token?.id && (
34
+ <Regenerate
35
+ backUrl={regenerateUrl}
36
+ onRegenerate={handleRegenerate}
37
+ idToRegenerate={token?.id}
38
+ />
32
39
  )}
33
40
  <Button
34
41
  disabled={isSubmitting}
@@ -45,13 +52,17 @@ const FormHead = ({ apiToken, setApiToken, canEditInputs, canRegenerate, isSubmi
45
52
  </Stack>
46
53
  ) : (
47
54
  canRegenerate &&
48
- apiToken?.id && (
49
- <Regenerate onRegenerate={handleRegenerate} idToRegenerate={apiToken?.id} />
55
+ token?.id && (
56
+ <Regenerate
57
+ onRegenerate={handleRegenerate}
58
+ idToRegenerate={token?.id}
59
+ backUrl={regenerateUrl}
60
+ />
50
61
  )
51
62
  )
52
63
  }
53
64
  navigationAction={
54
- <Link startIcon={<ArrowLeft />} to="/settings/api-tokens">
65
+ <Link startIcon={<ArrowLeft />} to={backUrl}>
55
66
  {formatMessage({
56
67
  id: 'global.back',
57
68
  defaultMessage: 'Back',
@@ -63,7 +74,7 @@ const FormHead = ({ apiToken, setApiToken, canEditInputs, canRegenerate, isSubmi
63
74
  };
64
75
 
65
76
  FormHead.propTypes = {
66
- apiToken: PropTypes.shape({
77
+ token: PropTypes.shape({
67
78
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
68
79
  type: PropTypes.string,
69
80
  lifespan: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -75,12 +86,18 @@ FormHead.propTypes = {
75
86
  }),
76
87
  canEditInputs: PropTypes.bool.isRequired,
77
88
  canRegenerate: PropTypes.bool.isRequired,
78
- setApiToken: PropTypes.func.isRequired,
89
+ setToken: PropTypes.func.isRequired,
79
90
  isSubmitting: PropTypes.bool.isRequired,
91
+ backUrl: PropTypes.string.isRequired,
92
+ title: PropTypes.shape({
93
+ id: PropTypes.string,
94
+ label: PropTypes.string,
95
+ }).isRequired,
96
+ regenerateUrl: PropTypes.string.isRequired,
80
97
  };
81
98
 
82
99
  FormHead.defaultProps = {
83
- apiToken: undefined,
100
+ token: undefined,
84
101
  };
85
102
 
86
103
  export default FormHead;
@@ -0,0 +1,95 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { usePersistentState } from '@strapi/helper-plugin';
5
+ import { Select, Option, Typography } from '@strapi/design-system';
6
+ import { getDateOfExpiration } from '../../../pages/ApiTokens/EditView/utils';
7
+
8
+ const LifeSpanInput = ({ token, errors, values, onChange, disabled }) => {
9
+ const { formatMessage } = useIntl();
10
+ const [lang] = usePersistentState('strapi-admin-language', 'en');
11
+
12
+ return (
13
+ <>
14
+ <Select
15
+ name="lifespan"
16
+ label={formatMessage({
17
+ id: 'Settings.apiTokens.form.duration',
18
+ defaultMessage: 'Token duration',
19
+ })}
20
+ value={values.lifespan !== null ? values.lifespan : '0'}
21
+ error={
22
+ errors.lifespan
23
+ ? formatMessage(
24
+ errors.lifespan?.id
25
+ ? errors.lifespan
26
+ : { id: errors.lifespan, defaultMessage: errors.lifespan }
27
+ )
28
+ : null
29
+ }
30
+ onChange={(value) => {
31
+ onChange({ target: { name: 'lifespan', value } });
32
+ }}
33
+ required
34
+ disabled={disabled}
35
+ placeholder="Select"
36
+ >
37
+ <Option value="604800000">
38
+ {formatMessage({
39
+ id: 'Settings.apiTokens.duration.7-days',
40
+ defaultMessage: '7 days',
41
+ })}
42
+ </Option>
43
+ <Option value="2592000000">
44
+ {formatMessage({
45
+ id: 'Settings.apiTokens.duration.30-days',
46
+ defaultMessage: '30 days',
47
+ })}
48
+ </Option>
49
+ <Option value="7776000000">
50
+ {formatMessage({
51
+ id: 'Settings.apiTokens.duration.90-days',
52
+ defaultMessage: '90 days',
53
+ })}
54
+ </Option>
55
+ <Option value="0">
56
+ {formatMessage({
57
+ id: 'Settings.apiTokens.duration.unlimited',
58
+ defaultMessage: 'Unlimited',
59
+ })}
60
+ </Option>
61
+ </Select>
62
+ <Typography variant="pi" textColor="neutral600">
63
+ {disabled &&
64
+ `${formatMessage({
65
+ id: 'Settings.apiTokens.duration.expiration-date',
66
+ defaultMessage: 'Expiration date',
67
+ })}: ${getDateOfExpiration(token?.createdAt, parseInt(values.lifespan, 10, lang))}`}
68
+ </Typography>
69
+ </>
70
+ );
71
+ };
72
+
73
+ LifeSpanInput.propTypes = {
74
+ errors: PropTypes.string,
75
+ onChange: PropTypes.func.isRequired,
76
+ values: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
77
+ disabled: PropTypes.bool.isRequired,
78
+ token: PropTypes.shape({
79
+ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
80
+ type: PropTypes.string,
81
+ lifespan: PropTypes.string,
82
+ name: PropTypes.string,
83
+ accessKey: PropTypes.string,
84
+ permissions: PropTypes.array,
85
+ description: PropTypes.string,
86
+ createdAt: PropTypes.string,
87
+ }),
88
+ };
89
+
90
+ LifeSpanInput.defaultProps = {
91
+ errors: {},
92
+ token: {},
93
+ };
94
+
95
+ export default LifeSpanInput;
@@ -0,0 +1,97 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { Select, Option, Typography } from '@strapi/design-system';
5
+ import { getDateOfExpiration } from '../../../pages/ApiTokens/EditView/utils';
6
+
7
+ const LifeSpanInput = ({ token, errors, values, onChange, isCreating }) => {
8
+ const { formatMessage } = useIntl();
9
+
10
+ return (
11
+ <>
12
+ <Select
13
+ name="lifespan"
14
+ label={formatMessage({
15
+ id: 'Settings.apiTokens.form.duration',
16
+ defaultMessage: 'Token duration',
17
+ })}
18
+ value={values.lifespan !== null ? values.lifespan : '0'}
19
+ error={
20
+ errors.lifespan
21
+ ? formatMessage(
22
+ errors.lifespan?.id
23
+ ? errors.lifespan
24
+ : { id: errors.lifespan, defaultMessage: errors.lifespan }
25
+ )
26
+ : null
27
+ }
28
+ onChange={(value) => {
29
+ onChange({ target: { name: 'lifespan', value } });
30
+ }}
31
+ required
32
+ disabled={!isCreating}
33
+ placeholder="Select"
34
+ >
35
+ <Option value="604800000">
36
+ {formatMessage({
37
+ id: 'Settings.tokens.duration.7-days',
38
+ defaultMessage: '7 days',
39
+ })}
40
+ </Option>
41
+ <Option value="2592000000">
42
+ {formatMessage({
43
+ id: 'Settings.tokens.duration.30-days',
44
+ defaultMessage: '30 days',
45
+ })}
46
+ </Option>
47
+ <Option value="7776000000">
48
+ {formatMessage({
49
+ id: 'Settings.tokens.duration.90-days',
50
+ defaultMessage: '90 days',
51
+ })}
52
+ </Option>
53
+ <Option value="0">
54
+ {formatMessage({
55
+ id: 'Settings.tokens.duration.unlimited',
56
+ defaultMessage: 'Unlimited',
57
+ })}
58
+ </Option>
59
+ </Select>
60
+ <Typography variant="pi" textColor="neutral600">
61
+ {!isCreating &&
62
+ `${formatMessage({
63
+ id: 'Settings.tokens.duration.expiration-date',
64
+ defaultMessage: 'Expiration date',
65
+ })}: ${getDateOfExpiration(token?.createdAt, parseInt(values.lifespan, 10))}`}
66
+ </Typography>
67
+ </>
68
+ );
69
+ };
70
+
71
+ LifeSpanInput.propTypes = {
72
+ errors: PropTypes.shape({
73
+ lifespan: PropTypes.string,
74
+ }),
75
+ onChange: PropTypes.func.isRequired,
76
+ values: PropTypes.shape({
77
+ lifespan: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
78
+ }).isRequired,
79
+ isCreating: PropTypes.bool.isRequired,
80
+ token: PropTypes.shape({
81
+ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
82
+ type: PropTypes.string,
83
+ lifespan: PropTypes.string,
84
+ name: PropTypes.string,
85
+ accessKey: PropTypes.string,
86
+ permissions: PropTypes.array,
87
+ description: PropTypes.string,
88
+ createdAt: PropTypes.string,
89
+ }),
90
+ };
91
+
92
+ LifeSpanInput.defaultProps = {
93
+ errors: {},
94
+ token: {},
95
+ };
96
+
97
+ export default LifeSpanInput;
@@ -0,0 +1,73 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { Button } from '@strapi/design-system';
5
+ import { Refresh } from '@strapi/icons';
6
+ import { ConfirmDialog } from '@strapi/helper-plugin';
7
+ import { useRegenerate } from '../../../../../hooks';
8
+
9
+ export const Regenerate = ({ onRegenerate, idToRegenerate, backUrl }) => {
10
+ const { formatMessage } = useIntl();
11
+ const [showConfirmDialog, setShowConfirmDialog] = useState(false);
12
+ const { regenerateData, isLoadingConfirmation } = useRegenerate(
13
+ backUrl,
14
+ idToRegenerate,
15
+ onRegenerate
16
+ );
17
+ const handleConfirmRegeneration = async () => {
18
+ regenerateData();
19
+ setShowConfirmDialog(false);
20
+ };
21
+
22
+ return (
23
+ <>
24
+ <Button
25
+ startIcon={<Refresh />}
26
+ type="button"
27
+ size="S"
28
+ variant="tertiary"
29
+ onClick={() => setShowConfirmDialog(true)}
30
+ name="regenerate"
31
+ >
32
+ {formatMessage({
33
+ id: 'Settings.apiTokens.regenerate',
34
+ defaultMessage: 'Regenerate',
35
+ })}
36
+ </Button>
37
+
38
+ <ConfirmDialog
39
+ bodyText={{
40
+ id: 'Settings.apiTokens.popUpWarning.message',
41
+ defaultMessage: 'Are you sure you want to regenerate this token?',
42
+ }}
43
+ iconRightButton={<Refresh />}
44
+ isConfirmButtonLoading={isLoadingConfirmation}
45
+ isOpen={showConfirmDialog}
46
+ onToggleDialog={() => setShowConfirmDialog(false)}
47
+ onConfirm={handleConfirmRegeneration}
48
+ leftButtonText={{
49
+ id: 'Settings.apiTokens.Button.cancel',
50
+ defaultMessage: 'Cancel',
51
+ }}
52
+ rightButtonText={{
53
+ id: 'Settings.apiTokens.Button.regenerate',
54
+ defaultMessage: 'Regenerate',
55
+ }}
56
+ title={{
57
+ id: 'Settings.apiTokens.RegenerateDialog.title',
58
+ defaultMessage: 'Regenerate token',
59
+ }}
60
+ />
61
+ </>
62
+ );
63
+ };
64
+
65
+ Regenerate.defaultProps = { onRegenerate() {} };
66
+
67
+ Regenerate.propTypes = {
68
+ onRegenerate: PropTypes.func,
69
+ idToRegenerate: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
70
+ backUrl: PropTypes.string.isRequired,
71
+ };
72
+
73
+ export default Regenerate;
@@ -1,20 +1,27 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { Trash } from '@strapi/icons';
3
3
  import { IconButton, Box } from '@strapi/design-system';
4
- import { stopPropagation, useTracking } from '@strapi/helper-plugin';
4
+ import { useTracking, ConfirmDialog } from '@strapi/helper-plugin';
5
5
  import { useIntl } from 'react-intl';
6
6
  import PropTypes from 'prop-types';
7
7
 
8
- const DeleteButton = ({ tokenName, onClickDelete }) => {
8
+ const DeleteButton = ({ tokenName, onClickDelete, tokenType }) => {
9
9
  const { formatMessage } = useIntl();
10
10
  const { trackUsage } = useTracking();
11
+ const [showConfirmDialog, setShowConfirmDialog] = useState(false);
12
+ const handleClickDelete = () => {
13
+ setShowConfirmDialog(false);
14
+ trackUsage('willDeleteToken', {
15
+ tokenType,
16
+ });
17
+ onClickDelete();
18
+ };
11
19
 
12
20
  return (
13
- <Box paddingLeft={1} {...stopPropagation}>
21
+ <Box paddingLeft={1} onClick={(e) => e.stopPropagation()}>
14
22
  <IconButton
15
23
  onClick={() => {
16
- trackUsage('willDeleteToken');
17
- onClickDelete();
24
+ setShowConfirmDialog(true);
18
25
  }}
19
26
  label={formatMessage(
20
27
  {
@@ -27,6 +34,11 @@ const DeleteButton = ({ tokenName, onClickDelete }) => {
27
34
  noBorder
28
35
  icon={<Trash />}
29
36
  />
37
+ <ConfirmDialog
38
+ onToggleDialog={() => setShowConfirmDialog(false)}
39
+ onConfirm={handleClickDelete}
40
+ isOpen={showConfirmDialog}
41
+ />
30
42
  </Box>
31
43
  );
32
44
  };
@@ -34,6 +46,7 @@ const DeleteButton = ({ tokenName, onClickDelete }) => {
34
46
  DeleteButton.propTypes = {
35
47
  tokenName: PropTypes.string.isRequired,
36
48
  onClickDelete: PropTypes.func.isRequired,
49
+ tokenType: PropTypes.string.isRequired,
37
50
  };
38
51
 
39
52
  export default DeleteButton;