chore: use monorepo organization

This commit is contained in:
Rafael Taranto 2025-05-12 10:52:54 +01:00
parent deaf7d6ecc
commit a687827f7e
1099 changed files with 8184 additions and 11535 deletions

View file

@ -0,0 +1,78 @@
import { ApolloClient, ApolloProvider, InMemoryCache, ApolloLink } from "@apollo/client";
import { onError } from "@apollo/client/link/error"
import createUploadLink from "apollo-upload-client/createUploadLink.mjs";
import React, { useContext } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import AppContext from 'src/AppContext'
const uploadLink = createUploadLink({
credentials: 'include',
uri: `/graphql`
})
const getClient = (history, location, getUserData, setUserData, setRole) =>
new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path, extensions }) => {
if (extensions?.code === 'UNAUTHENTICATED') {
setUserData(null)
if (location.pathname !== '/login') history.push('/login')
}
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
})
if (networkError) console.log(`[Network error]: ${networkError}`)
}),
new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
const context = operation.getContext()
const {
response: { headers }
} = context
if (headers) {
const role = headers.get('lamassu_role')
setRole(role)
}
return response
})
}),
uploadLink
]),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'no-cache',
errorPolicy: 'ignore'
},
query: {
fetchPolicy: 'no-cache',
errorPolicy: 'all'
},
mutate: {
errorPolicy: 'all'
}
}
})
const Provider = ({ children }) => {
const history = useHistory()
const location = useLocation()
const { userData, setUserData, setRole } = useContext(AppContext)
const client = getClient(
history,
location,
() => userData,
setUserData,
setRole
)
return <ApolloProvider client={client}>{children}</ApolloProvider>
}
export default Provider

View file

@ -0,0 +1,433 @@
export default {
AED: {
thickness: 0x0c,
lengths: {
5: [0x99, 0x85],
10: [0x9d, 0x89],
20: [0x9f, 0x8b],
50: [0xa1, 0x8d],
100: [0xa5, 0x91],
200: [0xa7, 0x93],
500: [0xa9, 0x95],
1000: [0xad, 0x99]
},
polymer: false
},
ANG: {
thickness: 0x0c,
lengths: {
10: [0x98, 0x8e],
25: [0x98, 0x8e],
50: [0x98, 0x8e],
100: [0x98, 0x8e]
},
polymer: false
},
AUD: {
thickness: 0x0d,
lengths: {
5: [0x8c, 0x78],
10: [0x93, 0x7f],
20: [0x9a, 0x86],
50: [0xa1, 0x8d],
100: [0xa8, 0x94]
},
polymer: true
},
BGN: {
thickness: 0x0d,
lengths: {
1: [0x75, 0x6b],
2: [0x79, 0x6f],
5: [0x7e, 0x74],
10: [0x83, 0x79],
20: [0x88, 0x7e],
50: [0x8d, 0x83],
100: [0x92, 0x88]
},
polymer: false
},
CAD: {
thickness: 0x0d,
lengths: {
5: [0xa2, 0x8e],
10: [0xa2, 0x8e],
20: [0xa2, 0x8e],
50: [0xa2, 0x8e],
100: [0xa2, 0x8e]
},
polymer: true
},
CHF: {
thickness: 0x0d,
lengths: {
10: [0x85, 0x71],
20: [0x8c, 0x78],
50: [0x93, 0x7f],
100: [0x9a, 0x86],
200: [0xa1, 0x8d],
1000: [0xa8, 0x94]
},
polymer: false
},
CNY: {
thickness: 0x0d,
lengths: {
1: [0x8c, 0x78],
5: [0x91, 0x7d],
10: [0x96, 0x82],
20: [0x9b, 0x87],
50: [0xa0, 0x8c],
100: [0x91, 0xa5]
},
polymer: false
},
COP: {
thickness: 0x0d,
lengths: {
2000: [0x8a, 0x76],
5000: [0x8f, 0x7b],
10000: [0x94, 0x80],
20000: [0x99, 0x85],
50000: [0x9e, 0x8a],
100000: [0xa3, 0x8f]
},
polymer: false
},
CRC: {
thickness: 0x0d,
lengths: {
1000: [0x82, 0x78],
2000: [0x89, 0x7f],
5000: [0x90, 0x86],
10000: [0x97, 0x8d],
20000: [0x9e, 0x94],
50000: [0xaa, 0x96]
},
polymer: true
},
CZK: {
thickness: 0x0c,
lengths: {
100: [0x96, 0x82],
200: [0x9c, 0x88],
500: [0xa2, 0x8e],
1000: [0xa8, 0x94],
2000: [0xae, 0x9a],
5000: [0xb4, 0xa0]
},
polymer: false
},
EUR: {
thickness: 0x0c,
lengths: {
5: [0x82, 0x6e],
10: [0x89, 0x75],
20: [0x8f, 0x7b],
50: [0x96, 0x82],
100: [0x9d, 0x89],
200: [0xa3, 0x8f],
500: [0xaa, 0x96]
},
polymer: false
},
GBP: {
thickness: 0x0d,
lengths: {
5: [0x91, 0x7d],
10: [0x98, 0x84],
20: [0x95, 0x81],
50: [0xa6, 0x92]
},
polymer: true
},
GHS: {
thickness: 0x0c,
lengths: {
1: [0x93, 0x7f],
2: [0x96, 0x82],
5: [0x97, 0x83],
10: [0x9b, 0x87],
20: [0x9f, 0x8b],
50: [0xa3, 0x8f]
},
polymer: false
},
GIP: {
thickness: 0x0c,
lengths: {
5: [0x8a, 0x80],
10: [0x92, 0x88],
20: [0x9b, 0x91],
50: [0xa2, 0x98],
100: [0xa9, 0x9f]
},
polymer: false
},
GTQ: {
thickness: 0x0c,
lengths: {
5: [0xa3, 0x99],
10: [0xa1, 0x97],
20: [0xa3, 0x99],
50: [0xa1, 0x97],
100: [0xa1, 0x97],
200: [0xa1, 0x97]
},
polymer: false
},
HKD: {
thickness: 0x0d,
lengths: {
10: [0x90, 0x7c],
20: [0x99, 0x85],
50: [0x9e, 0x8a],
100: [0xa3, 0x8f],
500: [0xa8, 0x94],
1000: [0xae, 0x9a]
},
polymer: false
},
HNL: {
thickness: 0x0c,
lengths: {
1: [0xa6, 0x92],
2: [0xa6, 0x92],
5: [0xa6, 0x92],
10: [0xa6, 0x92],
20: [0xa6, 0x92],
50: [0xa6, 0x92],
100: [0xa6, 0x92],
200: [0xa6, 0x92],
500: [0xa6, 0x92]
},
polymer: false
},
HRK: {
thickness: 0x0c,
lengths: {
5: [0x7f, 0x75],
10: [0x83, 0x79],
20: [0x87, 0x7d],
50: [0x8b, 0x81],
100: [0x8f, 0x85],
200: [0x93, 0x89],
500: [0x97, 0x8d],
1000: [0x9b, 0x91]
},
polymer: false
},
ILS: {
thickness: 0x0d,
lengths: {
20: [0x8b, 0x77],
50: [0x92, 0x7e],
100: [0x99, 0x85],
200: [0xa0, 0x8c]
},
polymer: false
},
JPY: {
thickness: 0x0d,
lengths: {
1000: [0x99, 0x93],
2000: [0x9c, 0x98],
5000: [0x9d, 0x9a],
10000: [0xa3, 0x9e]
},
polymer: false
},
KRW: {
thickness: 0x0d,
lengths: {
1000: [0x92, 0x7e],
2000: [0x96, 0x82],
5000: [0x98, 0x84],
10000: [0x9e, 0x8a],
50000: [0xa4, 0x90]
},
polymer: false
},
MDL: {
thickness: 0x0c,
lengths: {
1: [0x7c, 0x6e],
5: [0x7c, 0x6e],
10: [0x83, 0x6f],
20: [0x83, 0x6f],
50: [0x83, 0x6f],
100: [0x83, 0x6f],
200: [0x8f, 0x7b],
500: [0x8f, 0x7b],
1000: [0x8f, 0x7b]
},
polymer: false
},
MKD: {
thickness: 0x0c,
lengths: {
10: [0x91, 0x87],
50: [0x94, 0x8a],
100: [0x97, 0x8d],
200: [0x95, 0x8b],
500: [0x9a, 0x90],
1000: [0x9d, 0x93],
2000: [0x9d, 0x93],
5000: [0xa0, 0x96]
},
polymer: true
},
MXN: {
thickness: 0x0c,
lengths: {
20: [0x7d, 0x73],
50: [0x82, 0x78],
100: [0x89, 0x7f],
200: [0x90, 0x86],
500: [0x97, 0x8d],
1000: [0x9e, 0x94]
},
polymer: true
},
MYR: {
thickness: 0x0c,
lengths: {
1: [0x82, 0x6e],
5: [0x91, 0x7d],
10: [0x96, 0x82],
20: [0x9b, 0x87],
50: [0x9b, 0x87],
100: [0xa0, 0x8c]
},
polymer: false
},
NAD: {
thickness: 0x0c,
lengths: {
10: [0x86, 0x7c],
20: [0x8b, 0x81],
50: [0x91, 0x87],
100: [0x97, 0x8d],
200: [0x9d, 0x93]
},
polymer: false
},
NZD: {
thickness: 0x0c,
lengths: {
5: [0x8c, 0x82],
10: [0x91, 0x87],
20: [0x96, 0x8c],
50: [0x9b, 0x91],
100: [0xa0, 0x96]
},
polymer: true
},
PHP: {
thickness: 0x0c,
lengths: {
20: [0xaa, 0x96],
100: [0xaa, 0x96],
200: [0xaa, 0x96],
500: [0xaa, 0x96],
1000: [0xaa, 0x96]
},
polymer: false
},
PLN: {
thickness: 0x0c,
lengths: {
10: [0x7d, 0x73],
20: [0x83, 0x79],
50: [0x89, 0x7f],
100: [0x8f, 0x85],
200: [0x95, 0x8b],
500: [0x9b, 0x91]
},
polymer: false
},
RON: {
thickness: 0x0c,
lengths: {
1: [0x82, 0x6e],
5: [0x89, 0x75],
10: [0x8f, 0x7b],
50: [0x96, 0x82],
100: [0x9d, 0x89],
200: [0xa0, 0x8c],
500: [0xa3, 0x8f]
},
polymer: true
},
SGD: {
thickness: 0x0c,
lengths: {
2: [0x88, 0x74],
5: [0x8f, 0x7b],
10: [0x97, 0x83],
50: [0xa6, 0x92],
100: [0xac, 0x98],
1000: [0xb4, 0xa0]
},
polymer: false
},
TWD: {
thickness: 0x0d,
lengths: {
100: [0x9b, 0x87],
200: [0xa0, 0x8c],
500: [0xa5, 0x91],
1000: [0xaa, 0x96],
2000: [0xaf, 0x9b]
},
polymer: false
},
USD: {
thickness: 0x0d,
lengths: {
1: [0xa6, 0x92],
2: [0xa6, 0x92],
5: [0xa6, 0x92],
10: [0xa6, 0x92],
20: [0xa6, 0x92],
50: [0xa6, 0x92],
100: [0xa6, 0x92]
},
polymer: false
},
UYU: {
thickness: 0x0d,
lengths: {
20: [0xa4, 0x9a],
50: [0x96, 0x8c],
100: [0xa4, 0x9a],
200: [0xa4, 0x9a],
500: [0xa4, 0x9a],
1000: [0xa4, 0x9a],
2000: [0xa4, 0x9a]
},
polymer: false
},
XCD: {
thickness: 0x0c,
lengths: {
5: [0x9b, 0x87],
10: [0x9b, 0x87],
20: [0x9b, 0x87],
50: [0x9b, 0x87],
100: [0x9b, 0x87]
},
polymer: true
},
ZAR: {
thickness: 0x0c,
lengths: {
10: [0x8a, 0x76],
20: [0x90, 0x7c],
50: [0x96, 0x82],
100: [0x9c, 0x88],
200: [0xa2, 0x8e]
},
polymer: false
}
}

View file

@ -0,0 +1,12 @@
import * as R from 'ramda'
const getBillOptions = R.curry((locale, denomiations) => {
const currency = R.prop('fiatCurrency')(locale)
return R.compose(
R.map(code => ({ code: parseInt(code), display: code })),
R.keys,
R.path([currency, 'lengths'])
)(denomiations)
})
export { getBillOptions }

View file

@ -0,0 +1,42 @@
import * as R from 'ramda'
const namespaces = {
ADVANCED: 'advanced',
CASH_IN: 'cashIn',
CASH_OUT: 'cashOut',
WALLETS: 'wallets',
OPERATOR_INFO: 'operatorInfo',
NOTIFICATIONS: 'notifications',
LOCALE: 'locale',
COMMISSIONS: 'commissions',
RECEIPT: 'receipt',
COIN_ATM_RADAR: 'coinAtmRadar',
TERMS_CONDITIONS: 'termsConditions',
TRIGGERS: 'triggersConfig',
MACHINE_SCREENS: 'machineScreens'
}
const mapKeys = R.curry((fn, obj) =>
R.fromPairs(R.map(R.adjust(0, fn), R.toPairs(obj)))
)
const filterByKey = R.curry((fn, obj) =>
R.fromPairs(R.filter(it => fn(it[0]), R.toPairs(obj)))
)
const stripl = R.curry((q, str) =>
R.startsWith(q, str) ? str.slice(q.length) : str
)
const filtered = key => filterByKey(R.startsWith(`${key}_`))
const stripped = key => mapKeys(stripl(`${key}_`))
const fromNamespace = R.curry((key, config) =>
R.compose(stripped(key), filtered(key))(config)
)
const toNamespace = R.curry((key, config) =>
mapKeys(it => `${key}_${it}`)(config)
)
export { fromNamespace, toNamespace, namespaces }

View file

@ -0,0 +1,23 @@
const CURRENCY_MAX = 9999999
const MIN_NUMBER_OF_CASSETTES = 2
const MAX_NUMBER_OF_CASSETTES = 4
const WALLET_SCORING_DEFAULT_THRESHOLD = 9
const AUTOMATIC = 'automatic'
const MANUAL = 'manual'
const IP_CHECK_REGEX =
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
const SWEEPABLE_CRYPTOS = ['ETH']
export {
CURRENCY_MAX,
MIN_NUMBER_OF_CASSETTES,
MAX_NUMBER_OF_CASSETTES,
AUTOMATIC,
MANUAL,
WALLET_SCORING_DEFAULT_THRESHOLD,
IP_CHECK_REGEX,
SWEEPABLE_CRYPTOS
}

View file

@ -0,0 +1,40 @@
import * as R from 'ramda'
import { onlyFirstToUpper } from 'src/utils/string'
/* Expects a customer ID card data object */
const formatFullName = R.pipe(
R.pick(['firstName', 'lastName']),
R.values,
R.reject(R.allPass([R.isNil, R.isEmpty])),
R.map(onlyFirstToUpper),
R.join(' ')
)
const formatName = idCardData => {
const innerFormatName = ({ firstName, lastName }) =>
firstName && lastName
? `${R.o(R.toUpper, R.head)(firstName)}. ${lastName}`
: R.isNil(firstName)
? lastName
: R.isNil(lastName)
? firstName
: null
return idCardData ? innerFormatName(idCardData) : null
}
/* Expects a transaction object */
const displayName = ({
isAnonymous,
customerName,
customerIdCardData,
customerPhone,
customerEmail
}) =>
isAnonymous
? 'Anonymous'
: customerName ||
customerEmail ||
R.defaultTo(customerPhone, formatName(customerIdCardData))
export { displayName, formatFullName, formatName }

View file

@ -0,0 +1,54 @@
const modelPrettifier = {
douro1: 'Douro',
sintra: 'Sintra',
gaia: 'Gaia',
tejo: 'Tejo',
aveiro: 'Aveiro',
grandola: 'Grândola'
}
const cashUnitCapacity = {
default: {
cashbox: 600,
cassette: 500
},
douro: {
cashbox: 600,
cassette: 500
},
grandola: {
cashbox: 2500,
recycler: 2800
},
aveiro: {
cashbox: 1500,
recycler: 60,
escrow: 20,
cassette: 500
},
tejo: {
// TODO: add support for the different cashbox configuration in Tejo
cashbox: 1000,
cassette: 500
},
gaia: {
cashbox: 600
},
sintra: {
cashbox: 1000,
cassette: 500
},
gmuk1: {
cashbox: 2200,
cassette: 2000
}
}
const getCashUnitCapacity = (model, device) => {
if (!cashUnitCapacity[model]) {
return cashUnitCapacity.default[device]
}
return cashUnitCapacity[model][device]
}
export { modelPrettifier, cashUnitCapacity, getCashUnitCapacity }

View file

@ -0,0 +1,7 @@
import * as R from 'ramda'
const ifNotNull = (value, valueIfNotNull) => {
return R.isNil(value) ? '' : valueIfNotNull
}
export { ifNotNull }

View file

@ -0,0 +1,21 @@
import * as R from 'ramda'
const isValidNumber = R.both(R.is(Number), R.complement(R.equals(NaN)))
const transformNumber = value => (isValidNumber(value) ? value : null)
const defaultToZero = value =>
isValidNumber(parseInt(value)) ? parseInt(value) : 0
const numberToFiatAmount = value =>
value.toLocaleString('en-US', { maximumFractionDigits: 2 })
const numberToCryptoAmount = value =>
value.toLocaleString('en-US', { maximumFractionDigits: 5 })
export {
defaultToZero,
transformNumber,
numberToFiatAmount,
numberToCryptoAmount
}

View file

@ -0,0 +1,36 @@
import * as R from 'ramda'
const formatLong = value => {
if (!value || value.length <= 20) return value
return `${value.slice(0, 8)}(...)${value.slice(
value.length - 8,
value.length
)}`
}
const toFirstLower = R.compose(R.join(''), R.adjust(0, R.toLower))
const toFirstUpper = R.compose(R.join(''), R.adjust(0, R.toUpper))
const onlyFirstToUpper = R.compose(toFirstUpper, R.toLower)
const splitOnUpper = R.compose(
R.split(' '),
R.replace(/([A-Z])/g, ' $1'),
toFirstLower
)
const startCase = R.compose(R.join(' '), R.map(onlyFirstToUpper), splitOnUpper)
const sentenceCase = R.compose(onlyFirstToUpper, R.join(' '), splitOnUpper)
const singularOrPlural = (amount, singularStr, pluralStr) =>
parseInt(amount) === 1 ? singularStr : pluralStr
export {
startCase,
onlyFirstToUpper,
formatLong,
singularOrPlural,
sentenceCase
}

View file

@ -0,0 +1,7 @@
const MINUTE = 60 * 1000
const HOUR = 60 * 60 * 1000
const DAY = 24 * 60 * 60 * 1000
const WEEK = 7 * 24 * 60 * 60 * 1000
const MONTH = 30 * 24 * 60 * 60 * 1000
export { MINUTE, HOUR, DAY, WEEK, MONTH }

View file

@ -0,0 +1,148 @@
import { intervalToDuration } from 'date-fns'
import { getTimezoneOffset } from 'date-fns-tz'
import * as R from 'ramda'
const timezones = {
'Pacific/Midway': { short: 'SST', long: 'Midway Island, Samoa' },
'Pacific/Honolulu': { short: 'HAST', long: 'Hawaii' },
'America/Juneau': { short: 'AKST', long: 'Alaska' },
'America/Boise': { short: 'MST', long: 'Mountain Time' },
'America/Dawson': { short: 'MST', long: 'Dawson, Yukon' },
'America/Chihuahua': { short: null, long: 'Chihuahua, La Paz, Mazatlan' },
'America/Phoenix': { short: 'MST', long: 'Arizona' },
'America/Chicago': { short: 'CST', long: 'Central Time' },
'America/Regina': { short: 'CST', long: 'Saskatchewan' },
'America/Mexico_City': {
short: 'CST',
long: 'Guadalajara, Mexico City, Monterrey'
},
'America/Belize': { short: 'CST', long: 'Central America' },
'America/Detroit': { short: 'EST', long: 'Eastern Time' },
'America/Bogota': { short: 'COT', long: 'Bogota, Lima, Quito' },
'America/Caracas': { short: 'VET', long: 'Caracas, La Paz' },
'America/Santiago': { short: 'CLST', long: 'Santiago' },
'America/St_Johns': { short: 'HNTN', long: 'Newfoundland and Labrador' },
'America/Sao_Paulo': { short: 'BRT', long: 'Brasilia' },
'America/Tijuana': { short: 'PST', long: 'Tijuana' },
'America/Montevideo': { short: 'UYT', long: 'Montevideo' },
'America/Argentina/Buenos_Aires': {
short: null,
long: 'Buenos Aires, Georgetown'
},
'America/Godthab': { short: null, long: 'Greenland' },
'America/Los_Angeles': { short: 'PST', long: 'Pacific Time' },
'Atlantic/Azores': { short: 'AZOT', long: 'Azores' },
'Atlantic/Cape_Verde': { short: 'CVT', long: 'Cape Verde Islands' },
GMT: { short: 'GMT', long: 'UTC' },
'Europe/London': { short: 'GMT', long: 'Edinburgh, London' },
'Europe/Dublin': { short: 'GMT', long: 'Dublin' },
'Europe/Lisbon': { short: 'WET', long: 'Lisbon' },
'Africa/Casablanca': { short: 'WET', long: 'Casablanca, Monrovia' },
'Atlantic/Canary': { short: 'WET', long: 'Canary Islands' },
'Europe/Belgrade': {
short: 'CET',
long: 'Belgrade, Bratislava, Budapest, Ljubljana, Prague'
},
'Europe/Sarajevo': { short: 'CET', long: 'Sarajevo, Skopje, Warsaw, Zagreb' },
'Europe/Brussels': {
short: 'CET',
long: 'Brussels, Copenhagen, Madrid, Paris'
},
'Europe/Amsterdam': {
short: 'CET',
long: 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna'
},
'Africa/Algiers': { short: 'CET', long: 'West Central Africa' },
'Europe/Bucharest': { short: 'EET', long: 'Bucharest' },
'Africa/Cairo': { short: 'EET', long: 'Cairo' },
'Europe/Helsinki': {
short: 'EET',
long: 'Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius'
},
'Europe/Athens': { short: 'EET', long: 'Athens, Istanbul, Minsk' },
'Asia/Jerusalem': { short: 'IST', long: 'Jerusalem' },
'Africa/Harare': { short: 'CAT', long: 'Harare, Pretoria' },
'Europe/Moscow': { short: 'MSK', long: 'Moscow, St. Petersburg, Volgograd' },
'Asia/Kuwait': { short: 'AST', long: 'Kuwait, Riyadh' },
'Africa/Nairobi': { short: 'EAT', long: 'Nairobi' },
'Asia/Baghdad': { short: 'AST', long: 'Baghdad' },
'Asia/Tehran': { short: 'IRST', long: 'Tehran' },
'Asia/Dubai': { short: 'GST', long: 'Abu Dhabi, Muscat' },
'Asia/Baku': { short: 'AZT', long: 'Baku, Tbilisi, Yerevan' },
'Asia/Kabul': { short: 'AFT', long: 'Kabul' },
'Asia/Yekaterinburg': { short: 'YEKT', long: 'Ekaterinburg' },
'Asia/Karachi': { short: 'PKT', long: 'Islamabad, Karachi, Tashkent' },
'Asia/Kolkata': { short: 'IST', long: 'Chennai, Kolkata, Mumbai, New Delhi' },
'Asia/Kathmandu': { short: null, long: 'Kathmandu' },
'Asia/Dhaka': { short: 'BST', long: 'Astana, Dhaka' },
'Asia/Colombo': { short: 'IST', long: 'Sri Jayawardenepura' },
'Asia/Almaty': { short: 'ALMT', long: 'Almaty, Novosibirsk' },
'Asia/Rangoon': { short: null, long: 'Yangon Rangoon' },
'Asia/Bangkok': { short: 'ICT', long: 'Bangkok, Hanoi, Jakarta' },
'Asia/Krasnoyarsk': { short: 'KRAT', long: 'Krasnoyarsk' },
'Asia/Shanghai': {
short: 'CST',
long: 'Beijing, Chongqing, Hong Kong SAR, Urumqi'
},
'Asia/Kuala_Lumpur': { short: 'MYT', long: 'Kuala Lumpur, Singapore' },
'Asia/Taipei': { short: 'CST', long: 'Taipei' },
'Australia/Perth': { short: 'AWST', long: 'Perth' },
'Asia/Irkutsk': { short: 'IRKT', long: 'Irkutsk, Ulaanbaatar' },
'Asia/Seoul': { short: 'KST', long: 'Seoul' },
'Asia/Tokyo': { short: 'JST', long: 'Osaka, Sapporo, Tokyo' },
'Asia/Yakutsk': { short: 'YAKT', long: 'Yakutsk' },
'Australia/Darwin': { short: 'ACST', long: 'Darwin' },
'Australia/Adelaide': { short: 'ACDT', long: 'Adelaide' },
'Australia/Sydney': { short: 'AEDT', long: 'Canberra, Melbourne, Sydney' },
'Australia/Brisbane': { short: 'AEST', long: 'Brisbane' },
'Australia/Hobart': { short: 'AEDT', long: 'Hobart' },
'Asia/Vladivostok': { short: 'VLAT', long: 'Vladivostok' },
'Pacific/Guam': { short: 'ChST', long: 'Guam, Port Moresby' },
'Asia/Magadan': {
short: 'MAGT',
long: 'Magadan, Solomon Islands, New Caledonia'
},
'Asia/Kamchatka': { short: 'PETT', long: 'Kamchatka, Marshall Islands' },
'Pacific/Fiji': { short: 'FJT', long: 'Fiji Islands' },
'Pacific/Auckland': { short: 'NZDT', long: 'Auckland, Wellington' },
'Pacific/Tongatapu': { short: null, long: "Nuku'alofa" }
}
const buildTzLabels = timezoneList => {
const pairs = R.toPairs(timezoneList)
return R.reduce(
(acc, value) => {
const isNegative = getTimezoneOffset(value[0]) < 0
const duration = intervalToDuration({
start: 0,
end: Math.abs(getTimezoneOffset(value[0]))
})
const hours = duration.hours.toLocaleString('en-US', {
minimumIntegerDigits: 2,
useGrouping: false
})
const minutes = duration.minutes.toLocaleString('en-US', {
minimumIntegerDigits: 2,
useGrouping: false
})
const prefix = `(GMT${isNegative ? `-` : `+`}${hours}:${minutes})`
acc.push({
label: `${prefix} - ${value[1].long}`,
code: value[0]
})
return acc
},
[],
pairs
)
}
const labels = buildTzLabels(timezones)
const DEFAULT_TIMEZONE = 'GMT'
export { labels, timezones, DEFAULT_TIMEZONE }

View file

@ -0,0 +1,25 @@
import { zonedTimeToUtc, utcToZonedTime } from 'date-fns-tz/fp'
import { format } from 'date-fns/fp'
const toUtc = date => {
const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone
return zonedTimeToUtc(browserTimezone, date)
}
const toTimezone = (date, timezone) => {
const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone
return utcToZonedTime(timezone, zonedTimeToUtc(browserTimezone, date))
}
const formatDate = (date, timezone, pattern) => {
const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone
const newDate = utcToZonedTime(
timezone,
zonedTimeToUtc(browserTimezone, date)
)
return format(pattern, newDate)
}
const formatDateNonUtc = (date, pattern) => format(pattern, date)
export { toUtc, toTimezone, formatDate, formatDateNonUtc }

View file

@ -0,0 +1,9 @@
const url = `https://${
process.env.NODE_ENV === 'development'
? window.location.host
: window.location.hostname
}`
const urlResolver = content => `${url}${content}`
export { urlResolver }