From d200793d20e95d5f9b97a31c645b703d1b781604 Mon Sep 17 00:00:00 2001
From: Bryann Yeap Kok Keong <bryannyeapkk@gmail.com>
Date: Fri, 21 Oct 2022 21:50:01 +0800
Subject: [PATCH] [offers][feat] Allowing showing income based on selected
 salary

---
 .../components/offers/table/OffersTable.tsx   |   6 +-
 .../src/pages/offers/test/listOffers.tsx      |   3 +-
 .../router/offers/offers-comments-router.ts   |  57 +--
 .../portal/src/server/router/offers/offers.ts |  52 ++-
 .../utils/offers/currency/CurrencyEnum.tsx    | 329 +++++++++---------
 .../offers/currency/currency-exchange.ts      |  14 +
 yarn.lock                                     |  74 ++--
 7 files changed, 295 insertions(+), 240 deletions(-)
 create mode 100644 apps/portal/src/utils/offers/currency/currency-exchange.ts

diff --git a/apps/portal/src/components/offers/table/OffersTable.tsx b/apps/portal/src/components/offers/table/OffersTable.tsx
index 9f46d4d4..df0622f6 100644
--- a/apps/portal/src/components/offers/table/OffersTable.tsx
+++ b/apps/portal/src/components/offers/table/OffersTable.tsx
@@ -15,6 +15,7 @@ import { trpc } from '~/utils/trpc';
 import OffersRow from './OffersRow';
 
 import type { DashboardOffer, GetOffersResponse, Paging } from '~/types/offers';
+import { Currency } from '~/utils/offers/currency/CurrencyEnum';
 
 const NUMBER_OF_OFFERS_IN_PAGE = 10;
 export type OffersTableProps = Readonly<{
@@ -25,7 +26,7 @@ export default function OffersTable({
   companyFilter,
   jobTitleFilter,
 }: OffersTableProps) {
-  const [currency, setCurrency] = useState('SGD'); // TODO: Detect location
+  const [currency, setCurrency] = useState(Currency.SGD.toString()); // TODO: Detect location
   const [selectedTab, setSelectedTab] = useState(YOE_CATEGORY.ENTRY);
   const [pagination, setPagination] = useState<Paging>({
     currentPage: 0,
@@ -44,12 +45,13 @@ export default function OffersTable({
       numOfPages: 0,
       totalItems: 0,
     });
-  }, [selectedTab]);
+  }, [selectedTab, currency]);
   const offersQuery = trpc.useQuery(
     [
       'offers.list',
       {
         companyId: companyFilter,
+        currency,
         limit: NUMBER_OF_OFFERS_IN_PAGE,
         location: 'Singapore, Singapore', // TODO: Geolocation
         offset: pagination.currentPage,
diff --git a/apps/portal/src/pages/offers/test/listOffers.tsx b/apps/portal/src/pages/offers/test/listOffers.tsx
index 143d1826..1e8545d2 100644
--- a/apps/portal/src/pages/offers/test/listOffers.tsx
+++ b/apps/portal/src/pages/offers/test/listOffers.tsx
@@ -6,11 +6,12 @@ function Test() {
   const data = trpc.useQuery([
     'offers.list',
     {
+      currency: "aed",
       limit: 100,
       location: 'Singapore, Singapore',
       offset: 0,
       sortBy: '+totalCompensation',
-      yoeCategory: 1,
+      yoeCategory: 2,
     },
   ]);
 
diff --git a/apps/portal/src/server/router/offers/offers-comments-router.ts b/apps/portal/src/server/router/offers/offers-comments-router.ts
index 2e6b9e38..f2160243 100644
--- a/apps/portal/src/server/router/offers/offers-comments-router.ts
+++ b/apps/portal/src/server/router/offers/offers-comments-router.ts
@@ -26,26 +26,27 @@ export const offersCommentsRouter = createRouter()
                   user: true,
                 },
                 orderBy: {
-                  createdAt: 'desc'
-                }
+                  createdAt: 'desc',
+                },
               },
               replyingTo: true,
               user: true,
             },
             orderBy: {
-              createdAt: 'desc'
-            }
+              createdAt: 'desc',
+            },
           },
         },
         where: {
           id: input.profileId,
-        }
+        },
       });
 
       const discussions: OffersDiscussion = {
-        data: result?.discussion
+        data:
+          result?.discussion
             .filter((x) => {
-            return x.replyingToId === null
+              return x.replyingToId === null;
             })
             .map((x) => {
               if (x.user == null) {
@@ -81,18 +82,18 @@ export const offersCommentsRouter = createRouter()
                     message: reply.message,
                     replies: [],
                     replyingToId: reply.replyingToId,
-                    user: reply.user
-                  }
+                    user: reply.user,
+                  };
                 }),
                 replyingToId: x.replyingToId,
-                user: x.user
-              }
+                user: x.user,
+              };
 
-              return replyType
-            }) ?? []
-      }
+              return replyType;
+            }) ?? [],
+      };
 
-      return discussions
+      return discussions;
     },
   })
   .mutation('create', {
@@ -101,7 +102,7 @@ export const offersCommentsRouter = createRouter()
       profileId: z.string(),
       replyingToId: z.string().optional(),
       token: z.string().optional(),
-      userId: z.string().optional()
+      userId: z.string().optional(),
     }),
     async resolve({ ctx, input }) {
       const profile = await ctx.prisma.offersProfile.findFirst({
@@ -156,7 +157,7 @@ export const offersCommentsRouter = createRouter()
 
         const created = await ctx.prisma.offersReply.findFirst({
           include: {
-            user: true
+            user: true,
           },
           where: {
             id: createdReply.id,
@@ -175,10 +176,10 @@ export const offersCommentsRouter = createRouter()
             id: '',
             image: '',
             name: profile?.profileName ?? '<missing name>',
-          }
-        }
+          },
+        };
 
-        return result
+        return result;
       }
 
       throw new trpc.TRPCError({
@@ -223,10 +224,10 @@ export const offersCommentsRouter = createRouter()
           include: {
             replies: {
               include: {
-                user: true
-              }
+                user: true,
+              },
             },
-            user: true
+            user: true,
           },
           where: {
             id: input.id,
@@ -250,8 +251,8 @@ export const offersCommentsRouter = createRouter()
                 id: '',
                 image: '',
                 name: profile?.profileName ?? '<missing name>',
-              }
-            }
+              },
+            };
           }),
           replyingToId: updated!.replyingToId,
           user: updated!.user ?? {
@@ -260,10 +261,10 @@ export const offersCommentsRouter = createRouter()
             id: '',
             image: '',
             name: profile?.profileName ?? '<missing name>',
-          }
-        }
+          },
+        };
 
-        return result
+        return result;
       }
 
       throw new trpc.TRPCError({
diff --git a/apps/portal/src/server/router/offers/offers.ts b/apps/portal/src/server/router/offers/offers.ts
index 953e133a..f4ba3b4f 100644
--- a/apps/portal/src/server/router/offers/offers.ts
+++ b/apps/portal/src/server/router/offers/offers.ts
@@ -1,12 +1,9 @@
-import { z } from 'zod';
-import { TRPCError } from '@trpc/server';
-
-import {
-  dashboardOfferDtoMapper,
-  getOffersResponseMapper,
-} from '~/mappers/offers-mappers';
-
-import { createRouter } from '../context';
+import { TRPCError } from "@trpc/server";
+import { z } from "zod";
+import { dashboardOfferDtoMapper, getOffersResponseMapper } from "~/mappers/offers-mappers";
+import { convert } from "~/utils/offers/currency/currency-exchange";
+import { Currency } from "~/utils/offers/currency/CurrencyEnum";
+import { createRouter } from "../context";
 
 const yoeCategoryMap: Record<number, string> = {
   0: 'Internship',
@@ -38,6 +35,7 @@ const createSortByValidationRegex = () => {
 export const offersRouter = createRouter().query('list', {
   input: z.object({
     companyId: z.string().nullish(),
+    currency: z.string().nullish(),
     dateEnd: z.date().nullish(),
     dateStart: z.date().nullish(),
     limit: z.number().positive(),
@@ -80,6 +78,9 @@ export const offersRouter = createRouter().query('list', {
               },
             },
           },
+          orderBy: {
+            monthYearReceived: 'desc',
+          },
           where: {
             AND: [
               {
@@ -121,6 +122,9 @@ export const offersRouter = createRouter().query('list', {
               },
             },
           },
+          orderBy: {
+            monthYearReceived: 'desc',
+          },
           where: {
             AND: [
               {
@@ -150,6 +154,36 @@ export const offersRouter = createRouter().query('list', {
           },
         });
 
+    // CONVERTING
+    const currency = input.currency?.toUpperCase()
+    if (currency != null && currency in Currency) {
+      data = await Promise.all(
+        data.map(async (offer) => {
+
+          if (offer.offersFullTime?.totalCompensation) {
+            offer.offersFullTime.totalCompensation.value = await convert(offer.offersFullTime.totalCompensation.value, offer.offersFullTime.totalCompensation.currency, currency);
+            offer.offersFullTime.totalCompensation.currency = currency;
+            offer.offersFullTime.baseSalary.value = await convert(offer.offersFullTime.totalCompensation.value, offer.offersFullTime.totalCompensation.currency, currency);
+            offer.offersFullTime.baseSalary.currency = currency;
+            offer.offersFullTime.stocks.value = await convert(offer.offersFullTime.totalCompensation.value, offer.offersFullTime.totalCompensation.currency, currency);
+            offer.offersFullTime.stocks.currency = currency;
+            offer.offersFullTime.bonus.value = await convert(offer.offersFullTime.totalCompensation.value, offer.offersFullTime.totalCompensation.currency, currency);
+            offer.offersFullTime.bonus.currency = currency;
+          } else if (offer.offersIntern?.monthlySalary) {
+            offer.offersIntern.monthlySalary.value = await convert(offer.offersIntern.monthlySalary.value, offer.offersIntern.monthlySalary.currency, currency);
+            offer.offersIntern.monthlySalary.currency = currency;
+          } else {
+            throw new TRPCError({
+              code: 'NOT_FOUND',
+              message: 'Total Compensation or Salary not found',
+            });
+          }
+
+          return offer;
+        }),
+      );
+    }
+
     // FILTERING
     data = data.filter((offer) => {
       let validRecord = true;
diff --git a/apps/portal/src/utils/offers/currency/CurrencyEnum.tsx b/apps/portal/src/utils/offers/currency/CurrencyEnum.tsx
index 7cfbc0cb..88efa1c1 100644
--- a/apps/portal/src/utils/offers/currency/CurrencyEnum.tsx
+++ b/apps/portal/src/utils/offers/currency/CurrencyEnum.tsx
@@ -1,167 +1,170 @@
 // eslint-disable-next-line no-shadow
-export enum Currency {
-  AED = 'AED', // United Arab Emirates Dirham
-  AFN = 'AFN', // Afghanistan Afghani
-  ALL = 'ALL', // Albania Lek
-  AMD = 'AMD', // Armenia Dram
-  ANG = 'ANG', // Netherlands Antilles Guilder
-  AOA = 'AOA', // Angola Kwanza
-  ARS = 'ARS', // Argentina Peso
-  AUD = 'AUD', // Australia Dollar
-  AWG = 'AWG', // Aruba Guilder
-  AZN = 'AZN', // Azerbaijan New Manat
-  BAM = 'BAM', // Bosnia and Herzegovina Convertible Marka
-  BBD = 'BBD', // Barbados Dollar
-  BDT = 'BDT', // Bangladesh Taka
-  BGN = 'BGN', // Bulgaria Lev
-  BHD = 'BHD', // Bahrain Dinar
-  BIF = 'BIF', // Burundi Franc
-  BMD = 'BMD', // Bermuda Dollar
-  BND = 'BND', // Brunei Darussalam Dollar
-  BOB = 'BOB', // Bolivia Bolíviano
-  BRL = 'BRL', // Brazil Real
-  BSD = 'BSD', // Bahamas Dollar
-  BTN = 'BTN', // Bhutan Ngultrum
-  BWP = 'BWP', // Botswana Pula
-  BYR = 'BYR', // Belarus Ruble
-  BZD = 'BZD', // Belize Dollar
-  CAD = 'CAD', // Canada Dollar
-  CDF = 'CDF', // Congo/Kinshasa Franc
-  CHF = 'CHF', // Switzerland Franc
-  CLP = 'CLP', // Chile Peso
-  CNY = 'CNY', // China Yuan Renminbi
-  COP = 'COP', // Colombia Peso
-  CRC = 'CRC', // Costa Rica Colon
-  CUC = 'CUC', // Cuba Convertible Peso
-  CUP = 'CUP', // Cuba Peso
-  CVE = 'CVE', // Cape Verde Escudo
-  CZK = 'CZK', // Czech Republic Koruna
-  DJF = 'DJF', // Djibouti Franc
-  DKK = 'DKK', // Denmark Krone
-  DOP = 'DOP', // Dominican Republic Peso
-  DZD = 'DZD', // Algeria Dinar
-  EGP = 'EGP', // Egypt Pound
-  ERN = 'ERN', // Eritrea Nakfa
-  ETB = 'ETB', // Ethiopia Birr
-  EUR = 'EUR', // Euro Member Countries
-  FJD = 'FJD', // Fiji Dollar
-  FKP = 'FKP', // Falkland Islands (Malvinas) Pound
-  GBP = 'GBP', // United Kingdom Pound
-  GEL = 'GEL', // Georgia Lari
-  GGP = 'GGP', // Guernsey Pound
-  GHS = 'GHS', // Ghana Cedi
-  GIP = 'GIP', // Gibraltar Pound
-  GMD = 'GMD', // Gambia Dalasi
-  GNF = 'GNF', // Guinea Franc
-  GTQ = 'GTQ', // Guatemala Quetzal
-  GYD = 'GYD', // Guyana Dollar
-  HKD = 'HKD', // Hong Kong Dollar
-  HNL = 'HNL', // Honduras Lempira
-  HRK = 'HRK', // Croatia Kuna
-  HTG = 'HTG', // Haiti Gourde
-  HUF = 'HUF', // Hungary Forint
-  IDR = 'IDR', // Indonesia Rupiah
-  ILS = 'ILS', // Israel Shekel
-  IMP = 'IMP', // Isle of Man Pound
-  INR = 'INR', // India Rupee
-  IQD = 'IQD', // Iraq Dinar
-  IRR = 'IRR', // Iran Rial
-  ISK = 'ISK', // Iceland Krona
-  JEP = 'JEP', // Jersey Pound
-  JMD = 'JMD', // Jamaica Dollar
-  JOD = 'JOD', // Jordan Dinar
-  JPY = 'JPY', // Japan Yen
-  KES = 'KES', // Kenya Shilling
-  KGS = 'KGS', // Kyrgyzstan Som
-  KHR = 'KHR', // Cambodia Riel
-  KMF = 'KMF', // Comoros Franc
-  KPW = 'KPW', // Korea (North) Won
-  KRW = 'KRW', // Korea (South) Won
-  KWD = 'KWD', // Kuwait Dinar
-  KYD = 'KYD', // Cayman Islands Dollar
-  KZT = 'KZT', // Kazakhstan Tenge
-  LAK = 'LAK', // Laos Kip
-  LBP = 'LBP', // Lebanon Pound
-  LKR = 'LKR', // Sri Lanka Rupee
-  LRD = 'LRD', // Liberia Dollar
-  LSL = 'LSL', // Lesotho Loti
-  LYD = 'LYD', // Libya Dinar
-  MAD = 'MAD', // Morocco Dirham
-  MDL = 'MDL', // Moldova Leu
-  MGA = 'MGA', // Madagascar Ariary
-  MKD = 'MKD', // Macedonia Denar
-  MMK = 'MMK', // Myanmar (Burma) Kyat
-  MNT = 'MNT', // Mongolia Tughrik
-  MOP = 'MOP', // Macau Pataca
-  MRO = 'MRO', // Mauritania Ouguiya
-  MUR = 'MUR', // Mauritius Rupee
-  MVR = 'MVR', // Maldives (Maldive Islands) Rufiyaa
-  MWK = 'MWK', // Malawi Kwacha
-  MXN = 'MXN', // Mexico Peso
-  MYR = 'MYR', // Malaysia Ringgit
-  MZN = 'MZN', // Mozambique Metical
-  NAD = 'NAD', // Namibia Dollar
-  NGN = 'NGN', // Nigeria Naira
-  NIO = 'NIO', // Nicaragua Cordoba
-  NOK = 'NOK', // Norway Krone
-  NPR = 'NPR', // Nepal Rupee
-  NZD = 'NZD', // New Zealand Dollar
-  OMR = 'OMR', // Oman Rial
-  PAB = 'PAB', // Panama Balboa
-  PEN = 'PEN', // Peru Sol
-  PGK = 'PGK', // Papua New Guinea Kina
-  PHP = 'PHP', // Philippines Peso
-  PKR = 'PKR', // Pakistan Rupee
-  PLN = 'PLN', // Poland Zloty
-  PYG = 'PYG', // Paraguay Guarani
-  QAR = 'QAR', // Qatar Riyal
-  RON = 'RON', // Romania New Leu
-  RSD = 'RSD', // Serbia Dinar
-  RUB = 'RUB', // Russia Ruble
-  RWF = 'RWF', // Rwanda Franc
-  SAR = 'SAR', // Saudi Arabia Riyal
-  SBD = 'SBD', // Solomon Islands Dollar
-  SCR = 'SCR', // Seychelles Rupee
-  SDG = 'SDG', // Sudan Pound
-  SEK = 'SEK', // Sweden Krona
-  SGD = 'SGD', // Singapore Dollar
-  SHP = 'SHP', // Saint Helena Pound
-  SLL = 'SLL', // Sierra Leone Leone
-  SOS = 'SOS', // Somalia Shilling
-  SPL = 'SPL', // Seborga Luigino
-  SRD = 'SRD', // Suriname Dollar
-  STD = 'STD', // São Tomé and Príncipe Dobra
-  SVC = 'SVC', // El Salvador Colon
-  SYP = 'SYP', // Syria Pound
-  SZL = 'SZL', // Swaziland Lilangeni
-  THB = 'THB', // Thailand Baht
-  TJS = 'TJS', // Tajikistan Somoni
-  TMT = 'TMT', // Turkmenistan Manat
-  TND = 'TND', // Tunisia Dinar
-  TOP = 'TOP', // Tonga Pa'anga
-  TRY = 'TRY', // Turkey Lira
-  TTD = 'TTD', // Trinidad and Tobago Dollar
-  TVD = 'TVD', // Tuvalu Dollar
-  TWD = 'TWD', // Taiwan New Dollar
-  TZS = 'TZS', // Tanzania Shilling
-  UAH = 'UAH', // Ukraine Hryvnia
-  UGX = 'UGX', // Uganda Shilling
-  USD = 'USD', // United States Dollar
-  UYU = 'UYU', // Uruguay Peso
-  UZS = 'UZS', // Uzbekistan Som
-  VEF = 'VEF', // Venezuela Bolivar
-  VND = 'VND', // Viet Nam Dong
-  VUV = 'VUV', // Vanuatu Vatu
-  WST = 'WST', // Samoa Tala
-  XAF = 'XAF', // Communauté Financière Africaine (BEAC) CFA Franc BEAC
-  XCD = 'XCD', // East Caribbean Dollar
-  XDR = 'XDR', // International Monetary Fund (IMF) Special Drawing Rights
-  XOF = 'XOF', // Communauté Financière Africaine (BCEAO) Franc
-  XPF = 'XPF', // Comptoirs Français du Pacifique (CFP) Franc
-  YER = 'YER', // Yemen Rial
-  ZAR = 'ZAR', // South Africa Rand
-  ZMW = 'ZMW', // Zambia Kwacha
-  ZWD = 'ZWD', // Zimbabwe Dollar
+export enum Currency  {
+  AED = "AED", // 'UNITED ARAB EMIRATES DIRHAM'
+  AFN = "AFN", // 'AFGHAN AFGHANI'
+  ALL = "ALL", // 'ALBANIAN LEK'
+  AMD = "AMD", // 'ARMENIAN DRAM'
+  ANG = "ANG", // 'NETHERLANDS ANTILLEAN GUILDER'
+  AOA = "AOA", // 'ANGOLAN KWANZA'
+  ARS = "ARS", // 'ARGENTINE PESO'
+  AUD = "AUD", // 'AUSTRALIAN DOLLAR'
+  AWG = "AWG", // 'ARUBAN FLORIN'
+  AZN = "AZN", // 'AZERBAIJANI MANAT'
+  BAM = "BAM", // 'BOSNIA-HERZEGOVINA CONVERTIBLE MARK'
+  BBD = "BBD", // 'BAJAN DOLLAR'
+  BDT = "BDT", // 'BANGLADESHI TAKA'
+  BGN = "BGN", // 'BULGARIAN LEV'
+  BHD = "BHD", // 'BAHRAINI DINAR'
+  BIF = "BIF", // 'BURUNDIAN FRANC'
+  BMD = "BMD", // 'BERMUDAN DOLLAR'
+  BND = "BND", // 'BRUNEI DOLLAR'
+  BOB = "BOB", // 'BOLIVIAN BOLIVIANO'
+  BRL = "BRL", // 'BRAZILIAN REAL'
+  BSD = "BSD", // 'BAHAMIAN DOLLAR'
+  BTN = "BTN", // 'BHUTAN CURRENCY'
+  BWP = "BWP", // 'BOTSWANAN PULA'
+  BYN = "BYN", // 'NEW BELARUSIAN RUBLE'
+  BYR = "BYR", // 'BELARUSIAN RUBLE'
+  BZD = "BZD", // 'BELIZE DOLLAR'
+  CAD = "CAD", // 'CANADIAN DOLLAR'
+  CDF = "CDF", // 'CONGOLESE FRANC'
+  CHF = "CHF", // 'SWISS FRANC'
+  CLF = "CLF", // 'CHILEAN UNIT OF ACCOUNT (UF)'
+  CLP = "CLP", // 'CHILEAN PESO'
+  CNY = "CNY", // 'CHINESE YUAN'
+  COP = "COP", // 'COLOMBIAN PESO'
+  CRC = "CRC", // 'COSTA RICAN COLÓN'
+  CUC = "CUC", // 'CUBAN CONVERTIBLE PESO'
+  CUP = "CUP", // 'CUBAN PESO'
+  CVE = "CVE", // 'CAPE VERDEAN ESCUDO'
+  CVX = "CVX", // 'CONVEX FINANCE'
+  CZK = "CZK", // 'CZECH KORUNA'
+  DJF = "DJF", // 'DJIBOUTIAN FRANC'
+  DKK = "DKK", // 'DANISH KRONE'
+  DOP = "DOP", // 'DOMINICAN PESO'
+  DZD = "DZD", // 'ALGERIAN DINAR'
+  EGP = "EGP", // 'EGYPTIAN POUND'
+  ERN = "ERN", // 'ERITREAN NAKFA'
+  ETB = "ETB", // 'ETHIOPIAN BIRR'
+  ETC = "ETC", // 'ETHEREUM CLASSIC'
+  EUR = "EUR", // 'EURO'
+  FEI = "FEI", // 'FEI USD'
+  FJD = "FJD", // 'FIJIAN DOLLAR'
+  FKP = "FKP", // 'FALKLAND ISLANDS POUND'
+  GBP = "GBP", // 'POUND STERLING'
+  GEL = "GEL", // 'GEORGIAN LARI'
+  GHS = "GHS", // 'GHANAIAN CEDI'
+  GIP = "GIP", // 'GIBRALTAR POUND'
+  GMD = "GMD", // 'GAMBIAN DALASI'
+  GNF = "GNF", // 'GUINEAN FRANC'
+  GTQ = "GTQ", // 'GUATEMALAN QUETZAL'
+  GYD = "GYD", // 'GUYANAESE DOLLAR'
+  HKD = "HKD", // 'HONG KONG DOLLAR'
+  HNL = "HNL", // 'HONDURAN LEMPIRA'
+  HRK = "HRK", // 'CROATIAN KUNA'
+  HTG = "HTG", // 'HAITIAN GOURDE'
+  HUF = "HUF", // 'HUNGARIAN FORINT'
+  ICP = "ICP", // 'INTERNET COMPUTER'
+  IDR = "IDR", // 'INDONESIAN RUPIAH'
+  ILS = "ILS", // 'ISRAELI NEW SHEKEL'
+  INR = "INR", // 'INDIAN RUPEE'
+  IQD = "IQD", // 'IRAQI DINAR'
+  IRR = "IRR", // 'IRANIAN RIAL'
+  ISK = "ISK", // 'ICELANDIC KRÓNA'
+  JEP = "JEP", // 'JERSEY POUND'
+  JMD = "JMD", // 'JAMAICAN DOLLAR'
+  JOD = "JOD", // 'JORDANIAN DINAR'
+  JPY = "JPY", // 'JAPANESE YEN'
+  KES = "KES", // 'KENYAN SHILLING'
+  KGS = "KGS", // 'KYRGYSTANI SOM'
+  KHR = "KHR", // 'CAMBODIAN RIEL'
+  KMF = "KMF", // 'COMORIAN FRANC'
+  KPW = "KPW", // 'NORTH KOREAN WON'
+  KRW = "KRW", // 'SOUTH KOREAN WON'
+  KWD = "KWD", // 'KUWAITI DINAR'
+  KYD = "KYD", // 'CAYMAN ISLANDS DOLLAR'
+  KZT = "KZT", // 'KAZAKHSTANI TENGE'
+  LAK = "LAK", // 'LAOTIAN KIP'
+  LBP = "LPB", // 'LEBANESE POUND'
+  LKR = "LKR", // 'SRI LANKAN RUPEE'
+  LRD = "LRD", // 'LIBERIAN DOLLAR'
+  LSL = "LSL", // 'LESOTHO LOTI'
+  LTL = "LTL", // 'LITHUANIAN LITAS'
+  LVL = "LVL", // 'LATVIAN LATS'
+  LYD = "LYD", // 'LIBYAN DINAR'
+  MAD = "MAD", // 'MOROCCAN DIRHAM'
+  MDL = "MDL", // 'MOLDOVAN LEU'
+  MGA = "MGA", // 'MALAGASY ARIARY'
+  MKD = "MKD", // 'MACEDONIAN DENAR'
+  MMK = "MMK", // 'MYANMAR KYAT'
+  MNT = "MNT", // 'MONGOLIAN TUGRIK'
+  MOP = "MOP", // 'MACANESE PATACA'
+  MRO = "MRO", // 'MAURITANIAN OUGUIYA'
+  MUR = "MUR", // 'MAURITIAN RUPEE'
+  MVR = "MVR", // 'MALDIVIAN RUFIYAA'
+  MWK = "MWK", // 'MALAWIAN KWACHA'
+  MXN = "MXN", // 'MEXICAN PESO'
+  MYR = "MYR", // 'MALAYSIAN RINGGIT'
+  MZN = "MZN", // 'MOZAMBICAN METICAL'
+  NAD = "NAD", // 'NAMIBIAN DOLLAR'
+  NGN = "NGN", // 'NIGERIAN NAIRA'
+  NIO = "NIO", // 'NICARAGUAN CÓRDOBA'
+  NOK = "NOK", // 'NORWEGIAN KRONE'
+  NPR = "NPR", // 'NEPALESE RUPEE'
+  NZD = "NZD", // 'NEW ZEALAND DOLLAR'
+  OMR = "OMR", // 'OMANI RIAL'
+  ONE = "ONE", // 'MENLO ONE'
+  PAB = "PAB", // 'PANAMANIAN BALBOA'
+  PGK = "PGK", // 'PAPUA NEW GUINEAN KINA'
+  PHP = "PHP", // 'PHILIPPINE PESO'
+  PKR = "PKR", // 'PAKISTANI RUPEE'
+  PLN = "PLN", // 'POLAND ZŁOTY'
+  PYG = "PYG", // 'PARAGUAYAN GUARANI'
+  QAR = "QAR", // 'QATARI RIAL'
+  RON = "RON", // 'ROMANIAN LEU'
+  RSD = "RSD", // 'SERBIAN DINAR'
+  RUB = "RUB", // 'RUSSIAN RUBLE'
+  RWF = "RWF", // 'RWANDAN FRANC'
+  SAR = "SAR", // 'SAUDI RIYAL'
+  SBD = "SBD", // 'SOLOMON ISLANDS DOLLAR'
+  SCR = "SCR", // 'SEYCHELLOIS RUPEE'
+  SDG = "SDG", // 'SUDANESE POUND'
+  SEK = "SEK", // 'SWEDISH KRONA'
+  SGD = "SGD", // 'SINGAPORE DOLLAR'
+  SHIB = "SHIB", // 'SHIBA INU'
+  SHP = "SHP", // 'SAINT HELENA POUND'
+  SLL = "SLL", // 'SIERRA LEONEAN LEONE'
+  SOS = "SOS", // 'SOMALI SHILLING'
+  SRD = "SRD", // 'SURINAMESE DOLLAR'
+  STD = "STD", // 'SÃO TOMÉ AND PRÍNCIPE DOBRA (PRE-2018)'
+  SVC = "SVC", // 'SALVADORAN COLÓN'
+  SYP = "SYP", // 'SYRIAN POUND'
+  SZL = "SZL", // 'SWAZI LILANGENI'
+  THB = "THB", // 'THAI BAHT'
+  TJS = "TJS", // 'TAJIKISTANI SOMONI'
+  TMT = "TMT", // 'TURKMENISTANI MANAT'
+  TND = "TND", // 'TUNISIAN DINAR'
+  TOP = "TOP", // "TONGAN PA'ANGA"
+  TRY = "TRY", // 'TURKISH LIRA'
+  TTD = "TTD", // 'TRINIDAD & TOBAGO DOLLAR'
+  TWD = "TWD", // 'NEW TAIWAN DOLLAR'
+  TZS = "TZS", // 'TANZANIAN SHILLING'
+  UAH = "UAH", // 'UKRAINIAN HRYVNIA'
+  UGX = "UGX", // 'UGANDAN SHILLING'
+  USD = "USD", // 'UNITED STATES DOLLAR'
+  UYU = "UYU", // 'URUGUAYAN PESO'
+  UZS = "UZS", // 'UZBEKISTANI SOM'
+  VND = "VND", // 'VIETNAMESE DONG'
+  VUV = "VUV", // 'VANUATU VATU'
+  WST = "WST", // 'SAMOAN TALA'
+  XAF = "XAF", // 'CENTRAL AFRICAN CFA FRANC'
+  XCD = "XCD", // 'EAST CARIBBEAN DOLLAR'
+  XOF = "XOF", // 'WEST AFRICAN CFA FRANC'
+  XPF = "XPF", // 'CFP FRANC'
+  YER = "YER", // 'YEMENI RIAL'
+  ZAR = "ZAR", // 'SOUTH AFRICAN RAND'
+  ZMW = "ZMW", // 'ZAMBIAN KWACHA'
+  ZWL = "ZWL", // 'ZIMBABWEAN DOLLAR'
 }
 
 export const CURRENCY_OPTIONS = Object.entries(Currency).map(
diff --git a/apps/portal/src/utils/offers/currency/currency-exchange.ts b/apps/portal/src/utils/offers/currency/currency-exchange.ts
new file mode 100644
index 00000000..31e02067
--- /dev/null
+++ b/apps/portal/src/utils/offers/currency/currency-exchange.ts
@@ -0,0 +1,14 @@
+// API from https://github.com/fawazahmed0/currency-api#readme
+export const convert = async (
+  value: number,
+  fromCurrency: string,
+  toCurrency: string,
+) => {
+  fromCurrency = fromCurrency.trim().toLowerCase();
+  toCurrency = toCurrency.trim().toLowerCase();
+  const url = ['https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies', fromCurrency, toCurrency].join('/');
+
+  return await fetch(url + '.json')
+    .then((res) => res.json())
+    .then((data) => value * data[toCurrency]);
+};
diff --git a/yarn.lock b/yarn.lock
index 04c23ef7..c144d5b6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4607,7 +4607,7 @@ atob@^2.1.2:
 
 attr-accept@^2.2.2:
   version "2.2.2"
-  resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
+  resolved "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz"
   integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==
 
 autoprefixer@^10.3.7, autoprefixer@^10.4.12, autoprefixer@^10.4.7:
@@ -7740,7 +7740,7 @@ file-loader@^6.0.0, file-loader@^6.2.0:
 
 file-selector@^0.6.0:
   version "0.6.0"
-  resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc"
+  resolved "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz"
   integrity sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==
   dependencies:
     tslib "^2.4.0"
@@ -12176,7 +12176,7 @@ react-dom@18.2.0, react-dom@^18.2.0:
 
 react-dropzone@^14.2.3:
   version "14.2.3"
-  resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.3.tgz#0acab68308fda2d54d1273a1e626264e13d4e84b"
+  resolved "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz"
   integrity sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==
   dependencies:
     attr-accept "^2.2.2"
@@ -14163,47 +14163,47 @@ tty-browserify@0.0.0:
   resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz"
   integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==
 
-turbo-darwin-64@1.5.5:
-  version "1.5.5"
-  resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.5.5.tgz#710d4e7999066bd4f500456f7cd1c30f6e6205ed"
-  integrity sha512-HvEn6P2B+NXDekq9LRpRgUjcT9/oygLTcK47U0qsAJZXRBSq/2hvD7lx4nAwgY/4W3rhYJeWtHTzbhoN6BXqGQ==
+turbo-darwin-64@1.5.6:
+  version "1.5.6"
+  resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.5.6.tgz#2e0e14343c84dde33b5a09ea5389ee6a9565779c"
+  integrity sha512-CWdXMwenBS2+QXIR2Czx7JPnAcoMzWx/QwTDcHVxZyeayMHgz8Oq5AHCtfaHDSfV8YhD3xa0GLSk6+cFt+W8BQ==
 
-turbo-darwin-arm64@1.5.5:
-  version "1.5.5"
-  resolved "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.5.5.tgz"
-  integrity sha512-Dmxr09IUy6M0nc7/xWod9galIO2DD500B75sJSkHeT+CCdJOWnlinux0ZPF8CSygNqymwYO8AO2l15/6yxcycg==
+turbo-darwin-arm64@1.5.6:
+  version "1.5.6"
+  resolved "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.5.6.tgz"
+  integrity sha512-c/aXgW9JuXT2bJSKf01pdSDQKnrdcdj3WFKmKiVldb9We6eqFzI0fLHBK97k5LM/OesmRMfCMQ2Cv2DU8RqBAA==
 
-turbo-linux-64@1.5.5:
-  version "1.5.5"
-  resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.5.5.tgz#f31eb117a9b605f5731048c50473bff903850047"
-  integrity sha512-wd07TZ4zXXWjzZE00FcFMLmkybQQK/NV9ff66vvAV0vdiuacSMBCNLrD6Mm4ncfrUPW/rwFW5kU/7hyuEqqtDw==
+turbo-linux-64@1.5.6:
+  version "1.5.6"
+  resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.5.6.tgz#e7ddaf7a87084dfdd9c6d79efb41084d75439b31"
+  integrity sha512-y/jNF7SG+XJEwk2GxIqy3g4dj/a0PgZKDGyOkp24qp4KBRcHBl6dI1ZEfNed30EhEqmW4F5Dr7IpeCZoqgbrMg==
 
-turbo-linux-arm64@1.5.5:
-  version "1.5.5"
-  resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.5.5.tgz#b9ce6912ae6477e829355d6f012500bfef58669d"
-  integrity sha512-q3q33tuo74R7gicnfvFbnZZvqmlq7Vakcvx0eshifnJw4PR+oMnTCb4w8ElVFx070zsb8DVTibq99y8NJH8T1Q==
+turbo-linux-arm64@1.5.6:
+  version "1.5.6"
+  resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.5.6.tgz#6445f00f84e0f356a6a369ba2d75ede43aaeb796"
+  integrity sha512-FRcxPtW7eFrbR3QaYBVX8cK7i+2Cerqi6F0t5ulcq+d1OGSdSW3l35rPPyJdwCzCy+k/S9sBcyCV0RtbS6RKCQ==
 
-turbo-windows-64@1.5.5:
-  version "1.5.5"
-  resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.5.5.tgz#609098de3bc6178f733615d21b06d5c1602637eb"
-  integrity sha512-lPp9kHonNFfqgovbaW+UAPO5cLmoAN+m3G3FzqcrRPnlzt97vXYsDhDd/4Zy3oAKoAcprtP4CGy0ddisqsKTVw==
+turbo-windows-64@1.5.6:
+  version "1.5.6"
+  resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.5.6.tgz#3638d5297319157031e4dc906dbae53a1db8562c"
+  integrity sha512-/5KIExY7zbrbeL5fhKGuO85u5VtJ3Ue4kI0MbYCNnTGe7a10yTYkwswgtGihsgEF4AW0Nm0159aHmXZS2Le8IA==
 
-turbo-windows-arm64@1.5.5:
-  version "1.5.5"
-  resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.5.5.tgz#60522e1e347a54c64bdddb68089fc322ee19c3d7"
-  integrity sha512-3AfGULKNZiZVrEzsIE+W79ZRW1+f5r4nM4wLlJ1PTBHyRxBZdD6KTH1tijGfy/uTlcV5acYnKHEkDc6Q9PAXGQ==
+turbo-windows-arm64@1.5.6:
+  version "1.5.6"
+  resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.5.6.tgz#9eff9d13721be0b905b0aad07667507380f738fe"
+  integrity sha512-p+LQN9O39+rZuOAyc6BzyVGvdEKo+v+XmtdeyZsZpfj4xuOLtsEptW1w6cUD439u0YcPknuccGq1MQ0lXQ6Xuw==
 
 turbo@latest:
-  version "1.5.5"
-  resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.5.5.tgz#9fc3a917c914ffa113c260a4eadb4bc632eee227"
-  integrity sha512-PVQSDl0STC9WXIyHcYUWs9gXsf8JjQig/FuHfuB8N6+XlgCGB3mPbfMEE6zrChGz2hufH4/guKRX1XJuNL6XTA==
+  version "1.5.6"
+  resolved "https://registry.npmjs.org/turbo/-/turbo-1.5.6.tgz"
+  integrity sha512-xJO/fhiMo4lI62iGR9OgUfJTC9tnnuoMwNC52IfvvBDEPlA8RWGMS8SFpDVG9bNCXvVRrtUTNJXMe6pJWBiOTA==
   optionalDependencies:
-    turbo-darwin-64 "1.5.5"
-    turbo-darwin-arm64 "1.5.5"
-    turbo-linux-64 "1.5.5"
-    turbo-linux-arm64 "1.5.5"
-    turbo-windows-64 "1.5.5"
-    turbo-windows-arm64 "1.5.5"
+    turbo-darwin-64 "1.5.6"
+    turbo-darwin-arm64 "1.5.6"
+    turbo-linux-64 "1.5.6"
+    turbo-linux-arm64 "1.5.6"
+    turbo-windows-64 "1.5.6"
+    turbo-windows-arm64 "1.5.6"
 
 type-check@^0.4.0, type-check@~0.4.0:
   version "0.4.0"
@@ -14641,7 +14641,7 @@ uuid-browser@^3.1.0:
 
 uuid@^3.3.2:
   version "3.4.0"
-  resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
   integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
 
 uuid@^8.3.2: