translations were added

This commit is contained in:
Vladimir Khvan 2023-05-18 12:59:48 +05:00
parent 1b823190a1
commit 073b01d2cf
25 changed files with 303 additions and 116 deletions

View File

@ -0,0 +1,68 @@
{
"Webhook": "Webhook",
"Webhooks": "Webhooks",
"CreateWebhook": "Create webhook",
"WebhooksInfo": "Use webhooks to perform custom actions on the side of any application or website you are using based on various events in ONLYOFFICE Docspace.\r\nHere, you can create and manage all your webhooks, configure them, and browse history of every webhook to audit their performance.",
"WebhooksGuide": "Webhooks guide",
"WebhookCreationHint": "This webhook will be assigned to all events in DocSpace",
"WebhookName": "Webhook name",
"EnterWebhookName": "Enter webhook name",
"PayloadUrl": "Payload URL",
"EnterUrl": "Enter URL",
"SSLVerification": "SSL verification",
"SSLHint": "By default, we verify SSL certificates when delivering payloads.",
"EnableSSL": "Enable SSL verification",
"DisableSSL": "Disable (not recommended)",
"EnterSecretKey": "Enter secret key",
"SecretKey": "Secret key",
"SecretKeyHint": "Setting a webhook secret allows you to verify requests sent to the payload URL.",
"ReadMore": "Read more",
"SecretKeyWarning": "You cannot retrieve your secret key again once it has been saved. If you've lost or forgotten this secret key, you can reset it, but all integrations using this secret will need to be updated.",
"ResetKey": "Reset key",
"Generate": "Generate",
"Save": "Save",
"Cancel": "Cancel",
"DeleteHint": "The webhook will be deleted permanently.\r\nYou will not be able to undo this action.",
"DeleteForever": "Delete forever",
"NotSent": "Not sent",
"WebhookEditedSuccessfully": "Webhook configuration edited successfully",
"WebhookRemoved": "Webhook removed",
"WebhookHistory": "Webhook history",
"DeleteWebhook": "Delete webhook",
"SettingsWebhook": "Settings webhook",
"DeleteWebhookForeverQuestion": "Delete Webhook forever?",
"URL": "URL",
"State": "State",
"WebhookRedilivered": "Webhook redelivered",
"WebhookDetails": "Webhooks details",
"Request": "Request",
"Response": "Response",
"FailedToConnect": "We couldnt deliver this payload: failed to connect to host",
"RequestPostHeader": "Request post header",
"RequestPostBody": "Request post body",
"RequestHeaderCopied": "Request post header successfully copied to clipboard",
"RequestBodyCopied": "Request post body successfully copied to clipboard",
"ResponsePostHeader": "Response post header",
"ResponsePostBody": "Response post body",
"ResponseHeaderCopied": "Response post header successfully copied to clipboard",
"ResponseBodyCopied": "Response post body successfully copied to clipboard",
"PayloadIsTooLarge": "Payload is too large to display.",
"ViewRawPayload": "View raw payload",
"Retry": "Retry",
"UnselectAll": "Unselect all",
"History": "History",
"EventHint": "Deliveries are automatically deleted after 15 days",
"NothingFound": "Nothing found",
"NoResultsMatched": "No results match this filter. Try a different one or clear filter to view all items.",
"SearchOptions": "Search options",
"Apply": "Apply",
"SelectDate": "Select date",
"SelectDeliveryTime": "Select Delivery time",
"DeliveryDate": "Delivery date",
"From": "From",
"Before": "Before",
"Status": "Status",
"EventID": "Event ID",
"Delivery": "Delivery",
"Add": "Add"
}

View File

@ -14,6 +14,7 @@ import Headline from "@docspace/common/components/Headline";
import IconButton from "@docspace/components/icon-button";
import { tablet } from "@docspace/components/utils/device";
import { useTranslation } from "react-i18next";
const HeaderContainer = styled.div`
position: sticky;
@ -42,13 +43,17 @@ const HeaderContainer = styled.div`
const DetailsNavigationHeader = (props) => {
const { eventId, retryWebhookEvent } = props;
const { t } = useTranslation(["Webhooks", "Common"]);
const navigate = useNavigate();
const onBack = () => {
navigate(-1);
};
const handleRetryEvent = async () => {
await retryWebhookEvent(eventId);
toastr.success("Webhook retry again", <b>Done</b>);
toastr.success(
t("WebhookRedilivered", { ns: "Webhooks" }),
<b>{t("Done", { ns: "Common" })}</b>,
);
};
return (
@ -61,7 +66,7 @@ const DetailsNavigationHeader = (props) => {
className="arrow-button"
/>
<Headline type="content" truncate={true} className="headline">
Webhook details
{t("WebhookDetails", { ns: "Webhooks" })}
</Headline>
<IconButton iconName={RetryIcon} size="17" isFill={true} onClick={handleRetryEvent} />

View File

@ -4,6 +4,7 @@ import Submenu from "@docspace/components/submenu";
import { RequestDetails } from "./RequestDetails";
import { ResponseDetails } from "./ResponseDetails";
import { useTranslation } from "react-i18next";
const SubmenuWrapper = styled.div`
.sticky {
@ -13,10 +14,11 @@ const SubmenuWrapper = styled.div`
`;
export const MessagesDetails = ({ webhookDetails }) => {
const { t } = useTranslation(["Webhooks"]);
const menuData = [
{
id: "webhookRequest",
name: "Request",
name: t("Request", { ns: "Webhooks" }),
content: <RequestDetails webhookDetails={webhookDetails} />,
},
];
@ -25,7 +27,7 @@ export const MessagesDetails = ({ webhookDetails }) => {
webhookDetails.status < 500 &&
menuData.push({
id: "webhookResponse",
name: "Response",
name: t("Response", { ns: "Webhooks" }),
content: <ResponseDetails webhookDetails={webhookDetails} />,
});

View File

@ -3,6 +3,7 @@ import styled from "styled-components";
import { Text, Textarea } from "@docspace/components";
import DangerIcon from "PUBLIC_DIR/images/danger.toast.react.svg?url";
import { useTranslation } from "react-i18next";
const DetailsWrapper = styled.div`
width: 100%;
@ -24,16 +25,17 @@ const ErrorMessageTooltip = styled.div`
`;
export const RequestDetails = ({ webhookDetails }) => {
const { t } = useTranslation(["Webhooks"]);
return (
<DetailsWrapper>
{webhookDetails.status === 0 && (
<ErrorMessageTooltip>
<img src={DangerIcon} alt="danger icon" style={{ marginRight: "8px" }} />
We couldnt deliver this payload: failed to connect to host
{t("FailedToConnect", { ns: "Webhooks" })}
</ErrorMessageTooltip>
)}
<Text as="h3" fontWeight={600} style={{ marginBottom: "4px" }}>
Request post header
{t("RequestPostHeader", { ns: "Webhooks" })}
</Text>
{!webhookDetails.requestHeaders ? (
<Textarea isDisabled />
@ -44,12 +46,12 @@ export const RequestDetails = ({ webhookDetails }) => {
hasNumeration
isFullHeight
isJSONField
copyInfoText="Request post header successfully copied to clipboard"
copyInfoText={t("RequestHeaderCopied", { ns: "Webhooks" })}
/>
)}
<Text as="h3" fontWeight={600} style={{ marginBottom: "4px", marginTop: "16px" }}>
Request post body
{t("RequestPostBody", { ns: "Webhooks" })}
</Text>
<Textarea
value={webhookDetails.requestPayload}
@ -57,7 +59,7 @@ export const RequestDetails = ({ webhookDetails }) => {
enableCopy
hasNumeration
isFullHeight
copyInfoText="Request post body successfully copied to clipboard"
copyInfoText={t("RequestBodyCopied", { ns: "Webhooks" })}
/>
</DetailsWrapper>
);

View File

@ -4,6 +4,7 @@ import { Text, Textarea, Button } from "@docspace/components";
import json_beautifier from "csvjson-json_beautifier";
import { isMobileOnly } from "react-device-detect";
import { useTranslation } from "react-i18next";
const DetailsWrapper = styled.div`
width: 100%;
@ -48,6 +49,7 @@ function isJSON(jsonString) {
export const ResponseDetails = ({ webhookDetails }) => {
const responsePayload = webhookDetails.responsePayload?.trim();
const { t } = useTranslation(["Webhooks"]);
const beautifiedJSON = isJSON(responsePayload)
? json_beautifier(JSON.parse(responsePayload), {
@ -70,7 +72,7 @@ export const ResponseDetails = ({ webhookDetails }) => {
return (
<DetailsWrapper>
<Text as="h3" fontWeight={600} style={{ marginBottom: "4px" }}>
Response post header
{t("ResponsePostHeader", { ns: "Webhooks" })}
</Text>
<Textarea
value={webhookDetails.responseHeaders}
@ -78,20 +80,20 @@ export const ResponseDetails = ({ webhookDetails }) => {
hasNumeration
isFullHeight
isJSONField
copyInfoText="Response post header successfully copied to clipboard"
copyInfoText={t("ResponseHeaderCopied", { ns: "Webhooks" })}
/>
<Text as="h3" fontWeight={600} style={{ marginBottom: "4px", marginTop: "16px" }}>
Response post body
{t("ResponsePostBody", { ns: "Webhooks" })}
</Text>
{responsePayload.length > 4000 || numberOfLines > 100 ? (
<LargePayloadStub>
<Text fontWeight={600} color="#657077">
Payload is too large to display.
{t("PayloadIsTooLarge", { ns: "Webhooks" })}
</Text>
<Button
size="small"
onClick={openRawPayload}
label="View raw payload"
label={t("ViewRawPayload", { ns: "Webhooks" })}
scale={isMobileOnly}
/>
</LargePayloadStub>
@ -104,7 +106,7 @@ export const ResponseDetails = ({ webhookDetails }) => {
enableCopy
hasNumeration
isFullHeight
copyInfoText="Response post body successfully copied to clipboard"
copyInfoText={t("ResponseBodyCopied", { ns: "Webhooks" })}
/>
) : (
<Textarea
@ -112,7 +114,7 @@ export const ResponseDetails = ({ webhookDetails }) => {
enableCopy
heightScale
className="textareaBody"
copyInfoText="Response post body successfully copied to clipboard"
copyInfoText={t("ResponseBodyCopied", { ns: "Webhooks" })}
/>
)}
</DetailsWrapper>

View File

@ -7,6 +7,7 @@ import { Text } from "@docspace/components";
import { Link } from "@docspace/components";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
const EmptyFilterWrapper = styled.div`
width: 100%;
@ -39,6 +40,7 @@ const EmptyFilterContent = styled.div`
const EmptyFilter = (props) => {
const { applyFilters, formatFilters, clearHistoryFilters } = props;
const { t } = useTranslation(["Webhooks", "Common"]);
const clearFilters = () => {
clearHistoryFilters(null);
@ -56,15 +58,15 @@ const EmptyFilter = (props) => {
<img src={EmptyFilterImg} alt="Empty filter" />
<div className="emptyFilterText">
<Text fontSize="16px" fontWeight={700} as="p" className="emptyFilterHeading">
Nothing found
{t("NothingFound", { ns: "Webhooks" })}
</Text>
<Text fontSize="12px" color="#555F65">
No results match this filter. Try a different one or clear filter to view all items.
{t("NoResultsMatched", { ns: "Webhooks" })}
</Text>
<span className="clearFilter" onClick={clearFilters}>
<img src={ClearEmptyFilterIcon} alt="Clear filter" className="clearFilterIcon" />
<img src={ClearEmptyFilterIcon} alt={t("ClearFilter", { ns: "Webhooks" })} className="clearFilterIcon" />
<Link color="#657077" isHovered fontWeight={600}>
Clear filter
{t("ClearFilter", { ns: "Common" })}
</Link>
</span>
</div>

View File

@ -12,6 +12,8 @@ import { Calendar } from "@docspace/components";
import { TimePicker } from "@docspace/components";
import { isMobileOnly } from "react-device-detect";
import { useTranslation } from "react-i18next";
const TimePickerCell = styled.span`
margin-left: 8px;
display: inline-flex;
@ -39,6 +41,8 @@ const DeliveryDatePicker = ({ Selectors, filters, setFilters, isApplied, setIsAp
const [isCalendarOpen, setIsCalendarOpen] = useState(false);
const [isTimeOpen, setIsTimeOpen] = useState(false);
const { t } = useTranslation(["Webhooks"]);
const calendarRef = useRef();
const selectorRef = useRef();
@ -95,9 +99,13 @@ const DeliveryDatePicker = ({ Selectors, filters, setFilters, isApplied, setIsAp
const DateSelector = () => (
<div>
<SelectorAddButton title="add" onClick={toggleCalendar} style={{ marginRight: "8px" }} />
<SelectorAddButton
title={t("Add", { ns: "Webhooks" })}
onClick={toggleCalendar}
style={{ marginRight: "8px" }}
/>
<Text isInline fontWeight={600} color="#A3A9AE">
Select date
{t("SelectDate", { ns: "Webhooks" })}
</Text>
{isCalendarOpen && <CalendarElement />}
</div>
@ -140,9 +148,13 @@ const DeliveryDatePicker = ({ Selectors, filters, setFilters, isApplied, setIsAp
const TimeSelectorAdder = () => (
<TimePickerCell>
<SelectorAddButton title="add" onClick={showTimePicker} style={{ marginRight: "8px" }} />
<SelectorAddButton
title={t("Add", { ns: "Webhooks" })}
onClick={showTimePicker}
style={{ marginRight: "8px" }}
/>
<Text isInline fontWeight={600} color="#A3A9AE">
Select Delivery time
{t("SelectDeliveryTime", { ns: "Webhooks" })}
</Text>
</TimePickerCell>
);
@ -160,7 +172,7 @@ const DeliveryDatePicker = ({ Selectors, filters, setFilters, isApplied, setIsAp
return (
<>
<Text fontWeight={600} fontSize="15px">
Delivery date
{t("DeliveryDate", { ns: "Webhooks" })}
</Text>
<Selectors ref={selectorRef}>
{filters.deliveryDate === null ? (
@ -180,7 +192,7 @@ const DeliveryDatePicker = ({ Selectors, filters, setFilters, isApplied, setIsAp
<TimePickerCell>
<span className="timePickerItem">
<Text isInline fontWeight={600} color="#A3A9AE" style={{ marginRight: "8px" }}>
From
{t("From", { ns: "Webhooks" })}
</Text>
<TimePicker
date={filters.deliveryFrom}
@ -190,7 +202,7 @@ const DeliveryDatePicker = ({ Selectors, filters, setFilters, isApplied, setIsAp
/>
</span>
<Text isInline fontWeight={600} color="#A3A9AE" style={{ marginRight: "8px" }}>
Before
{t("Before", { ns: "Webhooks" })}
</Text>
<TimePicker
date={filters.deliveryTo}

View File

@ -4,6 +4,7 @@ import { inject, observer } from "mobx-react";
import { Text } from "@docspace/components";
import { Button } from "@docspace/components";
import { useTranslation } from "react-i18next";
const RoundedButton = styled(Button)`
font-size: 13px;
@ -14,11 +15,20 @@ const RoundedButton = styled(Button)`
`;
const StatusPicker = ({ Selectors, filters, setFilters }) => {
const { t } = useTranslation(["Webhooks"]);
const StatusCodes = {
0: "Not sent",
200: "2XX",
300: "3XX",
400: "4XX",
500: "5XX",
};
const isStatusSelected = (statusCode) => {
return filters.status.includes(statusCode);
};
const handleStatusClick = (e) => {
const statusCode = e.target.textContent;
const handleStatusClick = (statusCode) => {
setFilters((prevFilters) => ({
...prevFilters,
status: prevFilters.status.includes(statusCode)
@ -29,18 +39,34 @@ const StatusPicker = ({ Selectors, filters, setFilters }) => {
return (
<>
<Text fontWeight={600} fontSize="15px">
Status
{t("Status", { ns: "Webhooks" })}
</Text>
<Selectors>
<RoundedButton
label="Not sent"
onClick={handleStatusClick}
primary={isStatusSelected("Not sent")}
label={t("NotSent", { ns: "Webhooks" })}
onClick={() => handleStatusClick(StatusCodes[0])}
primary={isStatusSelected(StatusCodes[0])}
/>
<RoundedButton
label={StatusCodes[200]}
onClick={() => handleStatusClick(StatusCodes[200])}
primary={isStatusSelected(StatusCodes[200])}
/>
<RoundedButton
label={StatusCodes[300]}
onClick={() => handleStatusClick(StatusCodes[300])}
primary={isStatusSelected(StatusCodes[300])}
/>
<RoundedButton
label={StatusCodes[400]}
onClick={() => handleStatusClick(StatusCodes[400])}
primary={isStatusSelected(StatusCodes[400])}
/>
<RoundedButton
label={StatusCodes[500]}
onClick={() => handleStatusClick(StatusCodes[500])}
primary={isStatusSelected(StatusCodes[500])}
/>
<RoundedButton label="2XX" onClick={handleStatusClick} primary={isStatusSelected("2XX")} />
<RoundedButton label="3XX" onClick={handleStatusClick} primary={isStatusSelected("3XX")} />
<RoundedButton label="4XX" onClick={handleStatusClick} primary={isStatusSelected("4XX")} />
<RoundedButton label="5XX" onClick={handleStatusClick} primary={isStatusSelected("5XX")} />
</Selectors>
</>
);

View File

@ -9,6 +9,8 @@ import { Button } from "@docspace/components";
import DeliveryDatePicker from "./DeliveryDatePicker";
import StatusPicker from "./StatusPicker";
import { useTranslation } from "react-i18next";
const Footer = styled.div`
width: 100%;
display: flex;
@ -34,6 +36,7 @@ const Separator = styled.hr`
const FilterDialog = (props) => {
const { visible, closeModal, applyFilters, formatFilters, setHistoryFilters } = props;
const { t } = useTranslation(["Webhooks"]);
const [filters, setFilters] = useState({
deliveryDate: null,
@ -58,7 +61,7 @@ const FilterDialog = (props) => {
return (
<ModalDialog withFooterBorder visible={visible} onClose={closeModal} displayType="aside">
<ModalDialog.Header>Search options</ModalDialog.Header>
<ModalDialog.Header>{t("SearchOptions", { ns: "Webhooks" })}</ModalDialog.Header>
<ModalDialog.Body>
<DeliveryDatePicker
Selectors={Selectors}
@ -74,8 +77,13 @@ const FilterDialog = (props) => {
<ModalDialog.Footer>
<Footer>
<Button label="Apply" size="normal" primary={true} onClick={handleApplyFilters} />
<Button label="Cancel" size="normal" onClick={closeModal} />
<Button
label={t("Apply", { ns: "Webhooks" })}
size="normal"
primary={true}
onClick={handleApplyFilters}
/>
<Button label={t("Cancel", { ns: "Webhooks" })} size="normal" onClick={closeModal} />
</Footer>
</ModalDialog.Footer>
</ModalDialog>

View File

@ -9,6 +9,7 @@ import IconButton from "@docspace/components/icon-button";
import { useParams } from "react-router-dom";
import FilterDialog from "./FilterDialog";
import StatusBar from "./StatusBar";
import { useTranslation } from "react-i18next";
const ListHeader = styled.header`
display: flex;
@ -54,6 +55,7 @@ const FilterButton = styled.div`
const HistoryFilterHeader = (props) => {
const { applyFilters, historyFilters } = props;
const { t } = useTranslation(["Webhooks"]);
const { id } = useParams();
const [isFiltersVisible, setIsFiltersVisible] = useState(false);
@ -69,7 +71,9 @@ const HistoryFilterHeader = (props) => {
return (
<div>
<ListHeader>
<ListHeading>Webhook {id}</ListHeading>
<ListHeading>
{t("Webhook", { ns: "Webhooks" })} {id}
</ListHeading>
<FilterButton onClick={openFiltersModal}>
<IconButton iconName={FilterReactSvrUrl} size={16} />

View File

@ -17,6 +17,7 @@ import { isMobile, isMobileOnly } from "react-device-detect";
import DropDownItem from "@docspace/components/drop-down-item";
import toastr from "@docspace/components/toast/toastr";
import { useTranslation } from "react-i18next";
const HeaderContainer = styled.div`
position: sticky;
@ -97,6 +98,7 @@ const HistoryHeader = (props) => {
const onBack = () => {
navigate(-1);
};
const { t } = useTranslation(["Webhooks", "Common"]);
const handleGroupSelection = (isChecked) => {
isChecked ? checkAllIds() : emptyCheckedIds();
@ -104,13 +106,16 @@ const HistoryHeader = (props) => {
const handleRetryAll = async () => {
await retryWebhookEvents(checkedEventIds);
toastr.success(`Webhooks redelivered: ${checkedEventIds.length}`, <b>Done</b>);
toastr.success(
`${t("WebhookRedilivered", { ns: "Webhooks" })}: ${checkedEventIds.length}`,
<b>{t("Done", { ns: "Common" })}</b>,
);
};
const headerMenu = [
{
id: "retry-event-option",
label: "Retry",
label: t("Retry", { ns: "Webhooks" }),
onClick: handleRetryAll,
iconUrl: RetryIcon,
},
@ -120,13 +125,13 @@ const HistoryHeader = (props) => {
<>
<DropDownItem
key="select-all-event-ids"
label="Select all"
label={t("SelectAll", { ns: "Commmon" })}
data-index={0}
onClick={() => checkAllIds()}
/>
<DropDownItem
key="unselect-all-event-ids"
label="Unselect all"
label={t("UnselectAll", { ns: "Webhooks" })}
data-index={1}
onClick={emptyCheckedIds}
/>
@ -143,10 +148,10 @@ const HistoryHeader = (props) => {
className="arrow-button"
/>
<Headline type="content" truncate={true} className="headline">
History
{t("History", { ns: "Webhooks" })}
</Headline>
<Hint backgroundColor="#F8F9F9" color="#555F65">
Deliveries are automatically deleted after 15 days
{t("EventHint", { ns: "Webhooks" })}
</Hint>
</>
);

View File

@ -11,27 +11,33 @@ import InfoIcon from "PUBLIC_DIR/images/info.outline.react.svg?url";
import toastr from "@docspace/components/toast/toastr";
import { useTranslation } from "react-i18next";
const HistoryRow = (props) => {
const { historyItem, sectionWidth, toggleEventId, isIdChecked, retryWebhookEvent } = props;
const { t } = useTranslation(["Webhooks", "Common"]);
const navigate = useNavigate();
const redirectToDetails = () => navigate(window.location.pathname + `/${historyItem.id}`);
const handleRetryEvent = async () => {
await retryWebhookEvent(historyItem.id);
toastr.success("Webhook redelivered", <b>Done</b>);
toastr.success(
t("WebhookRedilivered", { ns: "Webhooks" }),
<b>{t("Done", { ns: "Common" })}</b>,
);
};
const handleOnSelect = () => toggleEventId(historyItem.id);
const contextOptions = [
{
key: "Webhook details dropdownItem",
label: "Webhook details",
label: t("WebhookDetails", { ns: "Webhooks" }),
icon: InfoIcon,
onClick: redirectToDetails,
},
{
key: "Retry dropdownItem",
label: "Retry",
label: t("Retry", { ns: "Webhooks" }),
icon: RetryIcon,
onClick: handleRetryEvent,
},

View File

@ -1,14 +1,16 @@
import React, { useRef } from "react";
import TableHeader from "@docspace/components/table-container/TableHeader";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
const HistoryTableHeader = (props) => {
const { sectionWidth, tableRef } = props;
const { t } = useTranslation(["Webhooks"]);
const columns = useRef([
{
key: "Event ID",
title: "Event ID",
title: t("EventID", { ns: "Webhooks" }),
resizable: true,
enable: true,
default: true,
@ -16,13 +18,13 @@ const HistoryTableHeader = (props) => {
},
{
key: "Status",
title: "Status",
title: t("Status", { ns: "Webhooks" }),
enable: true,
resizable: true,
},
{
key: "Delivery",
title: "Delivery",
title: t("Delivery", { ns: "Webhooks" }),
enable: true,
resizable: true,
},

View File

@ -16,6 +16,8 @@ import toastr from "@docspace/components/toast/toastr";
import RetryIcon from "PUBLIC_DIR/images/refresh.react.svg?url";
import InfoIcon from "PUBLIC_DIR/images/info.outline.react.svg?url";
import { useTranslation } from "react-i18next";
const StyledTableRow = styled(TableRow)`
${(props) =>
props.isHighlight &&
@ -32,24 +34,28 @@ const StyledWrapper = styled.div`
const HistoryTableRow = (props) => {
const { item, toggleEventId, isIdChecked, retryWebhookEvent } = props;
const { t } = useTranslation(["Webhooks", "Common"]);
const navigate = useNavigate();
const redirectToDetails = () => navigate(window.location.pathname + `/${item.id}`);
const handleRetryEvent = async () => {
await retryWebhookEvent(item.id);
toastr.success("Webhook redelivered", <b>Done</b>);
toastr.success(
t("WebhookRedilivered", { ns: "Webhooks" }),
<b>{t("Done", { ns: "Common" })}</b>,
);
};
const contextOptions = [
{
key: "Webhook details dropdownItem",
label: "Webhook details",
label: t("WebhookDetails", { ns: "Webhooks" }),
icon: InfoIcon,
onClick: redirectToDetails,
},
{
key: "Retry dropdownItem",
label: "Retry",
label: t("Retry", { ns: "Webhooks" }),
icon: RetryIcon,
onClick: handleRetryEvent,
},
@ -71,7 +77,7 @@ const HistoryTableRow = (props) => {
<StyledTableRow contextOptions={contextOptions} checked={isChecked}>
<TableCell>
<TableCell checked={isChecked} className="noPadding">
<Checkbox onChange={onChange} isChecked={isChecked} title="TitleSelectFile" />
<Checkbox onChange={onChange} isChecked={isChecked} />
</TableCell>
<Text fontWeight={600}>{item.id}</Text>

View File

@ -11,6 +11,8 @@ import { WebhookConfigsLoader } from "./sub-components/Loaders";
import { isMobile } from "@docspace/components/utils/device";
import { useTranslation } from "react-i18next";
const MainWrapper = styled.div`
width: 100%;
@ -35,7 +37,9 @@ const Webhooks = (props) => {
const { state, loadWebhooks, addWebhook, isWebhookExist, isWebhooksEmpty, setDocumentTitle } =
props;
setDocumentTitle("Webhooks");
const { t, ready } = useTranslation(["Webhooks", "Common"]);
setDocumentTitle(t("Webhooks", { ns: "Webhooks" }));
const [isModalOpen, setIsModalOpen] = useState(false);
@ -53,13 +57,13 @@ const Webhooks = (props) => {
loadWebhooks();
}, []);
return state === "pending" ? (
return state === "pending" || !ready ? (
<WebhookConfigsLoader />
) : state === "success" ? (
<MainWrapper>
<WebhookInfo />
<StyledCreateButton
label="Create webhook"
label={t("CreateWebhook", { ns: "Webhooks" })}
primary
size={isMobile() ? "normal" : "small"}
onClick={openModal}
@ -68,12 +72,12 @@ const Webhooks = (props) => {
<WebhookDialog
visible={isModalOpen}
onClose={closeModal}
header="Create webhook"
header={t("CreateWebhook", { ns: "Webhooks" })}
onSubmit={onCreateWebhook}
/>
</MainWrapper>
) : state === "error" ? (
"error"
t("Error", { ns: "Common" })
) : (
""
);

View File

@ -2,6 +2,7 @@ import React, { useEffect } from "react";
import ModalDialog from "@docspace/components/modal-dialog";
import Button from "@docspace/components/button";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
const Footer = styled.div`
width: 100%;
@ -18,6 +19,8 @@ const Footer = styled.div`
export const DeleteWebhookDialog = ({ visible, onClose, header, handleSubmit }) => {
const onKeyPress = (e) => (e.key === "Esc" || e.key === "Escape") && onClose();
const { t } = useTranslation(["Webhooks"]);
useEffect(() => {
window.addEventListener("keyup", onKeyPress);
return () => window.removeEventListener("keyup", onKeyPress);
@ -26,15 +29,17 @@ export const DeleteWebhookDialog = ({ visible, onClose, header, handleSubmit })
return (
<ModalDialog withFooterBorder visible={visible} onClose={onClose} displayType="modal">
<ModalDialog.Header>{header}</ModalDialog.Header>
<ModalDialog.Body>
The webhook will be deleted permanently. <br />
You will not be able to undo this action.
</ModalDialog.Body>
<ModalDialog.Body>{t("DeleteHint", { ns: "Webhooks" })}</ModalDialog.Body>
<ModalDialog.Footer>
<Footer>
<Button label="Delete forever" size="normal" primary={true} onClick={handleSubmit} />
<Button label="Cancel" size="normal" onClick={onClose} />
<Button
label={t("DeleteForever", { ns: "Webhooks" })}
size="normal"
primary={true}
onClick={handleSubmit}
/>
<Button label={t("Cancel", { ns: "Webhooks" })} size="normal" onClick={onClose} />
</Footer>
</ModalDialog.Footer>
</ModalDialog>

View File

@ -6,6 +6,7 @@ import InfoIcon from "PUBLIC_DIR/images/info.react.svg?url";
import RadioButtonGroup from "@docspace/components/radio-button-group";
import { Hint } from "../styled-components";
import { useTranslation } from "react-i18next";
const Header = styled.h1`
font-family: "Open Sans";
@ -39,6 +40,7 @@ const InfoHint = styled(Hint)`
export const SSLVerification = ({ onChange, value }) => {
const [isHintVisible, setIsHintVisible] = useState(false);
const { t } = useTranslation(["Webhooks"]);
const handleOnChange = (e) => {
onChange({ target: { name: e.target.name, value: e.target.value === "true" } });
@ -48,11 +50,12 @@ export const SSLVerification = ({ onChange, value }) => {
return (
<div>
<Header>
SSL verification <StyledInfoIcon src={InfoIcon} alt="infoIcon" onClick={toggleHint} />
{t("SSLVerification", { ns: "Webhooks" })}{" "}
<StyledInfoIcon src={InfoIcon} alt="infoIcon" onClick={toggleHint} />
</Header>
<InfoHint hidden={!isHintVisible} onClick={toggleHint}>
By default, we verify SSL certificates when delivering payloads.
{t("SSLHint", { ns: "Webhooks" })}
</InfoHint>
<RadioButtonGroup
@ -62,11 +65,11 @@ export const SSLVerification = ({ onChange, value }) => {
onClick={handleOnChange}
options={[
{
label: "Enable SSL verification",
label: t("EnableSSL", { ns: "Webhooks" }),
value: "true",
},
{
label: "Disable (not recommended)",
label: t("DisableSSL", { ns: "Webhooks" }),
value: "false",
},
]}

View File

@ -9,6 +9,8 @@ import { Hint } from "../styled-components";
import { PasswordInput } from "@docspace/components";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
const Header = styled.h1`
font-family: "Open Sans";
font-weight: 600;
@ -62,6 +64,7 @@ const SecretKeyInput = (props) => {
} = props;
const [isHintVisible, setIsHintVisible] = useState(false);
const { t } = useTranslation(["Webhooks"]);
const secretKeyInputRef = useRef(null);
@ -97,25 +100,24 @@ const SecretKeyInput = (props) => {
return (
<div>
<Header>
Secret key <StyledInfoIcon src={InfoIcon} alt="infoIcon" onClick={toggleHint} />
{t("SecretKey", { ns: "Webhooks" })}{" "}
<StyledInfoIcon src={InfoIcon} alt="infoIcon" onClick={toggleHint} />
</Header>
<InfoHint hidden={!isHintVisible} onClick={handleHintDisapear}>
Setting a webhook secret allows you to verify requests sent to the payload URL. <br />
<ReadMore href="">Read more</ReadMore>
{t("SecretKeyHint", { ns: "Webhooks" })} <br />
<ReadMore href="">{t("ReadMore", { ns: "Webhooks" })}</ReadMore>
</InfoHint>
{isResetVisible && (
<InfoHint>
You cannot retrieve your secret key again once it has been saved. If you've lost or
forgotten this secret key, you can reset it, but all integrations using this secret will
need to be updated. <br />
{t("SecretKeyWarning", { ns: "Webhooks" })} <br />
<Link
type="action"
fontWeight={600}
isHovered={true}
onClick={hideReset}
style={{ marginTop: "6px", display: "inline-block" }}>
Reset key
{t("ResetKey", { ns: "Webhooks" })}
</Link>
</InfoHint>
)}
@ -124,7 +126,7 @@ const SecretKeyInput = (props) => {
onChange={handleOnChange}
value={value}
inputName={name}
placeholder="Enter secret key"
placeholder={t("EnterSecretKey", { ns: "Webhooks" })}
onValidateInput={handleInputValidation}
ref={secretKeyInputRef}
hasError={!isPasswordValid}
@ -139,7 +141,7 @@ const SecretKeyInput = (props) => {
isHovered={true}
onClick={generatePassword}
style={{ marginTop: "6px", display: "inline-block" }}>
Generate
{t("Generate", { ns: "Webhooks" })}
</Link>
</div>
</div>

View File

@ -1,11 +1,14 @@
import React, { useState, useEffect } from "react";
import { Badge } from "@docspace/components";
import { useTranslation } from "react-i18next";
export const StatusBadge = ({ status }) => {
const [badgeColorScheme, setBadgeColorScheme] = useState({
backgroundColor: "#2DB4821A",
color: "#2DB482",
});
const { t } = useTranslation(["Webhooks"]);
useEffect(() => {
if (status < 200 || status > 299) {
@ -23,7 +26,7 @@ export const StatusBadge = ({ status }) => {
<Badge
backgroundColor={badgeColorScheme.backgroundColor}
color={badgeColorScheme.color}
label={status === 0 ? "Not sent" : status.toString()}
label={status === 0 ? t("NotSent", { ns: "Webhooks" }) : status.toString()}
fontSize="9px"
fontWeight={700}
noHover

View File

@ -6,6 +6,7 @@ import styled from "styled-components";
import { Hint } from "../styled-components";
import { SSLVerification } from "./SSLVerification";
import SecretKeyInput from "./SecretKeyInput";
import { useTranslation } from "react-i18next";
const Footer = styled.div`
width: 100%;
@ -35,6 +36,8 @@ const WebhookDialog = (props) => {
const [isUrlValid, setIsUrlValid] = useState(false);
const [isPasswordValid, setIsPasswordValid] = useState(false);
const { t } = useTranslation(["Webhooks", "Common"]);
const [webhookInfo, setWebhookInfo] = useState({
id: webhook ? webhook.id : 0,
name: webhook ? webhook.name : "",
@ -106,18 +109,18 @@ const WebhookDialog = (props) => {
<ModalDialog.Header>{header}</ModalDialog.Header>
<ModalDialog.Body>
<form onSubmit={onFormSubmit}>
<Hint>This webhook will be assigned to all events in DocSpace</Hint>
<Hint>{t("WebhookCreationHint", { ns: "Webhooks" })}</Hint>
<LabledInput
label="Webhook name"
placeholder="Enter webhook name"
label={t("WebhookName", { ns: "Webhooks" })}
placeholder={t("EnterWebhookName", { ns: "Webhooks" })}
name="name"
value={webhookInfo.name}
onChange={onInputChange}
required
/>
<LabledInput
label="Payload URL"
placeholder="Enter URL"
label={t("PayloadUrl", { ns: "Webhooks" })}
placeholder={t("EnterUrl", { ns: "Webhooks" })}
name="uri"
value={webhookInfo.uri}
onChange={onInputChange}
@ -142,12 +145,12 @@ const WebhookDialog = (props) => {
<ModalDialog.Footer>
<Footer>
<Button
label={isSettingsModal ? "Save" : "Create"}
label={isSettingsModal ? t("Save", { ns: "Webhooks" }) : t("Create", { ns: "Common" })}
size="normal"
primary={true}
onClick={handleSubmitClick}
/>
<Button label="Cancel" size="normal" onClick={onModalClose} />
<Button label={t("Cancel", { ns: "Webhooks" })} size="normal" onClick={onModalClose} />
</Footer>
</ModalDialog.Footer>
</ModalDialog>

View File

@ -4,26 +4,25 @@ import { InfoText } from "../styled-components";
import { Link } from "@docspace/components";
import { useTranslation } from "react-i18next";
const InfoWrapper = styled.div`
margin-bottom: 27px;
`;
export const WebhookInfo = () => {
const { t } = useTranslation(["Webhooks"]);
return (
<InfoWrapper>
<InfoText>
Use webhooks to perform custom actions on the side of any application or website you are
using based on various events in ONLYOFFICE Docspace. <br />
Here, you can create and manage all your webhooks, configure them, and browse history of
every webhook to audit their performance.
</InfoText>
<InfoText>{t("WebhooksInfo", { ns: "Webhooks" })}</InfoText>
<Link
fontWeight={600}
color="#316DAA"
isHovered
type="page"
href="https://api.onlyoffice.com/portals/basic">
Webhooks Guide
{t("WebhooksGuide", { ns: "Webhooks" })}
</Link>
</InfoWrapper>
);

View File

@ -13,6 +13,7 @@ import DeleteIcon from "PUBLIC_DIR/images/delete.react.svg?url";
import toastr from "@docspace/components/toast/toastr";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
export const WebhookRow = ({
webhook,
@ -22,6 +23,7 @@ export const WebhookRow = ({
editWebhook,
}) => {
const navigate = useNavigate();
const { t } = useTranslation(["Webhooks", "Common"]);
const [isChecked, setIsChecked] = useState(webhook.enabled);
const [isSettingsOpened, setIsSettingsOpened] = useState(false);
@ -35,11 +37,14 @@ export const WebhookRow = ({
const handleWebhookUpdate = async (webhookInfo) => {
editWebhook(webhook, webhookInfo);
toastr.success("Webhook configuration edited successfully", <b>Done</b>);
toastr.success(
t("WebhookEditedSuccessfully", { ns: "Webhooks" }),
<b>{t("Done", { ns: "Common" })}</b>,
);
};
const handleWebhookDelete = async () => {
await deleteWebhook(webhook);
toastr.success("Webhook removed", <b>Done</b>);
toastr.success(t("WebhookRemoved", { ns: "Webhooks" }), <b>{t("Done", { ns: "Common" })}</b>);
};
const handleToggleEnabled = () => {
toggleEnabled(webhook);
@ -49,13 +54,13 @@ export const WebhookRow = ({
const contextOptions = [
{
key: "Settings dropdownItem",
label: "Settings",
label: t("Settings", { ns: "Common" }),
icon: SettingsIcon,
onClick: openSettings,
},
{
key: "Webhook history dropdownItem",
label: "Webhook history",
label: t("WebhookHistory", { ns: "Webhooks" }),
icon: HistoryIcon,
onClick: redirectToHistory,
},
@ -65,7 +70,7 @@ export const WebhookRow = ({
},
{
key: "Delete webhook dropdownItem",
label: "Delete webhook",
label: t("DeleteWebhook", { ns: "Webhooks" }),
icon: DeleteIcon,
onClick: onDeleteOpen,
},
@ -88,7 +93,7 @@ export const WebhookRow = ({
<WebhookDialog
visible={isSettingsOpened}
onClose={closeSettings}
header="Setting webhook"
header={t("SettingsWebhook", { ns: "Webhooks" })}
isSettingsModal={true}
webhook={webhook}
onSubmit={handleWebhookUpdate}
@ -96,7 +101,7 @@ export const WebhookRow = ({
<DeleteWebhookDialog
visible={isDeleteOpened}
onClose={onDeleteClose}
header="Delete Webhook forever?"
header={t("DeleteWebhookForeverQuestion", { ns: "Webhooks" })}
handleSubmit={handleWebhookDelete}
/>
</>

View File

@ -1,11 +1,14 @@
import React, { useRef } from "react";
import TableHeader from "@docspace/components/table-container/TableHeader";
import { useTranslation } from "react-i18next";
export const WebhookTableHeader = ({ sectionWidth, tableRef }) => {
const { t } = useTranslation(["Webhooks", "Common"]);
const columns = useRef([
{
key: "Name",
title: "Name",
title: t("Name", { ns: "Common" }),
resizable: true,
enable: true,
default: true,
@ -13,13 +16,13 @@ export const WebhookTableHeader = ({ sectionWidth, tableRef }) => {
},
{
key: "URL",
title: "URL",
title: t("URL", { ns: "Webhooks" }),
enable: true,
resizable: true,
},
{
key: "State",
title: "State",
title: t("State", { ns: "Webhooks" }),
enable: true,
resizable: true,
},

View File

@ -16,6 +16,7 @@ import { DeleteWebhookDialog } from "../../DeleteWebhookDialog";
import { StatusBadge } from "../../StatusBadge";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
const StyledTableRow = styled(TableRow)`
.mr-8 {
@ -26,6 +27,8 @@ const StyledTableRow = styled(TableRow)`
export const WebhooksTableRow = ({ webhook, toggleEnabled, deleteWebhook, editWebhook }) => {
const navigate = useNavigate();
const { t } = useTranslation(["Webhooks", "Common"]);
const [isChecked, setIsChecked] = useState(webhook.enabled);
const [isSettingsOpened, setIsSettingsOpened] = useState(false);
const [isDeleteOpened, setIsDeleteOpened] = useState(false);
@ -38,11 +41,14 @@ export const WebhooksTableRow = ({ webhook, toggleEnabled, deleteWebhook, editWe
const handleWebhookUpdate = async (webhookInfo) => {
await editWebhook(webhook, webhookInfo);
toastr.success("Webhook configuration edited successfully", <b>Done</b>);
toastr.success(
t("WebhookEditedSuccessfully", { ns: "Webhooks" }),
<b>{t("Done", { ns: "Common" })}</b>,
);
};
const handleWebhookDelete = async () => {
await deleteWebhook(webhook);
toastr.success("Webhook removed", <b>Done</b>);
toastr.success(t("WebhookRemoved", { ns: "Webhooks" }), <b>{t("Done", { ns: "Common" })}</b>);
};
const handleToggleEnabled = () => {
toggleEnabled(webhook);
@ -52,13 +58,13 @@ export const WebhooksTableRow = ({ webhook, toggleEnabled, deleteWebhook, editWe
const contextOptions = [
{
key: "Settings dropdownItem",
label: "Settings",
label: t("Settings", { ns: "Common" }),
icon: SettingsIcon,
onClick: openSettings,
},
{
key: "Webhook history dropdownItem",
label: "Webhook history",
label: t("WebhookHistory", { ns: "Webhooks" }),
icon: HistoryIcon,
onClick: redirectToHistory,
},
@ -68,7 +74,7 @@ export const WebhooksTableRow = ({ webhook, toggleEnabled, deleteWebhook, editWe
},
{
key: "Delete webhook dropdownItem",
label: "Delete webhook",
label: t("DeleteWebhook", { ns: "Webhooks" }),
icon: DeleteIcon,
onClick: onDeleteOpen,
},
@ -101,7 +107,7 @@ export const WebhooksTableRow = ({ webhook, toggleEnabled, deleteWebhook, editWe
<WebhookDialog
visible={isSettingsOpened}
onClose={closeSettings}
header="Setting webhook"
header={t("SettingsWebhook", { ns: "Webhooks" })}
isSettingsModal={true}
webhook={webhook}
onSubmit={handleWebhookUpdate}
@ -109,7 +115,7 @@ export const WebhooksTableRow = ({ webhook, toggleEnabled, deleteWebhook, editWe
<DeleteWebhookDialog
visible={isDeleteOpened}
onClose={onDeleteClose}
header="Delete Webhook forever?"
header={t("DeleteWebhookForeverQuestion", { ns: "Webhooks" })}
handleSubmit={handleWebhookDelete}
/>
</>

View File

@ -12,21 +12,25 @@ import Webhooks from "./Webhooks";
import AppLoader from "@docspace/common/components/AppLoader";
import SSOLoader from "./sub-components/ssoLoader";
import { useTranslation } from "react-i18next";
const DeveloperToolsWrapper = (props) => {
const { loadBaseInfo } = props;
const [currentTab, setCurrentTab] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();
const { t, ready } = useTranslation(["JavascriptSdk", "Webhooks"]);
const data = [
{
id: "javascript-sdk",
name: "Javascript sdk",
name: t("JavascriptSdk", { ns: "JavascriptSdk" }),
content: <JavascriptSDK />,
},
{
id: "webhooks",
name: "Webhooks",
name: t("Webhooks", { ns: "Webhooks" }),
content: <Webhooks />,
},
];
@ -54,7 +58,7 @@ const DeveloperToolsWrapper = (props) => {
);
};
if (!isLoading) return currentTab === 0 ? <SSOLoader /> : <AppLoader />;
if (!isLoading && !ready) return currentTab === 0 ? <SSOLoader /> : <AppLoader />;
return <Submenu data={data} startSelect={currentTab} onSelect={onSelect} />;
};