diff --git a/.github/scripts/download-translations.js b/.github/scripts/download-translations.js new file mode 100644 index 000000000..62f63a6a0 --- /dev/null +++ b/.github/scripts/download-translations.js @@ -0,0 +1,89 @@ +import axios from 'axios'; +import fs from 'fs-extra'; +import path from 'path'; +import { URLSearchParams } from 'url'; + +// POEditor API information +const API_TOKEN = process.env.POEDITOR_API; +const PROJECT_ID = process.env.POEDITOR_PROJECT_ID; +const LANGUAGES = (process.env.LANGUAGES || 'tr,en').split(','); +const EXPORT_FORMAT = process.env.EXPORT_FORMAT || 'key_value_json'; + +// POEditor API endpoint +const API_URL = 'https://api.poeditor.com/v2'; + +// Function to download translations +async function downloadTranslations() { + try { + console.log('Downloading translations from POEditor...'); + console.log(`Using export format: ${EXPORT_FORMAT}`); + + for (const language of LANGUAGES) { + console.log(`Downloading translations for ${language} language...`); + + // Get export URL from POEditor + const exportResponse = await axios.post(`${API_URL}/projects/export`, + new URLSearchParams({ + api_token: API_TOKEN, + id: PROJECT_ID, + language: language, + type: EXPORT_FORMAT + }) + ); + + if (exportResponse.data.response.status !== 'success') { + throw new Error(`Failed to get export URL for ${language} language: ${JSON.stringify(exportResponse.data)}`); + } + + const fileUrl = exportResponse.data.result.url; + console.log(`Export URL obtained for ${language}`); + + // Download translation file + const downloadResponse = await axios.get(fileUrl, { responseType: 'json' }); + const translations = downloadResponse.data; + console.log(`Downloaded translations for ${language}`); + + // Check the format of data returned from POEditor and convert if necessary + let formattedTranslations = translations; + + // If data is in array format, convert it to key-value format + if (Array.isArray(translations)) { + console.log(`Converting array format to key-value format for ${language}`); + formattedTranslations = {}; + translations.forEach(item => { + if (item.term && item.definition) { + formattedTranslations[item.term] = item.definition; + } + }); + } + + // Save file + const outputPath = path.join(process.cwd(), 'temp', `${language}.json`); + await fs.writeJson(outputPath, formattedTranslations, { spaces: 2 }); + + console.log(`Translations for ${language} language successfully downloaded and saved: ${outputPath}`); + } + + console.log('All translations successfully downloaded!'); + } catch (error) { + console.error('An error occurred while downloading translations:', error); + process.exit(1); + } +} + +// Main function +async function main() { + try { + // Clean temp folder + await fs.emptyDir(path.join(process.cwd(), 'temp')); + + // Download translations + await downloadTranslations(); + } catch (error) { + console.error('An error occurred during the process:', error); + process.exit(1); + } +} + +// Run script +main(); \ No newline at end of file diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 000000000..11b04d657 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,41 @@ +# POEditor Translation Synchronization + +This GitHub Actions workflow automatically downloads translation files from POEditor and integrates them into the project. + +## How It Works + +The workflow can be triggered in two ways: + +1. **Manual Trigger**: You can manually run the "POEditor Translation Synchronization" workflow from the "Actions" tab in the GitHub interface. +2. **Automatic Trigger**: The workflow runs automatically every day at midnight (UTC). + +## Required Settings + +For this workflow to function, you need to define the following secrets in your GitHub repository: + +1. `POEDITOR_API_TOKEN`: Your POEditor API token +2. `POEDITOR_PROJECT_ID`: Your POEditor project ID + +You can add these secrets in the "Settings > Secrets and variables > Actions" section of your GitHub repository. + +## Manual Execution + +When running the workflow manually, you can specify which languages to download. Languages should be entered as comma-separated values (e.g., `tr,gb,es`). + +If you don't specify any languages, the default languages `tr` and `en` will be downloaded. + +## Output + +When the workflow completes successfully: + +1. Translation files for the specified languages are downloaded from POEditor +2. These files are copied to the `src/locales/` directory +3. Changes are automatically committed and pushed to the main branch + +## Troubleshooting + +If the workflow fails: + +1. Check the GitHub Actions logs +2. Make sure your POEditor API token and project ID are correct +3. Ensure that the languages you specified exist in your POEditor project diff --git a/.github/workflows/first-issue-first-pr.yml b/.github/workflows/first-issue-first-pr.yml new file mode 100644 index 000000000..98f6ef9de --- /dev/null +++ b/.github/workflows/first-issue-first-pr.yml @@ -0,0 +1,9 @@ + - name: First interaction + uses: actions/first-interaction@v1.3.0 + with: + # Token for the repository. Can be passed in using {{ secrets.GITHUB_TOKEN }} + repo-token: + # Comment to post on an individual's first issue + issue-message: Thanks for opening your first issue! :) # optional + # Comment to post on an individual's first pull request + pr-message: Thanks for creating your first PR! :) # optional diff --git a/.github/workflows/poeditor-sync.yml b/.github/workflows/poeditor-sync.yml new file mode 100644 index 000000000..1f1194e3e --- /dev/null +++ b/.github/workflows/poeditor-sync.yml @@ -0,0 +1,92 @@ +name: POEditor Translation Synchronization + +on: + # For manual triggering + workflow_dispatch: + inputs: + languages: + description: "Languages to synchronize (comma separated, e.g.: tr,en,es)" + required: false + default: "tr,en" + format: + description: "Export format (key_value_json or json)" + required: false + default: "key_value_json" + + # For automatic execution at a specific time (every day at midnight) + schedule: + - cron: "0 0 * * *" + +jobs: + sync-translations: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "18" + + - name: Create package.json for scripts + run: | + mkdir -p .github/scripts + cat > .github/scripts/package.json << EOF + { + "name": "poeditor-scripts", + "version": "1.0.0", + "private": true, + "type": "module", + "dependencies": { + "axios": "^1.6.0", + "fs-extra": "^11.1.1" + } + } + EOF + + - name: Install dependencies + run: | + cd .github/scripts + npm install + + - name: Download translations from POEditor + env: + POEDITOR_API: ${{ secrets.POEDITOR_API }} + POEDITOR_PROJECT_ID: ${{ secrets.POEDITOR_PROJECT_ID }} + LANGUAGES: ${{ github.event.inputs.languages || 'tr,en' }} + EXPORT_FORMAT: ${{ github.event.inputs.format || 'key_value_json' }} + run: | + mkdir -p temp + node .github/scripts/download-translations.js + + - name: Verify translation files + run: | + echo "Verifying translation files..." + for file in temp/*.json; do + echo "Checking $file" + if [ ! -s "$file" ]; then + echo "Error: $file is empty or does not exist" + exit 1 + fi + # Validate JSON format + cat "$file" | jq . > /dev/null || { echo "Error: $file is not valid JSON"; exit 1; } + done + echo "All translation files are valid" + + - name: Copy translations to project + run: | + mkdir -p src/locales + cp -r temp/* src/locales/ + echo "Translation files copied to src/locales/" + + - name: Commit changes + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git add src/locales/*.json + git diff --staged --quiet || git commit -m "Translations updated from POEditor" + git push diff --git a/package-lock.json b/package-lock.json index 89b17b968..5120f576b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@mui/x-charts": "^7.5.1", "@mui/x-data-grid": "7.27.3", "@mui/x-date-pickers": "7.27.3", - "@reduxjs/toolkit": "2.6.0", + "@reduxjs/toolkit": "2.6.1", "@solana/wallet-adapter-base": "0.9.23", "@solana/wallet-adapter-material-ui": "0.16.34", "@solana/wallet-adapter-react": "0.15.35", @@ -4692,9 +4692,9 @@ } }, "node_modules/@reduxjs/toolkit": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.6.0.tgz", - "integrity": "sha512-mWJCYpewLRyTuuzRSEC/IwIBBkYg2dKtQas8mty5MaV2iXzcmicS3gW554FDeOvLnY3x13NIk8MB1e8wHO7rqQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.6.1.tgz", + "integrity": "sha512-SSlIqZNYhqm/oMkXbtofwZSt9lrncblzo6YcZ9zoX+zLngRBrCOjK4lNLdkNucJF58RHOWrD9txT3bT3piH7Zw==", "license": "MIT", "dependencies": { "immer": "^10.0.3", diff --git a/package.json b/package.json index a8fa1128e..5100a0529 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@mui/icons-material": "6.4.7", "@mui/lab": "6.0.0-beta.30", "@mui/material": "6.4.7", - "@reduxjs/toolkit": "2.6.0", + "@reduxjs/toolkit": "2.6.1", "@solana/wallet-adapter-base": "0.9.23", "@solana/wallet-adapter-material-ui": "0.16.34", "@solana/wallet-adapter-react": "0.15.35", diff --git a/src/Components/Inputs/Search/index.jsx b/src/Components/Inputs/Search/index.jsx index 662ade695..647af874a 100644 --- a/src/Components/Inputs/Search/index.jsx +++ b/src/Components/Inputs/Search/index.jsx @@ -128,7 +128,7 @@ const Search = ({ { @@ -156,7 +158,7 @@ const StatusLabel = ({ status, text, customStyles }) => { }; StatusLabel.propTypes = { - status: PropTypes.oneOf(["up", "down", "paused", "pending", "cannot resolve"]), + status: PropTypes.oneOf(["up", "down", "paused", "pending", "cannot resolve", "published", "unpublished"]), text: PropTypes.string, customStyles: PropTypes.object, }; diff --git a/src/Components/Link/index.jsx b/src/Components/Link/index.jsx index 6a317502b..7a623a3b0 100644 --- a/src/Components/Link/index.jsx +++ b/src/Components/Link/index.jsx @@ -14,7 +14,14 @@ const Link = ({ level, label, url }) => { const theme = useTheme(); const levelConfig = { - primary: {}, + primary: { + color: theme.palette.primary.contrastTextTertiary, + sx: { + ":hover": { + color: theme.palette.primary.contrastTextSecondary, + }, + }, + }, secondary: { color: theme.palette.primary.contrastTextSecondary, sx: { diff --git a/src/Components/NotificationIntegrationModal/NotificationIntegrationModal.jsx b/src/Components/NotificationIntegrationModal/NotificationIntegrationModal.jsx new file mode 100644 index 000000000..30587833a --- /dev/null +++ b/src/Components/NotificationIntegrationModal/NotificationIntegrationModal.jsx @@ -0,0 +1,273 @@ +import { useState, useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { + Dialog, + DialogContent, + DialogActions, + Button, + Typography, + Box, + Tabs, + Tab +} from "@mui/material"; +import { useTheme } from "@emotion/react"; +import TabPanel from "./TabPanel"; +import TabComponent from "./TabComponent"; + +const NotificationIntegrationModal = ({ + open, + onClose, + monitor, + setMonitor, + // Optional prop to configure available notification types + notificationTypes = null +}) => { + const { t } = useTranslation(); + const theme = useTheme(); + const [tabValue, setTabValue] = useState(0); + + // Define notification types + const DEFAULT_NOTIFICATION_TYPES = [ + { + id: 'slack', + label: t('notifications.slack.label'), + description: t('notifications.slack.description'), + fields: [ + { + id: 'webhook', + label: t('notifications.slack.webhookLabel'), + placeholder: t('notifications.slack.webhookPlaceholder'), + type: 'text' + } + ] + }, + { + id: 'discord', + label: t('notifications.discord.label'), + description: t('notifications.discord.description'), + fields: [ + { + id: 'webhook', + label: t('notifications.discord.webhookLabel'), + placeholder: t('notifications.discord.webhookPlaceholder'), + type: 'text' + } + ] + }, + { + id: 'telegram', + label: t('notifications.telegram.label'), + description: t('notifications.telegram.description'), + fields: [ + { + id: 'token', + label: t('notifications.telegram.tokenLabel'), + placeholder: t('notifications.telegram.tokenPlaceholder'), + type: 'text' + }, + { + id: 'chatId', + label: t('notifications.telegram.chatIdLabel'), + placeholder: t('notifications.telegram.chatIdPlaceholder'), + type: 'text' + } + ] + }, + { + id: 'webhook', + label: t('notifications.webhook.label'), + description: t('notifications.webhook.description'), + fields: [ + { + id: 'url', + label: t('notifications.webhook.urlLabel'), + placeholder: t('notifications.webhook.urlPlaceholder'), + type: 'text' + } + ] + } + ]; + + // Use provided notification types or default to our translated ones + const activeNotificationTypes = notificationTypes || DEFAULT_NOTIFICATION_TYPES; + + // Memoized function to initialize integrations state + const initialIntegrationsState = useMemo(() => { + const state = {}; + + activeNotificationTypes.forEach(type => { + // Add enabled flag for each notification type + state[type.id] = monitor?.notifications?.some(n => n.type === type.id) || false; + + // Add state for each field in the notification type + type.fields.forEach(field => { + const fieldKey = `${type.id}${field.id.charAt(0).toUpperCase() + field.id.slice(1)}`; + state[fieldKey] = monitor?.notifications?.find(n => n.type === type.id)?.[field.id] || ""; + }); + }); + + return state; + }, [monitor, activeNotificationTypes]); // Only recompute when these dependencies change + + const [integrations, setIntegrations] = useState(initialIntegrationsState); + + const handleChangeTab = (event, newValue) => { + setTabValue(newValue); + }; + + const handleIntegrationChange = (type, checked) => { + setIntegrations(prev => ({ + ...prev, + [type]: checked + })); + }; + + const handleInputChange = (type, value) => { + setIntegrations(prev => ({ + ...prev, + [type]: value + })); + }; + + const handleTestNotification = (type) => { + console.log(`Testing ${type} notification`); + //implement the test notification functionality + }; + + const handleSave = () => { + //notifications array for selected integrations + const notifications = [...(monitor?.notifications || [])]; + + // Get all notification types IDs + const existingTypes = activeNotificationTypes.map(type => type.id); + + // Filter out notifications that are configurable in this modal + const filteredNotifications = notifications.filter( + notification => !existingTypes.includes(notification.type) + ); + + // Add each enabled notification with its configured fields + activeNotificationTypes.forEach(type => { + if (integrations[type.id]) { + const notificationObject = { + type: type.id + }; + + // Add each field value to the notification object + type.fields.forEach(field => { + const fieldKey = `${type.id}${field.id.charAt(0).toUpperCase() + field.id.slice(1)}`; + notificationObject[field.id] = integrations[fieldKey]; + }); + + filteredNotifications.push(notificationObject); + } + }); + + // Update monitor with new notifications + setMonitor(prev => ({ + ...prev, + notifications: filteredNotifications + })); + + onClose(); + }; + + return ( + + + + {/* Left sidebar with tabs */} + + + {t('notifications.addOrEditNotifications')} + + + + {activeNotificationTypes.map((type) => ( + + ))} + + + + {/* Right side content */} + + {activeNotificationTypes.map((type, index) => ( + + + + ))} + + + + + + + + ); +}; + +export default NotificationIntegrationModal; \ No newline at end of file diff --git a/src/Components/NotificationIntegrationModal/TabComponent.jsx b/src/Components/NotificationIntegrationModal/TabComponent.jsx new file mode 100644 index 000000000..89216a8c1 --- /dev/null +++ b/src/Components/NotificationIntegrationModal/TabComponent.jsx @@ -0,0 +1,100 @@ +import React from "react"; +import { + Typography, + Box, + Button +} from "@mui/material"; +import { useTranslation } from "react-i18next"; +import { useTheme } from "@emotion/react"; +import TextInput from "../../../src/Components/Inputs/TextInput"; +import Checkbox from "../../../src/Components/Inputs/Checkbox"; + +const TabComponent = ({ + type, + integrations, + handleIntegrationChange, + handleInputChange, + handleTestNotification +}) => { + const theme = useTheme(); + const { t } = useTranslation(); + + // Helper to get the field state key (e.g., slackWebhook, telegramToken) + const getFieldKey = (typeId, fieldId) => { + return `${typeId}${fieldId.charAt(0).toUpperCase() + fieldId.slice(1)}`; + }; + + // Check if all fields have values to enable test button + const areAllFieldsFilled = () => { + return type.fields.every(field => { + const fieldKey = getFieldKey(type.id, field.id); + return integrations[fieldKey]; + }); + }; + + return ( + <> + + {type.label} + + + + {type.description} + + + + handleIntegrationChange(type.id, e.target.checked)} + /> + + + {type.fields.map(field => { + const fieldKey = getFieldKey(type.id, field.id); + + return ( + + + {field.label} + + + handleInputChange(fieldKey, e.target.value)} + disabled={!integrations[type.id]} + /> + + ); + })} + + + + + + ); +}; + +export default TabComponent; \ No newline at end of file diff --git a/src/Components/NotificationIntegrationModal/TabPanel.jsx b/src/Components/NotificationIntegrationModal/TabPanel.jsx new file mode 100644 index 000000000..a9252e710 --- /dev/null +++ b/src/Components/NotificationIntegrationModal/TabPanel.jsx @@ -0,0 +1,46 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Box } from "@mui/material"; +import { useTheme } from "@emotion/react"; + +/** + * TabPanel component that displays content for the selected tab. + * + * @component + * @param {Object} props - The component props. + * @param {React.ReactNode} props.children - The content to be displayed when this tab panel is selected. + * @param {number} props.value - The currently selected tab value. + * @param {number} props.index - The index of this specific tab panel. + * @param {Object} props.other - Any additional props to be spread to the root element. + * @returns {React.ReactElement|null} The rendered tab panel or null if not selected. + */ +function TabPanel({ children, value, index, ...other }) { + const theme = useTheme(); + + return ( + + ); +} + +TabPanel.propTypes = { + + children: PropTypes.node, + + index: PropTypes.number.isRequired, + + value: PropTypes.number.isRequired +}; + +export default TabPanel; \ No newline at end of file diff --git a/src/Components/WalletProvider/index.css b/src/Components/WalletProvider/index.css new file mode 100644 index 000000000..15fb9d816 --- /dev/null +++ b/src/Components/WalletProvider/index.css @@ -0,0 +1,51 @@ +.wallet-adapter-dropdown { + width: 100%; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: var(--env-var-spacing-1); +} + +.wallet-adapter-button { + display: flex; + align-items: center; + justify-content: center; + width: auto; + height: var(--env-var-height-2) !important; + font-size: var(--env-var-font-size-medium-plus) !important; + font-weight: 500 !important; + margin: 0; + padding: calc((var(--env-var-height-2) - var(--env-var-font-size-medium-plus) * 1.2) / 2) var(--env-var-spacing-1); + white-space: nowrap; +} + +.wallet-adapter-modal-title { + font-size: var(--font-size-h5) !important; +} + +/* Separator styling */ +.wallet-adapter-modal-divider { + background-color: var(--border-color) !important; + margin: var(--spacing-md) 0 !important; +} + +/* Responsive fixes */ +@media (max-width: 1200px) { + .wallet-adapter-button { + font-size: var(--env-var-font-size-medium) !important; + padding: calc((var(--env-var-height-2) - var(--env-var-font-size-medium) * 1.2) / 2) + var(--env-var-spacing-1-minus) !important; + + } +} + +@media (max-width: 900px) { + .wallet-adapter-modal-wrapper { + flex-direction: column !important; + } + + .wallet-adapter-modal-divider { + margin: var(--spacing-sm) 0 !important; + } +} \ No newline at end of file diff --git a/src/Components/WalletProvider/index.jsx b/src/Components/WalletProvider/index.jsx index a24124d93..9491c1d39 100644 --- a/src/Components/WalletProvider/index.jsx +++ b/src/Components/WalletProvider/index.jsx @@ -9,6 +9,7 @@ import { import { WalletModalProvider } from "@solana/wallet-adapter-react-ui"; import { clusterApiUrl } from "@solana/web3.js"; import PropTypes from "prop-types"; +import "./index.css"; // Default styles that can be overridden by your app import "@solana/wallet-adapter-react-ui/styles.css"; diff --git a/src/Pages/About/index.jsx b/src/Pages/About/index.jsx index 84b90af55..dcb9d3f43 100644 --- a/src/Pages/About/index.jsx +++ b/src/Pages/About/index.jsx @@ -1,12 +1,15 @@ import React from 'react'; import AppBar from '../../Components/Common/AppBar'; import Footer from '../../Components/Common/Footer'; +import { useTranslation } from "react-i18next"; const AboutUs = () => { + const { t } = useTranslation(); + return (
-

About Us

+

{t("aboutus")}

); diff --git a/src/Pages/Auth/CheckEmail.jsx b/src/Pages/Auth/CheckEmail.jsx index 7d7a6af64..695be62b0 100644 --- a/src/Pages/Auth/CheckEmail.jsx +++ b/src/Pages/Auth/CheckEmail.jsx @@ -5,6 +5,7 @@ import { useDispatch } from "react-redux"; import { useNavigate } from "react-router"; import { createToast } from "../../Utils/toastUtils"; import { forgotPassword } from "../../Features/Auth/authSlice"; +import { useTranslation } from "react-i18next"; import Background from "../../assets/Images/background-grid.svg?react"; import EmailIcon from "../../assets/icons/email.svg?react"; import Logo from "../../assets/icons/checkmate-icon.svg?react"; @@ -15,6 +16,7 @@ const CheckEmail = () => { const theme = useTheme(); const navigate = useNavigate(); const dispatch = useDispatch(); + const { t } = useTranslation(); const [email, setEmail] = useState(); const [disabled, setDisabled] = useState(false); @@ -161,9 +163,9 @@ const CheckEmail = () => { - Check your email + {t("authCheckEmailTitle")} - We sent a password reset link to{" "} + {t("authCheckEmailDescription")}{" "} { maxWidth: 400, }} > - Open email app + {t("authCheckEmailOpenEmailButton")} - Didn't receive the email?{" "} + {t("authCheckEmailDidntReceiveEmail")}{" "} - Click to resend + {t("authCheckEmailClickToResend")} @@ -205,15 +207,15 @@ const CheckEmail = () => { textAlign="center" p={theme.spacing(12)} > - Go back to — + {t("goBackTo")} - Log In + {t("authLoginTitle")} diff --git a/src/Pages/Auth/ForgotPassword.jsx b/src/Pages/Auth/ForgotPassword.jsx index 61234db73..a67f67c14 100644 --- a/src/Pages/Auth/ForgotPassword.jsx +++ b/src/Pages/Auth/ForgotPassword.jsx @@ -11,6 +11,7 @@ import Logo from "../../assets/icons/checkmate-icon.svg?react"; import Key from "../../assets/icons/key.svg?react"; import Background from "../../assets/Images/background-grid.svg?react"; import IconBox from "../../Components/IconBox"; +import { useTranslation } from "react-i18next"; import "./index.css"; const ForgotPassword = () => { @@ -29,6 +30,8 @@ const ForgotPassword = () => { email && setForm({ email: sessionStorage.getItem("email") }); }, []); + const { t } = useTranslation(); + const handleSubmit = async (event) => { event.preventDefault(); @@ -162,7 +165,7 @@ const ForgotPassword = () => { - Forgot password? + {t("authForgotPasswordTitle")} No worries, we'll send you reset instructions. { mt: theme.spacing(15), }} > - Send instructions + {t("continue")} @@ -204,15 +207,15 @@ const ForgotPassword = () => { textAlign="center" p={theme.spacing(12)} > - Go back to — + {t("goBackTo")} - Log In + {t("authLoginTitle")} diff --git a/src/Pages/Auth/NewPasswordConfirmed.jsx b/src/Pages/Auth/NewPasswordConfirmed.jsx index 6258e0fa5..9c7734083 100644 --- a/src/Pages/Auth/NewPasswordConfirmed.jsx +++ b/src/Pages/Auth/NewPasswordConfirmed.jsx @@ -8,12 +8,14 @@ import Background from "../../assets/Images/background-grid.svg?react"; import ConfirmIcon from "../../assets/icons/check-outlined.svg?react"; import Logo from "../../assets/icons/checkmate-icon.svg?react"; import IconBox from "../../Components/IconBox"; +import { useTranslation } from "react-i18next"; import "./index.css"; const NewPasswordConfirmed = () => { const theme = useTheme(); const navigate = useNavigate(); const dispatch = useDispatch(); + const { t } = useTranslation(); const handleNavigate = () => { dispatch(clearAuthState()); @@ -97,9 +99,9 @@ const NewPasswordConfirmed = () => { - Password reset + {t("passwordreset")} - Your password has been successfully reset. Click below to log in magically. + {t("authNewPasswordConfirmed")} @@ -118,15 +120,15 @@ const NewPasswordConfirmed = () => { textAlign="center" p={theme.spacing(12)} > - Go back to — + {t("goBackTo")} - Log In + {t("authLoginTitle")} diff --git a/src/Pages/Auth/Register/Register.jsx b/src/Pages/Auth/Register/Register.jsx index 9f93cc821..021cda18a 100644 --- a/src/Pages/Auth/Register/Register.jsx +++ b/src/Pages/Auth/Register/Register.jsx @@ -35,7 +35,7 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => { textAlign="center" > - Sign Up + {t("signUP")} {isSuperAdmin ? t("authRegisterCreateSuperAdminAccount") @@ -67,7 +67,7 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => { - {t("authRegisterBySigningUp")} + {t("authRegisterBySigningUp")}{" "} { @@ -239,7 +239,6 @@ const Register = ({ isSuperAdmin }) => { const action = await dispatch(register(registerForm)); if (action.payload.success) { const authToken = action.payload.data; - localStorage.setItem("token", authToken); navigate("/uptime"); createToast({ body: "Welcome! Your account was created successfully.", diff --git a/src/Pages/Auth/Register/StepOne/index.jsx b/src/Pages/Auth/Register/StepOne/index.jsx index 42e547b62..248f57f69 100644 --- a/src/Pages/Auth/Register/StepOne/index.jsx +++ b/src/Pages/Auth/Register/StepOne/index.jsx @@ -4,6 +4,7 @@ import { useTheme } from "@emotion/react"; import { Box, Button, Stack, Typography } from "@mui/material"; import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded"; import TextInput from "../../../../Components/Inputs/TextInput"; +import { useTranslation } from "react-i18next"; StepOne.propTypes = { form: PropTypes.object, @@ -28,6 +29,7 @@ StepOne.propTypes = { function StepOne({ form, errors, onSubmit, onChange, onBack }) { const theme = useTheme(); const inputRef = useRef(null); + const { t } = useTranslation(); useEffect(() => { if (inputRef.current) { @@ -43,8 +45,8 @@ function StepOne({ form, errors, onSubmit, onChange, onBack }) { textAlign="center" > - Sign Up - Enter your personal details + {t("signUp")} + {t("authRegisterStepOnePersonalDetails")} - Back + {t("commonBack")} diff --git a/src/Pages/DistributedUptime/Create/index.jsx b/src/Pages/DistributedUptime/Create/index.jsx index 57160d2ab..458aa011f 100644 --- a/src/Pages/DistributedUptime/Create/index.jsx +++ b/src/Pages/DistributedUptime/Create/index.jsx @@ -19,6 +19,7 @@ import { monitorValidation } from "../../../Validation/validation"; import { useParams } from "react-router-dom"; import { useCreateDistributedUptimeMonitor } from "./Hooks/useCreateDistributedUptimeMonitor"; import { useMonitorFetch } from "./Hooks/useMonitorFetch"; +import { useTranslation } from "react-i18next"; // Constants const MS_PER_MINUTE = 60000; @@ -41,6 +42,7 @@ const parseUrl = (url) => { const CreateDistributedUptime = () => { const { monitorId } = useParams(); const isCreate = typeof monitorId === "undefined"; + const { t } = useTranslation(); let BREADCRUMBS = [ { name: `distributed uptime`, path: "/distributed-uptime" }, @@ -191,14 +193,14 @@ const CreateDistributedUptime = () => { fontSize="inherit" fontWeight="inherit" > - monitor + {t("monitor")} - General settings + {t("settingsGeneralSettings")} - Here you can select the URL of the host, together with the type of monitor. + {t("distributedUptimeCreateSelectURL")} @@ -232,9 +234,9 @@ const CreateDistributedUptime = () => { - Checks to perform + {t("distributedUptimeCreateChecks")} - You can always add or remove checks after adding your site. + {t("distributedUptimeCreateChecksDescription")} @@ -259,14 +261,14 @@ const CreateDistributedUptime = () => { filled={https.toString()} onClick={() => setHttps(true)} > - HTTPS + {t("https")} ) : ( @@ -291,9 +293,9 @@ const CreateDistributedUptime = () => { - Incident notifications + {t("distributedUptimeCreateIncidentNotification")} - When there is an incident, notify users. + {t("distributedUptimeCreateIncidentDescription")} @@ -310,7 +312,7 @@ const CreateDistributedUptime = () => { - Advanced settings + {t("distributedUptimeCreateAdvancedSettings")}