node_modules ignore

This commit is contained in:
2025-05-08 23:43:47 +02:00
parent e19d52f172
commit 4574544c9f
65041 changed files with 10593536 additions and 0 deletions

View File

@@ -0,0 +1,344 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var strapiAdmin = require('@strapi/content-manager/strapi-admin');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var styledComponents = require('styled-components');
const StyledPopoverFlex = styledComponents.styled(designSystem.Flex)`
width: 100%;
max-width: 256px;
& > * {
border-bottom: 1px solid ${({ theme })=>theme.colors.neutral150};
}
& > *:last-child {
border-bottom: none;
}
`;
const EntryStatusTrigger = ({ action, status, hasErrors, requiredStage, entryStage })=>{
const { formatMessage } = reactIntl.useIntl();
if (action === 'publish') {
if (hasErrors || requiredStage && requiredStage.id !== entryStage?.id) {
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CrossCircle, {
fill: "danger600"
}),
endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "danger600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.not-ready',
defaultMessage: 'Not ready to publish'
})
})
})
});
}
if (status === 'draft') {
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
fill: "success600"
}),
endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "success600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish',
defaultMessage: 'Ready to publish'
})
})
})
});
}
if (status === 'modified') {
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.ArrowsCounterClockwise, {
fill: "alternative600"
}),
endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "omega",
fontWeight: "bold",
textColor: "alternative600",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.modified',
defaultMessage: 'Ready to publish changes'
})
})
})
});
}
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
fill: "success600"
}),
endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "success600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.already-published',
defaultMessage: 'Already published'
})
})
})
});
}
if (status === 'published') {
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
fill: "success600"
}),
endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "success600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish',
defaultMessage: 'Ready to unpublish'
})
})
})
});
}
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
fill: "success600"
}),
endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "success600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.already-unpublished',
defaultMessage: 'Already unpublished'
})
})
})
});
};
const FieldsValidation = ({ hasErrors, errors, kind, contentTypeUid, documentId, locale })=>{
const { formatMessage } = reactIntl.useIntl();
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 1,
width: "100%",
padding: 5,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
width: "100%",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.fields',
defaultMessage: 'Fields'
})
}),
hasErrors ? /*#__PURE__*/ jsxRuntime.jsx(icons.CrossCircle, {
fill: "danger600"
}) : /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
fill: "success600"
})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
width: "100%",
textColor: "neutral600",
children: hasErrors ? formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.error',
defaultMessage: '{errors} errors on fields.'
}, {
errors: errors ? Object.keys(errors).length : 0
}) : formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.success',
defaultMessage: 'All fields are filled correctly.'
})
}),
hasErrors && /*#__PURE__*/ jsxRuntime.jsx(designSystem.LinkButton, {
tag: reactRouterDom.Link,
to: {
pathname: `/content-manager/${kind === 'collectionType' ? 'collection-types' : 'single-types'}/${contentTypeUid}/${documentId}`,
search: locale ? qs.stringify({
plugins: {
i18n: {
locale
}
}
}) : ''
},
variant: "secondary",
fullWidth: true,
state: {
forceValidation: true
},
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.see-errors',
defaultMessage: 'See errors'
})
})
]
});
};
const getReviewStageIcon = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage })=>{
if (!contentTypeHasReviewWorkflow) {
return /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
fill: "neutral200"
});
}
if (requiredStage && requiredStage.id !== entryStage?.id) {
return /*#__PURE__*/ jsxRuntime.jsx(icons.CrossCircle, {
fill: "danger600"
});
}
return /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
fill: "success600"
});
};
const getReviewStageMessage = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage, formatMessage })=>{
if (!contentTypeHasReviewWorkflow) {
return formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-enabled',
defaultMessage: 'This entry is not associated to any workflow.'
});
}
if (requiredStage && requiredStage.id !== entryStage?.id) {
return formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-ready',
defaultMessage: 'This entry is not at the required stage for publishing. ({stageName})'
}, {
stageName: requiredStage?.name ?? ''
});
}
if (requiredStage && requiredStage.id === entryStage?.id) {
return formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.ready',
defaultMessage: 'This entry is at the required stage for publishing. ({stageName})'
}, {
stageName: requiredStage?.name ?? ''
});
}
return formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.stage-not-required',
defaultMessage: 'No required stage for publication'
});
};
const ReviewStageValidation = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage })=>{
const { formatMessage } = reactIntl.useIntl();
const Icon = getReviewStageIcon({
contentTypeHasReviewWorkflow,
requiredStage,
entryStage
});
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 1,
width: "100%",
padding: 5,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
width: "100%",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage',
defaultMessage: 'Review stage'
})
}),
Icon
]
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral600",
children: getReviewStageMessage({
contentTypeHasReviewWorkflow,
requiredStage,
entryStage,
formatMessage
})
})
]
});
};
const EntryValidationPopover = ({ schema, entry, status, action })=>{
const { validate, isLoading } = strapiAdmin.unstable_useDocument({
collectionType: schema?.kind ?? '',
model: schema?.uid ?? ''
}, {
// useDocument makes a request to get more data about the entry, but we only want to have the validation function so we skip the request
skip: true
});
// Validation errors
const errors = isLoading ? null : validate(entry);
const hasErrors = errors ? Object.keys(errors).length > 0 : false;
// Entry stage
const contentTypeHasReviewWorkflow = schema?.hasReviewWorkflow ?? false;
const requiredStage = schema?.stageRequiredToPublish;
const entryStage = entry.strapi_stage;
if (isLoading) {
return null;
}
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Popover.Root, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(EntryStatusTrigger, {
action: action,
status: status,
hasErrors: hasErrors,
requiredStage: requiredStage,
entryStage: entryStage
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Content, {
children: /*#__PURE__*/ jsxRuntime.jsxs(StyledPopoverFlex, {
direction: "column",
children: [
/*#__PURE__*/ jsxRuntime.jsx(FieldsValidation, {
hasErrors: hasErrors,
errors: errors,
contentTypeUid: schema?.uid,
kind: schema?.kind,
documentId: entry.documentId,
locale: entry.locale
}),
/*#__PURE__*/ jsxRuntime.jsx(ReviewStageValidation, {
contentTypeHasReviewWorkflow: contentTypeHasReviewWorkflow,
requiredStage: requiredStage,
entryStage: entryStage
})
]
})
})
]
});
};
exports.EntryValidationPopover = EntryValidationPopover;
//# sourceMappingURL=EntryValidationPopover.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,342 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import 'react';
import { unstable_useDocument } from '@strapi/content-manager/strapi-admin';
import { Flex, Popover, Button, Typography, LinkButton } from '@strapi/design-system';
import { CrossCircle, CaretDown, CheckCircle, ArrowsCounterClockwise } from '@strapi/icons';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { styled } from 'styled-components';
const StyledPopoverFlex = styled(Flex)`
width: 100%;
max-width: 256px;
& > * {
border-bottom: 1px solid ${({ theme })=>theme.colors.neutral150};
}
& > *:last-child {
border-bottom: none;
}
`;
const EntryStatusTrigger = ({ action, status, hasErrors, requiredStage, entryStage })=>{
const { formatMessage } = useIntl();
if (action === 'publish') {
if (hasErrors || requiredStage && requiredStage.id !== entryStage?.id) {
return /*#__PURE__*/ jsx(Popover.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsx(CrossCircle, {
fill: "danger600"
}),
endIcon: /*#__PURE__*/ jsx(CaretDown, {}),
children: /*#__PURE__*/ jsx(Typography, {
textColor: "danger600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.not-ready',
defaultMessage: 'Not ready to publish'
})
})
})
});
}
if (status === 'draft') {
return /*#__PURE__*/ jsx(Popover.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsx(CheckCircle, {
fill: "success600"
}),
endIcon: /*#__PURE__*/ jsx(CaretDown, {}),
children: /*#__PURE__*/ jsx(Typography, {
textColor: "success600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish',
defaultMessage: 'Ready to publish'
})
})
})
});
}
if (status === 'modified') {
return /*#__PURE__*/ jsx(Popover.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsx(ArrowsCounterClockwise, {
fill: "alternative600"
}),
endIcon: /*#__PURE__*/ jsx(CaretDown, {}),
children: /*#__PURE__*/ jsx(Typography, {
variant: "omega",
fontWeight: "bold",
textColor: "alternative600",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.modified',
defaultMessage: 'Ready to publish changes'
})
})
})
});
}
return /*#__PURE__*/ jsx(Popover.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsx(CheckCircle, {
fill: "success600"
}),
endIcon: /*#__PURE__*/ jsx(CaretDown, {}),
children: /*#__PURE__*/ jsx(Typography, {
textColor: "success600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.already-published',
defaultMessage: 'Already published'
})
})
})
});
}
if (status === 'published') {
return /*#__PURE__*/ jsx(Popover.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsx(CheckCircle, {
fill: "success600"
}),
endIcon: /*#__PURE__*/ jsx(CaretDown, {}),
children: /*#__PURE__*/ jsx(Typography, {
textColor: "success600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish',
defaultMessage: 'Ready to unpublish'
})
})
})
});
}
return /*#__PURE__*/ jsx(Popover.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
variant: "ghost",
startIcon: /*#__PURE__*/ jsx(CheckCircle, {
fill: "success600"
}),
endIcon: /*#__PURE__*/ jsx(CaretDown, {}),
children: /*#__PURE__*/ jsx(Typography, {
textColor: "success600",
variant: "omega",
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.already-unpublished',
defaultMessage: 'Already unpublished'
})
})
})
});
};
const FieldsValidation = ({ hasErrors, errors, kind, contentTypeUid, documentId, locale })=>{
const { formatMessage } = useIntl();
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 1,
width: "100%",
padding: 5,
children: [
/*#__PURE__*/ jsxs(Flex, {
gap: 2,
width: "100%",
children: [
/*#__PURE__*/ jsx(Typography, {
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.fields',
defaultMessage: 'Fields'
})
}),
hasErrors ? /*#__PURE__*/ jsx(CrossCircle, {
fill: "danger600"
}) : /*#__PURE__*/ jsx(CheckCircle, {
fill: "success600"
})
]
}),
/*#__PURE__*/ jsx(Typography, {
width: "100%",
textColor: "neutral600",
children: hasErrors ? formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.error',
defaultMessage: '{errors} errors on fields.'
}, {
errors: errors ? Object.keys(errors).length : 0
}) : formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.success',
defaultMessage: 'All fields are filled correctly.'
})
}),
hasErrors && /*#__PURE__*/ jsx(LinkButton, {
tag: Link,
to: {
pathname: `/content-manager/${kind === 'collectionType' ? 'collection-types' : 'single-types'}/${contentTypeUid}/${documentId}`,
search: locale ? stringify({
plugins: {
i18n: {
locale
}
}
}) : ''
},
variant: "secondary",
fullWidth: true,
state: {
forceValidation: true
},
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.see-errors',
defaultMessage: 'See errors'
})
})
]
});
};
const getReviewStageIcon = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage })=>{
if (!contentTypeHasReviewWorkflow) {
return /*#__PURE__*/ jsx(CheckCircle, {
fill: "neutral200"
});
}
if (requiredStage && requiredStage.id !== entryStage?.id) {
return /*#__PURE__*/ jsx(CrossCircle, {
fill: "danger600"
});
}
return /*#__PURE__*/ jsx(CheckCircle, {
fill: "success600"
});
};
const getReviewStageMessage = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage, formatMessage })=>{
if (!contentTypeHasReviewWorkflow) {
return formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-enabled',
defaultMessage: 'This entry is not associated to any workflow.'
});
}
if (requiredStage && requiredStage.id !== entryStage?.id) {
return formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-ready',
defaultMessage: 'This entry is not at the required stage for publishing. ({stageName})'
}, {
stageName: requiredStage?.name ?? ''
});
}
if (requiredStage && requiredStage.id === entryStage?.id) {
return formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.ready',
defaultMessage: 'This entry is at the required stage for publishing. ({stageName})'
}, {
stageName: requiredStage?.name ?? ''
});
}
return formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.stage-not-required',
defaultMessage: 'No required stage for publication'
});
};
const ReviewStageValidation = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage })=>{
const { formatMessage } = useIntl();
const Icon = getReviewStageIcon({
contentTypeHasReviewWorkflow,
requiredStage,
entryStage
});
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 1,
width: "100%",
padding: 5,
children: [
/*#__PURE__*/ jsxs(Flex, {
gap: 2,
width: "100%",
children: [
/*#__PURE__*/ jsx(Typography, {
fontWeight: "bold",
children: formatMessage({
id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage',
defaultMessage: 'Review stage'
})
}),
Icon
]
}),
/*#__PURE__*/ jsx(Typography, {
textColor: "neutral600",
children: getReviewStageMessage({
contentTypeHasReviewWorkflow,
requiredStage,
entryStage,
formatMessage
})
})
]
});
};
const EntryValidationPopover = ({ schema, entry, status, action })=>{
const { validate, isLoading } = unstable_useDocument({
collectionType: schema?.kind ?? '',
model: schema?.uid ?? ''
}, {
// useDocument makes a request to get more data about the entry, but we only want to have the validation function so we skip the request
skip: true
});
// Validation errors
const errors = isLoading ? null : validate(entry);
const hasErrors = errors ? Object.keys(errors).length > 0 : false;
// Entry stage
const contentTypeHasReviewWorkflow = schema?.hasReviewWorkflow ?? false;
const requiredStage = schema?.stageRequiredToPublish;
const entryStage = entry.strapi_stage;
if (isLoading) {
return null;
}
return /*#__PURE__*/ jsxs(Popover.Root, {
children: [
/*#__PURE__*/ jsx(EntryStatusTrigger, {
action: action,
status: status,
hasErrors: hasErrors,
requiredStage: requiredStage,
entryStage: entryStage
}),
/*#__PURE__*/ jsx(Popover.Content, {
children: /*#__PURE__*/ jsxs(StyledPopoverFlex, {
direction: "column",
children: [
/*#__PURE__*/ jsx(FieldsValidation, {
hasErrors: hasErrors,
errors: errors,
contentTypeUid: schema?.uid,
kind: schema?.kind,
documentId: entry.documentId,
locale: entry.locale
}),
/*#__PURE__*/ jsx(ReviewStageValidation, {
contentTypeHasReviewWorkflow: contentTypeHasReviewWorkflow,
requiredStage: requiredStage,
entryStage: entryStage
})
]
})
})
]
});
};
export { EntryValidationPopover };
//# sourceMappingURL=EntryValidationPopover.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,76 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var dateFns = require('date-fns');
var reactIntl = require('react-intl');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const intervals = [
'years',
'months',
'days',
'hours',
'minutes',
'seconds'
];
/**
* Displays the relative time between a given timestamp and the current time.
* You can display a custom message for given time intervals by passing an array of custom intervals.
*
* @example
* ```jsx
* <caption>Display "last hour" if the timestamp is less than an hour ago</caption>
* <RelativeTime
* timestamp={new Date('2021-01-01')}
* customIntervals={[
* { unit: 'hours', threshold: 1, text: 'last hour' },
* ]}
* ```
*/ const RelativeTime = /*#__PURE__*/ React__namespace.forwardRef(({ timestamp, customIntervals = [], ...restProps }, forwardedRef)=>{
const { formatRelativeTime, formatDate, formatTime } = reactIntl.useIntl();
/**
* TODO: make this auto-update, like a clock.
*/ const interval = dateFns.intervalToDuration({
start: timestamp,
end: Date.now()
});
const unit = intervals.find((intervalUnit)=>{
return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
});
const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
// Display custom text if interval is less than the threshold
const customInterval = customIntervals.find((custom)=>interval[custom.unit] < custom.threshold);
const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, {
numeric: 'auto'
});
return /*#__PURE__*/ jsxRuntime.jsx("time", {
ref: forwardedRef,
dateTime: timestamp.toISOString(),
role: "time",
title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
...restProps,
children: displayText
});
});
exports.RelativeTime = RelativeTime;
//# sourceMappingURL=RelativeTime.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RelativeTime.js","sources":["../../../admin/src/components/RelativeTime.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Duration, intervalToDuration, isPast } from 'date-fns';\nimport { useIntl } from 'react-intl';\n\nconst intervals: Array<keyof Duration> = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'];\n\ninterface CustomInterval {\n unit: keyof Duration;\n text: string;\n threshold: number;\n}\n\ninterface RelativeTimeProps extends React.ComponentPropsWithoutRef<'time'> {\n timestamp: Date;\n customIntervals?: CustomInterval[];\n}\n\n/**\n * Displays the relative time between a given timestamp and the current time.\n * You can display a custom message for given time intervals by passing an array of custom intervals.\n *\n * @example\n * ```jsx\n * <caption>Display \"last hour\" if the timestamp is less than an hour ago</caption>\n * <RelativeTime\n * timestamp={new Date('2021-01-01')}\n * customIntervals={[\n * { unit: 'hours', threshold: 1, text: 'last hour' },\n * ]}\n * ```\n */\nconst RelativeTime = React.forwardRef<HTMLTimeElement, RelativeTimeProps>(\n ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {\n const { formatRelativeTime, formatDate, formatTime } = useIntl();\n\n /**\n * TODO: make this auto-update, like a clock.\n */\n const interval = intervalToDuration({\n start: timestamp,\n end: Date.now(),\n // see https://github.com/date-fns/date-fns/issues/2891 No idea why it's all partial it returns it every time.\n }) as Required<Duration>;\n\n const unit = intervals.find((intervalUnit) => {\n return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);\n })!;\n\n const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];\n\n // Display custom text if interval is less than the threshold\n const customInterval = customIntervals.find(\n (custom) => interval[custom.unit] < custom.threshold\n );\n\n const displayText = customInterval\n ? customInterval.text\n : formatRelativeTime(relativeTime, unit, { numeric: 'auto' });\n\n return (\n <time\n ref={forwardedRef}\n dateTime={timestamp.toISOString()}\n role=\"time\"\n title={`${formatDate(timestamp)} ${formatTime(timestamp)}`}\n {...restProps}\n >\n {displayText}\n </time>\n );\n }\n);\n\nexport { RelativeTime };\nexport type { CustomInterval, RelativeTimeProps };\n"],"names":["intervals","RelativeTime","React","forwardRef","timestamp","customIntervals","restProps","forwardedRef","formatRelativeTime","formatDate","formatTime","useIntl","interval","intervalToDuration","start","end","Date","now","unit","find","intervalUnit","Object","keys","includes","relativeTime","isPast","customInterval","custom","threshold","displayText","text","numeric","_jsx","time","ref","dateTime","toISOString","role","title"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,MAAMA,SAAmC,GAAA;AAAC,IAAA,OAAA;AAAS,IAAA,QAAA;AAAU,IAAA,MAAA;AAAQ,IAAA,OAAA;AAAS,IAAA,SAAA;AAAW,IAAA;AAAU,CAAA;AAanG;;;;;;;;;;;;;AAaC,IACKC,MAAAA,YAAAA,iBAAeC,gBAAMC,CAAAA,UAAU,CACnC,CAAC,EAAEC,SAAS,EAAEC,eAAkB,GAAA,EAAE,EAAE,GAAGC,WAAW,EAAEC,YAAAA,GAAAA;AAClD,IAAA,MAAM,EAAEC,kBAAkB,EAAEC,UAAU,EAAEC,UAAU,EAAE,GAAGC,iBAAAA,EAAAA;AAEvD;;QAGA,MAAMC,WAAWC,0BAAmB,CAAA;QAClCC,KAAOV,EAAAA,SAAAA;AACPW,QAAAA,GAAAA,EAAKC,KAAKC,GAAG;AAEf,KAAA,CAAA;AAEA,IAAA,MAAMC,IAAOlB,GAAAA,SAAAA,CAAUmB,IAAI,CAAC,CAACC,YAAAA,GAAAA;QAC3B,OAAOR,QAAQ,CAACQ,YAAAA,CAAa,GAAG,CAAA,IAAKC,OAAOC,IAAI,CAACV,QAAUW,CAAAA,CAAAA,QAAQ,CAACH,YAAAA,CAAAA;AACtE,KAAA,CAAA;IAEA,MAAMI,YAAAA,GAAeC,cAAOrB,CAAAA,SAAAA,CAAAA,GAAa,CAACQ,QAAQ,CAACM,IAAK,CAAA,GAAGN,QAAQ,CAACM,IAAK,CAAA;;AAGzE,IAAA,MAAMQ,cAAiBrB,GAAAA,eAAAA,CAAgBc,IAAI,CACzC,CAACQ,MAAAA,GAAWf,QAAQ,CAACe,MAAOT,CAAAA,IAAI,CAAC,GAAGS,OAAOC,SAAS,CAAA;AAGtD,IAAA,MAAMC,cAAcH,cAChBA,GAAAA,cAAAA,CAAeI,IAAI,GACnBtB,kBAAAA,CAAmBgB,cAAcN,IAAM,EAAA;QAAEa,OAAS,EAAA;AAAO,KAAA,CAAA;AAE7D,IAAA,qBACEC,cAACC,CAAAA,MAAAA,EAAAA;QACCC,GAAK3B,EAAAA,YAAAA;AACL4B,QAAAA,QAAAA,EAAU/B,UAAUgC,WAAW,EAAA;QAC/BC,IAAK,EAAA,MAAA;QACLC,KAAO,EAAA,CAAC,EAAE7B,UAAWL,CAAAA,SAAAA,CAAAA,CAAW,CAAC,EAAEM,UAAAA,CAAWN,WAAW,CAAC;AACzD,QAAA,GAAGE,SAAS;AAEZuB,QAAAA,QAAAA,EAAAA;;AAGP,CAAA;;;;"}

View File

@@ -0,0 +1,55 @@
import { jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { intervalToDuration, isPast } from 'date-fns';
import { useIntl } from 'react-intl';
const intervals = [
'years',
'months',
'days',
'hours',
'minutes',
'seconds'
];
/**
* Displays the relative time between a given timestamp and the current time.
* You can display a custom message for given time intervals by passing an array of custom intervals.
*
* @example
* ```jsx
* <caption>Display "last hour" if the timestamp is less than an hour ago</caption>
* <RelativeTime
* timestamp={new Date('2021-01-01')}
* customIntervals={[
* { unit: 'hours', threshold: 1, text: 'last hour' },
* ]}
* ```
*/ const RelativeTime = /*#__PURE__*/ React.forwardRef(({ timestamp, customIntervals = [], ...restProps }, forwardedRef)=>{
const { formatRelativeTime, formatDate, formatTime } = useIntl();
/**
* TODO: make this auto-update, like a clock.
*/ const interval = intervalToDuration({
start: timestamp,
end: Date.now()
});
const unit = intervals.find((intervalUnit)=>{
return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
});
const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
// Display custom text if interval is less than the threshold
const customInterval = customIntervals.find((custom)=>interval[custom.unit] < custom.threshold);
const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, {
numeric: 'auto'
});
return /*#__PURE__*/ jsx("time", {
ref: forwardedRef,
dateTime: timestamp.toISOString(),
role: "time",
title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
...restProps,
children: displayText
});
});
export { RelativeTime };
//# sourceMappingURL=RelativeTime.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RelativeTime.mjs","sources":["../../../admin/src/components/RelativeTime.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Duration, intervalToDuration, isPast } from 'date-fns';\nimport { useIntl } from 'react-intl';\n\nconst intervals: Array<keyof Duration> = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'];\n\ninterface CustomInterval {\n unit: keyof Duration;\n text: string;\n threshold: number;\n}\n\ninterface RelativeTimeProps extends React.ComponentPropsWithoutRef<'time'> {\n timestamp: Date;\n customIntervals?: CustomInterval[];\n}\n\n/**\n * Displays the relative time between a given timestamp and the current time.\n * You can display a custom message for given time intervals by passing an array of custom intervals.\n *\n * @example\n * ```jsx\n * <caption>Display \"last hour\" if the timestamp is less than an hour ago</caption>\n * <RelativeTime\n * timestamp={new Date('2021-01-01')}\n * customIntervals={[\n * { unit: 'hours', threshold: 1, text: 'last hour' },\n * ]}\n * ```\n */\nconst RelativeTime = React.forwardRef<HTMLTimeElement, RelativeTimeProps>(\n ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {\n const { formatRelativeTime, formatDate, formatTime } = useIntl();\n\n /**\n * TODO: make this auto-update, like a clock.\n */\n const interval = intervalToDuration({\n start: timestamp,\n end: Date.now(),\n // see https://github.com/date-fns/date-fns/issues/2891 No idea why it's all partial it returns it every time.\n }) as Required<Duration>;\n\n const unit = intervals.find((intervalUnit) => {\n return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);\n })!;\n\n const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];\n\n // Display custom text if interval is less than the threshold\n const customInterval = customIntervals.find(\n (custom) => interval[custom.unit] < custom.threshold\n );\n\n const displayText = customInterval\n ? customInterval.text\n : formatRelativeTime(relativeTime, unit, { numeric: 'auto' });\n\n return (\n <time\n ref={forwardedRef}\n dateTime={timestamp.toISOString()}\n role=\"time\"\n title={`${formatDate(timestamp)} ${formatTime(timestamp)}`}\n {...restProps}\n >\n {displayText}\n </time>\n );\n }\n);\n\nexport { RelativeTime };\nexport type { CustomInterval, RelativeTimeProps };\n"],"names":["intervals","RelativeTime","React","forwardRef","timestamp","customIntervals","restProps","forwardedRef","formatRelativeTime","formatDate","formatTime","useIntl","interval","intervalToDuration","start","end","Date","now","unit","find","intervalUnit","Object","keys","includes","relativeTime","isPast","customInterval","custom","threshold","displayText","text","numeric","_jsx","time","ref","dateTime","toISOString","role","title"],"mappings":";;;;;AAKA,MAAMA,SAAmC,GAAA;AAAC,IAAA,OAAA;AAAS,IAAA,QAAA;AAAU,IAAA,MAAA;AAAQ,IAAA,OAAA;AAAS,IAAA,SAAA;AAAW,IAAA;AAAU,CAAA;AAanG;;;;;;;;;;;;;AAaC,IACKC,MAAAA,YAAAA,iBAAeC,KAAMC,CAAAA,UAAU,CACnC,CAAC,EAAEC,SAAS,EAAEC,eAAkB,GAAA,EAAE,EAAE,GAAGC,WAAW,EAAEC,YAAAA,GAAAA;AAClD,IAAA,MAAM,EAAEC,kBAAkB,EAAEC,UAAU,EAAEC,UAAU,EAAE,GAAGC,OAAAA,EAAAA;AAEvD;;QAGA,MAAMC,WAAWC,kBAAmB,CAAA;QAClCC,KAAOV,EAAAA,SAAAA;AACPW,QAAAA,GAAAA,EAAKC,KAAKC,GAAG;AAEf,KAAA,CAAA;AAEA,IAAA,MAAMC,IAAOlB,GAAAA,SAAAA,CAAUmB,IAAI,CAAC,CAACC,YAAAA,GAAAA;QAC3B,OAAOR,QAAQ,CAACQ,YAAAA,CAAa,GAAG,CAAA,IAAKC,OAAOC,IAAI,CAACV,QAAUW,CAAAA,CAAAA,QAAQ,CAACH,YAAAA,CAAAA;AACtE,KAAA,CAAA;IAEA,MAAMI,YAAAA,GAAeC,MAAOrB,CAAAA,SAAAA,CAAAA,GAAa,CAACQ,QAAQ,CAACM,IAAK,CAAA,GAAGN,QAAQ,CAACM,IAAK,CAAA;;AAGzE,IAAA,MAAMQ,cAAiBrB,GAAAA,eAAAA,CAAgBc,IAAI,CACzC,CAACQ,MAAAA,GAAWf,QAAQ,CAACe,MAAOT,CAAAA,IAAI,CAAC,GAAGS,OAAOC,SAAS,CAAA;AAGtD,IAAA,MAAMC,cAAcH,cAChBA,GAAAA,cAAAA,CAAeI,IAAI,GACnBtB,kBAAAA,CAAmBgB,cAAcN,IAAM,EAAA;QAAEa,OAAS,EAAA;AAAO,KAAA,CAAA;AAE7D,IAAA,qBACEC,GAACC,CAAAA,MAAAA,EAAAA;QACCC,GAAK3B,EAAAA,YAAAA;AACL4B,QAAAA,QAAAA,EAAU/B,UAAUgC,WAAW,EAAA;QAC/BC,IAAK,EAAA,MAAA;QACLC,KAAO,EAAA,CAAC,EAAE7B,UAAWL,CAAAA,SAAAA,CAAAA,CAAW,CAAC,EAAEM,UAAAA,CAAWN,WAAW,CAAC;AACzD,QAAA,GAAGE,SAAS;AAEZuB,QAAAA,QAAAA,EAAAA;;AAGP,CAAA;;;;"}

View File

@@ -0,0 +1,201 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var formik = require('formik');
var reactIntl = require('react-intl');
var constants = require('../constants.js');
var release = require('../services/release.js');
var ReleaseActionModal = require('./ReleaseActionModal.js');
var ReleaseActionOptions = require('./ReleaseActionOptions.js');
const getContentPermissions = (subject)=>{
const permissions = {
publish: [
{
action: 'plugin::content-manager.explorer.publish',
subject,
id: '',
actionParameters: {},
properties: {},
conditions: []
}
]
};
return permissions;
};
const ReleaseAction = ({ documents, model })=>{
const { formatMessage } = reactIntl.useIntl();
const { toggleNotification } = strapiAdmin.useNotification();
const { formatAPIError } = strapiAdmin.useAPIErrorHandler();
const [{ query }] = strapiAdmin.useQueryParams();
const contentPermissions = getContentPermissions(model);
const { allowedActions: { canPublish } } = strapiAdmin.useRBAC(contentPermissions);
const { allowedActions: { canCreate } } = strapiAdmin.useRBAC(constants.PERMISSIONS);
// Get all the releases not published
const response = release.useGetReleasesQuery();
const releases = response.data?.data;
const [createManyReleaseActions, { isLoading }] = release.useCreateManyReleaseActionsMutation();
const documentIds = documents.map((doc)=>doc.documentId);
const handleSubmit = async (values)=>{
const locale = query.plugins?.i18n?.locale;
const releaseActionEntries = documentIds.map((entryDocumentId)=>({
type: values.type,
contentType: model,
entryDocumentId,
locale
}));
const response = await createManyReleaseActions({
body: releaseActionEntries,
params: {
releaseId: values.releaseId
}
});
if ('data' in response) {
// Handle success
const notificationMessage = formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.notification.success.message',
defaultMessage: '{entriesAlreadyInRelease} out of {totalEntries} entries were already in the release.'
}, {
entriesAlreadyInRelease: response.data.meta.entriesAlreadyInRelease,
totalEntries: response.data.meta.totalEntries
});
const notification = {
type: 'success',
title: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.notification.success.title',
defaultMessage: 'Successfully added to release.'
}, {
entriesAlreadyInRelease: response.data.meta.entriesAlreadyInRelease,
totalEntries: response.data.meta.totalEntries
}),
message: response.data.meta.entriesAlreadyInRelease ? notificationMessage : ''
};
toggleNotification(notification);
return true;
}
if ('error' in response) {
if (strapiAdmin.isFetchError(response.error)) {
// Handle fetch error
toggleNotification({
type: 'warning',
message: formatAPIError(response.error)
});
} else {
// Handle generic error
toggleNotification({
type: 'warning',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
}
};
if (!canCreate || !canPublish) return null;
return {
actionType: 'release',
variant: 'tertiary',
label: formatMessage({
id: 'content-manager-list-view.add-to-release',
defaultMessage: 'Add to Release'
}),
dialog: {
type: 'modal',
title: formatMessage({
id: 'content-manager-list-view.add-to-release',
defaultMessage: 'Add to Release'
}),
content: ({ onClose })=>{
return /*#__PURE__*/ jsxRuntime.jsx(formik.Formik, {
onSubmit: async (values)=>{
const data = await handleSubmit(values);
if (data) {
return onClose();
}
},
validationSchema: ReleaseActionModal.RELEASE_ACTION_FORM_SCHEMA,
initialValues: ReleaseActionModal.INITIAL_VALUES,
children: ({ values, setFieldValue })=>/*#__PURE__*/ jsxRuntime.jsxs(formik.Form, {
children: [
releases?.length === 0 ? /*#__PURE__*/ jsxRuntime.jsx(ReleaseActionModal.NoReleases, {}) : /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Body, {
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingBottom: 6,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
required: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.select-label',
defaultMessage: 'Select a release'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelect, {
placeholder: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.select-placeholder',
defaultMessage: 'Select'
}),
onChange: (value)=>setFieldValue('releaseId', value),
value: values.releaseId,
children: releases?.map((release)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
value: release.id,
children: release.name
}, release.id))
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.action-type-label',
defaultMessage: 'What do you want to do with these entries?'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(ReleaseActionOptions.ReleaseActionOptions, {
selected: values.type,
handleChange: (e)=>setFieldValue('type', e.target.value),
name: "type"
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Footer, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
onClick: onClose,
variant: "tertiary",
name: "cancel",
children: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.cancel-button',
defaultMessage: 'Cancel'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
type: "submit",
disabled: !values.releaseId,
loading: isLoading,
children: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.continue-button',
defaultMessage: 'Continue'
})
})
]
})
]
})
});
}
}
};
};
exports.ReleaseAction = ReleaseAction;
//# sourceMappingURL=ReleaseAction.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,199 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import 'react';
import { useNotification, useAPIErrorHandler, useQueryParams, useRBAC, isFetchError } from '@strapi/admin/strapi-admin';
import { Modal, Flex, Box, Field, SingleSelect, SingleSelectOption, Button } from '@strapi/design-system';
import { Formik, Form } from 'formik';
import { useIntl } from 'react-intl';
import { PERMISSIONS } from '../constants.mjs';
import { useGetReleasesQuery, useCreateManyReleaseActionsMutation } from '../services/release.mjs';
import { RELEASE_ACTION_FORM_SCHEMA, INITIAL_VALUES, NoReleases } from './ReleaseActionModal.mjs';
import { ReleaseActionOptions } from './ReleaseActionOptions.mjs';
const getContentPermissions = (subject)=>{
const permissions = {
publish: [
{
action: 'plugin::content-manager.explorer.publish',
subject,
id: '',
actionParameters: {},
properties: {},
conditions: []
}
]
};
return permissions;
};
const ReleaseAction = ({ documents, model })=>{
const { formatMessage } = useIntl();
const { toggleNotification } = useNotification();
const { formatAPIError } = useAPIErrorHandler();
const [{ query }] = useQueryParams();
const contentPermissions = getContentPermissions(model);
const { allowedActions: { canPublish } } = useRBAC(contentPermissions);
const { allowedActions: { canCreate } } = useRBAC(PERMISSIONS);
// Get all the releases not published
const response = useGetReleasesQuery();
const releases = response.data?.data;
const [createManyReleaseActions, { isLoading }] = useCreateManyReleaseActionsMutation();
const documentIds = documents.map((doc)=>doc.documentId);
const handleSubmit = async (values)=>{
const locale = query.plugins?.i18n?.locale;
const releaseActionEntries = documentIds.map((entryDocumentId)=>({
type: values.type,
contentType: model,
entryDocumentId,
locale
}));
const response = await createManyReleaseActions({
body: releaseActionEntries,
params: {
releaseId: values.releaseId
}
});
if ('data' in response) {
// Handle success
const notificationMessage = formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.notification.success.message',
defaultMessage: '{entriesAlreadyInRelease} out of {totalEntries} entries were already in the release.'
}, {
entriesAlreadyInRelease: response.data.meta.entriesAlreadyInRelease,
totalEntries: response.data.meta.totalEntries
});
const notification = {
type: 'success',
title: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.notification.success.title',
defaultMessage: 'Successfully added to release.'
}, {
entriesAlreadyInRelease: response.data.meta.entriesAlreadyInRelease,
totalEntries: response.data.meta.totalEntries
}),
message: response.data.meta.entriesAlreadyInRelease ? notificationMessage : ''
};
toggleNotification(notification);
return true;
}
if ('error' in response) {
if (isFetchError(response.error)) {
// Handle fetch error
toggleNotification({
type: 'warning',
message: formatAPIError(response.error)
});
} else {
// Handle generic error
toggleNotification({
type: 'warning',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
}
};
if (!canCreate || !canPublish) return null;
return {
actionType: 'release',
variant: 'tertiary',
label: formatMessage({
id: 'content-manager-list-view.add-to-release',
defaultMessage: 'Add to Release'
}),
dialog: {
type: 'modal',
title: formatMessage({
id: 'content-manager-list-view.add-to-release',
defaultMessage: 'Add to Release'
}),
content: ({ onClose })=>{
return /*#__PURE__*/ jsx(Formik, {
onSubmit: async (values)=>{
const data = await handleSubmit(values);
if (data) {
return onClose();
}
},
validationSchema: RELEASE_ACTION_FORM_SCHEMA,
initialValues: INITIAL_VALUES,
children: ({ values, setFieldValue })=>/*#__PURE__*/ jsxs(Form, {
children: [
releases?.length === 0 ? /*#__PURE__*/ jsx(NoReleases, {}) : /*#__PURE__*/ jsx(Modal.Body, {
children: /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "stretch",
gap: 2,
children: [
/*#__PURE__*/ jsx(Box, {
paddingBottom: 6,
children: /*#__PURE__*/ jsxs(Field.Root, {
required: true,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.select-label',
defaultMessage: 'Select a release'
})
}),
/*#__PURE__*/ jsx(SingleSelect, {
placeholder: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.select-placeholder',
defaultMessage: 'Select'
}),
onChange: (value)=>setFieldValue('releaseId', value),
value: values.releaseId,
children: releases?.map((release)=>/*#__PURE__*/ jsx(SingleSelectOption, {
value: release.id,
children: release.name
}, release.id))
})
]
})
}),
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.action-type-label',
defaultMessage: 'What do you want to do with these entries?'
})
}),
/*#__PURE__*/ jsx(ReleaseActionOptions, {
selected: values.type,
handleChange: (e)=>setFieldValue('type', e.target.value),
name: "type"
})
]
})
}),
/*#__PURE__*/ jsxs(Modal.Footer, {
children: [
/*#__PURE__*/ jsx(Button, {
onClick: onClose,
variant: "tertiary",
name: "cancel",
children: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.cancel-button',
defaultMessage: 'Cancel'
})
}),
/*#__PURE__*/ jsx(Button, {
type: "submit",
disabled: !values.releaseId,
loading: isLoading,
children: formatMessage({
id: 'content-releases.content-manager-list-view.add-to-release.continue-button',
defaultMessage: 'Continue'
})
})
]
})
]
})
});
}
}
};
};
export { ReleaseAction };
//# sourceMappingURL=ReleaseAction.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,243 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var styledComponents = require('styled-components');
var constants = require('../constants.js');
var release = require('../services/release.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const StyledMenuItem = styledComponents.styled(designSystem.Menu.Item)`
&:hover {
background: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}100`]};
svg {
fill: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}600`]};
}
a {
color: ${({ theme })=>theme.colors.neutral800};
}
}
svg {
color: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}500`]};
}
span {
color: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}800`]};
}
span,
a {
width: 100%;
}
`;
const DeleteReleaseActionItem = ({ releaseId, actionId })=>{
const { formatMessage } = reactIntl.useIntl();
const { toggleNotification } = strapiAdmin.useNotification();
const { formatAPIError } = strapiAdmin.useAPIErrorHandler();
const [deleteReleaseAction] = release.useDeleteReleaseActionMutation();
const { allowedActions: { canDeleteAction } } = strapiAdmin.useRBAC(constants.PERMISSIONS);
const handleDeleteAction = async ()=>{
const response = await deleteReleaseAction({
params: {
releaseId,
actionId
}
});
if ('data' in response) {
// Handle success
toggleNotification({
type: 'success',
message: formatMessage({
id: 'content-releases.content-manager-edit-view.remove-from-release.notification.success',
defaultMessage: 'Entry removed from release'
})
});
return;
}
if ('error' in response) {
if (strapiAdmin.isFetchError(response.error)) {
// Handle fetch error
toggleNotification({
type: 'danger',
message: formatAPIError(response.error)
});
} else {
// Handle generic error
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
}
};
if (!canDeleteAction) {
return null;
}
return /*#__PURE__*/ jsxRuntime.jsx(StyledMenuItem, {
$variant: "danger",
onSelect: handleDeleteAction,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(icons.Cross, {
width: "1.6rem",
height: "1.6rem"
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "danger600",
variant: "omega",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.remove-from-release',
defaultMessage: 'Remove from release'
})
})
]
})
});
};
const ReleaseActionEntryLinkItem = ({ contentTypeUid, documentId, locale })=>{
const { formatMessage } = reactIntl.useIntl();
const userPermissions = strapiAdmin.useAuth('ReleaseActionEntryLinkItem', (state)=>state.permissions);
// Confirm user has permissions to access the entry for the given locale
const canUpdateEntryForLocale = React__namespace.useMemo(()=>{
const updatePermissions = userPermissions.find((permission)=>permission.subject === contentTypeUid && permission.action === 'plugin::content-manager.explorer.update');
if (!updatePermissions) {
return false;
}
return Boolean(!locale || updatePermissions.properties?.locales?.includes(locale));
}, [
contentTypeUid,
locale,
userPermissions
]);
const { allowedActions: { canUpdate: canUpdateContentType } } = strapiAdmin.useRBAC({
updateContentType: [
{
action: 'plugin::content-manager.explorer.update',
subject: contentTypeUid
}
]
});
if (!canUpdateContentType || !canUpdateEntryForLocale) {
return null;
}
return /*#__PURE__*/ jsxRuntime.jsx(StyledMenuItem, {
/* @ts-expect-error inference isn't working in DS */ tag: reactRouterDom.NavLink,
isLink: true,
to: {
pathname: `/content-manager/collection-types/${contentTypeUid}/${documentId}`,
search: locale && `?plugins[i18n][locale]=${locale}`
},
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(icons.Pencil, {
width: "1.6rem",
height: "1.6rem"
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "omega",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.edit-entry',
defaultMessage: 'Edit entry'
})
})
]
})
});
};
const EditReleaseItem = ({ releaseId })=>{
const { formatMessage } = reactIntl.useIntl();
return /* @ts-expect-error inference isn't working in DS */ /*#__PURE__*/ jsxRuntime.jsx(StyledMenuItem, {
tag: reactRouterDom.NavLink,
isLink: true,
to: `/plugins/content-releases/${releaseId}`,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(icons.Pencil, {
width: "1.6rem",
height: "1.6rem"
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "neutral800",
variant: "omega",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.edit-release',
defaultMessage: 'Edit release'
})
})
]
})
});
};
const Root = ({ children })=>{
const { formatMessage } = reactIntl.useIntl();
const { allowedActions } = strapiAdmin.useRBAC(constants.PERMISSIONS);
return(// A user can access the dropdown if they have permissions to delete a release-action OR update a release
allowedActions.canDeleteAction || allowedActions.canUpdate ? /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Menu.Root, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(StyledMoreButton, {
variant: "tertiary",
endIcon: null,
paddingLeft: "7px",
paddingRight: "7px",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.AccessibleIcon, {
label: formatMessage({
id: 'content-releases.content-manager-edit-view.release-action-menu',
defaultMessage: 'Release action options'
}),
children: /*#__PURE__*/ jsxRuntime.jsx(icons.More, {})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Menu.Content, {
top: 1,
popoverPlacement: "bottom-end",
children: children
})
]
}) : null);
};
const StyledMoreButton = styledComponents.styled(designSystem.Menu.Trigger)`
& > span {
display: flex;
}
`;
const ReleaseActionMenu = {
Root,
EditReleaseItem,
DeleteReleaseActionItem,
ReleaseActionEntryLinkItem
};
exports.ReleaseActionMenu = ReleaseActionMenu;
//# sourceMappingURL=ReleaseActionMenu.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,222 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import * as React from 'react';
import { useNotification, useAPIErrorHandler, useRBAC, useAuth, isFetchError } from '@strapi/admin/strapi-admin';
import { Menu, Flex, Typography, AccessibleIcon } from '@strapi/design-system';
import { Cross, Pencil, More } from '@strapi/icons';
import { useIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import { styled } from 'styled-components';
import { PERMISSIONS } from '../constants.mjs';
import { useDeleteReleaseActionMutation } from '../services/release.mjs';
const StyledMenuItem = styled(Menu.Item)`
&:hover {
background: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}100`]};
svg {
fill: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}600`]};
}
a {
color: ${({ theme })=>theme.colors.neutral800};
}
}
svg {
color: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}500`]};
}
span {
color: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}800`]};
}
span,
a {
width: 100%;
}
`;
const DeleteReleaseActionItem = ({ releaseId, actionId })=>{
const { formatMessage } = useIntl();
const { toggleNotification } = useNotification();
const { formatAPIError } = useAPIErrorHandler();
const [deleteReleaseAction] = useDeleteReleaseActionMutation();
const { allowedActions: { canDeleteAction } } = useRBAC(PERMISSIONS);
const handleDeleteAction = async ()=>{
const response = await deleteReleaseAction({
params: {
releaseId,
actionId
}
});
if ('data' in response) {
// Handle success
toggleNotification({
type: 'success',
message: formatMessage({
id: 'content-releases.content-manager-edit-view.remove-from-release.notification.success',
defaultMessage: 'Entry removed from release'
})
});
return;
}
if ('error' in response) {
if (isFetchError(response.error)) {
// Handle fetch error
toggleNotification({
type: 'danger',
message: formatAPIError(response.error)
});
} else {
// Handle generic error
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
}
};
if (!canDeleteAction) {
return null;
}
return /*#__PURE__*/ jsx(StyledMenuItem, {
$variant: "danger",
onSelect: handleDeleteAction,
children: /*#__PURE__*/ jsxs(Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsx(Cross, {
width: "1.6rem",
height: "1.6rem"
}),
/*#__PURE__*/ jsx(Typography, {
textColor: "danger600",
variant: "omega",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.remove-from-release',
defaultMessage: 'Remove from release'
})
})
]
})
});
};
const ReleaseActionEntryLinkItem = ({ contentTypeUid, documentId, locale })=>{
const { formatMessage } = useIntl();
const userPermissions = useAuth('ReleaseActionEntryLinkItem', (state)=>state.permissions);
// Confirm user has permissions to access the entry for the given locale
const canUpdateEntryForLocale = React.useMemo(()=>{
const updatePermissions = userPermissions.find((permission)=>permission.subject === contentTypeUid && permission.action === 'plugin::content-manager.explorer.update');
if (!updatePermissions) {
return false;
}
return Boolean(!locale || updatePermissions.properties?.locales?.includes(locale));
}, [
contentTypeUid,
locale,
userPermissions
]);
const { allowedActions: { canUpdate: canUpdateContentType } } = useRBAC({
updateContentType: [
{
action: 'plugin::content-manager.explorer.update',
subject: contentTypeUid
}
]
});
if (!canUpdateContentType || !canUpdateEntryForLocale) {
return null;
}
return /*#__PURE__*/ jsx(StyledMenuItem, {
/* @ts-expect-error inference isn't working in DS */ tag: NavLink,
isLink: true,
to: {
pathname: `/content-manager/collection-types/${contentTypeUid}/${documentId}`,
search: locale && `?plugins[i18n][locale]=${locale}`
},
children: /*#__PURE__*/ jsxs(Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsx(Pencil, {
width: "1.6rem",
height: "1.6rem"
}),
/*#__PURE__*/ jsx(Typography, {
variant: "omega",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.edit-entry',
defaultMessage: 'Edit entry'
})
})
]
})
});
};
const EditReleaseItem = ({ releaseId })=>{
const { formatMessage } = useIntl();
return /* @ts-expect-error inference isn't working in DS */ /*#__PURE__*/ jsx(StyledMenuItem, {
tag: NavLink,
isLink: true,
to: `/plugins/content-releases/${releaseId}`,
children: /*#__PURE__*/ jsxs(Flex, {
gap: 2,
children: [
/*#__PURE__*/ jsx(Pencil, {
width: "1.6rem",
height: "1.6rem"
}),
/*#__PURE__*/ jsx(Typography, {
textColor: "neutral800",
variant: "omega",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.edit-release',
defaultMessage: 'Edit release'
})
})
]
})
});
};
const Root = ({ children })=>{
const { formatMessage } = useIntl();
const { allowedActions } = useRBAC(PERMISSIONS);
return(// A user can access the dropdown if they have permissions to delete a release-action OR update a release
allowedActions.canDeleteAction || allowedActions.canUpdate ? /*#__PURE__*/ jsxs(Menu.Root, {
children: [
/*#__PURE__*/ jsx(StyledMoreButton, {
variant: "tertiary",
endIcon: null,
paddingLeft: "7px",
paddingRight: "7px",
children: /*#__PURE__*/ jsx(AccessibleIcon, {
label: formatMessage({
id: 'content-releases.content-manager-edit-view.release-action-menu',
defaultMessage: 'Release action options'
}),
children: /*#__PURE__*/ jsx(More, {})
})
}),
/*#__PURE__*/ jsx(Menu.Content, {
top: 1,
popoverPlacement: "bottom-end",
children: children
})
]
}) : null);
};
const StyledMoreButton = styled(Menu.Trigger)`
& > span {
display: flex;
}
`;
const ReleaseActionMenu = {
Root,
EditReleaseItem,
DeleteReleaseActionItem,
ReleaseActionEntryLinkItem
};
export { ReleaseActionMenu };
//# sourceMappingURL=ReleaseActionMenu.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,268 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var strapiAdmin$1 = require('@strapi/content-manager/strapi-admin');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
var symbols = require('@strapi/icons/symbols');
var formik = require('formik');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var yup = require('yup');
var constants = require('../constants.js');
var release = require('../services/release.js');
var ReleaseActionOptions = require('./ReleaseActionOptions.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var yup__namespace = /*#__PURE__*/_interopNamespaceDefault(yup);
/* -------------------------------------------------------------------------------------------------
* AddActionToReleaseModal
* -----------------------------------------------------------------------------------------------*/ const RELEASE_ACTION_FORM_SCHEMA = yup__namespace.object().shape({
type: yup__namespace.string().oneOf([
'publish',
'unpublish'
]).required(),
releaseId: yup__namespace.string().required()
});
const INITIAL_VALUES = {
type: 'publish',
releaseId: ''
};
const NoReleases = ()=>{
const { formatMessage } = reactIntl.useIntl();
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.EmptyStateLayout, {
icon: /*#__PURE__*/ jsxRuntime.jsx(symbols.EmptyDocuments, {
width: "16rem"
}),
content: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.no-releases-message',
defaultMessage: 'No available releases. Open the list of releases and create a new one from there.'
}),
action: /*#__PURE__*/ jsxRuntime.jsx(designSystem.LinkButton, {
to: {
pathname: '/plugins/content-releases'
},
tag: reactRouterDom.Link,
variant: "secondary",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.redirect-button',
defaultMessage: 'Open the list of releases'
})
}),
shadow: "none"
});
};
const AddActionToReleaseModal = ({ contentType, documentId, onInputChange, values })=>{
const { formatMessage } = reactIntl.useIntl();
const [{ query }] = strapiAdmin.useQueryParams();
const locale = query.plugins?.i18n?.locale;
// Get all 'pending' releases that do not have the entry attached
const response = release.useGetReleasesForEntryQuery({
contentType,
entryDocumentId: documentId,
hasEntryAttached: false,
locale
});
const releases = response.data?.data;
if (releases?.length === 0) {
return /*#__PURE__*/ jsxRuntime.jsx(NoReleases, {});
}
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 2,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingBottom: 6,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
required: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.select-label',
defaultMessage: 'Select a release'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelect, {
required: true,
placeholder: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.select-placeholder',
defaultMessage: 'Select'
}),
name: "releaseId",
onChange: (value)=>onInputChange('releaseId', value),
value: values.releaseId,
children: releases?.map((release)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
value: release.id,
children: release.name
}, release.id))
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.action-type-label',
defaultMessage: 'What do you want to do with this entry?'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(ReleaseActionOptions.ReleaseActionOptions, {
selected: values.type,
handleChange: (e)=>onInputChange('type', e.target.value),
name: "type"
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* ReleaseActionModalForm
* -----------------------------------------------------------------------------------------------*/ const ReleaseActionModalForm = ({ documentId, document, model, collectionType })=>{
const { formatMessage } = reactIntl.useIntl();
const { allowedActions } = strapiAdmin.useRBAC(constants.PERMISSIONS);
const { canCreateAction } = allowedActions;
const [createReleaseAction, { isLoading }] = release.useCreateReleaseActionMutation();
const { toggleNotification } = strapiAdmin.useNotification();
const { formatAPIError } = strapiAdmin.useAPIErrorHandler();
const [{ query }] = strapiAdmin.useQueryParams();
const locale = query.plugins?.i18n?.locale;
const handleSubmit = async (e, onClose)=>{
try {
await formik$1.handleSubmit(e);
onClose();
} catch (error) {
if (strapiAdmin.isFetchError(error)) {
// Handle axios error
toggleNotification({
type: 'danger',
message: formatAPIError(error)
});
} else {
// Handle generic error
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
}
};
const formik$1 = formik.useFormik({
initialValues: INITIAL_VALUES,
validationSchema: RELEASE_ACTION_FORM_SCHEMA,
onSubmit: async (values)=>{
if (collectionType === 'collection-types' && !documentId) {
throw new Error('Document id is required');
}
const response = await createReleaseAction({
body: {
type: values.type,
contentType: model,
entryDocumentId: documentId,
locale
},
params: {
releaseId: values.releaseId
}
});
if ('data' in response) {
// Handle success
toggleNotification({
type: 'success',
message: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.notification.success',
defaultMessage: 'Entry added to release'
})
});
return;
}
if ('error' in response) {
throw response.error;
}
}
});
const { edit: { options } } = strapiAdmin$1.unstable_useDocumentLayout(model);
// Project is not EE or contentType does not have draftAndPublish enabled
if (!window.strapi.isEE || !options?.draftAndPublish || !canCreateAction) {
return null;
}
if (collectionType === 'collection-types' && (!documentId || documentId === 'create')) {
return null;
}
return {
label: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release',
defaultMessage: 'Add to release'
}),
icon: /*#__PURE__*/ jsxRuntime.jsx(icons.PaperPlane, {}),
// Entry is creating so we don't want to allow adding it to a release
disabled: !document,
position: [
'panel',
'table-row'
],
dialog: {
type: 'modal',
title: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release',
defaultMessage: 'Add to release'
}),
content: /*#__PURE__*/ jsxRuntime.jsx(AddActionToReleaseModal, {
contentType: model,
documentId: documentId,
onInputChange: formik$1.setFieldValue,
values: formik$1.values
}),
footer: ({ onClose })=>/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Footer, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
onClick: onClose,
variant: "tertiary",
name: "cancel",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.cancel-button',
defaultMessage: 'Cancel'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
type: "submit",
// @ts-expect-error - formik ReactEvent types don't match button onClick types as they expect a MouseEvent
onClick: (e)=>handleSubmit(e, onClose),
disabled: !formik$1.values.releaseId,
loading: isLoading,
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.continue-button',
defaultMessage: 'Continue'
})
})
]
})
}
};
};
exports.INITIAL_VALUES = INITIAL_VALUES;
exports.NoReleases = NoReleases;
exports.RELEASE_ACTION_FORM_SCHEMA = RELEASE_ACTION_FORM_SCHEMA;
exports.ReleaseActionModalForm = ReleaseActionModalForm;
//# sourceMappingURL=ReleaseActionModal.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,244 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import 'react';
import { useRBAC, useNotification, useAPIErrorHandler, useQueryParams, isFetchError } from '@strapi/admin/strapi-admin';
import { unstable_useDocumentLayout } from '@strapi/content-manager/strapi-admin';
import { Modal, Button, EmptyStateLayout, LinkButton, Flex, Box, Field, SingleSelect, SingleSelectOption } from '@strapi/design-system';
import { PaperPlane } from '@strapi/icons';
import { EmptyDocuments } from '@strapi/icons/symbols';
import { useFormik } from 'formik';
import { useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import * as yup from 'yup';
import { PERMISSIONS } from '../constants.mjs';
import { useCreateReleaseActionMutation, useGetReleasesForEntryQuery } from '../services/release.mjs';
import { ReleaseActionOptions } from './ReleaseActionOptions.mjs';
/* -------------------------------------------------------------------------------------------------
* AddActionToReleaseModal
* -----------------------------------------------------------------------------------------------*/ const RELEASE_ACTION_FORM_SCHEMA = yup.object().shape({
type: yup.string().oneOf([
'publish',
'unpublish'
]).required(),
releaseId: yup.string().required()
});
const INITIAL_VALUES = {
type: 'publish',
releaseId: ''
};
const NoReleases = ()=>{
const { formatMessage } = useIntl();
return /*#__PURE__*/ jsx(EmptyStateLayout, {
icon: /*#__PURE__*/ jsx(EmptyDocuments, {
width: "16rem"
}),
content: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.no-releases-message',
defaultMessage: 'No available releases. Open the list of releases and create a new one from there.'
}),
action: /*#__PURE__*/ jsx(LinkButton, {
to: {
pathname: '/plugins/content-releases'
},
tag: Link,
variant: "secondary",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.redirect-button',
defaultMessage: 'Open the list of releases'
})
}),
shadow: "none"
});
};
const AddActionToReleaseModal = ({ contentType, documentId, onInputChange, values })=>{
const { formatMessage } = useIntl();
const [{ query }] = useQueryParams();
const locale = query.plugins?.i18n?.locale;
// Get all 'pending' releases that do not have the entry attached
const response = useGetReleasesForEntryQuery({
contentType,
entryDocumentId: documentId,
hasEntryAttached: false,
locale
});
const releases = response.data?.data;
if (releases?.length === 0) {
return /*#__PURE__*/ jsx(NoReleases, {});
}
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "stretch",
gap: 2,
children: [
/*#__PURE__*/ jsx(Box, {
paddingBottom: 6,
children: /*#__PURE__*/ jsxs(Field.Root, {
required: true,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.select-label',
defaultMessage: 'Select a release'
})
}),
/*#__PURE__*/ jsx(SingleSelect, {
required: true,
placeholder: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.select-placeholder',
defaultMessage: 'Select'
}),
name: "releaseId",
onChange: (value)=>onInputChange('releaseId', value),
value: values.releaseId,
children: releases?.map((release)=>/*#__PURE__*/ jsx(SingleSelectOption, {
value: release.id,
children: release.name
}, release.id))
})
]
})
}),
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.action-type-label',
defaultMessage: 'What do you want to do with this entry?'
})
}),
/*#__PURE__*/ jsx(ReleaseActionOptions, {
selected: values.type,
handleChange: (e)=>onInputChange('type', e.target.value),
name: "type"
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* ReleaseActionModalForm
* -----------------------------------------------------------------------------------------------*/ const ReleaseActionModalForm = ({ documentId, document, model, collectionType })=>{
const { formatMessage } = useIntl();
const { allowedActions } = useRBAC(PERMISSIONS);
const { canCreateAction } = allowedActions;
const [createReleaseAction, { isLoading }] = useCreateReleaseActionMutation();
const { toggleNotification } = useNotification();
const { formatAPIError } = useAPIErrorHandler();
const [{ query }] = useQueryParams();
const locale = query.plugins?.i18n?.locale;
const handleSubmit = async (e, onClose)=>{
try {
await formik.handleSubmit(e);
onClose();
} catch (error) {
if (isFetchError(error)) {
// Handle axios error
toggleNotification({
type: 'danger',
message: formatAPIError(error)
});
} else {
// Handle generic error
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
}
};
const formik = useFormik({
initialValues: INITIAL_VALUES,
validationSchema: RELEASE_ACTION_FORM_SCHEMA,
onSubmit: async (values)=>{
if (collectionType === 'collection-types' && !documentId) {
throw new Error('Document id is required');
}
const response = await createReleaseAction({
body: {
type: values.type,
contentType: model,
entryDocumentId: documentId,
locale
},
params: {
releaseId: values.releaseId
}
});
if ('data' in response) {
// Handle success
toggleNotification({
type: 'success',
message: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.notification.success',
defaultMessage: 'Entry added to release'
})
});
return;
}
if ('error' in response) {
throw response.error;
}
}
});
const { edit: { options } } = unstable_useDocumentLayout(model);
// Project is not EE or contentType does not have draftAndPublish enabled
if (!window.strapi.isEE || !options?.draftAndPublish || !canCreateAction) {
return null;
}
if (collectionType === 'collection-types' && (!documentId || documentId === 'create')) {
return null;
}
return {
label: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release',
defaultMessage: 'Add to release'
}),
icon: /*#__PURE__*/ jsx(PaperPlane, {}),
// Entry is creating so we don't want to allow adding it to a release
disabled: !document,
position: [
'panel',
'table-row'
],
dialog: {
type: 'modal',
title: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release',
defaultMessage: 'Add to release'
}),
content: /*#__PURE__*/ jsx(AddActionToReleaseModal, {
contentType: model,
documentId: documentId,
onInputChange: formik.setFieldValue,
values: formik.values
}),
footer: ({ onClose })=>/*#__PURE__*/ jsxs(Modal.Footer, {
children: [
/*#__PURE__*/ jsx(Button, {
onClick: onClose,
variant: "tertiary",
name: "cancel",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.cancel-button',
defaultMessage: 'Cancel'
})
}),
/*#__PURE__*/ jsx(Button, {
type: "submit",
// @ts-expect-error - formik ReactEvent types don't match button onClick types as they expect a MouseEvent
onClick: (e)=>handleSubmit(e, onClose),
disabled: !formik.values.releaseId,
loading: isLoading,
children: formatMessage({
id: 'content-releases.content-manager-edit-view.add-to-release.continue-button',
defaultMessage: 'Continue'
})
})
]
})
}
};
};
export { INITIAL_VALUES, NoReleases, RELEASE_ACTION_FORM_SCHEMA, ReleaseActionModalForm };
//# sourceMappingURL=ReleaseActionModal.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,104 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var designSystem = require('@strapi/design-system');
var styledComponents = require('styled-components');
const getBorderLeftRadiusValue = (actionType)=>{
return actionType === 'publish' ? 1 : 0;
};
const getBorderRightRadiusValue = (actionType)=>{
return actionType === 'publish' ? 0 : 1;
};
const FieldWrapper = styledComponents.styled(designSystem.Field.Root)`
border-top-left-radius: ${({ $actionType, theme })=>theme.spaces[getBorderLeftRadiusValue($actionType)]};
border-bottom-left-radius: ${({ $actionType, theme })=>theme.spaces[getBorderLeftRadiusValue($actionType)]};
border-top-right-radius: ${({ $actionType, theme })=>theme.spaces[getBorderRightRadiusValue($actionType)]};
border-bottom-right-radius: ${({ $actionType, theme })=>theme.spaces[getBorderRightRadiusValue($actionType)]};
> label {
color: inherit;
padding: ${({ theme })=>`${theme.spaces[2]} ${theme.spaces[3]}`};
text-align: center;
vertical-align: middle;
text-transform: capitalize;
}
&[data-checked='true'] {
color: ${({ theme, $actionType })=>$actionType === 'publish' ? theme.colors.primary700 : theme.colors.danger600};
background-color: ${({ theme, $actionType })=>$actionType === 'publish' ? theme.colors.primary100 : theme.colors.danger100};
border-color: ${({ theme, $actionType })=>$actionType === 'publish' ? theme.colors.primary700 : theme.colors.danger600};
}
&[data-checked='false'] {
border-left: ${({ $actionType })=>$actionType === 'unpublish' && 'none'};
border-right: ${({ $actionType })=>$actionType === 'publish' && 'none'};
}
&[data-checked='false'][data-disabled='false']:hover {
color: ${({ theme })=>theme.colors.neutral700};
background-color: ${({ theme })=>theme.colors.neutral100};
border-color: ${({ theme })=>theme.colors.neutral200};
& > label {
cursor: pointer;
}
}
&[data-disabled='true'] {
color: ${({ theme })=>theme.colors.neutral600};
background-color: ${({ theme })=>theme.colors.neutral150};
border-color: ${({ theme })=>theme.colors.neutral300};
}
`;
const ActionOption = ({ selected, actionType, handleChange, name, disabled = false })=>{
return /*#__PURE__*/ jsxRuntime.jsx(FieldWrapper, {
$actionType: actionType,
background: "primary0",
borderColor: "neutral200",
color: selected === actionType ? 'primary600' : 'neutral600',
position: "relative",
cursor: "pointer",
"data-checked": selected === actionType,
"data-disabled": disabled && selected !== actionType,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Label, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Input, {
type: "radio",
name: name,
checked: selected === actionType,
onChange: handleChange,
value: actionType,
disabled: disabled
})
}),
actionType
]
})
});
};
const ReleaseActionOptions = ({ selected, handleChange, name, disabled = false })=>{
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(ActionOption, {
actionType: "publish",
selected: selected,
handleChange: handleChange,
name: name,
disabled: disabled
}),
/*#__PURE__*/ jsxRuntime.jsx(ActionOption, {
actionType: "unpublish",
selected: selected,
handleChange: handleChange,
name: name,
disabled: disabled
})
]
});
};
exports.ReleaseActionOptions = ReleaseActionOptions;
//# sourceMappingURL=ReleaseActionOptions.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,102 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import 'react';
import { Field, Flex, VisuallyHidden } from '@strapi/design-system';
import { styled } from 'styled-components';
const getBorderLeftRadiusValue = (actionType)=>{
return actionType === 'publish' ? 1 : 0;
};
const getBorderRightRadiusValue = (actionType)=>{
return actionType === 'publish' ? 0 : 1;
};
const FieldWrapper = styled(Field.Root)`
border-top-left-radius: ${({ $actionType, theme })=>theme.spaces[getBorderLeftRadiusValue($actionType)]};
border-bottom-left-radius: ${({ $actionType, theme })=>theme.spaces[getBorderLeftRadiusValue($actionType)]};
border-top-right-radius: ${({ $actionType, theme })=>theme.spaces[getBorderRightRadiusValue($actionType)]};
border-bottom-right-radius: ${({ $actionType, theme })=>theme.spaces[getBorderRightRadiusValue($actionType)]};
> label {
color: inherit;
padding: ${({ theme })=>`${theme.spaces[2]} ${theme.spaces[3]}`};
text-align: center;
vertical-align: middle;
text-transform: capitalize;
}
&[data-checked='true'] {
color: ${({ theme, $actionType })=>$actionType === 'publish' ? theme.colors.primary700 : theme.colors.danger600};
background-color: ${({ theme, $actionType })=>$actionType === 'publish' ? theme.colors.primary100 : theme.colors.danger100};
border-color: ${({ theme, $actionType })=>$actionType === 'publish' ? theme.colors.primary700 : theme.colors.danger600};
}
&[data-checked='false'] {
border-left: ${({ $actionType })=>$actionType === 'unpublish' && 'none'};
border-right: ${({ $actionType })=>$actionType === 'publish' && 'none'};
}
&[data-checked='false'][data-disabled='false']:hover {
color: ${({ theme })=>theme.colors.neutral700};
background-color: ${({ theme })=>theme.colors.neutral100};
border-color: ${({ theme })=>theme.colors.neutral200};
& > label {
cursor: pointer;
}
}
&[data-disabled='true'] {
color: ${({ theme })=>theme.colors.neutral600};
background-color: ${({ theme })=>theme.colors.neutral150};
border-color: ${({ theme })=>theme.colors.neutral300};
}
`;
const ActionOption = ({ selected, actionType, handleChange, name, disabled = false })=>{
return /*#__PURE__*/ jsx(FieldWrapper, {
$actionType: actionType,
background: "primary0",
borderColor: "neutral200",
color: selected === actionType ? 'primary600' : 'neutral600',
position: "relative",
cursor: "pointer",
"data-checked": selected === actionType,
"data-disabled": disabled && selected !== actionType,
children: /*#__PURE__*/ jsxs(Field.Label, {
children: [
/*#__PURE__*/ jsx(VisuallyHidden, {
children: /*#__PURE__*/ jsx(Field.Input, {
type: "radio",
name: name,
checked: selected === actionType,
onChange: handleChange,
value: actionType,
disabled: disabled
})
}),
actionType
]
})
});
};
const ReleaseActionOptions = ({ selected, handleChange, name, disabled = false })=>{
return /*#__PURE__*/ jsxs(Flex, {
children: [
/*#__PURE__*/ jsx(ActionOption, {
actionType: "publish",
selected: selected,
handleChange: handleChange,
name: name,
disabled: disabled
}),
/*#__PURE__*/ jsx(ActionOption, {
actionType: "unpublish",
selected: selected,
handleChange: handleChange,
name: name,
disabled: disabled
})
]
});
};
export { ReleaseActionOptions };
//# sourceMappingURL=ReleaseActionOptions.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,103 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var icons = require('@strapi/icons');
var reactIntl = require('react-intl');
var release = require('../services/release.js');
const useReleasesList = (contentTypeUid, documentId)=>{
const listViewData = strapiAdmin.useTable('ListView', (state)=>state.rows);
const documentIds = listViewData.map((entry)=>entry.documentId);
const [{ query }] = strapiAdmin.useQueryParams();
const locale = query?.plugins?.i18n?.locale || undefined;
const response = release.useGetMappedEntriesInReleasesQuery({
contentTypeUid,
documentIds,
locale
}, {
skip: !documentIds || !contentTypeUid || documentIds.length === 0
});
const mappedEntriesInReleases = response.data || {};
return mappedEntriesInReleases?.[documentId] || [];
};
const addColumnToTableHook = ({ displayedHeaders, layout })=>{
const { options } = layout;
if (!options?.draftAndPublish) {
return {
displayedHeaders,
layout
};
}
return {
displayedHeaders: [
...displayedHeaders,
{
searchable: false,
sortable: false,
name: 'releases',
label: {
id: 'content-releases.content-manager.list-view.releases.header',
defaultMessage: 'To be released in'
},
cellFormatter: (props, _, { model })=>/*#__PURE__*/ jsxRuntime.jsx(ReleaseListCell, {
...props,
model: model
})
}
],
layout
};
};
const ReleaseListCell = ({ documentId, model })=>{
const releases = useReleasesList(model, documentId);
const { formatMessage } = reactIntl.useIntl();
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Popover.Root, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "ghost",
onClick: (e)=>e.stopPropagation(),
// TODO: find a way in the DS to define the widht and height of the icon
endIcon: releases.length > 0 ? /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {
width: "1.2rem",
height: "1.2rem"
}) : null,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
style: {
maxWidth: '252px',
cursor: 'pointer'
},
textColor: "neutral800",
fontWeight: "regular",
children: releases.length > 0 ? formatMessage({
id: 'content-releases.content-manager.list-view.releases-number',
defaultMessage: '{number} {number, plural, one {release} other {releases}}'
}, {
number: releases.length
}) : '-'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Content, {
children: /*#__PURE__*/ jsxRuntime.jsx("ul", {
children: releases.map(({ id, name })=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
padding: 3,
tag: "li",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Link, {
href: `/admin/plugins/content-releases/${id}`,
isExternal: false,
children: name
})
}, id))
})
})
]
});
};
exports.ReleaseListCell = ReleaseListCell;
exports.addColumnToTableHook = addColumnToTableHook;
//# sourceMappingURL=ReleaseListCell.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,100 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import 'react';
import { useTable, useQueryParams } from '@strapi/admin/strapi-admin';
import { Popover, Button, Typography, Box, Link } from '@strapi/design-system';
import { CaretDown } from '@strapi/icons';
import { useIntl } from 'react-intl';
import { useGetMappedEntriesInReleasesQuery } from '../services/release.mjs';
const useReleasesList = (contentTypeUid, documentId)=>{
const listViewData = useTable('ListView', (state)=>state.rows);
const documentIds = listViewData.map((entry)=>entry.documentId);
const [{ query }] = useQueryParams();
const locale = query?.plugins?.i18n?.locale || undefined;
const response = useGetMappedEntriesInReleasesQuery({
contentTypeUid,
documentIds,
locale
}, {
skip: !documentIds || !contentTypeUid || documentIds.length === 0
});
const mappedEntriesInReleases = response.data || {};
return mappedEntriesInReleases?.[documentId] || [];
};
const addColumnToTableHook = ({ displayedHeaders, layout })=>{
const { options } = layout;
if (!options?.draftAndPublish) {
return {
displayedHeaders,
layout
};
}
return {
displayedHeaders: [
...displayedHeaders,
{
searchable: false,
sortable: false,
name: 'releases',
label: {
id: 'content-releases.content-manager.list-view.releases.header',
defaultMessage: 'To be released in'
},
cellFormatter: (props, _, { model })=>/*#__PURE__*/ jsx(ReleaseListCell, {
...props,
model: model
})
}
],
layout
};
};
const ReleaseListCell = ({ documentId, model })=>{
const releases = useReleasesList(model, documentId);
const { formatMessage } = useIntl();
return /*#__PURE__*/ jsxs(Popover.Root, {
children: [
/*#__PURE__*/ jsx(Popover.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
variant: "ghost",
onClick: (e)=>e.stopPropagation(),
// TODO: find a way in the DS to define the widht and height of the icon
endIcon: releases.length > 0 ? /*#__PURE__*/ jsx(CaretDown, {
width: "1.2rem",
height: "1.2rem"
}) : null,
children: /*#__PURE__*/ jsx(Typography, {
style: {
maxWidth: '252px',
cursor: 'pointer'
},
textColor: "neutral800",
fontWeight: "regular",
children: releases.length > 0 ? formatMessage({
id: 'content-releases.content-manager.list-view.releases-number',
defaultMessage: '{number} {number, plural, one {release} other {releases}}'
}, {
number: releases.length
}) : '-'
})
})
}),
/*#__PURE__*/ jsx(Popover.Content, {
children: /*#__PURE__*/ jsx("ul", {
children: releases.map(({ id, name })=>/*#__PURE__*/ jsx(Box, {
padding: 3,
tag: "li",
children: /*#__PURE__*/ jsx(Link, {
href: `/admin/plugins/content-releases/${id}`,
isExternal: false,
children: name
})
}, id))
})
})
]
});
};
export { ReleaseListCell, addColumnToTableHook };
//# sourceMappingURL=ReleaseListCell.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,323 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var designSystem = require('@strapi/design-system');
var dateFns = require('date-fns');
var dateFnsTz = require('date-fns-tz');
var formik = require('formik');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var pluginId = require('../pluginId.js');
var time = require('../utils/time.js');
var schemas = require('../validation/schemas.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const ReleaseModal = ({ handleClose, open, handleSubmit, initialValues, isLoading = false })=>{
const { formatMessage } = reactIntl.useIntl();
const { pathname } = reactRouterDom.useLocation();
const isCreatingRelease = pathname === `/plugins/${pluginId.pluginId}`;
// Set default first timezone from the list if no system timezone detected
const { timezoneList, systemTimezone = {
value: 'UTC+00:00-Africa/Abidjan '
} } = time.getTimezones(initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : new Date());
/**
* Generate scheduled time using selected date, time and timezone
*/ const getScheduledTimestamp = (values)=>{
const { date, time, timezone } = values;
if (!date || !time || !timezone) return null;
const timezoneWithoutOffset = timezone.split('&')[1];
return dateFnsTz.zonedTimeToUtc(`${date} ${time}`, timezoneWithoutOffset);
};
/**
* Get timezone with offset to show the selected value in the dropdown
*/ const getTimezoneWithOffset = ()=>{
const currentTimezone = timezoneList.find((timezone)=>timezone.value.split('&')[1] === initialValues.timezone);
return currentTimezone?.value || systemTimezone.value;
};
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Root, {
open: open,
onOpenChange: handleClose,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Content, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Header, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Title, {
children: formatMessage({
id: 'content-releases.modal.title',
defaultMessage: '{isCreatingRelease, select, true {New release} other {Edit release}}'
}, {
isCreatingRelease: isCreatingRelease
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(formik.Formik, {
onSubmit: (values)=>{
handleSubmit({
...values,
timezone: values.timezone ? values.timezone.split('&')[1] : null,
scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
});
},
initialValues: {
...initialValues,
timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
},
validationSchema: schemas.RELEASE_SCHEMA,
validateOnChange: false,
children: ({ values, errors, handleChange, setFieldValue })=>{
return /*#__PURE__*/ jsxRuntime.jsxs(formik.Form, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Body, {
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
name: "name",
error: errors.name && formatMessage({
id: errors.name,
defaultMessage: errors.name
}),
required: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'content-releases.modal.form.input.label.release-name',
defaultMessage: 'Name'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.TextInput, {
value: values.name,
onChange: handleChange
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
width: "max-content",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Checkbox, {
name: "isScheduled",
checked: values.isScheduled,
onCheckedChange: (checked)=>{
setFieldValue('isScheduled', checked);
if (!checked) {
// Clear scheduling info from a release on unchecking schedule release, which reset scheduling info in DB
setFieldValue('date', null);
setFieldValue('time', '');
setFieldValue('timezone', null);
} else {
// On ticking back schedule release date, time and timezone should be restored to the initial state
setFieldValue('date', initialValues.date);
setFieldValue('time', initialValues.time);
setFieldValue('timezone', initialValues.timezone ?? systemTimezone?.value);
}
},
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: values.isScheduled ? 'primary600' : 'neutral800',
fontWeight: values.isScheduled ? 'semiBold' : 'regular',
children: formatMessage({
id: 'modal.form.input.label.schedule-release',
defaultMessage: 'Schedule release'
})
})
})
}),
values.isScheduled && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
gap: 4,
alignItems: "start",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
width: "100%",
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
name: "date",
error: errors.date && formatMessage({
id: errors.date,
defaultMessage: errors.date
}),
required: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'content-releases.modal.form.input.label.date',
defaultMessage: 'Date'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.DatePicker, {
onChange: (date)=>{
const isoFormatDate = date ? dateFns.formatISO(date, {
representation: 'date'
}) : null;
setFieldValue('date', isoFormatDate);
},
clearLabel: formatMessage({
id: 'content-releases.modal.form.input.clearLabel',
defaultMessage: 'Clear'
}),
onClear: ()=>{
setFieldValue('date', null);
},
value: values.date ? new Date(values.date) : new Date(),
minDate: dateFnsTz.utcToZonedTime(new Date(), values.timezone.split('&')[1])
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
width: "100%",
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
name: "time",
error: errors.time && formatMessage({
id: errors.time,
defaultMessage: errors.time
}),
required: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'content-releases.modal.form.input.label.time',
defaultMessage: 'Time'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.TimePicker, {
onChange: (time)=>{
setFieldValue('time', time);
},
clearLabel: formatMessage({
id: 'content-releases.modal.form.input.clearLabel',
defaultMessage: 'Clear'
}),
onClear: ()=>{
setFieldValue('time', '');
},
value: values.time || undefined
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
]
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(TimezoneComponent, {
timezoneOptions: timezoneList
})
]
})
]
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Footer, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Close, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "tertiary",
name: "cancel",
children: formatMessage({
id: 'cancel',
defaultMessage: 'Cancel'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
name: "submit",
loading: isLoading,
type: "submit",
children: formatMessage({
id: 'content-releases.modal.form.button.submit',
defaultMessage: '{isCreatingRelease, select, true {Continue} other {Save}}'
}, {
isCreatingRelease: isCreatingRelease
})
})
]
})
]
});
}
})
]
})
});
};
const TimezoneComponent = ({ timezoneOptions })=>{
const { values, errors, setFieldValue } = formik.useFormikContext();
const { formatMessage } = reactIntl.useIntl();
const [timezoneList, setTimezoneList] = React__namespace.useState(timezoneOptions);
React__namespace.useEffect(()=>{
if (values.date) {
// Update the timezone offset which varies with DST based on the date selected
const { timezoneList } = time.getTimezones(new Date(values.date));
setTimezoneList(timezoneList);
const updatedTimezone = values.timezone && timezoneList.find((tz)=>tz.value.split('&')[1] === values.timezone.split('&')[1]);
if (updatedTimezone) {
setFieldValue('timezone', updatedTimezone.value);
}
}
}, [
setFieldValue,
values.date,
values.timezone
]);
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
name: "timezone",
error: errors.timezone && formatMessage({
id: errors.timezone,
defaultMessage: errors.timezone
}),
required: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: formatMessage({
id: 'content-releases.modal.form.input.label.timezone',
defaultMessage: 'Timezone'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Combobox, {
autocomplete: {
type: 'list',
filter: 'contains'
},
value: values.timezone || undefined,
textValue: values.timezone ? values.timezone.replace(/&/, ' ') : undefined,
onChange: (timezone)=>{
setFieldValue('timezone', timezone);
},
onTextValueChange: (timezone)=>{
setFieldValue('timezone', timezone);
},
onClear: ()=>{
setFieldValue('timezone', '');
},
children: timezoneList.map((timezone)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.ComboboxOption, {
value: timezone.value,
children: timezone.value.replace(/&/, ' ')
}, timezone.value))
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
]
});
};
exports.ReleaseModal = ReleaseModal;
//# sourceMappingURL=ReleaseModal.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,302 @@
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import * as React from 'react';
import { Modal, Flex, Field, TextInput, Box, Checkbox, Typography, DatePicker, TimePicker, Button, Combobox, ComboboxOption } from '@strapi/design-system';
import { formatISO } from 'date-fns';
import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
import { Formik, Form, useFormikContext } from 'formik';
import { useIntl } from 'react-intl';
import { useLocation } from 'react-router-dom';
import { pluginId } from '../pluginId.mjs';
import { getTimezones } from '../utils/time.mjs';
import { RELEASE_SCHEMA } from '../validation/schemas.mjs';
const ReleaseModal = ({ handleClose, open, handleSubmit, initialValues, isLoading = false })=>{
const { formatMessage } = useIntl();
const { pathname } = useLocation();
const isCreatingRelease = pathname === `/plugins/${pluginId}`;
// Set default first timezone from the list if no system timezone detected
const { timezoneList, systemTimezone = {
value: 'UTC+00:00-Africa/Abidjan '
} } = getTimezones(initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : new Date());
/**
* Generate scheduled time using selected date, time and timezone
*/ const getScheduledTimestamp = (values)=>{
const { date, time, timezone } = values;
if (!date || !time || !timezone) return null;
const timezoneWithoutOffset = timezone.split('&')[1];
return zonedTimeToUtc(`${date} ${time}`, timezoneWithoutOffset);
};
/**
* Get timezone with offset to show the selected value in the dropdown
*/ const getTimezoneWithOffset = ()=>{
const currentTimezone = timezoneList.find((timezone)=>timezone.value.split('&')[1] === initialValues.timezone);
return currentTimezone?.value || systemTimezone.value;
};
return /*#__PURE__*/ jsx(Modal.Root, {
open: open,
onOpenChange: handleClose,
children: /*#__PURE__*/ jsxs(Modal.Content, {
children: [
/*#__PURE__*/ jsx(Modal.Header, {
children: /*#__PURE__*/ jsx(Modal.Title, {
children: formatMessage({
id: 'content-releases.modal.title',
defaultMessage: '{isCreatingRelease, select, true {New release} other {Edit release}}'
}, {
isCreatingRelease: isCreatingRelease
})
})
}),
/*#__PURE__*/ jsx(Formik, {
onSubmit: (values)=>{
handleSubmit({
...values,
timezone: values.timezone ? values.timezone.split('&')[1] : null,
scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
});
},
initialValues: {
...initialValues,
timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
},
validationSchema: RELEASE_SCHEMA,
validateOnChange: false,
children: ({ values, errors, handleChange, setFieldValue })=>{
return /*#__PURE__*/ jsxs(Form, {
children: [
/*#__PURE__*/ jsx(Modal.Body, {
children: /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
children: [
/*#__PURE__*/ jsxs(Field.Root, {
name: "name",
error: errors.name && formatMessage({
id: errors.name,
defaultMessage: errors.name
}),
required: true,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'content-releases.modal.form.input.label.release-name',
defaultMessage: 'Name'
})
}),
/*#__PURE__*/ jsx(TextInput, {
value: values.name,
onChange: handleChange
}),
/*#__PURE__*/ jsx(Field.Error, {})
]
}),
/*#__PURE__*/ jsx(Box, {
width: "max-content",
children: /*#__PURE__*/ jsx(Checkbox, {
name: "isScheduled",
checked: values.isScheduled,
onCheckedChange: (checked)=>{
setFieldValue('isScheduled', checked);
if (!checked) {
// Clear scheduling info from a release on unchecking schedule release, which reset scheduling info in DB
setFieldValue('date', null);
setFieldValue('time', '');
setFieldValue('timezone', null);
} else {
// On ticking back schedule release date, time and timezone should be restored to the initial state
setFieldValue('date', initialValues.date);
setFieldValue('time', initialValues.time);
setFieldValue('timezone', initialValues.timezone ?? systemTimezone?.value);
}
},
children: /*#__PURE__*/ jsx(Typography, {
textColor: values.isScheduled ? 'primary600' : 'neutral800',
fontWeight: values.isScheduled ? 'semiBold' : 'regular',
children: formatMessage({
id: 'modal.form.input.label.schedule-release',
defaultMessage: 'Schedule release'
})
})
})
}),
values.isScheduled && /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsxs(Flex, {
gap: 4,
alignItems: "start",
children: [
/*#__PURE__*/ jsx(Box, {
width: "100%",
children: /*#__PURE__*/ jsxs(Field.Root, {
name: "date",
error: errors.date && formatMessage({
id: errors.date,
defaultMessage: errors.date
}),
required: true,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'content-releases.modal.form.input.label.date',
defaultMessage: 'Date'
})
}),
/*#__PURE__*/ jsx(DatePicker, {
onChange: (date)=>{
const isoFormatDate = date ? formatISO(date, {
representation: 'date'
}) : null;
setFieldValue('date', isoFormatDate);
},
clearLabel: formatMessage({
id: 'content-releases.modal.form.input.clearLabel',
defaultMessage: 'Clear'
}),
onClear: ()=>{
setFieldValue('date', null);
},
value: values.date ? new Date(values.date) : new Date(),
minDate: utcToZonedTime(new Date(), values.timezone.split('&')[1])
}),
/*#__PURE__*/ jsx(Field.Error, {})
]
})
}),
/*#__PURE__*/ jsx(Box, {
width: "100%",
children: /*#__PURE__*/ jsxs(Field.Root, {
name: "time",
error: errors.time && formatMessage({
id: errors.time,
defaultMessage: errors.time
}),
required: true,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'content-releases.modal.form.input.label.time',
defaultMessage: 'Time'
})
}),
/*#__PURE__*/ jsx(TimePicker, {
onChange: (time)=>{
setFieldValue('time', time);
},
clearLabel: formatMessage({
id: 'content-releases.modal.form.input.clearLabel',
defaultMessage: 'Clear'
}),
onClear: ()=>{
setFieldValue('time', '');
},
value: values.time || undefined
}),
/*#__PURE__*/ jsx(Field.Error, {})
]
})
})
]
}),
/*#__PURE__*/ jsx(TimezoneComponent, {
timezoneOptions: timezoneList
})
]
})
]
})
}),
/*#__PURE__*/ jsxs(Modal.Footer, {
children: [
/*#__PURE__*/ jsx(Modal.Close, {
children: /*#__PURE__*/ jsx(Button, {
variant: "tertiary",
name: "cancel",
children: formatMessage({
id: 'cancel',
defaultMessage: 'Cancel'
})
})
}),
/*#__PURE__*/ jsx(Button, {
name: "submit",
loading: isLoading,
type: "submit",
children: formatMessage({
id: 'content-releases.modal.form.button.submit',
defaultMessage: '{isCreatingRelease, select, true {Continue} other {Save}}'
}, {
isCreatingRelease: isCreatingRelease
})
})
]
})
]
});
}
})
]
})
});
};
const TimezoneComponent = ({ timezoneOptions })=>{
const { values, errors, setFieldValue } = useFormikContext();
const { formatMessage } = useIntl();
const [timezoneList, setTimezoneList] = React.useState(timezoneOptions);
React.useEffect(()=>{
if (values.date) {
// Update the timezone offset which varies with DST based on the date selected
const { timezoneList } = getTimezones(new Date(values.date));
setTimezoneList(timezoneList);
const updatedTimezone = values.timezone && timezoneList.find((tz)=>tz.value.split('&')[1] === values.timezone.split('&')[1]);
if (updatedTimezone) {
setFieldValue('timezone', updatedTimezone.value);
}
}
}, [
setFieldValue,
values.date,
values.timezone
]);
return /*#__PURE__*/ jsxs(Field.Root, {
name: "timezone",
error: errors.timezone && formatMessage({
id: errors.timezone,
defaultMessage: errors.timezone
}),
required: true,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: formatMessage({
id: 'content-releases.modal.form.input.label.timezone',
defaultMessage: 'Timezone'
})
}),
/*#__PURE__*/ jsx(Combobox, {
autocomplete: {
type: 'list',
filter: 'contains'
},
value: values.timezone || undefined,
textValue: values.timezone ? values.timezone.replace(/&/, ' ') : undefined,
onChange: (timezone)=>{
setFieldValue('timezone', timezone);
},
onTextValueChange: (timezone)=>{
setFieldValue('timezone', timezone);
},
onClear: ()=>{
setFieldValue('timezone', '');
},
children: timezoneList.map((timezone)=>/*#__PURE__*/ jsx(ComboboxOption, {
value: timezone.value,
children: timezone.value.replace(/&/, ' ')
}, timezone.value))
}),
/*#__PURE__*/ jsx(Field.Error, {})
]
});
};
export { ReleaseModal };
//# sourceMappingURL=ReleaseModal.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,138 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var strapiAdmin$1 = require('@strapi/content-manager/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactIntl = require('react-intl');
var constants = require('../constants.js');
var release = require('../services/release.js');
var time = require('../utils/time.js');
var ReleaseActionMenu = require('./ReleaseActionMenu.js');
const Panel = ({ model, document, documentId, collectionType })=>{
const [{ query }] = strapiAdmin.useQueryParams();
const locale = query.plugins?.i18n?.locale;
const { edit: { options } } = strapiAdmin$1.unstable_useDocumentLayout(model);
const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
const { allowedActions } = strapiAdmin.useRBAC(constants.PERMISSIONS);
const { canRead, canDeleteAction } = allowedActions;
const response = release.useGetReleasesForEntryQuery({
contentType: model,
entryDocumentId: documentId,
locale,
hasEntryAttached: true
}, {
skip: !document
});
const releases = response.data?.data;
const getReleaseColorVariant = (actionType, shade)=>{
if (actionType === 'unpublish') {
return `secondary${shade}`;
}
return `success${shade}`;
};
// Project is not EE or contentType does not have draftAndPublish enabled
if (!window.strapi.isEE || !options?.draftAndPublish || !canRead) {
return null;
}
if (collectionType === 'collection-types' && (!documentId || documentId === 'create')) {
return null;
}
if (!releases || releases.length === 0) {
return null;
}
return {
title: formatMessage({
id: 'content-releases.plugin.name',
defaultMessage: 'Releases'
}),
content: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 3,
width: "100%",
children: releases?.map((release)=>/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "start",
borderWidth: "1px",
borderStyle: "solid",
borderColor: getReleaseColorVariant(release.actions[0].type, '200'),
overflow: "hidden",
hasRadius: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingTop: 3,
paddingBottom: 3,
paddingLeft: 4,
paddingRight: 4,
background: getReleaseColorVariant(release.actions[0].type, '100'),
width: "100%",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
fontSize: 1,
variant: "pi",
textColor: getReleaseColorVariant(release.actions[0].type, '600'),
children: formatMessage({
id: 'content-releases.content-manager-edit-view.list-releases.title',
defaultMessage: '{isPublish, select, true {Will be published in} other {Will be unpublished in}}'
}, {
isPublish: release.actions[0].type === 'publish'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
padding: 4,
direction: "column",
gap: 2,
width: "100%",
alignItems: "flex-start",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
fontSize: 2,
fontWeight: "bold",
variant: "omega",
textColor: "neutral700",
children: release.name
}),
release.scheduledAt && release.timezone && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "pi",
textColor: "neutral600",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.scheduled.date',
defaultMessage: '{date} at {time} ({offset})'
}, {
date: formatDate(new Date(release.scheduledAt), {
day: '2-digit',
month: '2-digit',
year: 'numeric',
timeZone: release.timezone
}),
time: formatTime(new Date(release.scheduledAt), {
hourCycle: 'h23',
timeZone: release.timezone
}),
offset: time.getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
})
}),
canDeleteAction ? /*#__PURE__*/ jsxRuntime.jsxs(ReleaseActionMenu.ReleaseActionMenu.Root, {
hasTriggerBorder: true,
children: [
/*#__PURE__*/ jsxRuntime.jsx(ReleaseActionMenu.ReleaseActionMenu.EditReleaseItem, {
releaseId: release.id
}),
/*#__PURE__*/ jsxRuntime.jsx(ReleaseActionMenu.ReleaseActionMenu.DeleteReleaseActionItem, {
releaseId: release.id,
actionId: release.actions[0].id
})
]
}) : null
]
})
]
}, release.id))
})
};
};
exports.Panel = Panel;
//# sourceMappingURL=ReleasesPanel.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,136 @@
import { jsx, jsxs } from 'react/jsx-runtime';
import { useQueryParams, useRBAC } from '@strapi/admin/strapi-admin';
import { unstable_useDocumentLayout } from '@strapi/content-manager/strapi-admin';
import { Flex, Box, Typography } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { PERMISSIONS } from '../constants.mjs';
import { useGetReleasesForEntryQuery } from '../services/release.mjs';
import { getTimezoneOffset } from '../utils/time.mjs';
import { ReleaseActionMenu } from './ReleaseActionMenu.mjs';
const Panel = ({ model, document, documentId, collectionType })=>{
const [{ query }] = useQueryParams();
const locale = query.plugins?.i18n?.locale;
const { edit: { options } } = unstable_useDocumentLayout(model);
const { formatMessage, formatDate, formatTime } = useIntl();
const { allowedActions } = useRBAC(PERMISSIONS);
const { canRead, canDeleteAction } = allowedActions;
const response = useGetReleasesForEntryQuery({
contentType: model,
entryDocumentId: documentId,
locale,
hasEntryAttached: true
}, {
skip: !document
});
const releases = response.data?.data;
const getReleaseColorVariant = (actionType, shade)=>{
if (actionType === 'unpublish') {
return `secondary${shade}`;
}
return `success${shade}`;
};
// Project is not EE or contentType does not have draftAndPublish enabled
if (!window.strapi.isEE || !options?.draftAndPublish || !canRead) {
return null;
}
if (collectionType === 'collection-types' && (!documentId || documentId === 'create')) {
return null;
}
if (!releases || releases.length === 0) {
return null;
}
return {
title: formatMessage({
id: 'content-releases.plugin.name',
defaultMessage: 'Releases'
}),
content: /*#__PURE__*/ jsx(Flex, {
direction: "column",
alignItems: "stretch",
gap: 3,
width: "100%",
children: releases?.map((release)=>/*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "start",
borderWidth: "1px",
borderStyle: "solid",
borderColor: getReleaseColorVariant(release.actions[0].type, '200'),
overflow: "hidden",
hasRadius: true,
children: [
/*#__PURE__*/ jsx(Box, {
paddingTop: 3,
paddingBottom: 3,
paddingLeft: 4,
paddingRight: 4,
background: getReleaseColorVariant(release.actions[0].type, '100'),
width: "100%",
children: /*#__PURE__*/ jsx(Typography, {
fontSize: 1,
variant: "pi",
textColor: getReleaseColorVariant(release.actions[0].type, '600'),
children: formatMessage({
id: 'content-releases.content-manager-edit-view.list-releases.title',
defaultMessage: '{isPublish, select, true {Will be published in} other {Will be unpublished in}}'
}, {
isPublish: release.actions[0].type === 'publish'
})
})
}),
/*#__PURE__*/ jsxs(Flex, {
padding: 4,
direction: "column",
gap: 2,
width: "100%",
alignItems: "flex-start",
children: [
/*#__PURE__*/ jsx(Typography, {
fontSize: 2,
fontWeight: "bold",
variant: "omega",
textColor: "neutral700",
children: release.name
}),
release.scheduledAt && release.timezone && /*#__PURE__*/ jsx(Typography, {
variant: "pi",
textColor: "neutral600",
children: formatMessage({
id: 'content-releases.content-manager-edit-view.scheduled.date',
defaultMessage: '{date} at {time} ({offset})'
}, {
date: formatDate(new Date(release.scheduledAt), {
day: '2-digit',
month: '2-digit',
year: 'numeric',
timeZone: release.timezone
}),
time: formatTime(new Date(release.scheduledAt), {
hourCycle: 'h23',
timeZone: release.timezone
}),
offset: getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
})
}),
canDeleteAction ? /*#__PURE__*/ jsxs(ReleaseActionMenu.Root, {
hasTriggerBorder: true,
children: [
/*#__PURE__*/ jsx(ReleaseActionMenu.EditReleaseItem, {
releaseId: release.id
}),
/*#__PURE__*/ jsx(ReleaseActionMenu.DeleteReleaseActionItem, {
releaseId: release.id,
actionId: release.actions[0].id
})
]
}) : null
]
})
]
}, release.id))
})
};
};
export { Panel };
//# sourceMappingURL=ReleasesPanel.mjs.map

File diff suppressed because one or more lines are too long