Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cv2 4498 edit article #1985

Merged
merged 11 commits into from
Jul 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@
"description": "Title for the slideout edit fact-check form",
"defaultMessage": "Edit Claim & Fact-Check"
},
{
"id": "articleForm.editedByLabel",
"description": "Label to convey when the item was last edited",
"defaultMessage": "Edited by:"
},
{
"id": "articleForm.publishedAtDate",
"description": "Label to convey when the item was last published",
"defaultMessage": "Last Published:"
},
{
"id": "articleForm.publishedReport",
"description": "A label on a button that opens the report for this item. This displays if the report for this media item is currently in the 'Published' state.",
"defaultMessage": "Published report"
},
{
"id": "articleForm.unpublishedReport",
"description": "A label on a button that opens the report for this item. This displays if the report for this media item is NOT currently in the 'Published' state.",
"defaultMessage": "Unpublished report"
},
{
"id": "articleForm.claim",
"description": "Title of the claim section.",
Expand Down Expand Up @@ -123,10 +143,5 @@
"id": "articleForm.formSaveButton",
"description": "the save button for the article forom",
"defaultMessage": "Create content"
},
{
"id": "articleForm.formDeleteButton",
"description": "delete the current article",
"defaultMessage": "Move to Trash"
}
]
171 changes: 142 additions & 29 deletions src/app/components/article/ArticleForm.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
import React, { useEffect } from 'react';
import { graphql, createFragmentContainer } from 'react-relay/compat';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, FormattedDate } from 'react-intl';
import Slideout from '../cds/slideout/Slideout';
import ButtonMain from '../cds/buttons-checkboxes-chips/ButtonMain';
import IconReport from '../../icons/fact_check.svg';
import IconUnpublishedReport from '../../icons/unpublished_report.svg';
import TagList from '../cds/menus-lists-dialogs/TagList';
import TextArea from '../cds/inputs/TextArea';
import TextField from '../cds/inputs/TextField';
import LanguagePickerSelect from '../cds/inputs/LanguagePickerSelect';
import LimitedTextArea from '../layout/inputs/LimitedTextArea';
import DeleteIcon from '../../icons/delete.svg';
// import DeleteIcon from '../../icons/delete.svg';
import inputStyles from '../../styles/css/inputs.module.css';
import { safelyParseJSON, truncateLength } from '../../helpers';
import styles from './ArticleForm.module.css';
import RatingSelector from '../cds/inputs/RatingSelector';

const ArticleForm = ({
handleSave,
onClose,
handleBlur,
handleDelete,
articleType,
mode,
article,
team,
projectMedia,
}) => {
const title = (
<>
Expand All @@ -34,18 +35,26 @@ const ArticleForm = ({
</>
);

const [claimDescription, setClaimDescription] = React.useState(articleType === 'fact-check' && projectMedia?.suggested_main_item ? projectMedia.suggested_main_item.claim_description : '');
const [claimContext, setClaimContext] = React.useState('');
const options = team?.tag_texts?.edges.map(edge => ({ label: edge.node.text, value: edge.node.text })) || [];
const [claimDescription, setClaimDescription] = React.useState(article?.claim_description?.description || '');
const [claimContext, setClaimContext] = React.useState(article?.claim_description?.context || '');
const options = team?.tag_texts?.edges.map(edge => ({ label: edge.node.text, value: edge.node.text })) || team?.teamTags?.map(tag => ({ label: tag, value: tag }));


const languages = safelyParseJSON(team.get_languages) || ['en'];
const defaultArticleLanguage = languages && languages.length === 1 ? languages[0] : null;
const [articleTitle, setArticleTitle] = React.useState(article?.title || '');
const [summary, setSummary] = React.useState(article?.summary || '');
const [summary, setSummary] = React.useState(article?.summary || article?.description || '');
const [url, setUrl] = React.useState(article?.url || '');
const [language, setLanguage] = React.useState(article?.language || null);
const [tags, setTags] = React.useState(article?.tags || []);
const [status, setStatus] = React.useState(article?.claim_description?.project_media?.status || article?.rating || '');
const claimDescriptionMissing = !claimDescription || claimDescription.description?.trim()?.length === 0;
const statuses = article?.statuses || team.verification_statuses || null;

const [summaryError, setSummaryError] = React.useState(false);
const [titleError, setTitleError] = React.useState(false);
const [claimDescriptionError, setClaimDescriptionError] = React.useState(false);


const [isValid, setIsValid] = React.useState(false);

Expand All @@ -61,6 +70,15 @@ const ArticleForm = ({
}
}, [articleTitle, summary, claimDescription, language]);

const handleGoToReport = (id) => {
if (window.location.pathname.indexOf('/media/') > 0) {
window.location.assign(`${window.location.pathname.replace(/\/(suggested-matches|similar-media)\/?$/, '').replace(/\/$/, '')}/report`);
} else {
const teamSlug = window.location.pathname.match(/^\/([^/]+)/)[1];
window.location.assign(`../../${teamSlug}/media/${id}/report`);
}
};

const handleLanguageSubmit = (value) => {
const { languageCode } = value;
setLanguage(languageCode);
Expand All @@ -72,13 +90,86 @@ const ArticleForm = ({
handleBlur('tags', newtags);
};

const handleStatusChange = (clickedStatus) => {
setStatus(clickedStatus);
handleBlur('rating', clickedStatus);
};

return (
<Slideout
title={title}
content={
<>
<div className={styles['article-form-container']}>
{ mode === 'edit' &&
<div className={styles['article-form-info']}>
<div className={styles['article-form-info-labels']}>
{ article.updated_at &&
<div className="typography-subtitle2">
<FormattedMessage
id="articleForm.editedByLabel"
defaultMessage="Edited by:"
description="Label to convey when the item was last edited"
/>
</div>
}
{ article.publishedAt && articleType === 'fact-check' &&
<div className="typography-subtitle2">
<FormattedMessage
id="articleForm.publishedAtDate"
defaultMessage="Last Published:"
description="Label to convey when the item was last published"
/>
</div>
}
</div>
<div className={styles['article-form-info-content']}>
{ article.updated_at &&
<div className="typography-body2">
{article.user.name}, <FormattedDate value={new Date(article.updated_at * 1000)} year="numeric" month="long" day="numeric" />
</div>
}
{ article.publishedAt && articleType === 'fact-check' &&
<div className="typography-body2">
<FormattedDate value={new Date(article.publishedAt * 1000)} year="numeric" month="long" day="numeric" />
</div>
}
</div>
</div>
}
<div className={inputStyles['form-inner-wrapper']}>
<div className={styles['article-rating-wrapper']}>
{ articleType === 'fact-check' && statuses &&
<RatingSelector status={status} statuses={statuses} onStatusChange={handleStatusChange} />
}
{ articleType === 'fact-check' && article?.claim_description?.project_media?.id &&
<div className={inputStyles['form-fieldset-field']}>
<ButtonMain
onClick={() => handleGoToReport(article.claim_description.project_media.id)}
className="media-fact-check__report-designer"
variant="contained"
theme={article?.publishedAt ? 'brand' : 'alert'}
size="default"
iconLeft={article?.publishedAt ? <IconReport /> : <IconUnpublishedReport />}
disabled={claimDescriptionMissing}
label={article?.publishedAt ?
<FormattedMessage
className="media-fact-check__published-report"
id="articleForm.publishedReport"
defaultMessage="Published report"
description="A label on a button that opens the report for this item. This displays if the report for this media item is currently in the 'Published' state."
/> :
<FormattedMessage
className="media-fact-check__unpublished-report"
id="articleForm.unpublishedReport"
defaultMessage="Unpublished report"
description="A label on a button that opens the report for this item. This displays if the report for this media item is NOT currently in the 'Published' state."
/>
}
/>
</div>
}
</div>
<div id="article_form_tags" className={inputStyles['form-fieldset']}>
<TagList
tags={tags}
Expand Down Expand Up @@ -108,11 +199,17 @@ const ArticleForm = ({
id="article-form__description"
className="article-form__description"
placeholder={placeholder}
defaultValue={claimDescription ? claimDescription.description : ''}
defaultValue={claimDescription || ''}
error={claimDescriptionError}
onBlur={(e) => {
const newValue = e.target.value;
setClaimDescription(newValue);
handleBlur('claim description', newValue);
if (newValue.length) {
setClaimDescriptionError(false);
setClaimDescription(newValue);
handleBlur('claim description', newValue);
} else {
setClaimDescriptionError(true);
}
}}
label={
<FormattedMessage
Expand Down Expand Up @@ -200,12 +297,18 @@ const ArticleForm = ({
rows="1"
autoGrow
maxHeight="266px"
error={titleError}
placeholder={placeholder}
label={<FormattedMessage id="articleForm.explainerTitle" defaultMessage="Title" description="Label for explainer title field" />}
onBlur={(e) => {
const newValue = e.target.value;
setArticleTitle(newValue);
handleBlur('title', newValue);
if (newValue.length) {
setTitleError(false);
setArticleTitle(newValue);
handleBlur('title', newValue);
} else {
setTitleError(true);
}
}}
/>)}
</FormattedMessage> :
Expand All @@ -225,12 +328,18 @@ const ArticleForm = ({
rows="1"
autoGrow
maxHeight="266px"
error={titleError}
placeholder={placeholder}
label={<FormattedMessage id="articleForm.factCheckTitle" defaultMessage="Title" description="Label for fact-check title field" />}
onBlur={(e) => {
const newValue = e.target.value;
setArticleTitle(newValue);
handleBlur('title', newValue);
if (newValue.length) {
setTitleError(false);
setArticleTitle(newValue);
handleBlur('title', newValue);
} else {
setTitleError(true);
}
}}
/>)}
</FormattedMessage>}
Expand All @@ -254,12 +363,18 @@ const ArticleForm = ({
maxChars={900 - articleTitle.length - url.length}
rows="1"
label={<FormattedMessage id="articleForm.explainerSummary" defaultMessage="Summary" description="Label for article summary field" />}
error={summaryError}
autoGrow
placeholder={placeholder}
onBlur={(e) => {
const newValue = e.target.value;
setSummary(newValue);
handleBlur('description', newValue);
if (newValue.length) {
setSummaryError(false);
setSummary(newValue);
handleBlur('description', newValue);
} else {
setSummaryError(true);
}
}}
/>
)}
Expand All @@ -283,11 +398,17 @@ const ArticleForm = ({
rows="1"
label={<FormattedMessage id="articleForm.summary" defaultMessage="Summary" description="Label for article summary field" />}
autoGrow
error={summaryError}
placeholder={placeholder}
onBlur={(e) => {
const newValue = e.target.value;
setSummary(newValue);
handleBlur('description', newValue);
if (newValue.length) {
setSummaryError(false);
setSummary(newValue);
handleBlur('summary', newValue);
} else {
setSummaryError(true);
}
}}
/>
)}
Expand Down Expand Up @@ -368,38 +489,30 @@ const ArticleForm = ({
</>
}
onClose={onClose}
footer={mode === 'create'} // just here until trash is added
showCancel={mode === 'create'}
mainActionButton={mode === 'create' ? <ButtonMain
onClick={handleSave}
disabled={!isValid}
label={<FormattedMessage id="articleForm.formSaveButton" defaultMessage="Create content" description="the save button for the article forom" />}
/> : <ButtonMain
onClick={handleDelete}
iconLeft={<DeleteIcon />}
theme="error"
label={<FormattedMessage id="articleForm.formDeleteButton" defaultMessage="Move to Trash" description="delete the current article" />}
/>}
/> : null}
/>
);
};

ArticleForm.defaultProps = {
handleSave: null,
handleDelete: null,
article: null,
projectMedia: null,
};

ArticleForm.propTypes = {
handleSave: PropTypes.func,
onClose: PropTypes.func.isRequired,
handleBlur: PropTypes.func.isRequired,
handleDelete: PropTypes.func,
articleType: PropTypes.oneOf(['fact-check', 'explainer']).isRequired,
mode: PropTypes.oneOf(['create', 'edit']).isRequired,
article: PropTypes.object,
team: PropTypes.object.isRequired,
projectMedia: PropTypes.object,
};

export default createFragmentContainer(ArticleForm, graphql`
Expand Down
28 changes: 28 additions & 0 deletions src/app/components/article/ArticleForm.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,37 @@
z-index: 10;
}

.article-rating-wrapper {
display: flex;
gap: 4px;
padding-bottom: 5px;
}

.article-form-no-claim-container {
color: var(--color-blue-32);
margin: 0 20px;
position: absolute;
top: 30%;
}

.article-form-info {
border: 1px solid var(--color-gray-88);
border-radius: 5px;
display: flex;
margin-bottom: 10px;
padding: 8px;

.article-form-info-labels {
align-items: flex-end;
display: flex;
flex-flow: column;
padding-right: 8px;
}

.article-form-info-content {
align-items: flex-start;
display: flex;
flex-flow: column;
justify-content: space-around;
}
}
Loading