Commit 20979336 authored by Djordje's avatar Djordje

Implement building invoices

parent 61b30350
......@@ -18,8 +18,11 @@
"@types/node": "^16.11.41",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
"i18next": "^21.9.1",
"i18next-browser-languagedetector": "^6.1.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^11.18.4",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"typescript": "^4.7.4",
......@@ -1804,9 +1807,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz",
"integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==",
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
......@@ -8804,6 +8807,14 @@
"node": ">=12"
}
},
"node_modules/html-parse-stringify": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
"dependencies": {
"void-elements": "3.1.0"
}
},
"node_modules/html-webpack-plugin": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
......@@ -8938,6 +8949,36 @@
"node": ">=10.17.0"
}
},
"node_modules/i18next": {
"version": "21.9.1",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-21.9.1.tgz",
"integrity": "sha512-ITbDrAjbRR73spZAiu6+ex5WNlHRr1mY+acDi2ioTHuUiviJqSz269Le1xHAf0QaQ6GgIHResUhQNcxGwa/PhA==",
"funding": [
{
"type": "individual",
"url": "https://locize.com"
},
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"dependencies": {
"@babel/runtime": "^7.17.2"
}
},
"node_modules/i18next-browser-languagedetector": {
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.5.tgz",
"integrity": "sha512-11t7b39oKeZe4uyMxLSPnfw28BCPNLZgUk7zyufex0zKXZ+Bv+JnmJgoB+IfQLZwDt1d71PM8vwBX1NCgliY3g==",
"dependencies": {
"@babel/runtime": "^7.18.9"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
......@@ -14175,6 +14216,27 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
},
"node_modules/react-i18next": {
"version": "11.18.4",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.18.4.tgz",
"integrity": "sha512-gK/AylAQC5DvCD5YLNCHW4PNzpCfrWIyVAXbSMl+/5QXzlDP8VdBoqE2s2niGHB+zIXwBV9hRXbDrVuupbgHcg==",
"dependencies": {
"@babel/runtime": "^7.14.5",
"html-parse-stringify": "^3.0.1"
},
"peerDependencies": {
"i18next": ">= 19.0.0",
"react": ">= 16.8.0"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
......@@ -16112,6 +16174,14 @@
"node": ">= 0.8"
}
},
"node_modules/void-elements": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
......@@ -18214,9 +18284,9 @@
}
},
"@babel/runtime": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz",
"integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==",
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
"integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
......@@ -23247,6 +23317,14 @@
"terser": "^5.10.0"
}
},
"html-parse-stringify": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
"requires": {
"void-elements": "3.1.0"
}
},
"html-webpack-plugin": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
......@@ -23338,6 +23416,22 @@
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
},
"i18next": {
"version": "21.9.1",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-21.9.1.tgz",
"integrity": "sha512-ITbDrAjbRR73spZAiu6+ex5WNlHRr1mY+acDi2ioTHuUiviJqSz269Le1xHAf0QaQ6GgIHResUhQNcxGwa/PhA==",
"requires": {
"@babel/runtime": "^7.17.2"
}
},
"i18next-browser-languagedetector": {
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.5.tgz",
"integrity": "sha512-11t7b39oKeZe4uyMxLSPnfw28BCPNLZgUk7zyufex0zKXZ+Bv+JnmJgoB+IfQLZwDt1d71PM8vwBX1NCgliY3g==",
"requires": {
"@babel/runtime": "^7.18.9"
}
},
"iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
......@@ -26950,6 +27044,15 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
},
"react-i18next": {
"version": "11.18.4",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.18.4.tgz",
"integrity": "sha512-gK/AylAQC5DvCD5YLNCHW4PNzpCfrWIyVAXbSMl+/5QXzlDP8VdBoqE2s2niGHB+zIXwBV9hRXbDrVuupbgHcg==",
"requires": {
"@babel/runtime": "^7.14.5",
"html-parse-stringify": "^3.0.1"
}
},
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
......@@ -28379,6 +28482,11 @@
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
},
"void-elements": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="
},
"w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
......@@ -13,8 +13,11 @@
"@types/node": "^16.11.41",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
"i18next": "^21.9.1",
"i18next-browser-languagedetector": "^6.1.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^11.18.4",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"typescript": "^4.7.4",
......
@media only screen and (min-width: 1921px) {
@media only screen and (max-width: 799px) {
html {
font-size: 24px;
font-size: 14px;
}
}
@media only screen and (max-width: 1920px) {
@media only screen and (min-width: 800px) {
html {
font-size: 16px;
font-size: 10px;
}
.hamburger-menu {
display: none;
}
}
@media only screen and (max-width: 1370px) {
@media only screen and (min-width: 1020px) {
html {
font-size: 14px;
font-size: 12px;
}
}
@media only screen and (max-width: 1200px) {
@media only screen and (min-width: 1200px) {
html {
font-size: 12px;
font-size: 14px;
}
}
@media only screen and (max-width: 1020px) {
@media only screen and (min-width: 1370px) {
html {
font-size: 10px;
font-size: 16px;
}
}
@media only screen and (max-width: 800px) {
@media only screen and (min-width: 1921px) {
html {
font-size: 14px;
font-size: 24px;
}
}
......@@ -9,9 +9,10 @@ import { createTheme, ThemeProvider } from '@mui/material';
const theme = createTheme({
typography: {
fontFamily: 'Inter',
body1: {
fontSize: '1em',
}
fontSize: '1rem',
},
},
});
......
......@@ -213,20 +213,10 @@ export function postLogin(username, password, isManager, callback) {
}, callback, "POST", false)(null)
}
export function getBuilding(buildingId) {
export function getBuilding(buildingId, callback) {
return thunkRequestTenant('/' + buildingId + "/building",
null, (err, data, dsp) => {
if (err) {
if (err.status === 401) {
dsp(genericAction(UNAUTHENTICATED))
}
// console.log(err)
console.error("Can not get tenant building")
return
}
dsp(genericAction(TENANT_BUILDING_RECEIVED, data))
},
"GET", false)
null, callback,
"GET", false)(null);
}
//TODO: Consider caching, forcing request
......
import { Box } from "@mui/material"
import { Navigate, Route, Routes } from "react-router-dom";
import { BuildingInvoices } from "./BuildingInvoices/BuildingInvoices";
import { TabWrapper } from "./TabWrapper/TabWrapper";
const classes = {
wrapper: {
backgroundColor: 'rgb(241, 245, 249)',
minHeight: '95vh',
width: '100%',
flex: 1,
}
} as const;
......@@ -19,7 +20,7 @@ export const ApplicationTab = () => {
<Route path='issues' element={<div>Hello from issues</div>} />
<Route path='voting' element={<div>Hello from voting</div>} />
<Route path='invoices' element={<div>Hello from invoices</div>} />
<Route path='building-invoices' element={<div>Hello from building invoices</div>} />
<Route path='building-invoices' element={<TabWrapper><BuildingInvoices/></TabWrapper>} />
<Route path='documents' element={<div>Hello from documents</div>} />
<Route path='*' element={<Navigate replace to="overview" />} />
</Routes>
......
import { Box, Typography } from "@mui/material";
import { useEffect, useState } from "react";
import { getBuilding } from "../../actions/data-manager";
import { BuildingInvoicesList } from "./BuildingInvoicesList";
const classes = {
wrapper: {
display: 'flex',
flexDirection: 'column',
gap: '50px',
},
header: {
fontWeight: 600,
fontSize: '1.6em',
color: '#27313a'
}
} as const;
export const BuildingInvoices = () => {
const [building, setBuilding] = useState<any>();
useEffect(() => {
getBuilding('62a1ca0cf2c321d1f5d4aa8d', (err: any, data: any) => {setBuilding(data)});
}, []);
return (
<>
{ building &&
<Box sx={classes.wrapper}>
<Typography variant="h5" sx={classes.header}>Finansije Zgrade</Typography>
<BuildingInvoicesList building={building}/>
</Box>
}
</>
);
}
\ No newline at end of file
import { Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
import { useTranslation } from "react-i18next";
import { PayableStatus } from "./PayableStatus";
const classes = {
tableWrapper: {
borderRadius: '15px',
boxSizing: 'border-box',
paddingBlock: '10px',
paddingInline: '40px',
display: 'flex',
flexDirection: 'column',
gap: '30px',
backgroundColor: 'white',
boxShadow: 'rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px'
},
tableHead: {
'& .MuiTableCell-root': {
color: '#64748b',
fontWeight: 'bold',
paddingBlock: '8px',
paddingInline: '0px',
}
},
tableBody: {
'& .MuiTableCell-root': {
paddingInline: '0px',
}
},
bolder: {
fontWeight: 'bolder'
},
status: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}
} as const;
export const BuildingInvoicesList = ({ building }: { building: any }) => {
const { t } = useTranslation();
let finance = building.finance || {}
let buildingPayable = building.finance && building.finance.payable && building.finance.payable.reduce((acc: any, x: any) => acc + x.price, 0)
let inflowSum = building.finance && building.finance.inflow && building.finance.inflow.reduce((acc: any, x: any) => acc + x.amount, 0)
let outflowSum = building.finance && building.finance.outflow && building.finance.outflow.reduce((acc: any, x: any) => acc + x.amount, 0)
let sumFundsPayables = (building.finance && building.finance.fund && building.finance.fund.reduce((acc: any, x: any) => acc + (x.receivable || 0), 0) || 0)
let fullBalance = building.finance && inflowSum && buildingPayable && (inflowSum - buildingPayable - sumFundsPayables)
let cashBalance = building.finance && building.finance.balance
let calculatedCashBalance = building.finance && (inflowSum - outflowSum)
let outflows = building.finance ? building.finance.outflow : []
let fundMap = (finance.fund || []).reduce((a: any, x: any) => ({ ...a, [x._id]: x.name }), {})
let fundOutflows = (outflows || []).filter((x: any) => !!x.fundId)
fundOutflows = fundOutflows.map((x: any) => ({ name: x.reason, price: x.amount, accounting: fundMap[x.fundId], created: x.date, type: "fund" }))
let payables = building.finance ? building.finance.payable : []
payables.push(...fundOutflows)
payables.sort((a: any, b: any) => Date.parse(a.created) - Date.parse(b.created))
return (
<Box style={classes.tableWrapper}>
<TableContainer >
<Table size="medium">
<TableHead>
<TableRow sx={classes.tableHead}>
<TableCell align="left">{t("Payables")}</TableCell>
<TableCell align="center">{t("Date")}</TableCell>
<TableCell align="center">{t("Accounting")}</TableCell>
<TableCell align="center">{t("Total")}</TableCell>
<TableCell align="center">{t("Status")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{payables.map((payable: any) => (
<TableRow key={payable._id} sx={classes.tableBody}>
<TableCell align="left">{payable.name} </TableCell>
<TableCell align="center">{t("formatDate", { date: new Date(payable.created) })}</TableCell>
<TableCell align="center">{t(payable.accounting)} </TableCell>
<TableCell align="center" sx={classes.bolder}>{t("priceFormat", { num: (payable.unitPrice || payable.price) })}</TableCell>
<TableCell sx={classes.status}><PayableStatus status={payable.status === "paid" || payable.type === "fund"} /></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Box>
);
}
\ No newline at end of file
import { Box } from "@mui/material";
const classes = {
green: {
backgroundColor: '#bbf7d0',
color: '#16693f',
paddingBlock: '5px',
paddingInline: '15px',
borderRadius: '50px',
fontWeight: 'bolder',
boxSizing: 'border-box',
fontSize: '0.8em'
},
red: {
backgroundColor: '#fecaca',
color: '#9c1c60',
paddingBlock: '5px',
paddingInline: '15px',
borderRadius: '50px',
fontWeight: 'bolder',
boxSizing: 'border-box',
fontSize: '0.8em',
display: 'inline'
}
} as const;
export const PayableStatus = ({status}: {status: boolean}) => {
return (<Box sx={status ? classes.green : classes.red}>{status ? 'PLAĆENO' : 'NIJE PLAĆENO'}</Box>);
};
\ No newline at end of file
......@@ -14,7 +14,6 @@ const classes = {
gap: '20px'
},
title: {
fontFamily: 'Inter',
fontWeight: 'bold',
},
textField: {
......
......@@ -3,15 +3,8 @@
background-color: white;
box-sizing: border-box;
border: 2px solid rgb(213, 225, 239);
position: sticky;
z-index: 0;
top: 0;
padding: 0.5vh;
}
\ No newline at end of file
@media only screen and (max-width: 800px) {
}
@media only screen and (min-width: 801px) {
.hamburger-menu {
display: none;
}
}
\ No newline at end of file
.side-menu {
height: 100vh;
width: 100%;
position: fixed;
width: 15%;
position: sticky;
top: 0;
left: -100%;
transition: 860ms;
z-index: 1;
display: flex;
flex-direction: row;
}
.side-menu.active {
......@@ -16,47 +16,46 @@
.content {
background-color: rgba(19, 34, 50, 1);
width: 70%;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-inline: 5px;
padding-block: 15px;
gap: 30px;
width: 100%;
height: 100%;
}
.transparent {
height: 100%;
width: 30%;
display: none;
}
@media only screen and (max-width: 800px) {
.icons-stroke {
width: 25px;
}
.user-icon {
width: 50px;
}
}
@media only screen and (min-width: 801px) {
.side-menu {
width: 15%;
position: sticky;
}
.transparent {
display: none;
.content {
overflow-y: scroll;
width: 70%;
}
.navbar {
display: none;
.side-menu {
position: fixed;
width: 100%;
top: 0;
left: -100%;
transition: 860ms;
}
.content {
width: 100%;
.transparent {
height: 100%;
width: 30%;
display: unset;
}
}
......@@ -22,7 +22,6 @@ const classes = {
menuText: {
color: 'rgb(204, 217, 232)',
margin: '0px',
fontFamily: 'Inter',
},
boldText: {
fontWeight: 'bolder',
......@@ -64,7 +63,7 @@ export const SideMenu = ({ active, toggleSideMenu }: { active: boolean, toggleSi
<Box sx={classes.accountInfo}>
<UserIcon />
<Typography sx={{ ...classes.menuText, ...classes.boldText }}>{tenantInfo.firstName}</Typography>
<Typography style={classes.menuText}>{tenantInfo.email}</Typography>
<Typography sx={classes.menuText}>{tenantInfo.email}</Typography>
<Box sx={classes.signOut}>
<Typography style={classes.menuText}>Sign out</Typography>
......
import { Box, Typography } from "@mui/material";
import { useLocation, useNavigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { SubMenu } from "../internal-types";
const classes = {
......@@ -7,7 +7,7 @@ const classes = {
display: 'flex',
flexDirection: 'column',
color: 'white',
gap: '5px'
gap: '5px',
},
itemTitle: {
color: 'rgb(24, 95, 167)',
......@@ -21,9 +21,9 @@ const classes = {
paddingInline: '10%',
paddingBlock: '2.5px',
boxSizing: 'border-box',
// backgroundColor: 'red',
borderRadius: '5px',
userSelect: 'none',
cursor: 'pointer',
':hover': {
backgroundColor: 'rgba(150, 150, 150, 0.2)'
}
......
.wrapper {
box-sizing: border-box;
padding-block: 3%;
padding-inline: 11%;
min-height: 100%;
}
\ No newline at end of file
import { ReactNode } from "react"
import './TabWrapper.css'
export const TabWrapper = ({children}: {children: ReactNode}) => {
return (
<div className="wrapper">
{children}
</div>
)
}
\ No newline at end of file
.icons-stroke {
width: 12%;
stroke: rgb(86, 107, 130);
cursor: pointer;
}
.logout:hover {
......
......@@ -13,7 +13,6 @@ const classes = {
gap: '20px'
},
title: {
fontFamily: 'Inter',
fontWeight: 'bold',
},
nameWrapper: {
......
......@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './services/i18n'
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
......
{
"translation": {
}
}
\ No newline at end of file
{
"translation": {
"Payables": "Obaveze",
"Date": "Datum",
"Accounting": "Obračunavanje",
"Total": "Ukupno",
"Status": "Status",
"formatDate": "{{date, DD/MM/YYYY}}",
"priceFormat": "{{num, num2format}}",
"per group/per unit":"ukupno",
"per unit/per unit": "po stanu",
"per unit/per area": "po kvadraturi"
}
}
\ No newline at end of file
import i18next from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import en from '../locale/en.json';
import sr from '../locale/sr.json';
console.log(navigator.language);
const resources = {
en,
sr,
};
i18next
.use(initReactI18next)
.init({
resources,
debug: true,
fallbackLng: 'sr',
interpolation: {
escapeValue: false,
format: (data: any, format: any, language: any) => {
if (format === 'intlDate')
return new Intl.DateTimeFormat(language).format(data); // -> "12/20/2012" if run in en-US locale with time zone America/Los_Angeles
if (format === 'DD/MM/YYYY')
return data.getDate() + "." + (data.getMonth() + 1) + "." + data.getFullYear() + '.'
if (format === 'num2format')
return new Intl.NumberFormat(language, { maximumFractionDigits: 2, minimumFractionDigits: 2 }).format(data) + ' RSD';
return ''
}
},
});
export default i18next;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment