From 691d66cd8de98b96a79e17f79896ac8093c44620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Ja=C5=A1ek?= Date: Tue, 4 Feb 2025 09:55:20 +0100 Subject: [PATCH] allow planning items without time (#2192) * allow planning items without time toggled via `PLANNING_PLANNING_ALL_DAY` config. STT-48 --- .github/workflows/ci-e2e.yml | 8 ++++- client/actions/events/ui.ts | 9 ++++-- .../Planning/PlanningEditor/index.tsx | 16 ++++++---- .../fields/editor/PlanningDateTime.tsx | 31 +++++++++++++++++++ .../fields/editor/base/dateTime.tsx | 1 + client/globals.d.ts | 1 + client/interfaces.ts | 1 + client/planning-extension/src/globals.d.ts | 1 + client/utils/planning.tsx | 28 ++++++++++------- e2e/package.json | 9 +++--- scripts/ci-install.sh | 5 ++- server/planning/__init__.py | 1 + server/planning/planning/planning_schema.py | 1 + 13 files changed, 84 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci-e2e.yml b/.github/workflows/ci-e2e.yml index 6be47f922..76aaf4e9f 100644 --- a/.github/workflows/ci-e2e.yml +++ b/.github/workflows/ci-e2e.yml @@ -12,7 +12,7 @@ jobs: node-version: ['14'] e2e: ['a', 'b'] env: - INSTALL_NODE_MODULES: true + INSTALL_NODE_MODULES: false RUN_SERVICES: true E2E: true TZ: Australia/Sydney @@ -28,6 +28,12 @@ jobs: cache-dependency-path: 'server/requirements.txt' - name: Setup Environment run: ./scripts/ci-install.sh + - name: Install Planning Client + run: npm ci + working-directory: ./ + - name: Install E2E Client + run: npm install + working-directory: ./e2e - name: Build Client working-directory: ./e2e run: npm run build diff --git a/client/actions/events/ui.ts b/client/actions/events/ui.ts index 8904ac5f7..c7b52a3ae 100644 --- a/client/actions/events/ui.ts +++ b/client/actions/events/ui.ts @@ -758,10 +758,13 @@ const createEventFromPlanning = (plan: IPlanningItem) => ( ...eventUtils.defaultEventValues(occurStatuses, defaultCalendar, defaultPlace), dates: { start: moment(plan.planning_date).clone(), - end: moment(plan.planning_date) - .clone() - .add(defaultDurationOnChange, 'h'), + end: plan.all_day ? + moment(plan.planning_date).clone() : + moment(plan.planning_date) + .clone() + .add(defaultDurationOnChange, 'h'), tz: moment.tz.guess(), + all_day: plan.all_day, }, subject: plan.subject, anpa_category: plan.anpa_category, diff --git a/client/components/Planning/PlanningEditor/index.tsx b/client/components/Planning/PlanningEditor/index.tsx index fc4667dc1..0fc375088 100644 --- a/client/components/Planning/PlanningEditor/index.tsx +++ b/client/components/Planning/PlanningEditor/index.tsx @@ -317,14 +317,18 @@ class PlanningEditorComponent extends React.Component { } } - onPlanningDateChange(field, value) { - let changes: Partial = {planning_date: value}; + onPlanningDateChange(fieldOrValue: string | {[key: string]: any}, value?: any) { + if (typeof fieldOrValue === 'string') { + let changes: Partial = {planning_date: value}; - if (field.indexOf('.time') >= 0) { - changes._time_to_be_confirmed = false; - } + if (fieldOrValue.indexOf('.time') >= 0) { + changes._time_to_be_confirmed = false; + } - this.props.onChangeHandler(changes, null); + this.props.onChangeHandler(changes, null); + } else { + this.props.onChangeHandler(fieldOrValue, value); + } } onTimeToBeConfirmed() { diff --git a/client/components/fields/editor/PlanningDateTime.tsx b/client/components/fields/editor/PlanningDateTime.tsx index f177c3e34..b40f1fc40 100644 --- a/client/components/fields/editor/PlanningDateTime.tsx +++ b/client/components/fields/editor/PlanningDateTime.tsx @@ -3,6 +3,8 @@ import * as React from 'react'; import {superdeskApi} from '../../../superdeskApi'; import {IEditorFieldProps, IPlanningItem} from '../../../interfaces'; import {EditorFieldDateTime} from './base/dateTime'; +import {appConfig} from 'appConfig'; +import moment from 'moment'; interface IProps extends IEditorFieldProps { item: IPlanningItem; @@ -12,6 +14,31 @@ interface IProps extends IEditorFieldProps { } export class EditorFieldPlanningDateTime extends React.PureComponent { + allDay = appConfig.planning.all_day; + + constructor(props: IProps) { + super(props); + + this.onChange = this.onChange.bind(this); + } + + onChange(fieldOrValues: string | { [key: string]: any }, value?: any) { + if (typeof fieldOrValues === 'string') { + const updateValue = value?.isValid() ? this.formatValue(value) : null; + + this.props.onChange({ + [fieldOrValues]: updateValue, + all_day: this.allDay, + }); + } else { + this.props.onChange(fieldOrValues); + } + } + + formatValue(value: moment.Moment) : moment.MomentInput { + return this.allDay ? value.format('YYYY-MM-DD') : value; + } + render() { const {gettext} = superdeskApi.localization; const { @@ -29,6 +56,10 @@ export class EditorFieldPlanningDateTime extends React.PureComponent { label={label ?? gettext('Planning Date')} showToBeConfirmed={true} toBeConfirmed={this.props.item?._time_to_be_confirmed == true} + singleValue={true} + allDay={this.allDay} + hideTime={this.allDay} + onChange={this.onChange} /> ); } diff --git a/client/components/fields/editor/base/dateTime.tsx b/client/components/fields/editor/base/dateTime.tsx index a73cd87c2..0ba6ebc50 100644 --- a/client/components/fields/editor/base/dateTime.tsx +++ b/client/components/fields/editor/base/dateTime.tsx @@ -14,6 +14,7 @@ interface IProps extends IEditorFieldProps { singleValue?: boolean; onToBeConfirmed?(field: string): void; allDay?: boolean; + hideTime?: boolean; } export class EditorFieldDateTime extends React.PureComponent { diff --git a/client/globals.d.ts b/client/globals.d.ts index 04e2bb3a8..214eadc0f 100644 --- a/client/globals.d.ts +++ b/client/globals.d.ts @@ -186,6 +186,7 @@ declare module 'superdesk-api' { autosave_timeout?: number; default_create_planning_series_with_event_series?: boolean; event_related_item_search_provider_name?: string; + all_day?: boolean; }; coverage?: { diff --git a/client/interfaces.ts b/client/interfaces.ts index bcc53f37a..82e761ab0 100644 --- a/client/interfaces.ts +++ b/client/interfaces.ts @@ -725,6 +725,7 @@ export interface IPlanningItem extends IBaseRestApiResponse { scheduled: string | Date; }>; planning_date: IDateTime; + all_day?: boolean; flags?: { marked_for_not_publication?: boolean; overide_auto_assign_to_workflow?: boolean; diff --git a/client/planning-extension/src/globals.d.ts b/client/planning-extension/src/globals.d.ts index 32cb39e7e..c49183954 100644 --- a/client/planning-extension/src/globals.d.ts +++ b/client/planning-extension/src/globals.d.ts @@ -38,6 +38,7 @@ declare module 'superdesk-api' { autosave_timeout?: number; default_create_planning_series_with_event_series?: boolean; event_related_item_search_provider_name?: string; + all_day?: boolean; }; coverage?: { diff --git a/client/utils/planning.tsx b/client/utils/planning.tsx index 5f86087c8..43b2ad770 100644 --- a/client/utils/planning.tsx +++ b/client/utils/planning.tsx @@ -1719,18 +1719,24 @@ function getDateStringForPlanning(planning: IPlanningItem): string { planning.planning_date : moment(planning.planning_date); - return planning._time_to_be_confirmed ? ( - planning_date.format(appConfig.planning.dateformat) + - ' @ ' + - gettext('TBC') - ) : - getDateTimeString( - planning_date, - appConfig.planning.dateformat, - appConfig.planning.timeformat, - ' @ ', - false + if (planning._time_to_be_confirmed) { + return ( + planning_date.format(appConfig.planning.dateformat) + + ' @ ' + gettext('TBC') ); + } + + if (planning.all_day) { + return moment.utc(planning_date).format(appConfig.planning.dateformat); + } + + return getDateTimeString( + planning_date, + appConfig.planning.dateformat, + appConfig.planning.timeformat, + ' @ ', + false + ); } function getCoverageDateText(coverage: IPlanningCoverageItem): string { diff --git a/e2e/package.json b/e2e/package.json index 279d04306..40ee639ca 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -8,18 +8,19 @@ "devDependencies": { "@superdesk/build-tools": "^1.0.14", "cypress": "^13.16.1", - "cypress-real-events": "^1.7.6", + "cypress-real-events": "^1.14.0", "cypress-terminal-report": "^5.0.2", "extract-text-webpack-plugin": "3.0.2", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "moment": "^2.29.4", - "moment-timezone": "^0.5.42" + "moment-timezone": "^0.5.47" }, "scripts": { "cypress-ui": "cypress open", "cypress-ci": "cypress run --browser chrome", "clean": "grunt clean", "build": "npx @superdesk/build-tools build-root-repo ./", - "serve": "node --max-old-space-size=8192 ./node_modules/.bin/grunt server" + "serve": "node --max-old-space-size=8192 ./node_modules/.bin/grunt server", + "start": "grunt server" } } diff --git a/scripts/ci-install.sh b/scripts/ci-install.sh index 32955cc9b..2655e5040 100755 --- a/scripts/ci-install.sh +++ b/scripts/ci-install.sh @@ -7,8 +7,9 @@ sudo apt-get -y install libxml2-dev libxmlsec1-dev libxmlsec1-openssl # Update python core packages python -m pip install --upgrade pip wheel setuptools +git config --global url."https://git@".insteadOf git:// + if [ "$INSTALL_NODE_MODULES" == "true" ]; then - git config --global url."https://git@".insteadOf git:// npm install fi @@ -24,6 +25,4 @@ if [ "$E2E" == "true" ]; then cd e2e/server pip install -r requirements.txt cd ../ - git config --global url."https://git@".insteadOf git:// - npm install fi diff --git a/server/planning/__init__.py b/server/planning/__init__.py index d90bdfd65..6035fd412 100644 --- a/server/planning/__init__.py +++ b/server/planning/__init__.py @@ -242,6 +242,7 @@ def init_app(app): app.client_config["planning"][ "default_create_planning_series_with_event_series" ] = get_config_default_create_planning_series_with_event_series(app) + app.client_config["planning"]["all_day"] = bool(app.config.get("PLANNING_PLANNING_ALL_DAY", False)) app.client_config["planning_event_link_method"] = app.config.get(settings.PLANNING_EVENT_LINK_METHOD, "one_primary") diff --git a/server/planning/planning/planning_schema.py b/server/planning/planning/planning_schema.py index 0f28ae09b..f8b769f2d 100644 --- a/server/planning/planning/planning_schema.py +++ b/server/planning/planning/planning_schema.py @@ -335,6 +335,7 @@ "type": "datetime", "nullable": False, }, + "all_day": {"type": "boolean"}, "flags": { "type": "dict", "schema": {