From 3bf63471dd6d9afb177465b35a22dd9b643d9307 Mon Sep 17 00:00:00 2001 From: Tom Richards Date: Wed, 14 Dec 2022 21:32:34 +0000 Subject: [PATCH] WIP - add an `Imaging order` button beneath usual `Add to` button on `grid-original` and render this new type of payload, with 'request type' dropdown etc. --- client/src/addToPinboardButton.tsx | 49 +++++++++++ client/src/app.tsx | 3 +- client/src/createItemInputBox.tsx | 7 +- client/src/globalState.tsx | 8 +- .../src/grid/octopusImagingOrderDisplay.tsx | 83 +++++++++++++++++++ client/src/payloadDisplay.tsx | 24 ++++-- client/src/pinboard.tsx | 4 +- client/src/selectPinboard.tsx | 4 +- client/src/sendMessageArea.tsx | 7 +- client/src/types/PayloadAndType.ts | 24 +++++- client/src/types/Telemetry.ts | 1 + shared/octopusImaging.ts | 12 +++ 12 files changed, 206 insertions(+), 20 deletions(-) create mode 100644 client/src/grid/octopusImagingOrderDisplay.tsx diff --git a/client/src/addToPinboardButton.tsx b/client/src/addToPinboardButton.tsx index a8f630454..cbb2d3316 100644 --- a/client/src/addToPinboardButton.tsx +++ b/client/src/addToPinboardButton.tsx @@ -9,6 +9,10 @@ import { textSans } from "../fontNormaliser"; import root from "react-shadow/emotion"; import * as Sentry from "@sentry/react"; import { TelemetryContext, PINBOARD_TELEMETRY_TYPE } from "./types/Telemetry"; +import { + IMAGINE_REQUEST_TYPES, + IMAGING_REQUEST_ITEM_TYPE, +} from "../../shared/octopusImaging"; export const ASSET_HANDLE_HTML_TAG = "asset-handle"; @@ -80,6 +84,51 @@ const AddToPinboardButton = (props: AddToPinboardButtonProps) => { `} />{" "} + {payloadToBeSent.type === "grid-original" && ( + + )} ); }; diff --git a/client/src/app.tsx b/client/src/app.tsx index 67adbf417..0b2e895ab 100644 --- a/client/src/app.tsx +++ b/client/src/app.tsx @@ -64,7 +64,6 @@ export const PinBoardApp = ({ apolloClient, userEmail }: PinBoardAppProps) => { const [payloadToBeSent, setPayloadToBeSent] = useState( null ); - const clearPayloadToBeSent = () => setPayloadToBeSent(null); const [assetHandles, setAssetHandles] = useState([]); const [workflowPinboardElements, setWorkflowPinboardElements] = useState< @@ -371,7 +370,7 @@ export const PinBoardApp = ({ apolloClient, userEmail }: PinBoardAppProps) => { openPinboardIdBasedOnQueryParam={openPinboardIdBasedOnQueryParam} preselectedComposerId={preSelectedComposerId} payloadToBeSent={payloadToBeSent} - clearPayloadToBeSent={clearPayloadToBeSent} + setPayloadToBeSent={setPayloadToBeSent} isExpanded={isExpanded} setIsExpanded={setIsExpanded} userLookup={userLookup} diff --git a/client/src/createItemInputBox.tsx b/client/src/createItemInputBox.tsx index 15c234e8e..0b778cc42 100644 --- a/client/src/createItemInputBox.tsx +++ b/client/src/createItemInputBox.tsx @@ -14,6 +14,7 @@ import { gqlSearchMentionableUsers } from "../gql"; import { SvgSpinner } from "@guardian/source-react-components"; import { isGroup, isUser } from "../../shared/graphql/extraTypes"; import { groupToMentionHandle, userToMentionHandle } from "./mentionsUtil"; +import { IMAGING_REQUEST_ITEM_TYPE } from "../../shared/octopusImaging"; interface WithEntity { entity: E & { @@ -197,7 +198,11 @@ export const CreateItemInputBox = ({ onKeyPress={(event) => { event.stopPropagation(); if (isEnterKey(event)) { - if (message || payloadToBeSent) { + if ( + payloadToBeSent?.type === IMAGING_REQUEST_ITEM_TYPE + ? message + : message || payloadToBeSent + ) { sendItem(); } event.preventDefault(); diff --git a/client/src/globalState.tsx b/client/src/globalState.tsx index ae3ef7bec..53ee39c23 100644 --- a/client/src/globalState.tsx +++ b/client/src/globalState.tsx @@ -42,7 +42,7 @@ interface GlobalStateContextShape { activePinboards: PinboardData[]; payloadToBeSent: PayloadAndType | null; - clearPayloadToBeSent: () => void; + setPayloadToBeSent: (newPayload: PayloadAndType | null) => void; addManuallyOpenedPinboardId: ( pinboardId: string, @@ -102,7 +102,7 @@ interface GlobalStateProviderProps { openPinboardIdBasedOnQueryParam: string | null; preselectedComposerId: string | null | undefined; payloadToBeSent: PayloadAndType | null; - clearPayloadToBeSent: () => void; + setPayloadToBeSent: (newPayload: PayloadAndType | null) => void; isExpanded: boolean; setIsExpanded: (_: boolean) => void; userLookup: UserLookup; @@ -120,7 +120,7 @@ export const GlobalStateProvider: React.FC = ({ preselectedComposerId, presetUnreadNotificationCount, payloadToBeSent, - clearPayloadToBeSent, + setPayloadToBeSent, isExpanded, setIsExpanded, userLookup, @@ -496,7 +496,7 @@ export const GlobalStateProvider: React.FC = ({ activePinboardIds, payloadToBeSent, - clearPayloadToBeSent, + setPayloadToBeSent, addManuallyOpenedPinboardId, openPinboard, diff --git a/client/src/grid/octopusImagingOrderDisplay.tsx b/client/src/grid/octopusImagingOrderDisplay.tsx new file mode 100644 index 000000000..a860c046d --- /dev/null +++ b/client/src/grid/octopusImagingOrderDisplay.tsx @@ -0,0 +1,83 @@ +import { css } from "@emotion/react"; +import React from "react"; +import { ImagingOrderPayload } from "../types/PayloadAndType"; +import { + IMAGINE_REQUEST_TYPES, + IMAGING_REQUEST_ITEM_TYPE, + ImagingRequestType, +} from "../../../shared/octopusImaging"; +import { agateSans } from "../../fontNormaliser"; +import { useGlobalStateContext } from "../globalState"; +import { space } from "@guardian/source-foundations"; + +interface OctopusImagingOrderDisplayProps extends ImagingOrderPayload { + isEditable?: boolean; +} + +export const OctopusImagingOrderDisplay = ({ + payload, + isEditable, +}: OctopusImagingOrderDisplayProps) => { + const { setPayloadToBeSent } = useGlobalStateContext(); + return ( +
+

+ Imaging Order +

+ {isEditable && ( +
+ + { + "Don't forget to provide some notes to help Imaging team to understand your request" + } + +
+ )} + +
+ Request Type: + {isEditable ? ( + + ) : ( + payload.requestType + )} +
+
+ ); +}; diff --git a/client/src/payloadDisplay.tsx b/client/src/payloadDisplay.tsx index 7a971d3a3..01187e1b6 100644 --- a/client/src/payloadDisplay.tsx +++ b/client/src/payloadDisplay.tsx @@ -6,8 +6,10 @@ import { SvgCross } from "@guardian/source-react-components"; import { buttonBackground } from "./styling"; import { GridStaticImageDisplay } from "./grid/gridStaticImageDisplay"; import { GridDynamicSearchDisplay } from "./grid/gridDynamicSearchDisplay"; -import { TelemetryContext, PINBOARD_TELEMETRY_TYPE } from "./types/Telemetry"; +import { PINBOARD_TELEMETRY_TYPE, TelemetryContext } from "./types/Telemetry"; import { Tab } from "./types/Tab"; +import { IMAGING_REQUEST_ITEM_TYPE } from "../../shared/octopusImaging"; +import { OctopusImagingOrderDisplay } from "./grid/octopusImagingOrderDisplay"; interface PayloadDisplayProps { payloadAndType: PayloadAndType; @@ -20,7 +22,6 @@ export const PayloadDisplay = ({ clearPayloadToBeSent, tab, }: PayloadDisplayProps) => { - const { payload } = payloadAndType; const sendTelemetryEvent = useContext(TelemetryContext); return (
{ - event.dataTransfer.setData("URL", payload.embeddableUrl); + event.dataTransfer.setData( + "URL", + payloadAndType.payload.embeddableUrl + ); event.dataTransfer.setData( // prevent grid from accepting these as drops, as per https://github.com/guardian/grid/commit/4b72d93eedcbacb4f90680764d468781a72507f5#diff-771b9da876348ce4b4e057e2d8253324c30a8f3db4e434d49b3ce70dbbdb0775R138-R140 "application/vnd.mediaservice.kahuna.image", "true" ); sendTelemetryEvent?.(PINBOARD_TELEMETRY_TYPE.DRAG_FROM_PINBOARD, { - assetType: payloadAndType?.type, + assetType: payloadAndType.type, ...(tab && { tab }), }); }} onClick={() => { - window.open(payload.embeddableUrl, "_blank"); + window.open(payloadAndType.payload.embeddableUrl, "_blank"); sendTelemetryEvent?.(PINBOARD_TELEMETRY_TYPE.GRID_ASSET_OPENED, { - assetType: payloadAndType?.type, + assetType: payloadAndType.type, tab: tab as Tab, }); }} @@ -79,6 +83,14 @@ export const PayloadDisplay = ({ )} + {payloadAndType.type === IMAGING_REQUEST_ITEM_TYPE && ( + + )} + {clearPayloadToBeSent && (
= ({ addEmailsToLookup, payloadToBeSent, - clearPayloadToBeSent, + setPayloadToBeSent, showNotification, @@ -363,7 +363,7 @@ export const Pinboard: React.FC = ({ setPayloadToBeSent(null)} onError={(error) => setError(pinboardId, error)} userEmail={userEmail} pinboardId={pinboardId} diff --git a/client/src/selectPinboard.tsx b/client/src/selectPinboard.tsx index fa57a78e0..c394ae9b4 100644 --- a/client/src/selectPinboard.tsx +++ b/client/src/selectPinboard.tsx @@ -61,7 +61,7 @@ export const SelectPinboard = ({ activePinboards, activePinboardIds, payloadToBeSent, - clearPayloadToBeSent, + setPayloadToBeSent, isExpanded, @@ -370,7 +370,7 @@ export const SelectPinboard = ({ setPayloadToBeSent(null)} />
)} diff --git a/client/src/sendMessageArea.tsx b/client/src/sendMessageArea.tsx index 534cd6ff3..c34cf6b1a 100644 --- a/client/src/sendMessageArea.tsx +++ b/client/src/sendMessageArea.tsx @@ -15,6 +15,7 @@ import { SvgSpinner } from "@guardian/source-react-components"; import { isGroup, isUser } from "../../shared/graphql/extraTypes"; import { useConfirmModal } from "./modal"; import { groupToMentionHandle, userToMentionHandle } from "./mentionsUtil"; +import { IMAGING_REQUEST_ITEM_TYPE } from "../../shared/octopusImaging"; interface SendMessageAreaProps { payloadToBeSent: PayloadAndType | null; @@ -178,7 +179,11 @@ export const SendMessageArea = ({ } `} onClick={sendItem} - disabled={isItemSending || !(message || payloadToBeSent)} + disabled={ + isItemSending || + !(message || payloadToBeSent) || + (payloadToBeSent?.type === IMAGING_REQUEST_ITEM_TYPE && !message) + } > {isItemSending ? ( diff --git a/client/src/types/PayloadAndType.ts b/client/src/types/PayloadAndType.ts index 981a4fc5c..e06b5547d 100644 --- a/client/src/types/PayloadAndType.ts +++ b/client/src/types/PayloadAndType.ts @@ -1,3 +1,8 @@ +import { + IMAGING_REQUEST_ITEM_TYPE, + ImagingOrderPayloadFields, +} from "../../../shared/octopusImaging"; + export const sources = ["grid"] as const; export const sourceTypes = ["crop", "original", "search"] as const; @@ -8,10 +13,15 @@ export type SourceType = typeof sourceTypes[number]; export const isSourceType = (sourceType: unknown): sourceType is SourceType => sourceTypes.includes(sourceType as SourceType); -export type PayloadType = `${Source}-${SourceType}`; +export type PayloadType = + | `${Source}-${SourceType}` + | typeof IMAGING_REQUEST_ITEM_TYPE; export const isPayloadType = ( payloadType: string ): payloadType is PayloadType => { + if (payloadType === IMAGING_REQUEST_ITEM_TYPE) { + return true; + } const parts = payloadType.split("-"); return parts.length === 2 && isSource(parts[0]) && isSourceType(parts[1]); }; @@ -48,7 +58,15 @@ export type DynamicGridPayload = { payload: PayloadWithApiUrl; }; -export type PayloadAndType = StaticGridPayload | DynamicGridPayload; +export type ImagingOrderPayload = { + type: typeof IMAGING_REQUEST_ITEM_TYPE; + payload: PayloadWithThumbnail & ImagingOrderPayloadFields; +}; + +export type PayloadAndType = + | StaticGridPayload + | DynamicGridPayload + | ImagingOrderPayload; export const buildPayloadAndType = ( type: string, @@ -63,5 +81,7 @@ export const buildPayloadAndType = ( "thumbnail" in payload ) { return { type, payload }; + } else if (type === IMAGING_REQUEST_ITEM_TYPE && "requestType" in payload) { + return { type, payload }; } }; diff --git a/client/src/types/Telemetry.ts b/client/src/types/Telemetry.ts index 69f53a0d6..f7e74a13c 100644 --- a/client/src/types/Telemetry.ts +++ b/client/src/types/Telemetry.ts @@ -7,6 +7,7 @@ export enum PINBOARD_TELEMETRY_TYPE { FLOATY_EXPANDED = "FLOATY_EXPANDED", FLOATY_CLOSED = "FLOATY_CLOSED", ADD_TO_PINBOARD_BUTTON = "ADD_TO_PINBOARD_BUTTON", + IMAGING_REQUEST_VIA_BUTTON = "IMAGING_REQUEST_VIA_BUTTON", OPEN_FEEDBACK_FORM = "OPEN_FEEDBACK_FORM", OPEN_FEEDBACK_CHAT = "OPEN_FEEDBACK_CHAT", EXPAND_FEEDBACK_DETAILS = "OPEN_FEEDBACK_FORM", diff --git a/shared/octopusImaging.ts b/shared/octopusImaging.ts index ccb8bb6ce..c9bda3e1c 100644 --- a/shared/octopusImaging.ts +++ b/shared/octopusImaging.ts @@ -1 +1,13 @@ export const IMAGING_REQUEST_ITEM_TYPE = "imaging-request"; + +export const IMAGINE_REQUEST_TYPES = [ + "Cut out", // only 'cut out' is required for now + // "Break out", + // "Retouch", +] as const; + +export type ImagingRequestType = typeof IMAGINE_REQUEST_TYPES[number]; + +export type ImagingOrderPayloadFields = { + requestType: ImagingRequestType; +};