diff --git a/src/api/guardian-network/GuardianNetwork.ts b/src/api/guardian-network/GuardianNetwork.ts index 191ff4ff..fc932e56 100644 --- a/src/api/guardian-network/GuardianNetwork.ts +++ b/src/api/guardian-network/GuardianNetwork.ts @@ -45,10 +45,12 @@ export class GuardianNetwork { address, appId, exclusiveAppId, + from, pagination = DefaultPageRequest, payloadType, sourceChain, targetChain, + to, txHash, vaaID, }: GetOperationsInput): Promise { @@ -58,9 +60,11 @@ export class GuardianNetwork { address, appId, exclusiveAppId, + from, payloadType, sourceChain, targetChain, + to, txHash, }); diff --git a/src/api/guardian-network/types.ts b/src/api/guardian-network/types.ts index da9f6bd9..9ec6dd32 100644 --- a/src/api/guardian-network/types.ts +++ b/src/api/guardian-network/types.ts @@ -280,6 +280,8 @@ export interface GetOperationsInput { targetChain?: string; txHash?: string; vaaID?: string; + from?: string; + to?: string; } export interface INFTInfo { diff --git a/src/components/molecules/Calendar/index.tsx b/src/components/molecules/Calendar/index.tsx new file mode 100644 index 00000000..a03b4849 --- /dev/null +++ b/src/components/molecules/Calendar/index.tsx @@ -0,0 +1,262 @@ +import { useRef, useState } from "react"; +import ReactDatePicker from "react-datepicker"; +import { ArrowRightIcon, ChevronDownIcon } from "src/icons/generic"; +import { TSelectedPeriod } from "src/utils/chainActivityUtils"; +import { useOutsideClick } from "src/utils/hooks"; +import "./styles.scss"; + +interface ICalendarProps { + className?: string; + startDate: Date; + setStartDate: (date: Date) => void; + endDate: Date; + setEndDate: (date: Date) => void; + lastBtnSelected: TSelectedPeriod; + setLastBtnSelected: (btn: TSelectedPeriod) => void; + startDateDisplayed: Date; + endDateDisplayed: Date; + isDesktop: boolean; + showDateRange?: boolean; + showAgoButtons?: boolean; +} + +const Calendar = ({ + className = "", + startDate, + setStartDate, + endDate, + setEndDate, + lastBtnSelected, + setLastBtnSelected, + startDateDisplayed, + endDateDisplayed, + isDesktop, + showDateRange = false, + showAgoButtons = false, +}: ICalendarProps) => { + const [showCalendar, setShowCalendar] = useState(false); + const dateContainerRef = useRef(null); + + const setTimePeriod = ( + from: number, + to: number | undefined, + unit: "days" | "months" | "years" | undefined, + resetHours: boolean = true, + btnSelected: TSelectedPeriod = "custom", + ) => { + const start = from === Infinity ? new Date(2021, 7, 1) : new Date(); + const end = new Date(); + + setLastBtnSelected(btnSelected); + + if (resetHours) { + start.setHours(0, 0, 0, 0); + end.setHours(0, 0, 0, 0); + } + + const timeSetters = { + days: (date: Date, v: number) => date.setDate(date.getDate() - v), + months: (date: Date, v: number) => date.setMonth(date.getMonth() - v), + years: (date: Date, v: number) => date.setFullYear(date.getFullYear() - v), + }; + + if (from !== Infinity && unit) { + timeSetters[unit](start, from); + } + + if (to && unit) { + timeSetters[unit](end, to); + } + + setStartDate(start); + setEndDate(end); + setShowCalendar(false); + }; + + const handleLast24Hours = () => setTimePeriod(1, undefined, "days", false, "24h"); + const handleLastWeekBtn = () => setTimePeriod(7, undefined, "days", true, "week"); + const handleLastMonthBtn = () => setTimePeriod(1, undefined, "months", true, "month"); + const handleLast6MonthsBtn = () => setTimePeriod(6, undefined, "months", true, "custom"); + const handleLastYearBtn = () => setTimePeriod(1, undefined, "years", true, "year"); + const handleAllTime = () => setTimePeriod(Infinity, undefined, undefined, true, "all"); + + const handleAgo3Days = () => setTimePeriod(7, 3, "days", true, "custom"); + const handleAgo1Week = () => setTimePeriod(30, 7, "days", true, "ago1Week"); + const handleAgo1Month = () => setTimePeriod(3, 1, "months", true, "ago1Month"); + const handleAgo3Months = () => setTimePeriod(6, 3, "months", true, "ago3Months"); + const handleAgo6Months = () => setTimePeriod(12, 6, "months", true, "ago6Months"); + const handleAgoAllTime = () => { + setLastBtnSelected("all"); + setStartDate(null); + setEndDate(null); + setShowCalendar(false); + }; + + const handleOutsideClickDate = () => { + setStartDate(startDateDisplayed); + setEndDate(endDateDisplayed); + setShowCalendar(false); + }; + + useOutsideClick({ ref: dateContainerRef, callback: handleOutsideClickDate }); + + return ( +
+ + +
+
+ { + const [start, end] = dates; + start?.setHours(0, 0, 0, 0); + end?.setHours(0, 0, 0, 0); + + if (start?.getTime() !== end?.getTime()) { + setStartDate(start); + setEndDate(end); + } + + if (end?.getTime()) { + setLastBtnSelected("custom"); + setShowCalendar(false); + } + }} + startDate={startDate} + endDate={endDate} + selectsRange + inline + maxDate={new Date()} + monthsShown={isDesktop ? 2 : 1} + showMonthDropdown + /> + +
+ + + +
+
+ +
+ {showAgoButtons ? ( +
+ + + + + + +
+ ) : ( +
+ + + + + + +
+ )} +
+
+
+ ); +}; + +export default Calendar; diff --git a/src/components/molecules/Calendar/styles.scss b/src/components/molecules/Calendar/styles.scss new file mode 100644 index 00000000..ca4d9b1d --- /dev/null +++ b/src/components/molecules/Calendar/styles.scss @@ -0,0 +1,481 @@ +@use "src/styles/globals.scss" as *; + +.calendar-custom { + position: relative; + + &-text { + @include centered-row; + gap: 8px; + } + + &-btn { + @include button-secondary; + color: var(--color-white); + height: 48px; + justify-content: space-between; + padding-right: 8px; + width: 100%; + + @include desktop { + height: 36px; + position: relative; + } + + &-text { + display: flex; + align-items: center; + gap: 4px; + + & > svg { + color: var(--color-white-60); + } + + .range-text { + display: block; + + @include desktop { + display: none; + } + + @media only screen and (min-width: 1260px) { + display: block; + } + } + } + } + + &-box { + background-color: var(--color-gray-900); + border-radius: 16px; + flex-direction: column; + min-width: max-content; + position: fixed; + transition: inset 0.3s ease; + z-index: 55; + display: none; + margin-top: 12px; + width: calc(100% - 42px); + + @include desktop { + border-radius: 8px; + border: none; + display: none; + inset: unset; + left: 0; + margin-top: 0; + position: absolute; + transition: none; + width: auto; + } + + &.show-date { + display: flex; + flex-direction: column-reverse; + overflow: auto; + max-height: calc(100svh - 200px); + + @include desktop { + border-radius: 16px; + display: flex; + flex-direction: row-reverse; + inset: unset; + left: 0; + top: 52px; + transform: translateX(-42%); + } + + @include bigDesktop { + transform: unset; + } + } + + &-chains { + @include centered-column; + gap: 8px 24px; + justify-content: flex-start; + overflow-y: auto; + padding: 16px; + + @include desktop { + display: grid; + grid-template-columns: repeat(4, 1fr); + } + + &-item { + @include centered-row; + cursor: pointer; + gap: 8px; + justify-content: space-between; + opacity: 0.5; + white-space: nowrap; + + @include desktop { + flex-direction: row-reverse; + justify-content: start; + } + + &:hover { + opacity: 1; + } + + &.all-chains { + grid-column: span 4; + margin-bottom: 16px; + } + + &-checkbox { + @include centered-row; + border-radius: 2px; + border: 1px solid #6e71a3; + color: var(--color-white); + cursor: pointer; + height: 16px; + justify-content: center; + width: 16px; + } + } + + &-bottom { + display: grid; + gap: 16px; + grid-template-columns: 1fr 1fr; + padding: 24px 16px 16px 16px; + + @include desktop { + display: flex; + flex-direction: row-reverse; + } + + & > button { + align-items: center; + border-radius: 8px; + cursor: pointer; + display: flex; + font-size: 16px; + font-weight: 600; + gap: 16px; + height: 44px; + justify-content: center; + + &:disabled { + cursor: not-allowed; + opacity: 0.3; + } + } + } + } + + &-date { + &-selector { + @include centered-column; + border-bottom: 1px solid var(--color-gray-800); + gap: 73px; + justify-content: flex-end; + padding: 8px; + width: 100%; + + @include desktop { + border-bottom: none; + border-right: 1px solid var(--color-gray-800); + justify-content: flex-start; + width: 156px; + } + + & > div:first-child { + display: grid; + gap: 0 16px; + grid-template-columns: 1fr 1fr; + + @include desktop { + @include centered-column; + } + } + + & > div { + @include centered-column; + align-items: flex-start; + gap: 8px 0; + + & > .btn { + @include text-roboto-body-400; + background-color: transparent; + border-radius: 8px; + border: none; + color: var(--color-gray-400); + cursor: pointer; + font-size: 14px; + font-weight: 400; + padding: 8px 0 8px 8px; + text-align: start; + width: 100%; + + &:hover { + background-color: var(--color-white-05); + } + + &.active { + background-color: var(--color-gray-800); + color: var(--color-white); + } + } + } + } + + &-calendar { + margin: 0 auto auto auto; + padding: 16px 16px 72px 16px; + width: 100%; + + @include desktop { + margin: 0; + min-height: unset; + width: max-content; + } + + & .react-datepicker { + @include centered-column; + background-color: transparent; + border: none; + font-size: 16px; + + @include tablet { + @include centered-row; + align-items: flex-start; + } + + @include desktop { + display: block; + } + + & .react-datepicker__navigation { + border-radius: 50%; + top: 4px; + width: 36px; + + & > span::before { + border-color: var(--color-white); + top: 0; + + @include desktop { + top: 4px; + } + } + } + + & .react-datepicker__header { + background-color: transparent; + border-radius: 0; + border: none; + padding: 0; + + @include desktop { + padding: 8px 0 0 0; + } + + & .react-datepicker__current-month { + @include text-roboto-body-500; + color: var(--color-white); + } + + & .react-datepicker__day-names { + padding: 16px 0 0 0; + + @include desktop { + padding: 16px 0 8px 0; + } + + & .react-datepicker__day-name { + @include text-heading6; + color: var(--color-gray-600); + height: 36px; + line-height: 36px; + margin: 0; + text-transform: uppercase; + width: 36px; + } + } + } + + & .react-datepicker__month { + margin: 0 24px; + + & .react-datepicker__day { + @include text-roboto-body-400; + border-radius: 0; + color: var(--color-white); + line-height: 36px; + margin: 0; + width: 36px; + + &:hover { + background-color: var(--color-gray-600); + color: var(--color-white-50); + font-weight: 600; + } + + &--keyboard-selected { + background-color: transparent; + } + + &--in-selecting-range { + background-color: var(--color-white-05); + color: var(--color-white-50); + } + + &--selected { + background-color: var(--color-gray-800); + color: var(--color-white-50); + } + + &--in-range { + background-color: var(--color-white-05); + color: var(--color-white-50); + } + + &--range-start:not(&--outside-month) { + background-color: var(--color-white-05); + border-radius: 50% 0 0 50%; + color: var(--color-white-90); + font-weight: 600; + position: relative; + z-index: 1; + + &::before { + background-color: var(--color-gray-800); + border-radius: 50%; + content: ""; + display: block; + height: 28px; + left: 50%; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + width: 28px; + z-index: -1; + } + } + + &--range-end:not(&--outside-month) { + background-color: var(--color-white-05); + border-radius: 0 50% 50% 0; + color: var(--color-white-90); + font-weight: 600; + position: relative; + z-index: 1; + + &::before { + background-color: var(--color-gray-800); + border-radius: 50%; + content: ""; + display: block; + height: 28px; + left: 50%; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + width: 28px; + z-index: -1; + } + } + + &--disabled { + color: var(--color-white); + opacity: 0.3; + text-decoration: line-through; + + &:hover { + background-color: transparent; + color: var(--color-white); + cursor: not-allowed; + font-weight: 400; + } + } + } + } + + & .react-datepicker__header__dropdown { + position: absolute; + right: 0; + left: 0; + } + + & .react-datepicker__month-read-view { + & .react-datepicker__month-read-view--selected-month { + padding: 0 16px; + visibility: hidden; + } + + & .react-datepicker__month-read-view--down-arrow { + border-color: var(--color-gray-600); + left: 0; + margin: 0 auto; + right: 0; + } + } + + & .react-datepicker__month-dropdown { + background-color: var(--color-gray-800); + border-radius: 16px; + border: none; + padding: 8px; + z-index: 88; + margin-top: -24px; + + & .react-datepicker__month-option { + @include text-roboto-body-400; + color: var(--color-white); + + &:hover { + background-color: var(--color-white-05); + } + } + } + } + + &-one-day-selected { + & .react-datepicker { + & .react-datepicker__month { + & .react-datepicker__day { + &--range-start { + border-radius: 0; + } + + &--range-end { + border-radius: 0; + } + } + } + } + } + + &-btns { + @include centered-row; + bottom: 16px; + gap: 20px; + position: absolute; + right: 16px; + + & > .clear-btn { + @include button-ghost; + } + + & > .done-btn { + @include button-primary; + color: var(--color-black); + width: 100%; + + @include desktop { + padding: 8px 24px; + width: auto; + } + + &:disabled { + cursor: not-allowed; + opacity: 0.3; + } + } + } + } + } + } +} diff --git a/src/components/molecules/index.tsx b/src/components/molecules/index.tsx index b0990142..92c263be 100644 --- a/src/components/molecules/index.tsx +++ b/src/components/molecules/index.tsx @@ -1,5 +1,6 @@ export { default as Banner } from "./Banner"; export { default as BlockSection } from "./BlockSection"; +export { default as Calendar } from "./Calendar"; export { default as CopyToClipboard } from "./CopyToClipboard"; export { default as CrossChainChart } from "./CrossChainChart"; export { default as ErrorPlaceholder } from "./ErrorPlaceholder"; diff --git a/src/components/organisms/ChainActivity/Calendar.tsx b/src/components/organisms/ChainActivity/Calendar.tsx deleted file mode 100644 index 6ab0b5d7..00000000 --- a/src/components/organisms/ChainActivity/Calendar.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import { useRef, useState } from "react"; -import ReactDatePicker from "react-datepicker"; -import { ChevronDownIcon } from "src/icons/generic"; -import { TSelectedPeriod } from "src/utils/chainActivityUtils"; -import { useOutsideClick } from "src/utils/hooks"; - -interface ICalendarProps { - startDate: Date; - setStartDate: (date: Date) => void; - endDate: Date; - setEndDate: (date: Date) => void; - lastBtnSelected: TSelectedPeriod; - setLastBtnSelected: (btn: TSelectedPeriod) => void; - startDateDisplayed: Date; - endDateDisplayed: Date; - isDesktop: boolean; -} - -export const Calendar = ({ - startDate, - setStartDate, - endDate, - setEndDate, - lastBtnSelected, - setLastBtnSelected, - startDateDisplayed, - endDateDisplayed, - isDesktop, -}: ICalendarProps) => { - const [showCalendar, setShowCalendar] = useState(false); - const dateContainerRef = useRef(null); - - const setTimePeriod = ( - value: number, - unit: "days" | "months" | "years", - resetHours: boolean = true, - btnSelected: TSelectedPeriod = "custom", - startDate?: Date, - endDate?: Date, - ) => { - const start = startDate || new Date(); - const end = endDate || new Date(); - setLastBtnSelected(btnSelected); - - if (resetHours) { - start.setHours(0, 0, 0, 0); - end.setHours(0, 0, 0, 0); - } - - if (!startDate) { - const timeSetters = { - days: (date: Date, value: number) => date.setDate(date.getDate() - value), - months: (date: Date, value: number) => date.setMonth(date.getMonth() - value), - years: (date: Date, value: number) => date.setFullYear(date.getFullYear() - value), - }; - - timeSetters[unit](start, value); - } - - setStartDate(start); - setEndDate(end); - setShowCalendar(false); - }; - - const handleLast24Hours = () => setTimePeriod(1, "days", false, "24h"); - const handleLastWeekBtn = () => setTimePeriod(7, "days", true, "week"); - const handleLastMonthBtn = () => setTimePeriod(1, "months", true, "month"); - const handleLast6MonthsBtn = () => setTimePeriod(6, "months", true, "custom"); - const handleLastYearBtn = () => setTimePeriod(1, "years", true, "year"); - const handleAllTime = () => { - const start = new Date(2021, 7, 1); - const end = new Date(); - setTimePeriod(0, "days", true, "all", start, end); - }; - - const handleOutsideClickDate = () => { - setStartDate(startDateDisplayed); - setEndDate(endDateDisplayed); - setShowCalendar(false); - }; - - useOutsideClick({ ref: dateContainerRef, callback: handleOutsideClickDate }); - - return ( -
- - -
-
- { - const [start, end] = dates; - start?.setHours(0, 0, 0, 0); - end?.setHours(0, 0, 0, 0); - - if (start?.getTime() !== end?.getTime()) { - setStartDate(start); - setEndDate(end); - setLastBtnSelected("custom"); - } - }} - startDate={startDate} - endDate={endDate} - selectsRange - inline - maxDate={new Date()} - monthsShown={isDesktop ? 2 : 1} - showMonthDropdown - /> - -
- - - -
-
- -
-
- - - - - - -
-
-
-
- ); -}; diff --git a/src/components/organisms/ChainActivity/index.tsx b/src/components/organisms/ChainActivity/index.tsx index 082dc8fa..8ce8b003 100644 --- a/src/components/organisms/ChainActivity/index.tsx +++ b/src/components/organisms/ChainActivity/index.tsx @@ -13,7 +13,7 @@ import { Select, ToggleGroup, } from "src/components/atoms"; -import { ErrorPlaceholder, WormholeScanBrand } from "src/components/molecules"; +import { Calendar, ErrorPlaceholder, WormholeScanBrand } from "src/components/molecules"; import { changePathOpacity, formatterYAxis, updatePathStyles } from "src/utils/apexChartUtils"; import { getChainName } from "src/utils/wormhole"; import { getClient } from "src/api/Client"; @@ -50,7 +50,6 @@ import { TSelectedPeriod, } from "src/utils/chainActivityUtils"; import { BREAKPOINTS } from "src/consts"; -import { Calendar } from "./Calendar"; import "./styles.scss"; const TYPE_CHART_LIST = [ @@ -757,9 +756,22 @@ const ChainActivity = () => { + + { name="targetChain" onValueChange={(value: any) => handleChainSelection(value, "target")} text={ -
+
{filters?.targetChain?.length > 0 && ( {filters.targetChain.length} )} @@ -806,7 +818,7 @@ const ChainActivity = () => {