Commit daccff3f authored by Djordje's avatar Djordje

Add translations

parent 893f496e
......@@ -31,7 +31,7 @@ export const Application = () => {
const [logedIn, setLogedIn] = useState(false);
const [sideMenuOpen, setSideMenuOpen] = useState(false);
const [tenantInfo, setTenantInfo] = useState<any>();
const [selectedUnit, setSelectedUnit] = useState<any>();
const [selectedUnit, setSelectedUnit] = useState<any>('');
const [buildingId] = useState(getBuildingId());
const [building, setBuilding] = useState<any>();
......@@ -59,7 +59,7 @@ export const Application = () => {
if (!!building)
getTenantInfo((err: any, data: any) => {
setTenantInfo(data);
setSelectedUnit(building.units.filter((unit: any) => unit._id === data.buildings[0].units[0])[0] || "");
setSelectedUnit(building.units.filter((unit: any) => unit._id === data.buildings[0].units[0])[0] || '');
setLogedIn(true);
});
}, [building]);
......
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
import { useTranslation } from "react-i18next";
import { Card } from "../Card/Card";
import { PayableStatus } from "./PayableStatus";
const classes = {
tableContainer: {
minHeight: '150px',
position: 'relative'
},
tableHead: {
'& .MuiTableCell-root': {
color: '#64748b',
......@@ -24,6 +28,11 @@ const classes = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
},
noRecords: {
position: 'absolute',
top: `60%`,
left: '45%'
}
} as const;
......@@ -58,7 +67,7 @@ export const BuildingInvoicesList = ({ buildingFinance }: { buildingFinance: any
return (
<Card>
<TableContainer >
<TableContainer sx={classes.tableContainer}>
<Table size="medium">
<TableHead>
<TableRow sx={classes.tableHead}>
......@@ -81,6 +90,7 @@ export const BuildingInvoicesList = ({ buildingFinance }: { buildingFinance: any
))}
</TableBody>
</Table>
{payables.length === 0 && <Typography sx={classes.noRecords} >{t('No records')}</Typography>}
</TableContainer>
</Card>
);
......
......@@ -4,7 +4,7 @@ import { DocumentsList } from "./DocumentsList";
export const Documents = ({ documents }: { documents: any }) => {
return (
<TabWrapper header="Dokumenta">
<TabWrapper header="Documents">
<DocumentsList documents={documents} />
</TabWrapper>
);
......
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
import { useTranslation } from "react-i18next";
import { Card } from "../Card/Card";
import { DownloadIcon } from "../icons/DownloadIcon";
import { LinkIcon } from "../icons/LinkIcon";
const classes = {
tableContainer: {
minHeight: '150px',
position: 'relative'
},
tableHead: {
'& .MuiTableCell-root': {
color: '#64748b',
......@@ -25,6 +29,11 @@ const classes = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
},
noRecords: {
position: 'absolute',
top: `60%`,
left: '45%'
}
} as const;
......@@ -35,7 +44,7 @@ export const DocumentsList = ({ documents }: { documents: any }) => {
return (
<Card>
<TableContainer >
<TableContainer sx={classes.tableContainer}>
<Table size="medium">
<TableHead>
<TableRow sx={classes.tableHead}>
......@@ -54,6 +63,7 @@ export const DocumentsList = ({ documents }: { documents: any }) => {
))}
</TableBody>
</Table>
{documents.length === 0 && <Typography sx={classes.noRecords} >{t('No documents')}</Typography>}
</TableContainer>
</Card>
);
......
import { Box, Collapse, Divider, Typography } from "@mui/material";
import { display } from "@mui/system";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Card } from "../Card/Card";
......@@ -44,21 +43,21 @@ export const Issue = ({ issue }: { issue: any }) => {
</Box>
</Box>
<Box sx={classes.description}>
<Typography sx={classes.subheader}>Opis</Typography>
<Typography sx={classes.subheader}>{t("Description")}</Typography>
<Typography>{issue.description}</Typography>
</Box>
<Collapse in={expand} timeout={250}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
<Divider />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
<Typography sx={classes.subheader}>Rešenja</Typography>
<Typography sx={classes.subheader}>{t("Solutions")}</Typography>
{issue.solutions.map((solution: any, ind: number) => (
<Box key={ind}>
<Box sx={{ display: 'flex', flexDirection: 'row' }}>
<Typography >{`${ind + 1}. ${solution.name}`}</Typography>
<Typography sx={{ marginLeft: 'auto', fontWeight: 'bold' }}>{`${t("priceFormat", { num: (solution.price) })}`}</Typography>
</Box>
<Typography sx={{ paddingLeft: '10px', fontSize: '15px' }}>{solution.description || 'Opis resenja nije unet.'}</Typography>
<Typography sx={{ paddingLeft: '10px', fontSize: '15px' }}>{solution.description || t('Description was not provided.')}</Typography>
</Box>
))}
</Box>
......
import { Typography } from "@mui/material";
import { useTranslation } from "react-i18next";
import { TabWrapper } from "../TabWrapper/TabWrapper";
import { Issue } from "./Issue";
export const Issues = ({ issues }: { issues: any }) => {
const { t } = useTranslation();
return (
<TabWrapper header="Aktivnosti">
<TabWrapper header="Issues">
{
issues.map((issue: any, index: number) => (
<Issue issue={issue} key={index}/>
<Issue issue={issue} key={index} />
))
}
{issues.length === 0 && <Typography>Trenutno nema aktivnosti za ovu jedinicu.</Typography>}
{issues.length === 0 && <Typography>{t("Trenutno nema aktivnosti za ovu jedinicu")}.</Typography>}
</TabWrapper>
);
}
\ No newline at end of file
......@@ -29,11 +29,13 @@ const classes = {
export const Notification = ({ title, description }: { title: string, description: string }) => {
const { t } = useTranslation();
return (
<Card>
<Typography sx={classes.title}>{title}</Typography>
<Box sx={classes.description}>
<Typography sx={classes.subheader}>Opis</Typography>
<Typography sx={classes.subheader}>{t("Description")}</Typography>
<Typography>{description}</Typography>
</Box>
</Card>
......
import { Typography } from "@mui/material";
import { useTranslation } from "react-i18next";
import { TabWrapper } from "../TabWrapper/TabWrapper";
import { Notification } from "./Notification";
export const Notifications = ({ notifications }: { notifications: any }) => {
const { t } = useTranslation();
return (
<TabWrapper header="Obaveštenja">
<TabWrapper header="Notifications">
{
notifications.map((notification: any, index: number) => (
<Notification title={notification.title} description={notification.description} />
))
}
{notifications.length === 0 && <Typography>Trenutno nema obavestenja za ovu jedinicu.</Typography>}
{notifications.length === 0 && <Typography>{t("Currently there are not notifications for this unit")}.</Typography>}
</TabWrapper>);
}
\ No newline at end of file
......@@ -8,6 +8,7 @@ import { TabWrapper } from "../TabWrapper/TabWrapper";
import { Buffer } from "buffer";
import nacl, { SignKeyPair } from "tweetnacl";
import { HelperIcon } from "../icons/HelperIcon";
import { useTranslation } from "react-i18next";
const classes = {
votingSeetings: {
......@@ -58,6 +59,7 @@ export const Settings = () => {
const [toggleHelp, setToggleHelp] = useState(false);
const helperRef = useRef<SVGSVGElement>(null);
const { t } = useTranslation();
useEffect(() => {
getTenantInfo((err: any, user: any) => {
......@@ -86,7 +88,7 @@ export const Settings = () => {
setHelperText('');
}
} catch (error) {
setHelperText('Neispravan kljuc!');
setHelperText(t('Neispravan kljuc') + '!');
}
}
......@@ -107,7 +109,7 @@ export const Settings = () => {
setPublicKey(publicKeyBase64);
} else {
if (err.status === 412) {
alert("Not allowed already set key, contact your building manager")
alert(t("Not allowed already set key, contact your building manager"))
}
}
})(null);
......@@ -125,17 +127,17 @@ export const Settings = () => {
return (
<TabWrapper header="Podešavanja" onClick={handleClickOutside}>
<TabWrapper header="Settings" onClick={handleClickOutside}>
<Card>
{
!publicKey &&
<>
<Typography sx={classes.header}>Glasanje</Typography>
<Typography>Ukoliko zelite ucestvovati u glasanju potrebno je da generisete i sacuvate kljuceve u aplikaciji.</Typography>
<Typography sx={classes.header}>{t('Voting')}</Typography>
<Typography>{t("If you want to participate in voting you need to generate voting keys and save them in application")}</Typography>
{!!keyPair &&
<>
<Box>
<Typography>Privatni kljuc</Typography>
<Typography>{t('Private key')}</Typography>
<TextField
variant="outlined"
size='small'
......@@ -159,7 +161,7 @@ export const Settings = () => {
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<Box sx={classes.helperCloud}>
<Typography sx={classes.helperText}>U slučaju da izgubite pristup ovom uređaju, ovaj ključ će vam biti potreban kako bi na novom uređaju mogli da učestvujete u glasanju!</Typography>
<Typography sx={classes.helperText}>{t("In case you lose access to device, this key will be required to participate in voting")}!</Typography>
</Box>
</Fade>
)}
......@@ -167,7 +169,18 @@ export const Settings = () => {
</Box>
</>
}
<Button variant='contained' sx={classes.button} onClick={() => { if (!keyPair) { generateKeys() } else { saveKeys(keyPair.publicKey, keyPair.secretKey) } }} disabled={!!keyPair && !privateKeyWritten}>{!keyPair ? 'Generisi' : 'Sacuvaj'}</Button>
<Button
variant='contained'
sx={classes.button}
onClick={() => {
if (!keyPair) {
generateKeys()
}
else {
saveKeys(keyPair.publicKey, keyPair.secretKey)
}
}}
disabled={!!keyPair && !privateKeyWritten} >{!keyPair ? 'Generisi' : 'Sacuvaj'}</Button>
</>
}
{!!publicKey &&
......@@ -175,8 +188,8 @@ export const Settings = () => {
{!!privateKey &&
<Box sx={classes.votingSeetings}>
<Box sx={classes.textWrapper}>
<Typography sx={classes.header}>Glasanje</Typography>
<Typography>Privatni ključ je uspešno podešen u aplikaciji.</Typography>
<Typography sx={classes.header}>{t('Voting')}</Typography>
<Typography>{t("Private key successfully set in application")}.</Typography>
</Box>
<CheckMark />
</Box>
......@@ -184,15 +197,15 @@ export const Settings = () => {
{
!privateKey &&
<>
<Typography sx={classes.header}>Glasanje</Typography>
<Typography>Kako bi mogli da učestvujete u glasanju, molimo vas unesite vaš privatni ključ.</Typography>
<Typography sx={classes.header}>{t('Voting')}</Typography>
<Typography>{t("In order to participate in voting, please enter your private key")}.</Typography>
<TextField
error={!!helperText}
helperText={helperText}
onChange={(e) => {
setNewPrivateKey(e.target.value);
}} size='small' />
<Button variant='contained' sx={classes.button} onClick={verifyKey}>Potvrdi</Button>
<Button variant='contained' sx={classes.button} onClick={verifyKey}>{t('Confirm')}</Button>
</>
}
</>
......
......@@ -16,7 +16,7 @@ export const subMenu: SubMenu[] = [
items: [
{ title: 'Overview', icon: <HomeIcon />, path: 'overview' },
{ title: 'Notifications', icon: <NotificationIcon />, path: 'notifications' },
{ title: 'Activities', icon: <ActivityIcon />, path: 'issues' },
{ title: 'Issues', icon: <ActivityIcon />, path: 'issues' },
{ title: 'Voting', icon: <VotingIcon />, path: 'voting' }
]
},
......
import { Box, Typography } from "@mui/material"
import { ReactNode } from "react"
import { useTranslation } from "react-i18next";
import './TabWrapper.css'
const classes = {
......@@ -16,9 +17,12 @@ const classes = {
} as const;
export const TabWrapper = ({ header, children, onClick }: { header: string, children?: ReactNode, onClick?: (e: any) => void }) => {
const { t } = useTranslation();
return (
<Box sx={classes.wrapper} onClick={(e) => { if (!!onClick) { onClick(e) } }}>
<Typography variant='h5' sx={classes.header}>{header}</Typography>
<Typography variant='h5' sx={classes.header}>{t(header)}</Typography>
{children}
</Box>
)
......
......@@ -34,8 +34,8 @@ const classes = {
},
noRecords: {
position: 'absolute',
top: `calc(50% - 10px)`,
left: 'calc(50% - 30px)'
top: `60%`,
left: '45%'
}
} as const;
......@@ -67,7 +67,7 @@ export const TenantInvoices = ({ building, unit }: { building: any, unit: any })
}, [unit, building]);
return (
<TabWrapper header="Moj Račun">
<TabWrapper header="My Account">
<Card>
<TableContainer sx={classes.tableContainer}>
<Table size="medium">
......@@ -90,7 +90,7 @@ export const TenantInvoices = ({ building, unit }: { building: any, unit: any })
))}
</TableBody>
</Table>
{info.invoices.length === 0 && <Typography sx={classes.noRecords} >No records</Typography>}
{info.invoices.length === 0 && <Typography sx={classes.noRecords} >{t('No records')}</Typography>}
</TableContainer>
</Card>
......
......@@ -9,6 +9,7 @@ import { Buffer } from "buffer";
import { TabWrapper } from "../TabWrapper/TabWrapper";
import { Card } from "../Card/Card";
import { getBuildingId } from "../Application";
import { useTranslation } from "react-i18next";
const classes = {
wrapper: {
......@@ -41,6 +42,8 @@ export const Voting = (): JSX.Element => {
const [errorText, setErrorText] = useState('');
const [privateKey, setPrivateKey] = useState(localStorage.getItem(PRIVATE_KEY));
const { t } = useTranslation();
useEffect(() => {
getTenentVoting(getBuildingId(), (err: any, res: any, disp: any) => {
setStatus(res.status || "not allowed");
......@@ -62,7 +65,7 @@ export const Voting = (): JSX.Element => {
postVote(getBuildingId(), vote, (err: any, data: any) => {
if (err) {
if (err.status === 406) {
setErrorText("Signature not valid!");
setErrorText(t("Signature not valid") + "!");
} else {
setErrorText(err.errorMessage);
}
......@@ -76,33 +79,33 @@ export const Voting = (): JSX.Element => {
return (<>
{
!!voting &&
<TabWrapper header="Glasanje">
<TabWrapper header="Voting">
{!!privateKey &&
<>
{status === "not allowed" && <Warning text='Vaš nalog nije potvrđen kao vlasnik ni jedne jedinice. Molimo obratite se upravniku stambene zajednice.' />}
{status === "not allowed" && <Warning text="You account isn't confirmed as owner of any unit. Please contact your building manager" />}
{status !== "not allowed" &&
<Card>
<Typography sx={classes.subheader}>Tekuće glasanje</Typography>
{status === "no voting" && <Typography>U ovom trenutku nema aktivnog glasanja.</Typography>}
<Typography sx={classes.subheader}>{t('Current voting')}</Typography>
{status === "no voting" && <Typography>{t('There is no active voting at the moment')}.</Typography>}
{status === "in progress" &&
<>
<Typography sx={classes.subject}>{voting.subject}</Typography>
<FormControl>
<FormLabel>Opcije</FormLabel>
<FormLabel>{t('Options')}</FormLabel>
<RadioGroup value={selectedCandidateId} onChange={(event) => { setSelectedCandidateId(event.target.value) }}>
{voting.candidates.map((candidate, ind: number) => (<FormControlLabel value={candidate._id} control={<Radio size="small" />} label={candidate.name} key={ind} />))}
</RadioGroup>
</FormControl>
<Button variant='contained' sx={classes.button} disabled={selectedCandidateId === ''} onClick={handleVote}>Glasaj</Button>
<Button variant='contained' sx={classes.button} disabled={selectedCandidateId === ''} onClick={handleVote}>{t("Vote")}</Button>
</>
}
{status === "voted" && <Typography>Hvala vam na glasanju. </Typography>}
{status === "voted" && <Typography>{t('Thank you for voting')}. </Typography>}
</Card>
}
</>
}
{
!privateKey && <Warning text='Glasanje je onemogućeno jer privatan ključ nije sačuvan u aplikaciji. Molimo vas, podesite ključ u podešavanjima!' />
!privateKey && <Warning text={t("Voting is disabled because private key isn't saved in application. Please, set up key in settings")} />
}
</TabWrapper>
}
......
import { Typography } from "@mui/material";
import { Box } from "@mui/system";
import { useTranslation } from "react-i18next";
import { WarningIcon } from "../icons/WarningIcon";
const classes = {
......@@ -16,11 +17,14 @@ const classes = {
}
} as const;
export const Warning = ({text}: {text: string}) => {
export const Warning = ({ text }: { text: string }) => {
const { t } = useTranslation();
return (
<Box sx={classes.wrapper}>
<WarningIcon />
<Typography>{text}</Typography>
<Typography>{t(text)}.</Typography>
</Box>
);
}
\ No newline at end of file
......@@ -70,6 +70,10 @@ const classes = {
zIndex: 3
},
},
menuPaper: {
maxHeight: '250px'
},
voting: {
display: 'flex',
......@@ -269,13 +273,14 @@ export const RegisterForm = (): JSX.Element => {
}}
/>
</Box>
<FormControl fullWidth size="small" onKeyUp={(e) => { e.stopPropagation(); }}>
<FormControl fullWidth sx={{ maxHeight: '50px' }} size="small" onKeyUp={(e) => { e.stopPropagation(); }}>
<InputLabel>Select your units</InputLabel>
<Select
label="Select your units"
sx={classes.select}
value={registerInfo.pendingBuildings.units}
multiple
MenuProps={{ sx: classes.menuPaper }}
onChange={(e) => {
let units: string[] = typeof e.target.value === 'string' ? e.target.value.split(',') : e.target.value;
setRegisterInfo({ ...registerInfo, pendingBuildings: { ...registerInfo.pendingBuildings, units } })
......
......@@ -6,7 +6,7 @@
"Main menu": "Main menu",
"Overview": "Overview",
"Notifications": "Notifications",
"Activities": "Activities",
"Issues": "Issues",
"Voting": "Voting",
"Accounts/Finance": "Accounts/Finance",
"My account": "My account",
......@@ -15,11 +15,25 @@
"Application": "Application",
"Settings": "Settings",
"Currently there are not notifications for this unit": "Currently there are not notifications for this unit",
"Description": "Description",
"Currently there are not issues for this unit": "Currently there are not issues for this unit",
"Description was not provided.": "Description was not provided.",
"Solutions": "Solutions",
"You account isn't confirmed as owner of any unit. Please contact your building manager": "You account isn't confirmed as owner of any unit. Please contact your building manager",
"Current voting": "Current voting",
"Voting is disabled because private key isn't saved in application. Please, set up key in settings": "Voting is disabled because private key isn't saved in application. Please, set up key in settings",
"There is no active voting at the moment": "There is no active voting at the moment",
"Options": "Options",
"Vote": "Vote",
"Thank you for voting": "Thank you for voting",
"My Account": "My Account",
"No records": "No records",
"Invoice Id": "Invoice Id",
"Payables": "Payables",
"Date": "Datum",
"Date": "Date",
"Accounting": "Accounting",
"Total": "Total",
"Status": "Status",
......@@ -28,6 +42,20 @@
"nalog": "{{num, nalog}}",
"per group/per unit":"per group",
"per unit/per unit": "per unit",
"per unit/per area": "per area"
"per unit/per area": "per area",
"Name": "Name",
"Action": "Action",
"No documents": "No documents",
"If you want to participate in voting you need to generate voting keys and save them in application": "If you want to participate in voting you need to generate voting keys and save them in application",
"Private key": "Private key",
"Invalid key": "Invalid key",
"Not allowed already set key, contact your building manager": "Not allowed already set key, contact your building manager",
"Signature not valid": "Signature not valid",
"In case you lose access to device, this key will be required to participate in voting": "In case you lose access to device, this key will be required to participate in voting",
"Private key successfully set in application": "Private key successfully set in application",
"In order to participate in voting, please enter your private key": "In order to participate in voting, please enter your private key",
"Confirm": "Confirm"
}
}
\ No newline at end of file
......@@ -6,7 +6,7 @@
"Main menu": "Glavni meni",
"Overview": "Pregled",
"Notifications": "Obaveštenja",
"Activities": "Aktivnosti",
"Issues": "Aktivnosti",
"Voting": "Glasanje",
"Accounts/Finance": "Računi/Finansije",
"My account": "Moj račun",
......@@ -15,6 +15,23 @@
"Application": "Aplikacija",
"Settings": "Podešavanja",
"Currently there are not notifications for this unit": "Trenutno nema obavestenja za ovu jedinicu",
"Description": "Opis",
"Currently there are not issues for this unit": "Trenutno nema aktivnosti za ovu jedinicu",
"Description was not provided.": "Opis rešenja nije unet.",
"Solutions": "Rešenja",
"You account isn't confirmed as owner of any unit. Please contact your building manager": "Vaš nalog nije potvrđen kao vlasnik ni jedne jedinice. Molimo obratite se upravniku stambene zajednice",
"Current voting": "Tekuće glasanje",
"Voting is disabled because private key isn't saved in application. Please, set up key in settings": "Glasanje je onemogućeno jer privatan ključ nije sačuvan u aplikaciji. Molimo vas, podesite ključ u podešavanjima",
"There is no active voting at the moment": "Trenutno nema aktivnog glasanja",
"Options": "Opcije",
"Vote": "Glasaj",
"Thank you for voting": "Hvala Vam na glasanju",
"My Account": "Moj Račun",
"No records": "Nema zapisa",
"Invoice Id": "Poziv na broj",
"Period": "Period",
......@@ -28,7 +45,20 @@
"nalog": "{{num, nalog}}",
"per group/per unit":"ukupno",
"per unit/per unit": "po stanu",
"per unit/per area": "po kvadraturi"
"per unit/per area": "po kvadraturi",
"Name": "Naziv",
"Action": "Akcija",
"No documents": "Nema dokumenata",
"If you want to participate in voting you need to generate voting keys and save them in application": "Ukoliko zelite ucestvovati u glasanju potrebno je da generisete i sacuvate kljuceve u aplikaciji",
"Private key": "Privatni ključ",
"Invalid key": "Invalid ključ",
"Not allowed already set key, contact your building manager": "Ponovno podešavanje ključa nije dozvoljeno, molimo kontaktirajte upravnika zgrade",
"Signature not valid": "Potpis nije validan",
"In case you lose access to device, this key will be required to participate in voting": "U slučaju da izgubite pristup ovom uređaju, ovaj ključ će vam biti potreban kako bi na novom uređaju mogli da učestvujete u glasanju",
"Private key successfully set in application": "Privatni ključ je uspešno podešen u aplikaciji",
"In order to participate in voting, please enter your private key": "Kako bi mogli da učestvujete u glasanju, molimo vas unesite vaš privatni ključ",
"Confirm": "Potvrdi"
}
}
\ 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