Skip to content

Commit edbb5ff

Browse files
committed
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.
1 parent 164c4d1 commit edbb5ff

File tree

11 files changed

+206
-51
lines changed

11 files changed

+206
-51
lines changed

client/src/addToPinboardButton.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import { textSans } from "../fontNormaliser";
99
import root from "react-shadow/emotion";
1010
import * as Sentry from "@sentry/react";
1111
import { TelemetryContext, PINBOARD_TELEMETRY_TYPE } from "./types/Telemetry";
12+
import {
13+
IMAGINE_REQUEST_TYPES,
14+
IMAGING_REQUEST_ITEM_TYPE,
15+
} from "../../shared/octopusImaging";
1216

1317
export const ASSET_HANDLE_HTML_TAG = "asset-handle";
1418

@@ -80,6 +84,52 @@ const AddToPinboardButton = (props: AddToPinboardButtonProps) => {
8084
`}
8185
/>{" "}
8286
</button>
87+
{payloadToBeSent.type === "grid-original" && (
88+
<button
89+
onClick={() => {
90+
props.setPayloadToBeSent({
91+
type: IMAGING_REQUEST_ITEM_TYPE,
92+
payload: {
93+
...payloadToBeSent.payload,
94+
requestType: IMAGINE_REQUEST_TYPES[0],
95+
},
96+
});
97+
props.expand();
98+
sendTelemetryEvent?.(
99+
PINBOARD_TELEMETRY_TYPE.IMAGING_REQUEST_VIA_BUTTON,
100+
{
101+
assetType: IMAGING_REQUEST_ITEM_TYPE,
102+
}
103+
);
104+
}}
105+
css={css`
106+
display: flex;
107+
align-items: center;
108+
margin-top: ${space[1]}px;
109+
background-color: ${pinboard[500]};
110+
${textSans.xsmall()};
111+
border: none;
112+
border-radius: 100px;
113+
padding: 0 ${space[2]}px 0 ${space[3]}px;
114+
line-height: 2;
115+
cursor: pointer;
116+
color: ${pinMetal};
117+
white-space: nowrap; // TODO this is a hack to stop the button from wrapping, but we should think of wording that fits better
118+
`}
119+
>
120+
Imaging order
121+
<PinIcon
122+
css={css`
123+
height: 18px;
124+
margin-left: ${space[1]}px;
125+
path {
126+
stroke: ${pinMetal};
127+
stroke-width: 1px;
128+
}
129+
`}
130+
/>{" "}
131+
</button>
132+
)}
83133
</root.div>
84134
);
85135
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { css } from "@emotion/react";
2+
import React from "react";
3+
import { ImagingOrderPayload } from "../types/PayloadAndType";
4+
import {
5+
IMAGINE_REQUEST_TYPES,
6+
IMAGING_REQUEST_ITEM_TYPE,
7+
ImagingRequestType,
8+
} from "../../../shared/octopusImaging";
9+
import { agateSans } from "../../fontNormaliser";
10+
import { useGlobalStateContext } from "../globalState";
11+
import { space } from "@guardian/source-foundations";
12+
13+
interface OctopusImagingOrderDisplayProps extends ImagingOrderPayload {
14+
isEditable?: boolean;
15+
}
16+
17+
export const OctopusImagingOrderDisplay = ({
18+
payload,
19+
isEditable,
20+
}: OctopusImagingOrderDisplayProps) => {
21+
const { setPayloadToBeSent } = useGlobalStateContext();
22+
return (
23+
<div
24+
css={css`
25+
${agateSans.xxsmall()}
26+
`}
27+
>
28+
<h3
29+
css={css`
30+
margin: 0 0 ${space[1]}px;
31+
`}
32+
>
33+
Imaging Order
34+
</h3>
35+
{isEditable && (
36+
<div
37+
css={css`
38+
padding-right: 25px;
39+
`}
40+
>
41+
<em>
42+
{
43+
"Don't forget to provide some notes to help Imaging team to understand your request"
44+
}
45+
</em>
46+
</div>
47+
)}
48+
<img
49+
src={payload.thumbnail}
50+
css={css`
51+
object-fit: contain;
52+
width: 100%;
53+
height: 100%;
54+
`}
55+
draggable={false}
56+
// TODO: hover for larger thumbnail
57+
/>
58+
<div>
59+
<strong>Request Type: </strong>
60+
{isEditable ? (
61+
<select
62+
onClick={(e) => e.stopPropagation()}
63+
onChange={(event) =>
64+
setPayloadToBeSent({
65+
type: IMAGING_REQUEST_ITEM_TYPE,
66+
payload: {
67+
...payload,
68+
requestType: event.target.value as ImagingRequestType,
69+
},
70+
})
71+
}
72+
>
73+
{IMAGINE_REQUEST_TYPES.map((requestType, index) => (
74+
<option key={index}>{requestType}</option>
75+
))}
76+
</select>
77+
) : (
78+
payload.requestType
79+
)}
80+
</div>
81+
</div>
82+
);
83+
};

client/src/itemInputBox.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { isGroup, isUser } from "shared/graphql/extraTypes";
1616
import { groupToMentionHandle, userToMentionHandle } from "./mentionsUtil";
1717
import { useTourProgress } from "./tour/tourState";
1818
import { PINBOARD_TELEMETRY_TYPE, TelemetryContext } from "./types/Telemetry";
19+
import { IMAGING_REQUEST_ITEM_TYPE } from "../../shared/octopusImaging";
1920

2021
interface WithEntity<E> {
2122
entity: E & {
@@ -293,7 +294,11 @@ export const ItemInputBox = ({
293294
((event) => {
294295
event.stopPropagation();
295296
if (isEnterKey(event)) {
296-
if (!isAsGridPayloadLoading && (message || payloadToBeSent)) {
297+
const hasSomethingToSend =
298+
payloadToBeSent?.type === IMAGING_REQUEST_ITEM_TYPE
299+
? message
300+
: message || payloadToBeSent;
301+
if (!isAsGridPayloadLoading && hasSomethingToSend) {
297302
sendItem();
298303
}
299304
event.preventDefault();

client/src/payloadDisplay.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { PayloadAndType } from "./types/PayloadAndType";
44
import { neutral, palette, space } from "@guardian/source-foundations";
55
import { GridStaticImageDisplay } from "./grid/gridStaticImageDisplay";
66
import { GridDynamicSearchDisplay } from "./grid/gridDynamicSearchDisplay";
7-
import { TelemetryContext, PINBOARD_TELEMETRY_TYPE } from "./types/Telemetry";
7+
import { PINBOARD_TELEMETRY_TYPE, TelemetryContext } from "./types/Telemetry";
88
import { Tab } from "./types/Tab";
99
import { FloatingClearButton } from "./floatingClearButton";
1010
import { MamVideoDisplay } from "./mam/mamVideoDisplay";
11+
import { IMAGING_REQUEST_ITEM_TYPE } from "shared/octopusImaging";
12+
import { OctopusImagingOrderDisplay } from "./grid/octopusImagingOrderDisplay";
1113

1214
interface PayloadDisplayProps {
1315
payloadAndType: PayloadAndType;
@@ -22,7 +24,6 @@ export const PayloadDisplay = ({
2224
tab,
2325
shouldNotBeClickable,
2426
}: PayloadDisplayProps) => {
25-
const { payload } = payloadAndType;
2627
const sendTelemetryEvent = useContext(TelemetryContext);
2728
return (
2829
<div
@@ -58,26 +59,29 @@ export const PayloadDisplay = ({
5859
`}
5960
draggable={!shouldNotBeClickable}
6061
onDragStart={(event) => {
61-
event.dataTransfer.setData("URL", payload.embeddableUrl);
62+
event.dataTransfer.setData(
63+
"URL",
64+
payloadAndType.payload.embeddableUrl
65+
);
6266
event.dataTransfer.setData(
6367
// prevent grid from accepting these as drops, as per https://github.com/guardian/grid/commit/4b72d93eedcbacb4f90680764d468781a72507f5#diff-771b9da876348ce4b4e057e2d8253324c30a8f3db4e434d49b3ce70dbbdb0775R138-R140
6468
"application/vnd.mediaservice.kahuna.image",
6569
"true"
6670
);
6771
sendTelemetryEvent?.(PINBOARD_TELEMETRY_TYPE.DRAG_FROM_PINBOARD, {
68-
assetType: payloadAndType?.type,
72+
assetType: payloadAndType.type,
6973
...(tab && { tab }),
7074
});
7175
}}
7276
onClick={
7377
shouldNotBeClickable
7478
? undefined
7579
: () => {
76-
window.open(payload.embeddableUrl, "_blank");
80+
window.open(payloadAndType.payload.embeddableUrl, "_blank");
7781
sendTelemetryEvent?.(
7882
PINBOARD_TELEMETRY_TYPE.GRID_ASSET_OPENED,
7983
{
80-
assetType: payloadAndType?.type,
84+
assetType: payloadAndType.type,
8185
tab: tab as Tab,
8286
}
8387
);
@@ -106,6 +110,14 @@ export const PayloadDisplay = ({
106110
/>
107111
)}
108112

113+
{payloadAndType.type === IMAGING_REQUEST_ITEM_TYPE && (
114+
<OctopusImagingOrderDisplay
115+
type={payloadAndType.type}
116+
payload={payloadAndType.payload}
117+
isEditable={!!clearPayloadToBeSent}
118+
/>
119+
)}
120+
109121
{clearPayloadToBeSent && (
110122
<FloatingClearButton clear={clearPayloadToBeSent} />
111123
)}

client/src/pinboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ export const Pinboard: React.FC<PinboardProps> = ({
383383
onSuccessfulSend={onSuccessfulSend}
384384
payloadToBeSent={maybeEditingItemId ? null : payloadToBeSent}
385385
setPayloadToBeSent={setPayloadToBeSent}
386-
clearPayloadToBeSent={clearPayloadToBeSent}
386+
clearPayloadToBeSent={() => setPayloadToBeSent(null)}
387387
onError={(error) => setError(pinboardId, error)}
388388
userEmail={userEmail}
389389
pinboardId={pinboardId}

client/src/selectPinboard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export const SelectPinboard = ({
7474
activePinboards,
7575
activePinboardIds,
7676
payloadToBeSent,
77-
clearPayloadToBeSent,
77+
setPayloadToBeSent,
7878

7979
isExpanded,
8080

@@ -429,7 +429,7 @@ export const SelectPinboard = ({
429429

430430
<PayloadDisplay
431431
payloadAndType={payloadToBeSent}
432-
clearPayloadToBeSent={clearPayloadToBeSent}
432+
clearPayloadToBeSent={() => setPayloadToBeSent(null)}
433433
/>
434434
</div>
435435
)}

client/src/sendMessageArea.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { useConfirmModal } from "./modal";
1717
import { groupToMentionHandle, userToMentionHandle } from "./mentionsUtil";
1818
import { useTourProgress } from "./tour/tourState";
1919
import { demoPinboardData } from "./tour/tourConstants";
20+
import { IMAGING_REQUEST_ITEM_TYPE } from "../../shared/octopusImaging";
2021

2122
interface SendMessageAreaProps {
2223
payloadToBeSent: PayloadAndType | null;
@@ -219,7 +220,8 @@ export const SendMessageArea = ({
219220
disabled={
220221
isItemSending ||
221222
isAsGridPayloadLoading ||
222-
!(message || payloadToBeSent)
223+
!(message || payloadToBeSent) ||
224+
(payloadToBeSent?.type === IMAGING_REQUEST_ITEM_TYPE && !message)
223225
}
224226
>
225227
{isItemSending ? (

client/src/types/PayloadAndType.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import * as Sentry from "@sentry/react";
2+
import {
3+
IMAGING_REQUEST_ITEM_TYPE,
4+
ImagingRequestType,
5+
} from "shared/octopusImaging";
26

37
export const sources = ["grid", "mam"] as const;
48
export const sourceTypes = ["crop", "original", "search", "video"] as const;
@@ -10,10 +14,15 @@ export type SourceType = (typeof sourceTypes)[number];
1014
export const isSourceType = (sourceType: unknown): sourceType is SourceType =>
1115
sourceTypes.includes(sourceType as SourceType);
1216

13-
export type PayloadType = `${Source}-${SourceType}`; // TODO improve this type as it enumerates all the combinations, e.g. mam-original which is not valid
17+
export type PayloadType =
18+
| `${Source}-${SourceType}`
19+
| typeof IMAGING_REQUEST_ITEM_TYPE; // TODO improve this type as it enumerates all the combinations, e.g. mam-original which is not valid
1420
export const isPayloadType = (
1521
payloadType: string
1622
): payloadType is PayloadType => {
23+
if (payloadType === IMAGING_REQUEST_ITEM_TYPE) {
24+
return true;
25+
}
1726
const parts = payloadType.split("-");
1827
return parts.length === 2 && isSource(parts[0]) && isSourceType(parts[1]);
1928
};
@@ -34,10 +43,15 @@ export interface PayloadWithApiUrl extends PayloadCommon {
3443
apiUrl: string;
3544
}
3645

46+
export interface PayloadWithRequestType extends PayloadWithThumbnail {
47+
requestType: ImagingRequestType;
48+
}
49+
3750
export type Payload =
3851
| PayloadWithThumbnail
3952
| PayloadWithApiUrl
40-
| PayloadWithExternalUrl;
53+
| PayloadWithExternalUrl
54+
| PayloadWithRequestType;
4155
export const isPayload = (maybePayload: unknown): maybePayload is Payload => {
4256
return (
4357
typeof maybePayload === "object" &&
@@ -62,10 +76,16 @@ export type MamVideoPayload = {
6276
payload: PayloadWithExternalUrl;
6377
};
6478

79+
export type ImagingOrderPayload = {
80+
type: typeof IMAGING_REQUEST_ITEM_TYPE;
81+
payload: PayloadWithRequestType;
82+
};
83+
6584
export type PayloadAndType =
6685
| StaticGridPayload
6786
| DynamicGridPayload
68-
| MamVideoPayload;
87+
| MamVideoPayload
88+
| ImagingOrderPayload;
6989

7090
export const buildPayloadAndType = (
7191
type: string,
@@ -86,6 +106,8 @@ export const buildPayloadAndType = (
86106
"externalUrl" in payload
87107
) {
88108
return { type, payload };
109+
} else if (type === IMAGING_REQUEST_ITEM_TYPE && "requestType" in payload) {
110+
return { type, payload };
89111
}
90112
};
91113

client/src/types/Telemetry.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export enum PINBOARD_TELEMETRY_TYPE {
77
FLOATY_EXPANDED = "FLOATY_EXPANDED",
88
FLOATY_CLOSED = "FLOATY_CLOSED",
99
ADD_TO_PINBOARD_BUTTON = "ADD_TO_PINBOARD_BUTTON",
10+
IMAGING_REQUEST_VIA_BUTTON = "IMAGING_REQUEST_VIA_BUTTON",
1011
OPEN_FEEDBACK_FORM = "OPEN_FEEDBACK_FORM",
1112
OPEN_FEEDBACK_CHAT = "OPEN_FEEDBACK_CHAT",
1213
EXPAND_FEEDBACK_DETAILS = "OPEN_FEEDBACK_FORM",

shared/octopusImaging.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const IMAGING_REQUEST_ITEM_TYPE = "imaging-request";
2+
3+
export const IMAGINE_REQUEST_TYPES = [
4+
"Cut out", // only 'cut out' is required for now
5+
// "Break out",
6+
// "Retouch",
7+
] as const;
8+
9+
export type ImagingRequestType = (typeof IMAGINE_REQUEST_TYPES)[number];

0 commit comments

Comments
 (0)