From 016efc3229d83a39576ac3f1c240c77a2f6babd7 Mon Sep 17 00:00:00 2001 From: J <93984341+VWSCoronaDashboard18@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:09:35 +0100 Subject: [PATCH] Feature/cor 1335 automate sanity topical dates (#4659) * feat: setup date config logic hook * feat: Add date logic hook to topical page * feat: optimise find date range script * test: update testing script * test: updated test file * feat: Add explanation to the math done. For reproducible code and understanding of whats going on. * fix: add explanation to the math done in the function * feat: added topical date config * feat: Added fields in sanity * feat: update sanity structure * feat: add toggle * feat: update typing and added document to sanity. * feat: applying pair programming feedback * feat: sanity tile date field * fix: solved issues on custom sanity component. * fix: also accept 0 as the day value. * fix: inconsistent rendering * fix: change iso week to just week * Fix: remove file * fix: merge conflict * fix: apply descriptions based on input by communication * fix: Update formatting * fix: Updating dutch * fix: PR feedback * fix: PR feedback * fix PR feedback, dutch grammar * fix: PR feedback fix apply typing. * Fix: PR feedback --- packages/app/src/pages/index.tsx | 3 +- .../queries/get-topical-structure-query.ts | 1 + packages/app/src/queries/query-types.ts | 7 +- .../cms/src/components/topical-tile-date.tsx | 48 +++++++++++ packages/cms/src/desk-structure.ts | 1 + .../src/hooks/get-topical-tile-date-config.ts | 81 +++++++++++++++++++ packages/cms/src/schemas/topical/index.ts | 1 + .../schemas/topical/theme-tile-date-config.ts | 63 +++++++++++++++ .../cms/src/schemas/topical/theme-tile.ts | 32 ++++++++ packages/cms/src/validation/index.ts | 1 - yarn.lock | 22 +---- 11 files changed, 237 insertions(+), 23 deletions(-) create mode 100644 packages/cms/src/components/topical-tile-date.tsx create mode 100644 packages/cms/src/hooks/get-topical-tile-date-config.ts create mode 100644 packages/cms/src/schemas/topical/theme-tile-date-config.ts diff --git a/packages/app/src/pages/index.tsx b/packages/app/src/pages/index.tsx index 74271caffc..e442eb47ab 100644 --- a/packages/app/src/pages/index.tsx +++ b/packages/app/src/pages/index.tsx @@ -191,6 +191,7 @@ const Home = (props: StaticProps) => { marginBottom={{ _: space[4], sm: space[5] }} > {theme.tiles.map((themeTile) => { + const sourceLabel = themeTile.sourceLabel ? replaceVariablesInText(themeTile.sourceLabel, { date: themeTile.tileDate }) : null; return ( ) => { cta={themeTile.cta} key={themeTile.title} kpiValue={themeTile.kpiValue} - sourceLabel={themeTile.sourceLabel} + sourceLabel={sourceLabel} /> ); })} diff --git a/packages/app/src/queries/get-topical-structure-query.ts b/packages/app/src/queries/get-topical-structure-query.ts index 5bc4fd2e4d..990114292c 100644 --- a/packages/app/src/queries/get-topical-structure-query.ts +++ b/packages/app/src/queries/get-topical-structure-query.ts @@ -42,6 +42,7 @@ export function getTopicalStructureQuery(locale: string) { tileIcon, 'title':title.${locale}, 'sourceLabel':sourceLabel.${locale}, + 'tileDate': tileDate.${locale}, 'kpiValue': kpiValue.${locale}, 'hideTrendIcon': hideTrendIcon, 'trendIcon': { diff --git a/packages/app/src/queries/query-types.ts b/packages/app/src/queries/query-types.ts index 09f3bc99db..2f8dab3f72 100644 --- a/packages/app/src/queries/query-types.ts +++ b/packages/app/src/queries/query-types.ts @@ -84,11 +84,12 @@ export interface BaseTile { } interface TopicalTile extends BaseTile { + title: string; + sourceLabel: string | null; + tileDate: string; + kpiValue: string | null; cta: Cta; hideTrendIcon: boolean; - kpiValue: string | null; - sourceLabel: string | null; - title: string; trendIcon: TrendIcon; } diff --git a/packages/cms/src/components/topical-tile-date.tsx b/packages/cms/src/components/topical-tile-date.tsx new file mode 100644 index 0000000000..8e2a781b2c --- /dev/null +++ b/packages/cms/src/components/topical-tile-date.tsx @@ -0,0 +1,48 @@ +import React, { FormEvent, useState } from 'react'; +import { withDocument } from 'part:@sanity/form-builder'; +import { getTopicalTileDateConfig, ThemeTileDateConfig } from '../hooks/get-topical-tile-date-config'; +import PatchEvent, { set } from '@sanity/form-builder/PatchEvent'; +import { FormField, FormFieldProps } from '@sanity/base/components'; +import { TextInput } from '@sanity/ui'; +import { ValidationMarker } from '@sanity/types/dist/dts'; + +type ShowDateProps = { + type: { title: 'Nederlands' | 'Engels' }; + markers: ValidationMarker[]; + presence: FormFieldProps['__unstable_presence']; + onChange: (event: PatchEvent) => void; + document: { themeTileDateConfig: ThemeTileDateConfig }; +}; + +const ShowDate = (props: ShowDateProps) => { + const { + type, + markers, + presence, + onChange, + document: { themeTileDateConfig: dateConfig }, + } = props; + const isConfigValid = dateConfig?.startDayOfDate >= 0 && dateConfig?.weekOffset >= 0 && dateConfig?.timeSpanInDays >= 1; + const formattedDate = !isConfigValid ? '' : getTopicalTileDateConfig({ config: dateConfig, inputDate: new Date(), language: type.title }); + + // The following line makes it possible to do realtime updates on this field with the new date configurations + const initFormattedDateValue = set(formattedDate); + onChange(PatchEvent.from(initFormattedDateValue)); + const [value, setValue] = useState(''); + + const handleChange = (event: FormEvent) => { + const inputValue = event.currentTarget.value; + const newValue = inputValue === '' ? formattedDate : inputValue; + const patchEventValue = set(newValue); + setValue(newValue); + onChange(PatchEvent.from(patchEventValue)); + }; + + return ( + + + + ); +}; + +export const TopicalTileDate = withDocument(ShowDate); diff --git a/packages/cms/src/desk-structure.ts b/packages/cms/src/desk-structure.ts index 4b0fdf9b85..b8b4310e7b 100644 --- a/packages/cms/src/desk-structure.ts +++ b/packages/cms/src/desk-structure.ts @@ -57,6 +57,7 @@ const hiddenDocTypes = [ 'topicalPage', 'topicalPageConfig', 'trendIcon', + 'themeTileDateConfig', 'veelgesteldeVragen', 'veelgesteldeVragenGroups', 'warning', diff --git a/packages/cms/src/hooks/get-topical-tile-date-config.ts b/packages/cms/src/hooks/get-topical-tile-date-config.ts new file mode 100644 index 0000000000..ebb8778b64 --- /dev/null +++ b/packages/cms/src/hooks/get-topical-tile-date-config.ts @@ -0,0 +1,81 @@ +import { createFormatting } from '@corona-dashboard/common'; + +export interface ThemeTileDateConfig { + weekOffset: number; + startDayOfDate: 0 | 1 | 2 | 3 | 4 | 5 | 6; + timeSpanInDays: number; +} +/** + * Returns a object with the start and end date in seconds. Or a single unix date in seconds if it only contains one date. + */ + +export type TopicalDateConfig = { + config: ThemeTileDateConfig; + inputDate?: Date; + language: 'Nederlands' | 'Engels'; +}; + +const languages = { Nederlands: 'nl-NL', Engels: 'en-GB' }; + +const dayInMiliseconds = 86400000; +const weekInMiliseconds = 604800000; + +export const getTopicalTileDateConfig = ({ config, inputDate = new Date(), language }: TopicalDateConfig): string => { + const { formatDateFromMilliseconds, formatDateSpan } = createFormatting(languages[language], { + date_day_before_yesterday: 'eergisteren', + date_today: 'vandaag', + date_yesterday: 'gisteren', + }); + + /** + * Get the current index of the day of the week for the given input date. + * Starting with Sunday as index 0 and Saturday as index 6 + * Sunday = 0 + * Monday = 1 + * Tuesday = 2 + * Wednesday = 3 + * Thursday = 4 + * Friday = 5 + * Saturday = 6 + */ + const inputDateWeekDay = inputDate.getDay(); + + // Get the unix timestamp for the given input date. + const inputDateInUnixTime = inputDate.getTime(); + + /** + * Calulate how many milliseconds ago a IsoIndex was. + * (This would not be the data of that week. + * But the milliseconds passed since that week until the given input date.) + * + * Check if the weekday of given input date already passed the start day in the config. + * If so, calculate how many milliseconds ago the + * If not, add 1 week to the offset and calculate the amount of milliseconds for that offset + * This is done because: if the offset is from last wedsnesday too the one before last wednesday. + * We need to check if wedsneday already happened. Otherwise we need to add another week + */ + const millisecondsPassedSinceWeekOffset = config.startDayOfDate > inputDateWeekDay ? (config.weekOffset + 1) * weekInMiliseconds : config.weekOffset * weekInMiliseconds; + + // Now we set the start of the week by having the weekday of the given input added to the week offset. + // Basicly this always sets the week offset in milliseconds to the sunday of that week in the past. + const startOfTheWeekInMilliSecondsPassed = millisecondsPassedSinceWeekOffset + inputDateWeekDay * dayInMiliseconds; + + // get the milliseconds offset from the given input date weekday to the previous sunday. + const millisecondsPassedSinceSunday = config.startDayOfDate * dayInMiliseconds; + + // Get the length of the timespan in milliseconds + const timespanLengthInMiliseconds = (config.timeSpanInDays - 1) * dayInMiliseconds; + + // convert the weeks past in seconds to the actual date in milliseconds + const dateOfStartWeek = inputDateInUnixTime - startOfTheWeekInMilliSecondsPassed; + + // get form the sunday from the week to start to the day of the week that needs to be dsiplayed. + const startDate = dateOfStartWeek + millisecondsPassedSinceSunday; + + // Get the end date by adding the millisecond of the timespan to the start date. + const endDate = startDate + timespanLengthInMiliseconds; + + // Check if timespan is greater than one day. Or it's just a single day. create the return object. + const dateResult = config.timeSpanInDays === 1 ? formatDateFromMilliseconds(startDate, 'medium') : formatDateSpan(new Date(startDate), new Date(endDate), 'medium').join(' - '); + return dateResult; +}; diff --git a/packages/cms/src/schemas/topical/index.ts b/packages/cms/src/schemas/topical/index.ts index 12f14e9ef8..0bb5e0e84b 100644 --- a/packages/cms/src/schemas/topical/index.ts +++ b/packages/cms/src/schemas/topical/index.ts @@ -4,6 +4,7 @@ export * from './theme-collection'; export * from './theme-link'; export * from './theme-link-collection'; export * from './theme-tile'; +export * from './theme-tile-date-config'; export * from './theme-tile-collection'; export * from './measure-theme'; export * from './measure-tile'; diff --git a/packages/cms/src/schemas/topical/theme-tile-date-config.ts b/packages/cms/src/schemas/topical/theme-tile-date-config.ts new file mode 100644 index 0000000000..5a17bf6c32 --- /dev/null +++ b/packages/cms/src/schemas/topical/theme-tile-date-config.ts @@ -0,0 +1,63 @@ +import { Rule } from '~/sanity'; +import { REQUIRED } from '../../validation'; + +const DAYS_OF_THE_WEEK_LIST = [ + { + title: 'Zondag', + value: 0, + }, + { + title: 'Maandag', + value: 1, + }, + { + title: 'Dinsdag', + value: 2, + }, + { + title: 'Woensdag', + value: 3, + }, + { + title: 'Donderdag', + value: 4, + }, + { + title: 'Vrijdag', + value: 5, + }, + { + title: 'Zaterdag', + value: 6, + }, +]; + +export const themeTileDateConfig = { + name: 'themeTileDateConfig', + type: 'document', + fields: [ + { + title: 'Hoeveel weken geleden was de startdatum?', + name: 'weekOffset', + type: 'number', + validation: (rule: Rule) => rule.required().min(0), + }, + { + title: 'Op welke dag van de week start de datum?', + name: 'startDayOfDate', + options: { + list: DAYS_OF_THE_WEEK_LIST, + layout: 'dropdown', + }, + type: 'number', + validation: REQUIRED, + }, + { + title: 'Hoeveelheid dagen', + description: '(1 dag of bereik van – tot)', + name: 'timeSpanInDays', + type: 'number', + validation: (rule: Rule) => rule.required().min(1), + }, + ], +}; diff --git a/packages/cms/src/schemas/topical/theme-tile.ts b/packages/cms/src/schemas/topical/theme-tile.ts index 41c69743d5..6f9f697bd4 100644 --- a/packages/cms/src/schemas/topical/theme-tile.ts +++ b/packages/cms/src/schemas/topical/theme-tile.ts @@ -1,5 +1,7 @@ import { KpiIconInput } from '../../components/portable-text/kpi-configuration/kpi-icon-input'; import { REQUIRED } from '../../validation'; +import { TopicalTileDate } from '../../components/topical-tile-date'; +import { supportedLanguages } from '../../language/supported-languages'; export const themeTile = { type: 'document', @@ -10,6 +12,15 @@ export const themeTile = { title: 'KPI Waarde', name: 'kpiValue', }, + { + title: 'Thema tegeldatum-configuratie', + name: 'theme-tile-date-config', + description: 'Klik op het label om de velden te tonen.', + options: { + collapsible: true, + collapsed: true, + }, + }, ], fields: [ { @@ -47,9 +58,30 @@ export const themeTile = { }, { title: 'Metadata label', + description: 'Bij {{DATE}} wordt de tekst geplaatst van het tegeldatumveld. Deze kan handmatig overschreven worden.', name: 'sourceLabel', type: 'localeString', }, + { + title: 'Tegeldatum', + description: + 'Deze velden krijgen hun input van de tegeldatum-configuratie. Maar kunnen handmatig overschreven worden door een eigen tekst in te vullen. Om terug te gaan naar het gebruik van de configuratie kunnen deze velden leeg gemaakt worden.', + name: 'tileDate', + type: 'object', + fields: supportedLanguages.map((lang) => ({ + title: lang.title, + name: lang.id, + type: 'string', + inputComponent: TopicalTileDate, + })), + }, + { + title: 'Configuratie velden', + description: 'Voor de start- en einddatum van deze tegel op de samenvattingspagina.', + name: 'themeTileDateConfig', + type: 'themeTileDateConfig', + fieldset: 'theme-tile-date-config', + }, { title: 'Trend icon', name: 'trendIcon', diff --git a/packages/cms/src/validation/index.ts b/packages/cms/src/validation/index.ts index 7a608fda35..9a063316fe 100644 --- a/packages/cms/src/validation/index.ts +++ b/packages/cms/src/validation/index.ts @@ -1,4 +1,3 @@ import { Rule } from '~/sanity'; -export const REQUIRED_MIN_MAX = (rule: Rule, min: number, max: number) => rule.required().min(min).max(max); export const REQUIRED = (rule: Rule) => rule.required(); diff --git a/yarn.lock b/yarn.lock index 86ea5617f9..1aac4ec3b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10719,24 +10719,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30000792, caniuse-lite@npm:^1.0.30000805, caniuse-lite@npm:^1.0.30000844, caniuse-lite@npm:^1.0.30000981, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001271, caniuse-lite@npm:^1.0.30001272": - version: 1.0.30001272 - resolution: "caniuse-lite@npm:1.0.30001272" - checksum: 5a08fc35ae298acef6846111634201bde7b4710fe0ccabb69ad5681115f77971265d9b6b5cd9bb976bc44d8d6ff0119cb28d9032753e063a2c70e1e30b630ad9 - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001274": - version: 1.0.30001279 - resolution: "caniuse-lite@npm:1.0.30001279" - checksum: c0330577718e4221caae92a1bee8131f546668001840cf66455d5aa6050797f44709865779c5cdcba7cbf32527004a743dc2879497d23f60478f8bbb203cfeeb - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001283": - version: 1.0.30001312 - resolution: "caniuse-lite@npm:1.0.30001312" - checksum: 753fb9ea1e02e999430b323a71b5acab5120f3b5fc0161b01669f54a3ef5c5296240b6ae9b79b12a3742e3aed216aa9ee3d5398a23c16d08625ccd376b79545d +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30000792, caniuse-lite@npm:^1.0.30000805, caniuse-lite@npm:^1.0.30000844, caniuse-lite@npm:^1.0.30000981, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001271, caniuse-lite@npm:^1.0.30001272, caniuse-lite@npm:^1.0.30001274, caniuse-lite@npm:^1.0.30001283": + version: 1.0.30001451 + resolution: "caniuse-lite@npm:1.0.30001451" + checksum: 48a06a7881093bb4d8a08ed5428f24a1cbdaa544b0a6f0c3614287d4f34b6c853e79a0f608a5bd901c27995f5e951825606fba11e7930251cc422bd61de9d849 languageName: node linkType: hard