Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

[Code Simplification] Remove momentjs Dependency (#129)

* [Code Simplification] Remove momentjs Dependency

* code review fixes

authored by

Juan Mrad and committed by
GitHub
c095691e 3855d1fa

+86 -111
-1
client/package-lock.json
··· 55 55 "latlon-geohash": "^2.0.0", 56 56 "lodash": "^4.17.21", 57 57 "lucide-react": "^0.452.0", 58 - "moment": "^2.29.1", 59 58 "next-themes": "^0.3.0", 60 59 "papaparse": "^5.4.1", 61 60 "react": "^18.2.0",
-1
client/package.json
··· 63 63 "latlon-geohash": "^2.0.0", 64 64 "lodash": "^4.17.21", 65 65 "lucide-react": "^0.452.0", 66 - "moment": "^2.29.1", 67 66 "next-themes": "^0.3.0", 68 67 "papaparse": "^5.4.1", 69 68 "react": "^18.2.0",
+32 -26
client/src/utils/time.ts
··· 1 1 import { DateString } from '@roostorg/types'; 2 - import moment from 'moment'; 2 + import { addDays, addHours, format, isBefore } from 'date-fns'; 3 3 4 4 export enum LookbackLength { 5 5 CUSTOM = 'Custom', ··· 28 28 return date.toISOString().split('T')[0]; 29 29 } 30 30 31 + function toDate(date: string | DateString | Date): Date { 32 + return date instanceof Date ? date : new Date(date as string); 33 + } 34 + 31 35 export function parseDatetimeToReadableStringInUTC( 32 36 date: string | DateString | Date, 33 37 ): string { 34 - return moment(date).utc().format('MM/DD/YY hh:mm:ss a'); 38 + const d = toDate(date); 39 + return new Intl.DateTimeFormat('en-US', { 40 + timeZone: 'UTC', 41 + month: '2-digit', 42 + day: '2-digit', 43 + year: '2-digit', 44 + hour: '2-digit', 45 + minute: '2-digit', 46 + second: '2-digit', 47 + hour12: true, 48 + }) 49 + .format(d) 50 + .replace(',', ''); 35 51 } 36 52 37 53 export function parseDatetimeToReadableStringInCurrentTimeZone( 38 54 date: string | DateString | Date, 39 55 ): string { 40 - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; 41 - return moment(date).zone(timezone).format('MM/DD/YY hh:mm:ss a'); 56 + return format(toDate(date), 'MM/dd/yy hh:mm:ss a'); 42 57 } 43 58 44 59 export function parseDatetimeToMonthDayYearDateStringInCurrentTimeZone( 45 60 date: string | DateString | Date, 46 61 ): string { 47 - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; 48 - return moment(date).zone(timezone).format('MMM D, YYYY'); 62 + return format(toDate(date), 'MMM d, yyyy'); 63 + } 64 + 65 + export function startOfHourUTC(date: Date): Date { 66 + const d = new Date(date); 67 + d.setUTCMinutes(0, 0, 0); 68 + return d; 49 69 } 50 70 51 71 export function getEarliestDateWithLookback(lookback: LookbackLength): Date { ··· 71 91 } 72 92 73 93 export function getDateRange(start: Date, end: Date, interval: 'HOUR' | 'DAY') { 74 - // Start and end dates for the graph x axis 75 - const startDate = moment(start).local(); 76 - const endDate = moment(end).local(); 94 + const formatStr = interval === 'HOUR' ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd'; 95 + const advanceFn = interval === 'HOUR' ? addHours : addDays; 77 96 78 - // Generate an array of dates to be used as a baseline 79 - // in the case that some date objects have no data 80 97 const datesArray = []; 81 - const currentDate = startDate.clone(); 98 + let currentDate = new Date(start); 82 99 83 - while (currentDate.isBefore(endDate)) { 100 + while (isBefore(currentDate, end)) { 84 101 datesArray.push({ 85 - ds: currentDate.format( 86 - // format for hour or Day conditionally 87 - `YYYY-MM-DD${interval === 'HOUR' ? ' HH:mm' : ''}`, 88 - ), 102 + ds: format(currentDate, formatStr), 89 103 } as { [key: string]: any }); 90 - // The above cast is replicating the one done to construct 91 - // the `formattedData` variable, making typescript 92 - // happy with the subsequent reduce function typing. God Forgive us. 93 - const intervalToMomentDurationString = { 94 - HOUR: 'hours', 95 - DAY: 'days', 96 - } as const; 97 - 98 - currentDate.add(1, intervalToMomentDurationString[interval]); 104 + currentDate = advanceFn(currentDate, 1); 99 105 } 100 106 101 107 return datesArray;
+2 -2
client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx
··· 1 1 import { gql } from '@apollo/client'; 2 - import moment from 'moment'; 2 + import { format } from 'date-fns'; 3 3 import { useMemo } from 'react'; 4 4 import { useParams } from 'react-router-dom'; 5 5 import { Row } from 'react-table'; ··· 198 198 </div> 199 199 ), 200 200 createdAt: ( 201 - <div>{moment(values.createdAt).format('MM/DD/YY hh:mm A')}</div> 201 + <div>{format(new Date(values.createdAt), 'MM/dd/yy hh:mm a')}</div> 202 202 ), 203 203 jobId: values.jobId, 204 204 values,
+9 -21
client/src/webpages/dashboard/mrt/ManualReviewRecentDecisionsFilter.tsx
··· 1 + import { DateRangePicker } from '@/coop-ui/DateRangePicker'; 1 2 import { ReactComponent as ChevronDown } from '@/icons/lni/Direction/chevron-down.svg'; 2 3 import { ReactComponent as ChevronUp } from '@/icons/lni/Direction/chevron-up.svg'; 3 - import { DatePicker, Select } from 'antd'; 4 + import { Select } from 'antd'; 4 5 import without from 'lodash/without'; 5 - import moment from 'moment'; 6 6 import { useRef, useState } from 'react'; 7 7 8 8 import ComponentLoading from '../../../components/common/ComponentLoading'; ··· 20 20 import { JsonOf, jsonStringify } from '../../../utils/typescript-types'; 21 21 22 22 const { Option } = Select; 23 - const { RangePicker } = DatePicker; 24 23 25 24 type GQLRecentDecisionsFilterByColumns = Omit< 26 25 GQLRecentDecisionsFilterInput, ··· 279 278 const isExpanded = expandedColumnNames.includes(column); 280 279 const columnComponent = 281 280 column === 'dateRange' ? ( 282 - <RangePicker 283 - className="!min-w-[250px]" 284 - placeholder={['Start', 'End']} 285 - value={ 286 - unsavedFilterValues.dateRange 287 - ? [ 288 - moment(unsavedFilterValues.dateRange.startDate), 289 - moment(unsavedFilterValues.dateRange.endDate), 290 - ] 291 - : undefined 292 - } 293 - format="YYYY-MM-DD" 294 - showTime={{ format: 'hh:mm a' }} 295 - onChange={(dates) => { 281 + <DateRangePicker 282 + initialDateFrom={unsavedFilterValues.dateRange?.startDate} 283 + initialDateTo={unsavedFilterValues.dateRange?.endDate} 284 + onUpdate={({ range }) => { 296 285 setUnsavedFilterValues({ 297 286 ...unsavedFilterValues, 298 287 dateRange: { 299 - startDate: 300 - dates && dates[0] ? dates[0].toDate() : undefined, 301 - endDate: 302 - dates && dates[1] ? dates[1].toDate() : undefined, 288 + startDate: range.from, 289 + endDate: range.to ?? range.from, 303 290 }, 304 291 }); 305 292 }} 293 + isSingleMonthOnly 306 294 /> 307 295 ) : ( 308 296 filterByMenuColumn(column)
+2 -2
client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx
··· 11 11 import { filterNullOrUndefined } from '@/utils/collections'; 12 12 import { getFieldValueForRole } from '@/utils/itemUtils'; 13 13 import { ExternalLink } from 'lucide-react'; 14 - import moment from 'moment'; 14 + import { format } from 'date-fns'; 15 15 import { useCallback } from 'react'; 16 16 import { Link } from 'react-router-dom'; 17 17 ··· 144 144 {isAppeal ? 'Appeal ' : 'Report '}Received 145 145 </th> 146 146 <td className="py-1 align-top text-start text-slate-500"> 147 - {moment(createdAt).format('MM/DD/YY hh:mm A')} 147 + {format(new Date(createdAt as string), 'MM/dd/yy hh:mm a')} 148 148 </td> 149 149 </tr> 150 150 <tr>
+2 -2
client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx
··· 7 7 } from '@ant-design/icons'; 8 8 import { gql } from '@apollo/client'; 9 9 import { Button, Input } from 'antd'; 10 - import moment from 'moment'; 10 + import { formatDistanceToNow } from 'date-fns'; 11 11 import { useEffect, useRef, useState } from 'react'; 12 12 13 13 import ComponentLoading from '../../../../../components/common/ComponentLoading'; ··· 97 97 isBeingDeleted ? 'text-gray-300' : 'text-gray-500' 98 98 }`} 99 99 > 100 - {moment(new Date(comment.createdAt)).fromNow()} 100 + {formatDistanceToNow(new Date(comment.createdAt as string), { addSuffix: true })} 101 101 </div> 102 102 </div> 103 103 <div
+2 -4
client/src/webpages/dashboard/mrt/visualization/ManualReviewDashboardInsightsChart.tsx
··· 21 21 import sumBy from 'lodash/sumBy'; 22 22 import union from 'lodash/union'; 23 23 import without from 'lodash/without'; 24 - import moment from 'moment'; 24 + import { format } from 'date-fns'; 25 25 import React, { 26 26 ReactNode, 27 27 useCallback, ··· 698 698 699 699 const formattedData = countsByDay?.map((it) => { 700 700 const obj: { [key: string]: any } = { 701 - ds: moment(new Date(parseInt(it.time))) 702 - .local() 703 - .format(`YYYY-MM-DD${timeDivision === 'HOUR' ? ' HH:mm' : ''}`), 701 + ds: format(new Date(parseInt(it.time)), timeDivision === 'HOUR' ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd'), 704 702 }; 705 703 obj[getLineNameFromCount(it)] = it.count; 706 704 return obj;
+2 -2
client/src/webpages/dashboard/ncmec/NcmecReportsDashboard.tsx
··· 1 1 import { AuditOutlined, DownloadOutlined } from '@ant-design/icons'; 2 2 import { gql } from '@apollo/client'; 3 3 import { Button, Input } from 'antd'; 4 - import moment from 'moment'; 4 + import { format } from 'date-fns'; 5 5 import { useMemo, useState } from 'react'; 6 6 import { Helmet } from 'react-helmet-async'; 7 7 import { useNavigate } from 'react-router-dom'; ··· 222 222 dataValues?.map((report) => { 223 223 return { 224 224 date: ( 225 - <div>{moment(report.date).local().format('MM/DD/YY h:mm a')}</div> 225 + <div>{format(new Date(report.date), 'MM/dd/yy h:mm a')}</div> 226 226 ), 227 227 reviewer: <div className="whitespace-nowrap">{report.reviewer}</div>, 228 228 reportId: (
+2 -4
client/src/webpages/dashboard/overview/OverviewChart.tsx
··· 16 16 import sum from 'lodash/sum'; 17 17 import union from 'lodash/union'; 18 18 import without from 'lodash/without'; 19 - import moment from 'moment'; 19 + import { format } from 'date-fns'; 20 20 import { 21 21 useCallback, 22 22 useEffect, ··· 323 323 324 324 const formattedData = countsPerMetricPerTimeUnit?.map((it) => { 325 325 const obj: { [key: string]: string | number } = { 326 - ds: moment(new Date(parseInt(it.time))) 327 - .local() 328 - .format(`YYYY-MM-DD${timeDivision === 'HOUR' ? ' HH:mm' : ''}`), 326 + ds: format(new Date(parseInt(it.time)), timeDivision === 'HOUR' ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd'), 329 327 }; 330 328 obj[getLineNameFromCount(it)] = it.count; 331 329 return obj;
+2 -2
client/src/webpages/dashboard/rules/dashboard/RulesDashboard.tsx
··· 11 11 import capitalize from 'lodash/capitalize'; 12 12 import groupBy from 'lodash/groupBy'; 13 13 import lowerCase from 'lodash/lowerCase'; 14 - import moment from 'moment'; 15 14 import { MouseEvent, useCallback, useMemo, useState } from 'react'; 16 15 import { Helmet } from 'react-helmet-async'; 17 16 import { useNavigate } from 'react-router-dom'; ··· 52 51 import { 53 52 getEarliestDateWithLookback, 54 53 LookbackLength, 54 + startOfHourUTC, 55 55 } from '../../../../utils/time'; 56 56 import { 57 57 getDisplayNameForTimeDivision, ··· 543 543 const timeWindow = (() => { 544 544 //get current time truncated to hour 545 545 const oldestDate = getEarliestDateWithLookback(lookback); 546 - const oldestHour = moment.utc(oldestDate).startOf('hour').toDate(); 546 + const oldestHour = startOfHourUTC(oldestDate); 547 547 return { 548 548 start: oldestHour, 549 549 end: new Date(),
+3 -3
client/src/webpages/dashboard/rules/dashboard/visualization/RulesDashboardInsights.tsx
··· 25 25 import sumBy from 'lodash/sumBy'; 26 26 import union from 'lodash/union'; 27 27 import without from 'lodash/without'; 28 - import { format, parseISO } from 'date-fns'; 28 + import { format } from 'date-fns'; 29 29 import React, { ReactNode, useCallback, useMemo, useState } from 'react'; 30 30 import { 31 31 Area, ··· 405 405 fill="#71717a" 406 406 className="pt-3 text-slate-500" 407 407 > 408 - {format(parseISO(payload.value), 'MM/dd/yy')} 408 + {format(new Date(payload.value), 'MM/dd/yy')} 409 409 </text> 410 410 ); 411 411 }; ··· 474 474 return ( 475 475 <div className="flex flex-col bg-white rounded-lg shadow text-start"> 476 476 <div className="p-3 text-white rounded-t-lg bg-primary"> 477 - {format(parseISO(label), 'MM/dd/yy')} 477 + {format(new Date(label as string), 'MM/dd/yy')} 478 478 </div> 479 479 {data.length > 1 && ( 480 480 <div className="flex flex-col">
+2 -4
client/src/webpages/dashboard/rules/dashboard/visualization/rulesDashboardInsightsChart.tsx
··· 18 18 import sumBy from 'lodash/sumBy'; 19 19 import union from 'lodash/union'; 20 20 import without from 'lodash/without'; 21 - import moment from 'moment'; 21 + import { format } from 'date-fns'; 22 22 import { useCallback, useEffect, useMemo, useState } from 'react'; 23 23 import { CSVLink } from 'react-csv'; 24 24 import { ··· 197 197 198 198 const formattedData = countsByDay?.map((it) => { 199 199 const obj: { [key: string]: any } = { 200 - ds: moment(new Date(parseInt(it.time))) 201 - .local() 202 - .format(`YYYY-MM-DD${timeDivision === 'HOUR' ? ' HH:mm' : ''}`), 200 + ds: format(new Date(parseInt(it.time)), timeDivision === 'HOUR' ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd'), 203 201 }; 204 202 obj[getLineNameFromCount(it)] = it.count; 205 203 return obj;
+2 -2
client/src/webpages/dashboard/rules/info/insights/ReportingRuleInsightsActionsChart.tsx
··· 6 6 import orderBy from 'lodash/orderBy'; 7 7 import sortBy from 'lodash/sortBy'; 8 8 import sumBy from 'lodash/sumBy'; 9 - import moment from 'moment'; 9 + import { format } from 'date-fns'; 10 10 import { ReactNode, useCallback, useMemo, useState } from 'react'; 11 11 import { 12 12 Area, ··· 177 177 return ( 178 178 <div className="flex flex-col bg-white rounded-lg shadow text-start"> 179 179 <div className="p-3 text-white rounded-t-lg bg-primary"> 180 - {moment(label).format('MM/DD/YY')} 180 + {format(new Date(label as string), 'MM/dd/yy')} 181 181 </div> 182 182 {data.length > 1 && ( 183 183 <div className="flex flex-col">
+2 -2
client/src/webpages/dashboard/rules/info/insights/RuleInsightsActionsChart.tsx
··· 7 7 import orderBy from 'lodash/orderBy'; 8 8 import sortBy from 'lodash/sortBy'; 9 9 import sumBy from 'lodash/sumBy'; 10 - import { format, parseISO } from 'date-fns'; 10 + import { format } from 'date-fns'; 11 11 import { ReactNode, useCallback, useMemo, useState } from 'react'; 12 12 import { 13 13 Bar, ··· 124 124 new Date(a.date).getTime() - new Date(b.date).getTime(), 125 125 ) 126 126 .map((actionData: any) => ({ 127 - date: format(parseISO(actionData.date), 'MM/dd/yy'), 127 + date: format(new Date(actionData.date), 'MM/dd/yy'), 128 128 totalMatches: actionData.totalMatches, 129 129 totalRequests: actionData.totalRequests, 130 130 }));
+17 -26
client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx
··· 4 4 import { ReactComponent as TrashCan } from '@/icons/lni/Web and Technology/trash-can.svg'; 5 5 import { DownOutlined, PlusOutlined, UpOutlined } from '@ant-design/icons'; 6 6 import { gql } from '@apollo/client'; 7 - import { Button, DatePicker, Form, Input, Radio, Select, Tooltip } from 'antd'; 7 + import { Button, Form, Input, Radio, Select, Tooltip } from 'antd'; 8 8 import { useForm } from 'antd/lib/form/Form'; 9 - import moment, { Moment } from 'moment'; 9 + import { format } from 'date-fns'; 10 10 import { useMemo, useReducer } from 'react'; 11 11 import { Helmet } from 'react-helmet-async'; 12 12 import { useNavigate, useParams } from 'react-router-dom'; ··· 110 110 unlimitedDailyActionsChecked: false, 111 111 ruleMutationError: false, 112 112 expirationEnabled: false, 113 - expirationTime: null as Moment | null, 113 + expirationTime: null as Date | null, 114 114 lastVisibleSection: VisibleSections.BASIC_INFO, 115 115 ruleType: RuleType.CONTENT, 116 116 }; ··· 682 682 expirationEnabled: rule.expirationTime != null, 683 683 expirationTime: 684 684 rule.expirationTime != null 685 - ? // moment.unix expects unixtime in seconds, not milliseconds 686 - moment.unix(Number(rule.expirationTime) / 1000) 685 + ? new Date(Number(rule.expirationTime)) 687 686 : null, 688 687 ruleType: 689 688 rule.__typename === 'ContentRule' ··· 916 915 policyIds: state.policyIds, 917 916 tags: state.tags, 918 917 maxDailyActions: state.maxDailyActions, 919 - expirationTime: state.expirationTime?.format('YYYY-MM-DD hh:mm'), 918 + expirationTime: state.expirationTime ? format(state.expirationTime, 'yyyy-MM-dd HH:mm') : undefined, 920 919 }, 921 920 }, 922 921 refetchQueries: [{ query: RULES_QUERY }], ··· 940 939 policyIds: state.policyIds, 941 940 tags: state.tags, 942 941 maxDailyActions: state.maxDailyActions, 943 - expirationTime: state.expirationTime?.format('YYYY-MM-DD hh:mm'), 942 + expirationTime: state.expirationTime ? format(state.expirationTime, 'yyyy-MM-dd HH:mm') : undefined, 944 943 }, 945 944 }, 946 945 refetchQueries: [ ··· 963 962 policyIds: state.policyIds, 964 963 tags: state.tags, 965 964 maxDailyActions: state.maxDailyActions, 966 - expirationTime: state.expirationTime?.format('YYYY-MM-DD hh:mm'), 965 + expirationTime: state.expirationTime ? format(state.expirationTime, 'yyyy-MM-dd HH:mm') : undefined, 967 966 }, 968 967 }, 969 968 refetchQueries: [{ query: RULES_QUERY }], ··· 986 985 policyIds: state.policyIds, 987 986 tags: state.tags, 988 987 maxDailyActions: state.maxDailyActions, 989 - expirationTime: state.expirationTime?.format('YYYY-MM-DD hh:mm'), 988 + expirationTime: state.expirationTime ? format(state.expirationTime, 'yyyy-MM-dd HH:mm') : undefined, 990 989 }, 991 990 }, 992 991 refetchQueries: [ ··· 1615 1614 dispatch({ 1616 1615 type: RuleFormReducerActionType.UpdateExpirationTime, 1617 1616 payload: { 1618 - time: moment.unix(unixtime / 1000), 1617 + time: new Date(unixtime), 1619 1618 }, 1620 1619 }) 1621 1620 } ··· 1646 1645 </div> 1647 1646 } 1648 1647 /> 1649 - {/* Added extra padding when the Form.Item isn't rendered because Form.Item 1650 - has its own bottom padding */} 1651 1648 {!state.expirationEnabled && <div style={{ height: '12px' }} />} 1652 1649 {state.expirationEnabled && ( 1653 - <Form.Item 1654 - className="flex mt-2" 1655 - name="expiration" 1656 - initialValue={state.expirationTime} 1657 - style={{ width: '80%' }} 1658 - > 1659 - <DatePicker 1660 - value={state.expirationTime} 1661 - className="mr-4 rounded-lg" 1662 - showTime={{ format: 'HH:mm' }} 1663 - format="YYYY-MM-DD HH:mm" 1664 - onOk={(value) => 1650 + <div className="flex flex-wrap items-center mt-2 gap-1" style={{ width: '80%' }}> 1651 + <input 1652 + type="datetime-local" 1653 + value={state.expirationTime ? format(state.expirationTime, "yyyy-MM-dd'T'HH:mm") : ''} 1654 + className="mr-4 h-8 px-3 py-1 border border-solid border-slate-300 rounded-lg text-sm text-slate-700 focus:border-coop-blue focus:outline-none" 1655 + onChange={(e) => 1665 1656 dispatch({ 1666 1657 type: RuleFormReducerActionType.UpdateExpirationTime, 1667 1658 payload: { 1668 - time: value, 1659 + time: e.target.value ? new Date(e.target.value) : null, 1669 1660 }, 1670 1661 }) 1671 1662 } ··· 1677 1668 {expirationTimeButton('1 week', Date.now() + WEEK)} 1678 1669 {expirationTimeButton('2 weeks', Date.now() + 2 * WEEK)} 1679 1670 {expirationTimeButton('1 month', Date.now() + MONTH)} 1680 - </Form.Item> 1671 + </div> 1681 1672 )} 1682 1673 </div> 1683 1674 );
+3 -4
client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.tsx
··· 2 2 import cloneDeep from 'lodash/cloneDeep'; 3 3 import omit from 'lodash/omit'; 4 4 import uniqBy from 'lodash/uniqBy'; 5 - import moment, { Moment } from 'moment'; 6 5 7 6 import { 8 7 GQLConditionConjunction, ··· 179 178 } 180 179 | { 181 180 type: RuleFormReducerActionType.UpdateExpirationTime; 182 - payload: { time: Moment | null }; 181 + payload: { time: Date | null }; 183 182 } 184 183 | { 185 184 type: RuleFormReducerActionType.RuleQueryCompleted; ··· 195 194 maxDailyActions: number | null; 196 195 unlimitedDailyActionsChecked: boolean; 197 196 expirationEnabled: boolean; 198 - expirationTime: Moment | null; 197 + expirationTime: Date | null; 199 198 ruleType: RuleType; 200 199 }; 201 200 } ··· 331 330 return { 332 331 ...state, 333 332 expirationEnabled: newValue, 334 - expirationTime: newValue ? moment(Date.now() + DAY) : null, 333 + expirationTime: newValue ? new Date(Date.now() + DAY) : null, 335 334 ruleMutationError: false, 336 335 }; 337 336 case RuleFormReducerActionType.UpdateExpirationTime:
+2 -3
client/src/webpages/dashboard/userStrikes/StrikeAnalyticsTab.tsx
··· 5 5 useGQLUserStrikeThresholdsQuery, 6 6 } from '@/graphql/generated'; 7 7 import { gql } from '@apollo/client'; 8 - import moment from 'moment'; 8 + import { format } from 'date-fns'; 9 9 import { useMemo } from 'react'; 10 10 import { Link } from 'react-router-dom'; 11 11 import { ··· 174 174 ); 175 175 const recentUserStrikeActions = data?.recentUserStrikeActions; 176 176 177 - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; 178 177 const tableData = useMemo( 179 178 () => { 180 179 return recentUserStrikeActions ··· 196 195 action: actionsById 197 196 ? actionsById[values.actionId] || 'Unknown' 198 197 : 'Unknown', 199 - date: moment(values.time).zone(timezone).format('MM/DD/YY hh:mm'), 198 + date: format(new Date(values.time), 'MM/dd/yy hh:mm'), 200 199 }; 201 200 }); 202 201 },