mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-21 13:37:28 +00:00
[Feature]: Added Localisation (#3746)
* Added localisation * Closed modal after language selection * updated transaltaion setup * Updated language tooltip * Added fallback language support * Adding english library resource for translation (#3844) * Adding English dictionary for the widget lists in the inspector * added leftSideBar object in en.json and implemented it for leftSidebar icon text * renamed leftSideBar to leftSidebar and added resources for tip in the left side bar * added english translation resources for leftsidebar debugger * added english language resources for the global settings * added english language resources for data sources in left sidebar * added english language resources for the share button and share modal in the editor * added english language resources for release button, manageOrgUsers, appVersionManager * added english language resources for Queries and Please select a widget to inspect in the editor * added english language resources for data source list , data source manager, and query manager(partially) * added english language resources for queryManager, transformation, preview * added english language resources for dark mode toggle in the headers inside homepage * added fallback message for dark mode toggle * added resources for language change in the headers inside homepage * added resources for notification center in the header inside homepage * added resources for organization and manage users components in header inside homepage * added resources in manageGroupPermission * added resources for manageGroupPermissionsResources component * added resources for manageSSO and generalSettings components * added resources for google sso * added resources for github sso * added resources for environment variables in manageSSO * added resources for profile and setting page * added resources for app card and app card menu * added resources for folder section and app list in homepage * added resources for header section in the homepage * added resources for pagination in homepage * added resources for modals in the homepage * added resources for blank page * added resources for login page * added resources for forgot password page * added resources for sign up page * added resources for onBoarding component * added resources for reset password page * deleted duplicate key for readDocumentation * deleted duplicate key for cancel in en.json and added translation for cancel at few places * removing duplicate copy of save key in en.json * added translation for CommentActions.jsx components * deleted duplicate copy of search key in en.json and added resources for create and search queries , select keys * fix typo errors * fixed typo errors * shorten the key for loginAndSignUpAndForgotPassword to loginSignupPage in en.json file and related files * shorten the key noLoginMethodsEnabledForThisWorkspace to noLoginMethodsEnabled * shorten the key pleaseCheckYourEmailForConfirmationLink to emailConfirmLink * shorten the key dontHaveAccountYet to dontHaveAccount * shorten keys from loginSignupPage key in en.json * shorten keys of shareModal nested object in en.json * shorten the key in appVersionManager nested object * shorten the keys for queryManager nested object in the en.json * delete duplicate copy of environmentVar and shorten manageEnvironmentVariables,environmentVariables * shorten keys in the organization nested object * shorten keys in the homePage nested object in en.json file * added inspector and eventManager empty object * added resources to RedirectSSO component * added resources for OAuth2 * added resources for TemplateCard.jsx * added resources for TemplateLibraryModal.jsx * added resources for ConfirmationPage.jsx * added resources for ConfirmationPage component * removed translation in App.jsx file * added resources for Slack.jsx * added resources for GoogleSheets.jsx * added resources for CodeBuilder.jsx * added resources for CommentBody and CommentFooter * added resources for TestConnection component * added resources for AllignButton.jsx * added resources for Openapi and Stripe components * added resources for ErrorBoundary * added resources for Viewer.jsx * Translation for widgets, table Co-authored-by: Kavin Venkatachalam <kavin.saratha@gmail.com> * Commented Language selection * Fixed typos * Updated fr.json file Co-authored-by: Manish Kushare <kushare.manish9@gmail.com>
This commit is contained in:
parent
3f62230427
commit
7f702c1d6b
91 changed files with 9965 additions and 1431 deletions
921
frontend/assets/translations/en.json
Normal file
921
frontend/assets/translations/en.json
Normal file
|
|
@ -0,0 +1,921 @@
|
|||
{
|
||||
"globals":{
|
||||
"readDocumentation":"Read documentation",
|
||||
"cancel":"Cancel",
|
||||
"save":"Save",
|
||||
"back":"Back",
|
||||
"edit":"Edit",
|
||||
"search":"Search",
|
||||
"update":"Update",
|
||||
"delete":"Delete",
|
||||
"add":"Add",
|
||||
"view":"View",
|
||||
"create":"Create",
|
||||
"enabled":"Enabled",
|
||||
"disabled":"Disabled",
|
||||
"yes":"Yes",
|
||||
"submit":"Submit",
|
||||
"select":"Select",
|
||||
"environmentVar":"Environment Variables",
|
||||
"saving":"Saving...",
|
||||
"saveDatasource":"Save data source",
|
||||
"authorize":"Authorize",
|
||||
"connect":"Connect",
|
||||
"reconnect":"Reconnect",
|
||||
"components":"components",
|
||||
"send":"Send",
|
||||
"noConnection":"could not connect",
|
||||
"connectionVerifeid":"connection verified",
|
||||
"left":"Left",
|
||||
"center":"Center",
|
||||
"right":"Right",
|
||||
"justified":"Justified",
|
||||
"host":"Host",
|
||||
"operation":"Operation",
|
||||
"header":"HEADER",
|
||||
"path":"PATH",
|
||||
"query":"QUERY",
|
||||
"requestBody":"REQUEST BODY"
|
||||
},
|
||||
"errorBoundary":"Something went wrong.",
|
||||
"viewer":"Sorry!. This app is under maintenance",
|
||||
"app":{
|
||||
"updateAvailable":"Update available",
|
||||
"newVersionReleased":"A new version of ToolJet has been released.",
|
||||
"readReleaseNotes":"Read release notes & update",
|
||||
"skipVersion":"Skip this version"
|
||||
},
|
||||
"stripe":"Please wait whle we load the OpenAPI specification for Stripe.",
|
||||
"openApi":{
|
||||
"noValidOpenApi":"Valid OpenAPI Spec is not available!.",
|
||||
"selectHost":"Select a host",
|
||||
"selectOperation":"Select an operation"
|
||||
},
|
||||
"slack":{
|
||||
"authorize":"Authorize",
|
||||
"connectToolJetToSlack":"ToolJet can connect to Slack and list users, send messages, etc. Please select appropriate permission scopes.",
|
||||
"chatWrite":"chat:write",
|
||||
"listUsersAndSendMessage":"Your ToolJet app will be able to list users and send messages to users & channels.",
|
||||
"connectSlack":"Connect to Slack"
|
||||
},
|
||||
"googleSheets":{
|
||||
"readOnly":"Read only",
|
||||
"enableReadAndWrite":"If you want your ToolJet apps to modify your Google sheets, make sure to select read and write access",
|
||||
"readDataFromSheets":"Your ToolJet apps can only read data from Google sheets",
|
||||
"readWrite":"Read and write",
|
||||
"readModifySheets":"Your ToolJet apps can read data from sheets, modify sheets, and more.",
|
||||
"toGoogleSheets":"to Google Sheets"
|
||||
|
||||
},
|
||||
"profile": {
|
||||
"profileSettings": "Profile Settings"
|
||||
},
|
||||
"loginSignupPage":{
|
||||
"forgotPassword":"Forgot Password",
|
||||
"emailAddress":"Email address",
|
||||
"enterEmail":"Enter email",
|
||||
"resetPassword":"Reset Password",
|
||||
"dontHaveAccount":"Don't have account yet?",
|
||||
"signIn":"Sign in",
|
||||
"signUp":"Sign up",
|
||||
"createToolJetAccount":"Create a ToolJet account",
|
||||
"enterBusinessEmail":"Enter your business email",
|
||||
"alreadyHaveAnAccount":"Already have an account?",
|
||||
"password":"Password",
|
||||
"showPassword":"show password",
|
||||
"loginTo":"Login to",
|
||||
"yourAccount":"your account",
|
||||
"noLoginMethodsEnabled":"No login methods enabled for this workspace",
|
||||
"emailConfirmLink":"Please check your email for confirmation link",
|
||||
"newPassword":"New Password",
|
||||
"passwordConfirmation":"Password Confirmation"
|
||||
},
|
||||
"editor": {
|
||||
"preview": "Preview",
|
||||
"share":"Share",
|
||||
"shareModal": {
|
||||
"makeApplicationPublic":"Make application public ?",
|
||||
"shareableLink":"Get shareable link for this application",
|
||||
"copy":"copy",
|
||||
"embeddableLink":"Get embeddable link for this application",
|
||||
"manageUsers":"Manage Users"
|
||||
},
|
||||
"appVersionManager":{
|
||||
"version":"Version",
|
||||
"currentlyReleased":"Currently Released",
|
||||
"createVersion":"Create Version",
|
||||
"versionName":"Version Name",
|
||||
"createVersionFrom":"Create version from",
|
||||
"save":"Save",
|
||||
"create":"Create Version",
|
||||
"editVersion": "Edit Version",
|
||||
"deleteVersion":"Do you really want to delete this version?",
|
||||
"enterVersionName":"Enter version name",
|
||||
"versionAlreadyReleased":"Version already released. Kindly create a new version or switch to a different version to continue making changes."
|
||||
},
|
||||
"queries":"Queries",
|
||||
"inspectComponent":"Please select a component to inspect",
|
||||
"release":"Release",
|
||||
"searchQueries":"Search queries",
|
||||
"createQuery":"Create query",
|
||||
"queryManager":{
|
||||
"general":"General",
|
||||
"advanced":"Advanced",
|
||||
"preview":"Preview",
|
||||
"Save":"Save",
|
||||
"selectDatasource":"Select Datasource",
|
||||
"addDatasource":"Add datasource",
|
||||
"dataSourceManager":{
|
||||
"toast":{
|
||||
"success": {
|
||||
"dataSourceAdded": "Datasource Added",
|
||||
"dataSourceSaved": "Datasource Saved"
|
||||
},
|
||||
"error" :{
|
||||
"noEmptyDsName" :"The name of datasource should not be empty"
|
||||
}
|
||||
},
|
||||
"suggestDataSource":"Suggest Datasource",
|
||||
"suggestAnIntegration":"Suggest an integration",
|
||||
"whatLookingFor":"Tell us what you were looking for?",
|
||||
"noResultFound":"Don't see what you were looking for?",
|
||||
"suggest":"Suggest",
|
||||
"addNewDataSource":"Add new datasource",
|
||||
"whiteListIP":"Please white-list our IP address if the data source is not publicly accessible",
|
||||
"copied":"Copied",
|
||||
"copy":"Copy",
|
||||
"saving":"Saving",
|
||||
"noResultsFor":"No results for",
|
||||
"noteTaken":"Thank you, we've taken a note of that!",
|
||||
"goToAllDatasources":"Go to all Datasource",
|
||||
"send":"Send"
|
||||
},
|
||||
"runQueryOnPageLoad":"Run this query on page load?",
|
||||
"confirmBeforeQueryRun":"Request confirmation before running query?",
|
||||
"notificationOnSuccess":"Show notification on success?",
|
||||
"successMessage":"Success Message",
|
||||
"queryRanSuccessfully":"Query ran successfully",
|
||||
"notificationDuration":"Notification duration (s)",
|
||||
"events":"Events",
|
||||
"transformation":{
|
||||
"transformationToolTip":"Transformations can be used to transform the results of queries. All the app variables are accessible from transformers and supports JS libraries such as Lodash & Moment.",
|
||||
"transformations":"Transformations"
|
||||
}
|
||||
},
|
||||
"inspector":{
|
||||
"eventManager":{
|
||||
"event": "Event",
|
||||
"action": "Action",
|
||||
"actionOptions": "Action Options",
|
||||
"message": "Message",
|
||||
"alertType": "Alert Type",
|
||||
"url": "URL",
|
||||
"modal": "Modal",
|
||||
"text": "Text",
|
||||
"query": "Query",
|
||||
"key": "Key",
|
||||
"value": "Value",
|
||||
"type": "Type",
|
||||
"fileName": "File name",
|
||||
"data": "Data",
|
||||
"table": "Table",
|
||||
"pageIndex": "Page index",
|
||||
"component": "Component",
|
||||
"addHandler": "+ Add handler",
|
||||
"addEventHandler": "+ Add event handler",
|
||||
"emptyMessage": "This {{componentName}} doesn't have any event handlers"
|
||||
}
|
||||
}
|
||||
},
|
||||
"header":{
|
||||
"darkModeToggle":{
|
||||
"activateLightMode" :"Activate light mode",
|
||||
"activateDarkMode":"Activate dark mode"
|
||||
},
|
||||
"languageSelection":{
|
||||
"changeLanguage":"Change language",
|
||||
"searchLanguage":"Search language"
|
||||
},
|
||||
"notificationCenter":{
|
||||
"notifications":"Notifications",
|
||||
"markAllAs":"Mark all as",
|
||||
"read":"read",
|
||||
"un":"un",
|
||||
"youDontHaveany":"You don't have any",
|
||||
"youAreCaughtUp":"You're all caught up!",
|
||||
"view":"View"
|
||||
},
|
||||
"organization":{
|
||||
"loadOrganizations":"Load Organizations",
|
||||
"createWorkspace":"Create workspace",
|
||||
"workspaceName":"workspace name",
|
||||
"editWorkspace":"Edit workspace",
|
||||
"menus": {
|
||||
"addWorkspace":"Add workspace",
|
||||
"menusList":{
|
||||
"manageUsers":"Manage Users",
|
||||
"manageGroups":"Manage Groups",
|
||||
"manageSso":"Manage SSO",
|
||||
"manageEnv":"Manage Environment Variables"
|
||||
},
|
||||
"manageUsers":{
|
||||
"usersAndPermission":"Users & Permissions",
|
||||
"inviteNewUser":"Invite new user",
|
||||
"name":"NAME",
|
||||
"email":"EMAIL",
|
||||
"status":"STATUS",
|
||||
"archive":"Archive",
|
||||
"unarchive":"Unarchive",
|
||||
"addNewUser":"Add new user",
|
||||
"emailAddress":"Email address",
|
||||
"createUser":"Create User",
|
||||
"enterFirstName":"Enter First Name",
|
||||
"enterLastName":"Enter Last Name",
|
||||
"enterEmail":"Enter Email"
|
||||
},
|
||||
"manageGroups":{
|
||||
"permissions":{
|
||||
"userGroups":"User Groups",
|
||||
"createNewGroup":"Create new group",
|
||||
"udpateGroup":"Update group",
|
||||
"addNewGroup":"Add new group",
|
||||
"enterName":"Enter Name",
|
||||
"createGroup":"Create Group",
|
||||
"name":"Name"
|
||||
},
|
||||
"permissionResources":{
|
||||
"userGroup":"User group",
|
||||
"apps":"Apps",
|
||||
"users":"User",
|
||||
"permissions":"Permissions",
|
||||
"addAppsToGroup":"Select apps to add to the group",
|
||||
"name":"name",
|
||||
"addUsersToGroup":"Select users to add to the group",
|
||||
"email":"email",
|
||||
"resource":"resource",
|
||||
"createUpdateDelete":"Create/Update/Delete",
|
||||
"folder":"Folder"
|
||||
}
|
||||
},
|
||||
"manageSSO":{
|
||||
"manageSso":"Manage SSO",
|
||||
"generalSettings":{
|
||||
"title":"General Settings",
|
||||
"enableSignup":"Enable Signup",
|
||||
"newAccountWillBeCreated":"New account will be created for user's first time SSO sign in",
|
||||
"allowedDomains":"Allowed domains",
|
||||
"enterDomains":"Enter Domains",
|
||||
"supportMultiDomains":"Support multiple domains. Enter domain names separated by comma. example: tooljet.com,tooljet.io,yourorganization.com",
|
||||
"loginUrl":"Login URL",
|
||||
"workspaceLogin":"Use this URL to login directly to this workspace",
|
||||
"allowDefaultSso":"Allow default SSO",
|
||||
"ssoAuth":"Allow users to authenticate via default SSO. Default SSO configurations can be overridden by workspace level SSO."
|
||||
},
|
||||
"google":{
|
||||
"title":"Google",
|
||||
"enabled":"Enabled",
|
||||
"disabled":"Disabled",
|
||||
"clientId":"Client Id",
|
||||
"enterClientId":"Enter Client Id",
|
||||
"redirectUrl":"Redirect URL"
|
||||
},
|
||||
"github":{
|
||||
"title":"Github",
|
||||
"hostName":"Host Name",
|
||||
"enterHostName":"Enter Host Name",
|
||||
"requiredGithub":"Required if GitHub is self hosted",
|
||||
"clientId":"Client Id",
|
||||
"enterClientId":"Enter Client Id",
|
||||
"clientSecret":"Client Secret",
|
||||
"enterClientSecret":"Enter Client Secret",
|
||||
"encrypted":"Encrypted",
|
||||
"redirectUrl":"Redirect URL"
|
||||
},
|
||||
"passwordLogin":"Password Login",
|
||||
"environmentVar" : {
|
||||
"noEnvConfig":"You haven't configured any environment variables, press the 'Add new variable' button to create one",
|
||||
"envWillBeDeleted":"Variable will be deleted, do you want to continue?",
|
||||
"addNewVariable":"Add new variable",
|
||||
"variableForm":{
|
||||
"addNewVariable":"Add new variable",
|
||||
"updatevariable":"Update variable",
|
||||
"name":"Name",
|
||||
"value":"Value",
|
||||
"enterVariableName":"Enter Variable Name",
|
||||
"enterValue":"Enter Value",
|
||||
"type":"Type",
|
||||
"enableEncryption":"Enable encryption",
|
||||
"addVariable":"Add variable"
|
||||
},
|
||||
"variableTable":{
|
||||
"name":"name",
|
||||
"value":"value",
|
||||
"type":"type",
|
||||
"secret":"secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profileSettingPage":{
|
||||
"profileSettings":"Profile Settings",
|
||||
"firstName":"First name",
|
||||
"lastName":"Last name",
|
||||
"enterFirstName":"Enter First Name",
|
||||
"enterLastName":"Enter Last Name",
|
||||
"email":"Email",
|
||||
"avatar":"Avatar",
|
||||
"update":"Update",
|
||||
"profile":"Profile",
|
||||
"changePassword":"Change password",
|
||||
"currentPassword":"Current password",
|
||||
"newPassword":"New password",
|
||||
"confirmNewPassword":"Confirm new password",
|
||||
"enterCurrentPassword":"Enter current password",
|
||||
"enterNewPassword":"Enter new password"
|
||||
},
|
||||
"profile":"Profile",
|
||||
"logout":"Logout"
|
||||
},
|
||||
"homePage":{
|
||||
"appCard":{
|
||||
"changeIcon":"Change Icon",
|
||||
"addToFolder":"Add to folder",
|
||||
"move":"Move",
|
||||
"to":"to",
|
||||
"selectFolder":"Select folder",
|
||||
"deleteApp":"Delete app",
|
||||
"exportApp":"Export app",
|
||||
"cloneApp":"Clone app",
|
||||
"launch":"Launch",
|
||||
"maintenance":"Maintenance",
|
||||
"noDeployedVersion":"App does not have a deployed version",
|
||||
"openInAppViewer":"Open in app viewer",
|
||||
"removeFromFolder":"Remove from folder"
|
||||
},
|
||||
"blankPage":{
|
||||
"welcomeToToolJet":"Welcome to ToolJet!",
|
||||
"getStartedCreateNewApp":"You can get started by creating a new application or by creating an application using a template in ToolJet Library.",
|
||||
"importApplication":"Import an application"
|
||||
},
|
||||
"foldersSection":{
|
||||
"allApplications":"All applications",
|
||||
"folders":"Folders",
|
||||
"createNewFolder":"+ Create new folder",
|
||||
"noFolders":"You haven't created any folders. Use folders to organize your apps",
|
||||
"createFolder":"Create folder",
|
||||
"updateFolder":"Update folder",
|
||||
"editFolder":"Edit folder",
|
||||
"deleteFolder":"Delete folder",
|
||||
"folderName":"Folder name",
|
||||
"wishToDeleteFolder":"Are you sure you want to delete the folder? Apps within the folder will not be deleted."
|
||||
|
||||
},
|
||||
"header":{
|
||||
"createNewApplication":"Create new application",
|
||||
"import":"Import",
|
||||
"chooseFromTemplate":"Choose from template"
|
||||
},
|
||||
"pagination":{
|
||||
"showing":"Showing",
|
||||
"of":"of",
|
||||
"to":"to"
|
||||
},
|
||||
"noApplicationFound":"No Applications found",
|
||||
"thisFolderIsEmpty":"This folder is empty",
|
||||
"deleteAppAndData":"The app and the associated data will be permanently deleted, do you want to continue?",
|
||||
"removeAppFromFolder":"The app will be removed from this folder, do you want to continue?",
|
||||
"change":"Change",
|
||||
"templateCard":{
|
||||
"use":"Use",
|
||||
"preview":"Preview",
|
||||
"leadGeneretion":"Lead generetion"
|
||||
},
|
||||
"templateLibraryModal":{
|
||||
"select":"Select template",
|
||||
"createAppfromTemplate":"Create application from template"
|
||||
}
|
||||
},
|
||||
"confirmationPage":{
|
||||
"setupAccount":"Set up your account",
|
||||
"signupWithGoogle":"Sign up with Google",
|
||||
"signupWithGitHub":"Sign up with GitHub",
|
||||
"or":"OR",
|
||||
"firstName":"First name",
|
||||
"lastName":"Last name",
|
||||
"company":"Company",
|
||||
"role":"Role",
|
||||
"pleaseSelect":"Please select",
|
||||
"password":"Password",
|
||||
"confirmPassword":"Confirm Password",
|
||||
"clickAndAgree":"By clicking the button below, you agree to our",
|
||||
"termsAndConditions":"Terms and Conditions",
|
||||
"finishAccountSetup":"Finish account setup",
|
||||
"acceptInvite":"accept invite",
|
||||
"accountExists":"Already have an account?",
|
||||
"and":"and"
|
||||
|
||||
},
|
||||
"onBoarding":{
|
||||
"finishToolJetInstallation":"Finish ToolJet installation",
|
||||
"organization":"Organization",
|
||||
"name":"Name",
|
||||
"email":"Email",
|
||||
"receiveUpdatesFromToolJet":"You will receive updates from the ToolJet team ( 1-2 emails every month, we do not spam )",
|
||||
"finishSetup":"Finish setup",
|
||||
"skip":"Skip"
|
||||
},
|
||||
"redirectSso":{
|
||||
"upgradingTov1.13.0":"Upgrading to v1.13.0 and above.",
|
||||
"fromV1.13.0":"From v1.13.0 we have introduced",
|
||||
"multiWorkspace":"Multi-Workspace",
|
||||
"singleSignOnConfig":"The Single Sign-On related configurations are moved from environment variables to database. Please refer this",
|
||||
"link":"Link",
|
||||
"toConfigureSSO":"to configure SSO.",
|
||||
"haveGoogleGithubSSo":"If you have Google or GitHub SSO configurations before upgrade and disabled Multi-Workspace, then theSSO configurations will be migrated while upgrade but you have to re-configure the redirect URL in the SSO provider side. Redirect URLs for each SSO are given below.",
|
||||
"isMultiWorkspaceEnabled":"If you have enabled Multi-Workspace, then the SSO configurations will not be migrated while upgrade so you have to re-configure the SSO under the respective workspace.",
|
||||
"youHaveEnabled":"You have Enabled",
|
||||
"setupSsoWorkspace":"Please login with password and you can setup sso using workspace",
|
||||
"manageSsoMenu":"Manage SSO menu.",
|
||||
"youHaveDisabled":"You have Disabled",
|
||||
"configureRedirectUrl":"Please configure redirect url in SSO provider side.",
|
||||
"google":"Google",
|
||||
"redirectUrl":"Redirect URL:",
|
||||
"gitHub":"GitHub"
|
||||
},
|
||||
"oAuth2": {
|
||||
"pleaseWait":"Please wait...",
|
||||
"authSuccess":"Auth successful, you can close this tab now.",
|
||||
"authFailed":"Auth failed"
|
||||
},
|
||||
"widgetManager": {
|
||||
"commonlyUsed": "commonly used",
|
||||
"layouts" : "layouts",
|
||||
"forms" : "forms",
|
||||
"intregrations" : "integrations",
|
||||
"others":"others",
|
||||
"noResults": "No results found",
|
||||
"tryAdjustingFilterMessage": "Try adjusting your search or filter to find what you're looking for.",
|
||||
"clearQuery": "clear query"
|
||||
},
|
||||
"widget":{
|
||||
"common":{
|
||||
"properties": "Properties",
|
||||
"events": "Events",
|
||||
"layout": "Layout",
|
||||
"styles": "Styles",
|
||||
"general": "General",
|
||||
"validation": "Validation",
|
||||
"documentation": "{{componentMeta}} documentation",
|
||||
"widgetNameEmptyError": "Widget name cannot be empty",
|
||||
"componentNameExistsError": "Component name already exists",
|
||||
"invalidWidgetName": "Invalid widget name. Should be unique and only include letters, numbers and underscore."
|
||||
},
|
||||
"commonProperties": {
|
||||
"visibility": "Visibility",
|
||||
"disable": "Disable",
|
||||
"borderRadius": "Border Radius",
|
||||
"boxShadow": "Box Shadow",
|
||||
"tooltip": "Tooltip",
|
||||
"showOnDesktop": "Show on desktop",
|
||||
"showOnMobile": "Show on mobile",
|
||||
"showLoadingState": "Show loading state",
|
||||
"backgroundColor": "Background Color",
|
||||
"textColor": "Text color",
|
||||
"loaderColor": "Loader color",
|
||||
"defaultValue": "Default Value",
|
||||
"placeholder": "Placeholder",
|
||||
"label": "Label",
|
||||
"title": "Title",
|
||||
"code": "Code",
|
||||
"data": "Data",
|
||||
"tableData": "Table data",
|
||||
"tableColumns": "Table columns",
|
||||
"searverSideSearch": "Server-side search",
|
||||
"loadingState": "Loading State",
|
||||
"serverSidePagination": "Server-side pagination",
|
||||
"clientSidePagination": "Client-side pagination",
|
||||
"serverSideSearch": "Server-side search",
|
||||
"showSearchBox": "Show search box",
|
||||
"showDownloadButton": "Show download button",
|
||||
"showFilterButton": "Show filter button",
|
||||
"showBulkUpdateActions": "Show bulk update actions",
|
||||
"bulkSelection": "Bulk selection",
|
||||
"highlightSelectedRow": "Highlight selected row",
|
||||
"actionButtonRadius": "Action Button Radius",
|
||||
"tableType": "Table type",
|
||||
"cellSize": "Cell size",
|
||||
"setPage": "Set page",
|
||||
"page": "Page",
|
||||
"buttonText": "Button Text",
|
||||
"click": "Click",
|
||||
"setText": "Set text",
|
||||
"text": "Text",
|
||||
"markerColor": "Marker color",
|
||||
"showAxes": "Show axes",
|
||||
"showGridLines": "Show grid lines",
|
||||
"chartType": "Chart type",
|
||||
"jsonDescription": "Json Description",
|
||||
"usePlotlyJsonSchema": "Use Plotly JSON schema",
|
||||
"padding": "Padding",
|
||||
"hideTitleBar": "Hide title bar",
|
||||
"hideCloseButton": "Hide close button",
|
||||
"hideOnEscape": "Hide on escape",
|
||||
"modalSize": "Modal size",
|
||||
"open": "Open",
|
||||
"close": "Close",
|
||||
"regex": "Regex",
|
||||
"minLength": "Min length",
|
||||
"maxLength": "Max length",
|
||||
"customValidation": "Custom validation",
|
||||
"clear": "Clear",
|
||||
"minimumValue": "Minimum value",
|
||||
"maximumValue": "Maximum value",
|
||||
"format": "Format",
|
||||
"enableTimeSelection": "Enable time selection?",
|
||||
"enableDateSelection": "Enable date selection?",
|
||||
"disabledDates": "Disabled dates",
|
||||
"setChecked": "Set checked",
|
||||
"status": "status",
|
||||
"defaultStatus": "Default Status",
|
||||
"checkboxColor": "Checkbox Color",
|
||||
"optionValues": "Option values",
|
||||
"optionLabels": "Option labels",
|
||||
"activeColor": "Active Color",
|
||||
"selectOption": "Select option",
|
||||
"option": "Option",
|
||||
"toggleSwitchColor": "Toggle Switch Color",
|
||||
"defaultStartDate": "Default start date",
|
||||
"defaultEndDate": "Default end date",
|
||||
"textSize": "Text Size",
|
||||
"alignText": "Align Text",
|
||||
"url": "URL",
|
||||
"alternativeText": "Alternative text",
|
||||
"zoomButton": "Zoom button",
|
||||
"borderType": "Border type",
|
||||
"imageFit": "Image fit",
|
||||
"optionsLoadingState": "Options loading state",
|
||||
"selectedTextColor": "Selected Text Color",
|
||||
"select": "Select",
|
||||
"deselectOption": "Deselect Option",
|
||||
"clearSelections": "Clear selections",
|
||||
"enableSelectAllOption": "Enable select All option",
|
||||
"initialLocation": "Initial location",
|
||||
"defaultMarkers": "Default markers",
|
||||
"addNewMarkers": "Add new markers",
|
||||
"searchForPlaces": "Search for places",
|
||||
"setLocation": "Set Location",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"numberOfStars": "Number of stars",
|
||||
"defaultNoOfSelectedStars": "Default no of selected stars",
|
||||
"enableHalfStar": "Enable half star",
|
||||
"tooltips": "Tooltips",
|
||||
"starColor": "Star Color",
|
||||
"labelColor": "Label Color",
|
||||
"dividerColor": "Divider Color",
|
||||
"clearFiles": "Clear Files",
|
||||
"instructionText": "Instruction Text",
|
||||
"useDropZone": "Use Drop zone",
|
||||
"useFilePicker": "Use File Picker",
|
||||
"pickMultipleFiles": "Pick multiple files",
|
||||
"maxFileCount": "Max file count",
|
||||
"acceptFileTypes": "Accept file types",
|
||||
"maxSizeLimitBytes": "Max size limit (Bytes)",
|
||||
"minSizeLimitBytes": "Min size limit (Bytes)",
|
||||
"parseContent": "Parse content",
|
||||
"fileType": "File type",
|
||||
"dateFormat": "Date format",
|
||||
"defaultDate": "Default date",
|
||||
"events": "Events",
|
||||
"resources": "Resources",
|
||||
"defaultView": "Default view",
|
||||
"startTimeOnWeekAndDayView": "Start time on week and day view",
|
||||
"endTimeOnWeekAndDayView": "End time on week and day view",
|
||||
"showToolbar": "Show toolbar",
|
||||
"showViewSwitcher": "Show view switcher",
|
||||
"highlightToday": "Highlight today",
|
||||
"showPopoverWhenEventIsClicked": "Show popover when event is clicked",
|
||||
"cellSizeInViewsClassifiedByResource": "Cell size in views classified by resource",
|
||||
"headerDateFormatOnWeekView": "Header date format on week view",
|
||||
"showLineNumber": "Show Line Number",
|
||||
"mode": "Mode",
|
||||
"tabs": "Tabs",
|
||||
"defaultTab": "Default tab",
|
||||
"hideTabs": "Hide Tabs",
|
||||
"highlightColor": "Highlight Color",
|
||||
"tabWidth": "Tab width",
|
||||
"setCurrentTab": "Set current tab",
|
||||
"id": "Id",
|
||||
"timerType": "Timer type",
|
||||
"listData": "List data",
|
||||
"rowHeight": "Row height",
|
||||
"showBottomBorder": "Show bottom border",
|
||||
"tags": "Tags",
|
||||
"numberOfPages": "Number of pages",
|
||||
"defaultPageIndex": "Default page index",
|
||||
"progress": "Progress",
|
||||
"color": "Color",
|
||||
"strokeWidth": "Stroke Width",
|
||||
"counterClockwise": "Counter Clockwise",
|
||||
"circleRatio": "Circle Ratio",
|
||||
"colour": "Colour",
|
||||
"size": "Size",
|
||||
"primaryValueLabel": "Primary value label",
|
||||
"primaryValue": "Primary value",
|
||||
"hideSecondaryValue": "Hide secondary value",
|
||||
"secondaryValueLabel": "Secondary value label",
|
||||
"secondaryValue": "Secondary value",
|
||||
"secondarySignDisplay": "Secondary sign display",
|
||||
"primaryLabelColour": "Primary Label Colour",
|
||||
"primaryTextColour": "Primary Text Colour",
|
||||
"secondaryLabelColour": "Secondary Label Colour",
|
||||
"secondaryTextColour": "Secondary Text Colour",
|
||||
"min": "Min",
|
||||
"max": "Max",
|
||||
"value": "Value",
|
||||
"twoHandles": "Two handles",
|
||||
"lineColor": "Line color",
|
||||
"handleColor": "Handle color",
|
||||
"trackColor": "Track color",
|
||||
"timelineData": "Timeline data",
|
||||
"hideDate": "Hide Date",
|
||||
"svgData": "Svg data",
|
||||
"rawHtml": "Raw HTML",
|
||||
"values": "values",
|
||||
"labels": "Labels",
|
||||
"defaultSelected": "Default selected",
|
||||
"enableMutipleSelection": "Enable mutiple selection",
|
||||
"selectedTextColour": "Selected text colour",
|
||||
"selectedBackgroundColor": "Selected background color",
|
||||
"fileUrl": "File URL",
|
||||
"scalePageToWidth": "Scale page to width",
|
||||
"showPageControls": "Show page controls",
|
||||
"steps": "Steps",
|
||||
"currentStep": "Current step",
|
||||
"stepsSelectable": "Steps selectable",
|
||||
"theme": "Theme",
|
||||
"columns": "Columns",
|
||||
"cardData": "Card Data",
|
||||
"enableAddCard": "Enable Add Card",
|
||||
"width": "Width",
|
||||
"minWidth": "Min Width",
|
||||
"accentColor": "Accent color",
|
||||
"defaultColor": "Default Color",
|
||||
"setColor": "Set Color",
|
||||
"structure": "Structure",
|
||||
"checkedValues": "Checked Values",
|
||||
"expandedValues": "Expanded Values"
|
||||
},
|
||||
"Table": {
|
||||
"displayName": "Table",
|
||||
"description": "Display paginated tabular data",
|
||||
"columnType": "Column type",
|
||||
"columnName": "Column name",
|
||||
"overflow": "Overflow",
|
||||
"key": "key",
|
||||
"textColor": "Text color",
|
||||
"validation": "Validation",
|
||||
"regex": "Regex",
|
||||
"minLength": "Min length",
|
||||
"maxLength": "Max length",
|
||||
"customRule": "Custom rule",
|
||||
"values": "Values",
|
||||
"labels": "Labels",
|
||||
"cellBgColor": "Cell Background Color",
|
||||
"dateDisplayformat": "Date Display Format",
|
||||
"dateParseformat": "Date Parse Format",
|
||||
"showTime": "show time",
|
||||
"makeEditable": "make editable",
|
||||
"buttonText": "Button Text",
|
||||
"buttonPosition": "Button Position",
|
||||
"remove": "Remove",
|
||||
"addButton": "+ Add button",
|
||||
"addColumn": "+ Add column",
|
||||
"noActionMessage": "This table doesn't have any action buttons"
|
||||
},
|
||||
"Button": {
|
||||
"displayName": "Button",
|
||||
"description": "Trigger actions: queries, alerts etc"
|
||||
},
|
||||
"Chart": {
|
||||
"displayName": "Chart",
|
||||
"description": "Display charts"
|
||||
},
|
||||
"Modal" : {
|
||||
"displayName": "Modal",
|
||||
"description": "Modal triggered by events"
|
||||
},
|
||||
"TextInput" : {
|
||||
"displayName": "Text Input",
|
||||
"description": "Text field for forms"
|
||||
},
|
||||
"NumberInput" :{
|
||||
"displayName": "Number Input",
|
||||
"description": "Number field for forms"
|
||||
},
|
||||
"PasswordInput":{
|
||||
"displayName": "Password Input",
|
||||
"description": "Password input field for forms"
|
||||
},
|
||||
"Datepicker" :{
|
||||
"displayName": "Date Picker",
|
||||
"description": "Select a date and time"
|
||||
},
|
||||
"Checkbox":{
|
||||
"displayName": "Checkbox",
|
||||
"description": "A single checkbox"
|
||||
},
|
||||
"Radio-button":{
|
||||
"displayName": "Radio Button",
|
||||
"description": "Radio buttons"
|
||||
},
|
||||
"ToggleSwitch":{
|
||||
"displayName": "Toggle Switch",
|
||||
"description": "Toggle Switch"
|
||||
},
|
||||
"Textarea":{
|
||||
"displayName": "Textarea",
|
||||
"description": "Text area form field"
|
||||
},
|
||||
"DateRangePicker":{
|
||||
"displayName": "Range Picker",
|
||||
"description": "Select a date range"
|
||||
},
|
||||
"Text":{
|
||||
"displayName": "Text",
|
||||
"description": "Display markdown or HTML"
|
||||
},
|
||||
"Image":{
|
||||
"displayName": "Image",
|
||||
"description": "Display an Image"
|
||||
},
|
||||
"Container":{
|
||||
"displayName": "Container",
|
||||
"description": "Wrapper for multiple components"
|
||||
},
|
||||
"Dropdown":{
|
||||
"displayName": "Dropdown",
|
||||
"description": "Select one value from options"
|
||||
},
|
||||
"Multiselect":{
|
||||
"displayName": "Multiselect",
|
||||
"description": "Select multiple values from options"
|
||||
},
|
||||
"RichTextEditor":{
|
||||
"displayName": "Text Editor",
|
||||
"description": "Rich text editor"
|
||||
},
|
||||
"Map":{
|
||||
"displayName": "Map",
|
||||
"description": "Display Google Maps"
|
||||
},
|
||||
"QrScanner":{
|
||||
"displayName": "QR Scanner",
|
||||
"description": "Scan QR codes and hold its data"
|
||||
},
|
||||
"StarRating":{
|
||||
"displayName": "Rating",
|
||||
"description": "Star rating"
|
||||
},
|
||||
"Divider":{
|
||||
"displayName": "Divider",
|
||||
"description": "Separator between components"
|
||||
},
|
||||
"FilePicker":{
|
||||
"displayName": "File Picker",
|
||||
"description": "File Picker"
|
||||
},
|
||||
"Calendar":{
|
||||
"displayName": "Calendar",
|
||||
"description": "Calendar"
|
||||
},
|
||||
"Iframe":{
|
||||
"displayName": "Iframe",
|
||||
"description": "Display an Iframe"
|
||||
},
|
||||
"CodeEditor":{
|
||||
"displayName": "Code Editor",
|
||||
"description": "Code Editor"
|
||||
},
|
||||
"Tabs":{
|
||||
"displayName": "Tabs",
|
||||
"description": "Tabs component"
|
||||
},
|
||||
"Timer":{
|
||||
"displayName": "Timer",
|
||||
"description": "timer"
|
||||
},
|
||||
"Listview":{
|
||||
"displayName": "List View",
|
||||
"description": "Wrapper for multiple components"
|
||||
},
|
||||
"Tags":{
|
||||
"displayName": "Tags",
|
||||
"description": "Content can be shown as tags"
|
||||
},
|
||||
"Pagination":{
|
||||
"displayName": "Pagination",
|
||||
"description": "Pagination "
|
||||
},
|
||||
"CircularProgressbar":{
|
||||
"displayName": "Circular Progressbar",
|
||||
"description": "Show the progress using circular progressbar"
|
||||
},
|
||||
"Spinner":{
|
||||
"displayName": "Spinner",
|
||||
"description": "Spinner can be used to display loading status"
|
||||
},
|
||||
"Statistics":{
|
||||
"displayName": "Statistics",
|
||||
"description": "Statistics can be used to display different statistical information"
|
||||
},
|
||||
"RangeSlider":{
|
||||
"displayName": "Range Slider",
|
||||
"description": "Can be used to show slider with a range"
|
||||
},
|
||||
"Timeline":{
|
||||
"displayName": "Timeline",
|
||||
"description": "Visual representation of a sequence of events"
|
||||
},
|
||||
"SvgImage":{
|
||||
"displayName": "Svg Image",
|
||||
"description": "Svg image"
|
||||
},
|
||||
"Html":{
|
||||
"displayName": "HTML Viewer",
|
||||
"description": "HTML Viewer"
|
||||
},
|
||||
"VerticalDivider":{
|
||||
"displayName": "Vertical Divider",
|
||||
"description": "Vertical Separator between components"
|
||||
},
|
||||
"CustomComponent":{
|
||||
"displayName": "Custom Component",
|
||||
"description": "Add your custom react component"
|
||||
},
|
||||
"ButtonGroup":{
|
||||
"displayName": "Button Group",
|
||||
"description": "ButtonGroup"
|
||||
},
|
||||
"PDF":{
|
||||
"displayName": "PDF",
|
||||
"description": "Embed PDF file"
|
||||
},
|
||||
"Steps":{
|
||||
"displayName": "Steps",
|
||||
"description": "Steps"
|
||||
},
|
||||
"KanbanBoard":{
|
||||
"displayName": "Kanban Board",
|
||||
"description": "Kanban Board"
|
||||
},
|
||||
"ColorPicker":{
|
||||
"displayName": "Color Picker",
|
||||
"description": "Color Picker Pallete"
|
||||
},
|
||||
"TreeSelect":{
|
||||
"displayName": "Tree Select",
|
||||
"description": "Select values from a tree view"
|
||||
}
|
||||
},
|
||||
"leftSidebar":{
|
||||
"Inspector":{
|
||||
"text":"Inspector",
|
||||
"tip" : "Inspector"
|
||||
},
|
||||
"Sources" :{
|
||||
"text":"Sources",
|
||||
"tip" : "Add or edit datasources",
|
||||
"dataSources":"Data sources",
|
||||
"addDataSource":"+ add data source"
|
||||
},
|
||||
"Debugger":{
|
||||
"text":"Debugger",
|
||||
"tip" : "Debugger",
|
||||
"errors":"Errors",
|
||||
"noErrors":"No errors found",
|
||||
"clear":"clear"
|
||||
},
|
||||
"Comments":{
|
||||
"text":"Comments",
|
||||
"tip" : "toggle comments",
|
||||
"commentBody":"There are no comments to display",
|
||||
"typeComment":"Type your comment here"
|
||||
},
|
||||
"Settings":{
|
||||
"text":"Settings",
|
||||
"tip" : "Global Settings",
|
||||
"hideHeader": "Hide header for launched apps",
|
||||
"maintenanceMode":"Maintenance mode",
|
||||
"maxWidthOfCanvas":"Max width of canvas",
|
||||
"maxHeightOfCanvas" :"Max height of canvas",
|
||||
"backgroundColorOfCanvas":"Background color of canvas"
|
||||
|
||||
},
|
||||
"Back":{
|
||||
"text":"Back",
|
||||
"tip" : "Back to Home"
|
||||
}
|
||||
}
|
||||
}
|
||||
921
frontend/assets/translations/fr.json
Normal file
921
frontend/assets/translations/fr.json
Normal file
|
|
@ -0,0 +1,921 @@
|
|||
{
|
||||
"globals":{
|
||||
"readDocumentation":"Read documentation",
|
||||
"cancel":"Cancel",
|
||||
"save":"Save",
|
||||
"back":"Back",
|
||||
"edit":"Edit",
|
||||
"search":"Search",
|
||||
"update":"Update",
|
||||
"delete":"Delete",
|
||||
"add":"Add",
|
||||
"view":"View",
|
||||
"create":"Create",
|
||||
"enabled":"Enabled",
|
||||
"disabled":"Disabled",
|
||||
"yes":"Yes",
|
||||
"submit":"Submit",
|
||||
"select":"Select",
|
||||
"environmentVar":"Environment Variables",
|
||||
"saving":"Saving...",
|
||||
"saveDatasource":"Save data source",
|
||||
"authorize":"Authorize",
|
||||
"connect":"Connect",
|
||||
"reconnect":"Reconnect",
|
||||
"components":"components",
|
||||
"send":"Send",
|
||||
"noConnection":"could not connect",
|
||||
"connectionVerifeid":"connection verified",
|
||||
"left":"Left",
|
||||
"center":"Center",
|
||||
"right":"Right",
|
||||
"justified":"Justified",
|
||||
"host":"Host",
|
||||
"operation":"Operation",
|
||||
"header":"HEADER",
|
||||
"path":"PATH",
|
||||
"query":"QUERY",
|
||||
"requestBody":"REQUEST BODY"
|
||||
},
|
||||
"errorBoundary":"Something went wrong.",
|
||||
"viewer":"Sorry!. This app is under maintenance",
|
||||
"app":{
|
||||
"updateAvailable":"Update available",
|
||||
"newVersionReleased":"A new version of ToolJet has been released.",
|
||||
"readReleaseNotes":"Read release notes & update",
|
||||
"skipVersion":"Skip this version"
|
||||
},
|
||||
"stripe":"Please wait whle we load the OpenAPI specification for Stripe.",
|
||||
"openApi":{
|
||||
"noValidOpenApi":"Valid OpenAPI Spec is not available!.",
|
||||
"selectHost":"Select a host",
|
||||
"selectOperation":"Select an operation"
|
||||
},
|
||||
"slack":{
|
||||
"authorize":"Authorize",
|
||||
"connectToolJetToSlack":"ToolJet can connect to Slack and list users, send messages, etc. Please select appropriate permission scopes.",
|
||||
"chatWrite":"chat:write",
|
||||
"listUsersAndSendMessage":"Your ToolJet app will be able to list users and send messages to users & channels.",
|
||||
"connectSlack":"Connect to Slack"
|
||||
},
|
||||
"googleSheets":{
|
||||
"readOnly":"Read only",
|
||||
"enableReadAndWrite":"If you want your ToolJet apps to modify your Google sheets, make sure to select read and write access",
|
||||
"readDataFromSheets":"Your ToolJet apps can only read data from Google sheets",
|
||||
"readWrite":"Read and write",
|
||||
"readModifySheets":"Your ToolJet apps can read data from sheets, modify sheets, and more.",
|
||||
"toGoogleSheets":"to Google Sheets"
|
||||
|
||||
},
|
||||
"profile": {
|
||||
"profileSettings": "Profile Settings"
|
||||
},
|
||||
"loginSignupPage":{
|
||||
"forgotPassword":"Forgot Password",
|
||||
"emailAddress":"Email address",
|
||||
"enterEmail":"Enter email",
|
||||
"resetPassword":"Reset Password",
|
||||
"dontHaveAccount":"Don't have account yet?",
|
||||
"signIn":"Sign in",
|
||||
"signUp":"Sign up",
|
||||
"createToolJetAccount":"Create a ToolJet account",
|
||||
"enterBusinessEmail":"Enter your business email",
|
||||
"alreadyHaveAnAccount":"Already have an account?",
|
||||
"password":"Password",
|
||||
"showPassword":"show password",
|
||||
"loginTo":"Login to",
|
||||
"yourAccount":"your account",
|
||||
"noLoginMethodsEnabled":"No login methods enabled for this workspace",
|
||||
"emailConfirmLink":"Please check your email for confirmation link",
|
||||
"newPassword":"New Password",
|
||||
"passwordConfirmation":"Password Confirmation"
|
||||
},
|
||||
"editor": {
|
||||
"preview": "Preview",
|
||||
"share":"Share",
|
||||
"shareModal": {
|
||||
"makeApplicationPublic":"Make application public ?",
|
||||
"shareableLink":"Get shareable link for this application",
|
||||
"copy":"copy",
|
||||
"embeddableLink":"Get embeddable link for this application",
|
||||
"manageUsers":"Manage Users"
|
||||
},
|
||||
"appVersionManager":{
|
||||
"version":"Version",
|
||||
"currentlyReleased":"Currently Released",
|
||||
"createVersion":"Create Version",
|
||||
"versionName":"Version Name",
|
||||
"createVersionFrom":"Create version from",
|
||||
"save":"Save",
|
||||
"create":"Create Version",
|
||||
"editVersion": "Edit Version",
|
||||
"deleteVersion":"Do you really want to delete this version?",
|
||||
"enterVersionName":"Enter version name",
|
||||
"versionAlreadyReleased":"Version already released. Kindly create a new version or switch to a different version to continue making changes."
|
||||
},
|
||||
"queries":"Queries",
|
||||
"inspectComponent":"Please select a component to inspect",
|
||||
"release":"Release",
|
||||
"searchQueries":"Search queries",
|
||||
"createQuery":"Create query",
|
||||
"queryManager":{
|
||||
"general":"General",
|
||||
"advanced":"Advanced",
|
||||
"preview":"Preview",
|
||||
"Save":"Save",
|
||||
"selectDatasource":"Select Datasource",
|
||||
"addDatasource":"Add datasource",
|
||||
"dataSourceManager":{
|
||||
"toast":{
|
||||
"success": {
|
||||
"dataSourceAdded": "Datasource Added",
|
||||
"dataSourceSaved": "Datasource Saved"
|
||||
},
|
||||
"error" :{
|
||||
"noEmptyDsName" :"The name of datasource should not be empty"
|
||||
}
|
||||
},
|
||||
"suggestDataSource":"Suggest Datasource",
|
||||
"suggestAnIntegration":"Suggest an integration",
|
||||
"whatLookingFor":"Tell us what you were looking for?",
|
||||
"noResultFound":"Don't see what you were looking for?",
|
||||
"suggest":"Suggest",
|
||||
"addNewDataSource":"Add new datasource",
|
||||
"whiteListIP":"Please white-list our IP address if the data source is not publicly accessible",
|
||||
"copied":"Copied",
|
||||
"copy":"Copy",
|
||||
"saving":"Saving",
|
||||
"noResultsFor":"No results for",
|
||||
"noteTaken":"Thank you, we've taken a note of that!",
|
||||
"goToAllDatasources":"Go to all Datasource",
|
||||
"send":"Send"
|
||||
},
|
||||
"runQueryOnPageLoad":"Run this query on page load?",
|
||||
"confirmBeforeQueryRun":"Request confirmation before running query?",
|
||||
"notificationOnSuccess":"Show notification on success?",
|
||||
"successMessage":"Success Message",
|
||||
"queryRanSuccessfully":"Query ran successfully",
|
||||
"notificationDuration":"Notification duration (s)",
|
||||
"events":"Events",
|
||||
"transformation":{
|
||||
"transformationToolTip":"Transformations can be used to transform the results of queries. All the app variables are accessible from transformers and supports JS libraries such as Lodash & Moment.",
|
||||
"transformations":"Transformations"
|
||||
}
|
||||
},
|
||||
"inspector":{
|
||||
"eventManager":{
|
||||
"event": "Event",
|
||||
"action": "Action",
|
||||
"actionOptions": "Action Options",
|
||||
"message": "Message",
|
||||
"alertType": "Alert Type",
|
||||
"url": "URL",
|
||||
"modal": "Modal",
|
||||
"text": "Text",
|
||||
"query": "Query",
|
||||
"key": "Key",
|
||||
"value": "Value",
|
||||
"type": "Type",
|
||||
"fileName": "File name",
|
||||
"data": "Data",
|
||||
"table": "Table",
|
||||
"pageIndex": "Page index",
|
||||
"component": "Component",
|
||||
"addHandler": "+ Add handler",
|
||||
"addEventHandler": "+ Add event handler",
|
||||
"emptyMessage": "This {{componentName}} doesn't have any event handlers"
|
||||
}
|
||||
}
|
||||
},
|
||||
"header":{
|
||||
"darkModeToggle":{
|
||||
"activateLightMode" :"Activate light mode",
|
||||
"activateDarkMode":"Activate dark mode"
|
||||
},
|
||||
"languageSelection":{
|
||||
"changeLanguage":"Change language",
|
||||
"searchLanguage":"Search language"
|
||||
},
|
||||
"notificationCenter":{
|
||||
"notifications":"Notifications",
|
||||
"markAllAs":"Mark all as",
|
||||
"read":"read",
|
||||
"un":"un",
|
||||
"youDontHaveany":"You don't have any",
|
||||
"youAreCaughtUp":"You're all caught up!",
|
||||
"view":"View"
|
||||
},
|
||||
"organization":{
|
||||
"loadOrganizations":"Load Organizations",
|
||||
"createWorkspace":"Create workspace",
|
||||
"workspaceName":"workspace name",
|
||||
"editWorkspace":"Edit workspace",
|
||||
"menus": {
|
||||
"addWorkspace":"Add workspace",
|
||||
"menusList":{
|
||||
"manageUsers":"Manage Users",
|
||||
"manageGroups":"Manage Groups",
|
||||
"manageSso":"Manage SSO",
|
||||
"manageEnv":"Manage Environment Variables"
|
||||
},
|
||||
"manageUsers":{
|
||||
"usersAndPermission":"Users & Permissions",
|
||||
"inviteNewUser":"Invite new user",
|
||||
"name":"NAME",
|
||||
"email":"EMAIL",
|
||||
"status":"STATUS",
|
||||
"archive":"Archive",
|
||||
"unarchive":"Unarchive",
|
||||
"addNewUser":"Add new user",
|
||||
"emailAddress":"Email address",
|
||||
"createUser":"Create User",
|
||||
"enterFirstName":"Enter First Name",
|
||||
"enterLastName":"Enter Last Name",
|
||||
"enterEmail":"Enter Email"
|
||||
},
|
||||
"manageGroups":{
|
||||
"permissions":{
|
||||
"userGroups":"User Groups",
|
||||
"createNewGroup":"Create new group",
|
||||
"udpateGroup":"Update group",
|
||||
"addNewGroup":"Add new group",
|
||||
"enterName":"Enter Name",
|
||||
"createGroup":"Create Group",
|
||||
"name":"Name"
|
||||
},
|
||||
"permissionResources":{
|
||||
"userGroup":"User group",
|
||||
"apps":"Apps",
|
||||
"users":"User",
|
||||
"permissions":"Permissions",
|
||||
"addAppsToGroup":"Select apps to add to the group",
|
||||
"name":"name",
|
||||
"addUsersToGroup":"Select users to add to the group",
|
||||
"email":"email",
|
||||
"resource":"resource",
|
||||
"createUpdateDelete":"Create/Update/Delete",
|
||||
"folder":"Folder"
|
||||
}
|
||||
},
|
||||
"manageSSO":{
|
||||
"manageSso":"Manage SSO",
|
||||
"generalSettings":{
|
||||
"title":"General Settings",
|
||||
"enableSignup":"Enable Signup",
|
||||
"newAccountWillBeCreated":"New account will be created for user's first time SSO sign in",
|
||||
"allowedDomains":"Allowed domains",
|
||||
"enterDomains":"Enter Domains",
|
||||
"supportMultiDomains":"Support multiple domains. Enter domain names separated by comma. example: tooljet.com,tooljet.io,yourorganization.com",
|
||||
"loginUrl":"Login URL",
|
||||
"workspaceLogin":"Use this URL to login directly to this workspace",
|
||||
"allowDefaultSso":"Allow default SSO",
|
||||
"ssoAuth":"Allow users to authenticate via default SSO. Default SSO configurations can be overridden by workspace level SSO."
|
||||
},
|
||||
"google":{
|
||||
"title":"Google",
|
||||
"enabled":"Enabled",
|
||||
"disabled":"Disabled",
|
||||
"clientId":"Client Id",
|
||||
"enterClientId":"Enter Client Id",
|
||||
"redirectUrl":"Redirect URL"
|
||||
},
|
||||
"github":{
|
||||
"title":"Github",
|
||||
"hostName":"Host Name",
|
||||
"enterHostName":"Enter Host Name",
|
||||
"requiredGithub":"Required if GitHub is self hosted",
|
||||
"clientId":"Client Id",
|
||||
"enterClientId":"Enter Client Id",
|
||||
"clientSecret":"Client Secret",
|
||||
"enterClientSecret":"Enter Client Secret",
|
||||
"encrypted":"Encrypted",
|
||||
"redirectUrl":"Redirect URL"
|
||||
},
|
||||
"passwordLogin":"Password Login",
|
||||
"environmentVar" : {
|
||||
"noEnvConfig":"You haven't configured any environment variables, press the 'Add new variable' button to create one",
|
||||
"envWillBeDeleted":"Variable will be deleted, do you want to continue?",
|
||||
"addNewVariable":"Add new variable",
|
||||
"variableForm":{
|
||||
"addNewVariable":"Add new variable",
|
||||
"updatevariable":"Update variable",
|
||||
"name":"Name",
|
||||
"value":"Value",
|
||||
"enterVariableName":"Enter Variable Name",
|
||||
"enterValue":"Enter Value",
|
||||
"type":"Type",
|
||||
"enableEncryption":"Enable encryption",
|
||||
"addVariable":"Add variable"
|
||||
},
|
||||
"variableTable":{
|
||||
"name":"name",
|
||||
"value":"value",
|
||||
"type":"type",
|
||||
"secret":"secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profileSettingPage":{
|
||||
"profileSettings":"Profile Settings",
|
||||
"firstName":"First name",
|
||||
"lastName":"Last name",
|
||||
"enterFirstName":"Enter First Name",
|
||||
"enterLastName":"Enter Last Name",
|
||||
"email":"Email",
|
||||
"avatar":"Avatar",
|
||||
"update":"Update",
|
||||
"profile":"Profile",
|
||||
"changePassword":"Change password",
|
||||
"currentPassword":"Current password",
|
||||
"newPassword":"New password",
|
||||
"confirmNewPassword":"Confirm new password",
|
||||
"enterCurrentPassword":"Enter current password",
|
||||
"enterNewPassword":"Enter new password"
|
||||
},
|
||||
"profile":"Profile",
|
||||
"logout":"Logout"
|
||||
},
|
||||
"homePage":{
|
||||
"appCard":{
|
||||
"changeIcon":"Change Icon",
|
||||
"addToFolder":"Add to folder",
|
||||
"move":"Move",
|
||||
"to":"to",
|
||||
"selectFolder":"Select folder",
|
||||
"deleteApp":"Delete app",
|
||||
"exportApp":"Export app",
|
||||
"cloneApp":"Clone app",
|
||||
"launch":"Launch",
|
||||
"maintenance":"Maintenance",
|
||||
"noDeployedVersion":"App does not have a deployed version",
|
||||
"openInAppViewer":"Open in app viewer",
|
||||
"removeFromFolder":"Remove from folder"
|
||||
},
|
||||
"blankPage":{
|
||||
"welcomeToToolJet":"Welcome to ToolJet!",
|
||||
"getStartedCreateNewApp":"You can get started by creating a new application or by creating an application using a template in ToolJet Library.",
|
||||
"importApplication":"Import an application"
|
||||
},
|
||||
"foldersSection":{
|
||||
"allApplications":"All applications",
|
||||
"folders":"Folders",
|
||||
"createNewFolder":"+ Create new folder",
|
||||
"noFolders":"You haven't created any folders. Use folders to organize your apps",
|
||||
"createFolder":"Create folder",
|
||||
"updateFolder":"Update folder",
|
||||
"editFolder":"Edit folder",
|
||||
"deleteFolder":"Delete folder",
|
||||
"folderName":"Folder name",
|
||||
"wishToDeleteFolder":"Are you sure you want to delete the folder? Apps within the folder will not be deleted."
|
||||
|
||||
},
|
||||
"header":{
|
||||
"createNewApplication":"Create new application",
|
||||
"import":"Import",
|
||||
"chooseFromTemplate":"Choose from template"
|
||||
},
|
||||
"pagination":{
|
||||
"showing":"Showing",
|
||||
"of":"of",
|
||||
"to":"to"
|
||||
},
|
||||
"noApplicationFound":"No Applications found",
|
||||
"thisFolderIsEmpty":"This folder is empty",
|
||||
"deleteAppAndData":"The app and the associated data will be permanently deleted, do you want to continue?",
|
||||
"removeAppFromFolder":"The app will be removed from this folder, do you want to continue?",
|
||||
"change":"Change",
|
||||
"templateCard":{
|
||||
"use":"Use",
|
||||
"preview":"Preview",
|
||||
"leadGeneretion":"Lead generetion"
|
||||
},
|
||||
"templateLibraryModal":{
|
||||
"select":"Select template",
|
||||
"createAppfromTemplate":"Create application from template"
|
||||
}
|
||||
},
|
||||
"confirmationPage":{
|
||||
"setupAccount":"Set up your account",
|
||||
"signupWithGoogle":"Sign up with Google",
|
||||
"signupWithGitHub":"Sign up with GitHub",
|
||||
"or":"OR",
|
||||
"firstName":"First name",
|
||||
"lastName":"Last name",
|
||||
"company":"Company",
|
||||
"role":"Role",
|
||||
"pleaseSelect":"Please select",
|
||||
"password":"Password",
|
||||
"confirmPassword":"Confirm Password",
|
||||
"clickAndAgree":"By clicking the button below, you agree to our",
|
||||
"termsAndConditions":"Terms and Conditions",
|
||||
"finishAccountSetup":"Finish account setup",
|
||||
"acceptInvite":"accept invite",
|
||||
"accountExists":"Already have an account?",
|
||||
"and":"and"
|
||||
|
||||
},
|
||||
"onBoarding":{
|
||||
"finishToolJetInstallation":"Finish ToolJet installation",
|
||||
"organization":"Organization",
|
||||
"name":"Name",
|
||||
"email":"Email",
|
||||
"receiveUpdatesFromToolJet":"You will receive updates from the ToolJet team ( 1-2 emails every month, we do not spam )",
|
||||
"finishSetup":"Finish setup",
|
||||
"skip":"Skip"
|
||||
},
|
||||
"redirectSso":{
|
||||
"upgradingTov1.13.0":"Upgrading to v1.13.0 and above.",
|
||||
"fromV1.13.0":"From v1.13.0 we have introduced",
|
||||
"multiWorkspace":"Multi-Workspace",
|
||||
"singleSignOnConfig":"The Single Sign-On related configurations are moved from environment variables to database. Please refer this",
|
||||
"link":"Link",
|
||||
"toConfigureSSO":"to configure SSO.",
|
||||
"haveGoogleGithubSSo":"If you have Google or GitHub SSO configurations before upgrade and disabled Multi-Workspace, then theSSO configurations will be migrated while upgrade but you have to re-configure the redirect URL in the SSO provider side. Redirect URLs for each SSO are given below.",
|
||||
"isMultiWorkspaceEnabled":"If you have enabled Multi-Workspace, then the SSO configurations will not be migrated while upgrade so you have to re-configure the SSO under the respective workspace.",
|
||||
"youHaveEnabled":"You have Enabled",
|
||||
"setupSsoWorkspace":"Please login with password and you can setup sso using workspace",
|
||||
"manageSsoMenu":"Manage SSO menu.",
|
||||
"youHaveDisabled":"You have Disabled",
|
||||
"configureRedirectUrl":"Please configure redirect url in SSO provider side.",
|
||||
"google":"Google",
|
||||
"redirectUrl":"Redirect URL:",
|
||||
"gitHub":"GitHub"
|
||||
},
|
||||
"oAuth2": {
|
||||
"pleaseWait":"Please wait...",
|
||||
"authSuccess":"Auth successful, you can close this tab now.",
|
||||
"authFailed":"Auth failed"
|
||||
},
|
||||
"widgetManager": {
|
||||
"commonlyUsed": "commonly used",
|
||||
"layouts" : "layouts",
|
||||
"forms" : "forms",
|
||||
"intregrations" : "integrations",
|
||||
"others":"others",
|
||||
"noResults": "No results found",
|
||||
"tryAdjustingFilterMessage": "Try adjusting your search or filter to find what you're looking for.",
|
||||
"clearQuery": "clear query"
|
||||
},
|
||||
"widget":{
|
||||
"common":{
|
||||
"properties": "Properties",
|
||||
"events": "Events",
|
||||
"layout": "Layout",
|
||||
"styles": "Styles",
|
||||
"general": "General",
|
||||
"validation": "Validation",
|
||||
"documentation": "{{componentMeta}} documentation",
|
||||
"widgetNameEmptyError": "Widget name cannot be empty",
|
||||
"componentNameExistsError": "Component name already exists",
|
||||
"invalidWidgetName": "Invalid widget name. Should be unique and only include letters, numbers and underscore."
|
||||
},
|
||||
"commonProperties": {
|
||||
"visibility": "Visibility",
|
||||
"disable": "Disable",
|
||||
"borderRadius": "Border Radius",
|
||||
"boxShadow": "Box Shadow",
|
||||
"tooltip": "Tooltip",
|
||||
"showOnDesktop": "Show on desktop",
|
||||
"showOnMobile": "Show on mobile",
|
||||
"showLoadingState": "Show loading state",
|
||||
"backgroundColor": "Background Color",
|
||||
"textColor": "Text color",
|
||||
"loaderColor": "Loader color",
|
||||
"defaultValue": "Default Value",
|
||||
"placeholder": "Placeholder",
|
||||
"label": "Label",
|
||||
"title": "Title",
|
||||
"code": "Code",
|
||||
"data": "Data",
|
||||
"tableData": "Table data",
|
||||
"tableColumns": "Table columns",
|
||||
"searverSideSearch": "Server-side search",
|
||||
"loadingState": "Loading State",
|
||||
"serverSidePagination": "Server-side pagination",
|
||||
"clientSidePagination": "Client-side pagination",
|
||||
"serverSideSearch": "Server-side search",
|
||||
"showSearchBox": "Show search box",
|
||||
"showDownloadButton": "Show download button",
|
||||
"showFilterButton": "Show filter button",
|
||||
"showBulkUpdateActions": "Show bulk update actions",
|
||||
"bulkSelection": "Bulk selection",
|
||||
"highlightSelectedRow": "Highlight selected row",
|
||||
"actionButtonRadius": "Action Button Radius",
|
||||
"tableType": "Table type",
|
||||
"cellSize": "Cell size",
|
||||
"setPage": "Set page",
|
||||
"page": "Page",
|
||||
"buttonText": "Button Text",
|
||||
"click": "Click",
|
||||
"setText": "Set text",
|
||||
"text": "Text",
|
||||
"markerColor": "Marker color",
|
||||
"showAxes": "Show axes",
|
||||
"showGridLines": "Show grid lines",
|
||||
"chartType": "Chart type",
|
||||
"jsonDescription": "Json Description",
|
||||
"usePlotlyJsonSchema": "Use Plotly JSON schema",
|
||||
"padding": "Padding",
|
||||
"hideTitleBar": "Hide title bar",
|
||||
"hideCloseButton": "Hide close button",
|
||||
"hideOnEscape": "Hide on escape",
|
||||
"modalSize": "Modal size",
|
||||
"open": "Open",
|
||||
"close": "Close",
|
||||
"regex": "Regex",
|
||||
"minLength": "Min length",
|
||||
"maxLength": "Max length",
|
||||
"customValidation": "Custom validation",
|
||||
"clear": "Clear",
|
||||
"minimumValue": "Minimum value",
|
||||
"maximumValue": "Maximum value",
|
||||
"format": "Format",
|
||||
"enableTimeSelection": "Enable time selection?",
|
||||
"enableDateSelection": "Enable date selection?",
|
||||
"disabledDates": "Disabled dates",
|
||||
"setChecked": "Set checked",
|
||||
"status": "status",
|
||||
"defaultStatus": "Default Status",
|
||||
"checkboxColor": "Checkbox Color",
|
||||
"optionValues": "Option values",
|
||||
"optionLabels": "Option labels",
|
||||
"activeColor": "Active Color",
|
||||
"selectOption": "Select option",
|
||||
"option": "Option",
|
||||
"toggleSwitchColor": "Toggle Switch Color",
|
||||
"defaultStartDate": "Default start date",
|
||||
"defaultEndDate": "Default end date",
|
||||
"textSize": "Text Size",
|
||||
"alignText": "Align Text",
|
||||
"url": "URL",
|
||||
"alternativeText": "Alternative text",
|
||||
"zoomButton": "Zoom button",
|
||||
"borderType": "Border type",
|
||||
"imageFit": "Image fit",
|
||||
"optionsLoadingState": "Options loading state",
|
||||
"selectedTextColor": "Selected Text Color",
|
||||
"select": "Select",
|
||||
"deselectOption": "Deselect Option",
|
||||
"clearSelections": "Clear selections",
|
||||
"enableSelectAllOption": "Enable select All option",
|
||||
"initialLocation": "Initial location",
|
||||
"defaultMarkers": "Default markers",
|
||||
"addNewMarkers": "Add new markers",
|
||||
"searchForPlaces": "Search for places",
|
||||
"setLocation": "Set Location",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"numberOfStars": "Number of stars",
|
||||
"defaultNoOfSelectedStars": "Default no of selected stars",
|
||||
"enableHalfStar": "Enable half star",
|
||||
"tooltips": "Tooltips",
|
||||
"starColor": "Star Color",
|
||||
"labelColor": "Label Color",
|
||||
"dividerColor": "Divider Color",
|
||||
"clearFiles": "Clear Files",
|
||||
"instructionText": "Instruction Text",
|
||||
"useDropZone": "Use Drop zone",
|
||||
"useFilePicker": "Use File Picker",
|
||||
"pickMultipleFiles": "Pick multiple files",
|
||||
"maxFileCount": "Max file count",
|
||||
"acceptFileTypes": "Accept file types",
|
||||
"maxSizeLimitBytes": "Max size limit (Bytes)",
|
||||
"minSizeLimitBytes": "Min size limit (Bytes)",
|
||||
"parseContent": "Parse content",
|
||||
"fileType": "File type",
|
||||
"dateFormat": "Date format",
|
||||
"defaultDate": "Default date",
|
||||
"events": "Events",
|
||||
"resources": "Resources",
|
||||
"defaultView": "Default view",
|
||||
"startTimeOnWeekAndDayView": "Start time on week and day view",
|
||||
"endTimeOnWeekAndDayView": "End time on week and day view",
|
||||
"showToolbar": "Show toolbar",
|
||||
"showViewSwitcher": "Show view switcher",
|
||||
"highlightToday": "Highlight today",
|
||||
"showPopoverWhenEventIsClicked": "Show popover when event is clicked",
|
||||
"cellSizeInViewsClassifiedByResource": "Cell size in views classified by resource",
|
||||
"headerDateFormatOnWeekView": "Header date format on week view",
|
||||
"showLineNumber": "Show Line Number",
|
||||
"mode": "Mode",
|
||||
"tabs": "Tabs",
|
||||
"defaultTab": "Default tab",
|
||||
"hideTabs": "Hide Tabs",
|
||||
"highlightColor": "Highlight Color",
|
||||
"tabWidth": "Tab width",
|
||||
"setCurrentTab": "Set current tab",
|
||||
"id": "Id",
|
||||
"timerType": "Timer type",
|
||||
"listData": "List data",
|
||||
"rowHeight": "Row height",
|
||||
"showBottomBorder": "Show bottom border",
|
||||
"tags": "Tags",
|
||||
"numberOfPages": "Number of pages",
|
||||
"defaultPageIndex": "Default page index",
|
||||
"progress": "Progress",
|
||||
"color": "Color",
|
||||
"strokeWidth": "Stroke Width",
|
||||
"counterClockwise": "Counter Clockwise",
|
||||
"circleRatio": "Circle Ratio",
|
||||
"colour": "Colour",
|
||||
"size": "Size",
|
||||
"primaryValueLabel": "Primary value label",
|
||||
"primaryValue": "Primary value",
|
||||
"hideSecondaryValue": "Hide secondary value",
|
||||
"secondaryValueLabel": "Secondary value label",
|
||||
"secondaryValue": "Secondary value",
|
||||
"secondarySignDisplay": "Secondary sign display",
|
||||
"primaryLabelColour": "Primary Label Colour",
|
||||
"primaryTextColour": "Primary Text Colour",
|
||||
"secondaryLabelColour": "Secondary Label Colour",
|
||||
"secondaryTextColour": "Secondary Text Colour",
|
||||
"min": "Min",
|
||||
"max": "Max",
|
||||
"value": "Value",
|
||||
"twoHandles": "Two handles",
|
||||
"lineColor": "Line color",
|
||||
"handleColor": "Handle color",
|
||||
"trackColor": "Track color",
|
||||
"timelineData": "Timeline data",
|
||||
"hideDate": "Hide Date",
|
||||
"svgData": "Svg data",
|
||||
"rawHtml": "Raw HTML",
|
||||
"values": "values",
|
||||
"labels": "Labels",
|
||||
"defaultSelected": "Default selected",
|
||||
"enableMutipleSelection": "Enable mutiple selection",
|
||||
"selectedTextColour": "Selected text colour",
|
||||
"selectedBackgroundColor": "Selected background color",
|
||||
"fileUrl": "File URL",
|
||||
"scalePageToWidth": "Scale page to width",
|
||||
"showPageControls": "Show page controls",
|
||||
"steps": "Steps",
|
||||
"currentStep": "Current step",
|
||||
"stepsSelectable": "Steps selectable",
|
||||
"theme": "Theme",
|
||||
"columns": "Columns",
|
||||
"cardData": "Card Data",
|
||||
"enableAddCard": "Enable Add Card",
|
||||
"width": "Width",
|
||||
"minWidth": "Min Width",
|
||||
"accentColor": "Accent color",
|
||||
"defaultColor": "Default Color",
|
||||
"setColor": "Set Color",
|
||||
"structure": "Structure",
|
||||
"checkedValues": "Checked Values",
|
||||
"expandedValues": "Expanded Values"
|
||||
},
|
||||
"Table": {
|
||||
"displayName": "Table",
|
||||
"description": "Display paginated tabular data",
|
||||
"columnType": "Column type",
|
||||
"columnName": "Column name",
|
||||
"overflow": "Overflow",
|
||||
"key": "key",
|
||||
"textColor": "Text color",
|
||||
"validation": "Validation",
|
||||
"regex": "Regex",
|
||||
"minLength": "Min length",
|
||||
"maxLength": "Max length",
|
||||
"customRule": "Custom rule",
|
||||
"values": "Values",
|
||||
"labels": "Labels",
|
||||
"cellBgColor": "Cell Background Color",
|
||||
"dateDisplayformat": "Date Display Format",
|
||||
"dateParseformat": "Date Parse Format",
|
||||
"showTime": "show time",
|
||||
"makeEditable": "make editable",
|
||||
"buttonText": "Button Text",
|
||||
"buttonPosition": "Button Position",
|
||||
"remove": "Remove",
|
||||
"addButton": "+ Add button",
|
||||
"addColumn": "+ Add column",
|
||||
"noActionMessage": "This table doesn't have any action buttons"
|
||||
},
|
||||
"Button": {
|
||||
"displayName": "Button",
|
||||
"description": "Trigger actions: queries, alerts etc"
|
||||
},
|
||||
"Chart": {
|
||||
"displayName": "Chart",
|
||||
"description": "Display charts"
|
||||
},
|
||||
"Modal" : {
|
||||
"displayName": "Modal",
|
||||
"description": "Modal triggered by events"
|
||||
},
|
||||
"TextInput" : {
|
||||
"displayName": "Text Input",
|
||||
"description": "Text field for forms"
|
||||
},
|
||||
"NumberInput" :{
|
||||
"displayName": "Number Input",
|
||||
"description": "Number field for forms"
|
||||
},
|
||||
"PasswordInput":{
|
||||
"displayName": "Password Input",
|
||||
"description": "Password input field for forms"
|
||||
},
|
||||
"Datepicker" :{
|
||||
"displayName": "Date Picker",
|
||||
"description": "Select a date and time"
|
||||
},
|
||||
"Checkbox":{
|
||||
"displayName": "Checkbox",
|
||||
"description": "A single checkbox"
|
||||
},
|
||||
"Radio-button":{
|
||||
"displayName": "Radio Button",
|
||||
"description": "Radio buttons"
|
||||
},
|
||||
"ToggleSwitch":{
|
||||
"displayName": "Toggle Switch",
|
||||
"description": "Toggle Switch"
|
||||
},
|
||||
"Textarea":{
|
||||
"displayName": "Textarea",
|
||||
"description": "Text area form field"
|
||||
},
|
||||
"DateRangePicker":{
|
||||
"displayName": "Range Picker",
|
||||
"description": "Select a date range"
|
||||
},
|
||||
"Text":{
|
||||
"displayName": "Text",
|
||||
"description": "Display markdown or HTML"
|
||||
},
|
||||
"Image":{
|
||||
"displayName": "Image",
|
||||
"description": "Display an Image"
|
||||
},
|
||||
"Container":{
|
||||
"displayName": "Container",
|
||||
"description": "Wrapper for multiple components"
|
||||
},
|
||||
"Dropdown":{
|
||||
"displayName": "Dropdown",
|
||||
"description": "Select one value from options"
|
||||
},
|
||||
"Multiselect":{
|
||||
"displayName": "Multiselect",
|
||||
"description": "Select multiple values from options"
|
||||
},
|
||||
"RichTextEditor":{
|
||||
"displayName": "Text Editor",
|
||||
"description": "Rich text editor"
|
||||
},
|
||||
"Map":{
|
||||
"displayName": "Map",
|
||||
"description": "Display Google Maps"
|
||||
},
|
||||
"QrScanner":{
|
||||
"displayName": "QR Scanner",
|
||||
"description": "Scan QR codes and hold its data"
|
||||
},
|
||||
"StarRating":{
|
||||
"displayName": "Rating",
|
||||
"description": "Star rating"
|
||||
},
|
||||
"Divider":{
|
||||
"displayName": "Divider",
|
||||
"description": "Separator between components"
|
||||
},
|
||||
"FilePicker":{
|
||||
"displayName": "File Picker",
|
||||
"description": "File Picker"
|
||||
},
|
||||
"Calendar":{
|
||||
"displayName": "Calendar",
|
||||
"description": "Calendar"
|
||||
},
|
||||
"Iframe":{
|
||||
"displayName": "Iframe",
|
||||
"description": "Display an Iframe"
|
||||
},
|
||||
"CodeEditor":{
|
||||
"displayName": "Code Editor",
|
||||
"description": "Code Editor"
|
||||
},
|
||||
"Tabs":{
|
||||
"displayName": "Tabs",
|
||||
"description": "Tabs component"
|
||||
},
|
||||
"Timer":{
|
||||
"displayName": "Timer",
|
||||
"description": "timer"
|
||||
},
|
||||
"Listview":{
|
||||
"displayName": "List View",
|
||||
"description": "Wrapper for multiple components"
|
||||
},
|
||||
"Tags":{
|
||||
"displayName": "Tags",
|
||||
"description": "Content can be shown as tags"
|
||||
},
|
||||
"Pagination":{
|
||||
"displayName": "Pagination",
|
||||
"description": "Pagination "
|
||||
},
|
||||
"CircularProgressbar":{
|
||||
"displayName": "Circular Progressbar",
|
||||
"description": "Show the progress using circular progressbar"
|
||||
},
|
||||
"Spinner":{
|
||||
"displayName": "Spinner",
|
||||
"description": "Spinner can be used to display loading status"
|
||||
},
|
||||
"Statistics":{
|
||||
"displayName": "Statistics",
|
||||
"description": "Statistics can be used to display different statistical information"
|
||||
},
|
||||
"RangeSlider":{
|
||||
"displayName": "Range Slider",
|
||||
"description": "Can be used to show slider with a range"
|
||||
},
|
||||
"Timeline":{
|
||||
"displayName": "Timeline",
|
||||
"description": "Visual representation of a sequence of events"
|
||||
},
|
||||
"SvgImage":{
|
||||
"displayName": "Svg Image",
|
||||
"description": "Svg image"
|
||||
},
|
||||
"Html":{
|
||||
"displayName": "HTML Viewer",
|
||||
"description": "HTML Viewer"
|
||||
},
|
||||
"VerticalDivider":{
|
||||
"displayName": "Vertical Divider",
|
||||
"description": "Vertical Separator between components"
|
||||
},
|
||||
"CustomComponent":{
|
||||
"displayName": "Custom Component",
|
||||
"description": "Add your custom react component"
|
||||
},
|
||||
"ButtonGroup":{
|
||||
"displayName": "Button Group",
|
||||
"description": "ButtonGroup"
|
||||
},
|
||||
"PDF":{
|
||||
"displayName": "PDF",
|
||||
"description": "Embed PDF file"
|
||||
},
|
||||
"Steps":{
|
||||
"displayName": "Steps",
|
||||
"description": "Steps"
|
||||
},
|
||||
"KanbanBoard":{
|
||||
"displayName": "Kanban Board",
|
||||
"description": "Kanban Board"
|
||||
},
|
||||
"ColorPicker":{
|
||||
"displayName": "Color Picker",
|
||||
"description": "Color Picker Pallete"
|
||||
},
|
||||
"TreeSelect":{
|
||||
"displayName": "Tree Select",
|
||||
"description": "Select values from a tree view"
|
||||
}
|
||||
},
|
||||
"leftSidebar":{
|
||||
"Inspector":{
|
||||
"text":"Inspector",
|
||||
"tip" : "Inspector"
|
||||
},
|
||||
"Sources" :{
|
||||
"text":"Sources",
|
||||
"tip" : "Add or edit datasources",
|
||||
"dataSources":"Data sources",
|
||||
"addDataSource":"+ add data source"
|
||||
},
|
||||
"Debugger":{
|
||||
"text":"Debugger",
|
||||
"tip" : "Debugger",
|
||||
"errors":"Errors",
|
||||
"noErrors":"No errors found",
|
||||
"clear":"clear"
|
||||
},
|
||||
"Comments":{
|
||||
"text":"Comments",
|
||||
"tip" : "toggle comments",
|
||||
"commentBody":"There are no comments to display",
|
||||
"typeComment":"Type your comment here"
|
||||
},
|
||||
"Settings":{
|
||||
"text":"Settings",
|
||||
"tip" : "Global Settings",
|
||||
"hideHeader": "Hide header for launched apps",
|
||||
"maintenanceMode":"Maintenance mode",
|
||||
"maxWidthOfCanvas":"Max width of canvas",
|
||||
"maxHeightOfCanvas" :"Max height of canvas",
|
||||
"backgroundColorOfCanvas":"Background color of canvas"
|
||||
|
||||
},
|
||||
"Back":{
|
||||
"text":"Back",
|
||||
"tip" : "Back to Home"
|
||||
}
|
||||
}
|
||||
}
|
||||
6
frontend/assets/translations/languages.json
Normal file
6
frontend/assets/translations/languages.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"languageList": [
|
||||
{ "lang": "English", "code": "en", "nativeLang": "English" },
|
||||
{ "lang": "French", "code": "fr", "nativeLang": "Français" }
|
||||
]
|
||||
}
|
||||
1145
frontend/package-lock.json
generated
1145
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -21,6 +21,11 @@
|
|||
"emoji-mart": "^3.0.1",
|
||||
"fuse.js": "^6.4.6",
|
||||
"history": "^4.9.0",
|
||||
"html-loader": "^3.1.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"i18next": "^21.8.14",
|
||||
"i18next-browser-languagedetector": "^6.1.4",
|
||||
"i18next-http-backend": "^1.4.1",
|
||||
"immer": "^9.0.6",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
|
|
@ -50,6 +55,7 @@
|
|||
"react-google-login": "^5.2.2",
|
||||
"react-hot-toast": "^2.1.1",
|
||||
"react-hotkeys-hook": "^3.4.4",
|
||||
"react-i18next": "^11.18.3",
|
||||
"react-json-tree": "^0.16.1",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-lazy-load-image-component": "^1.5.1",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { Suspense } from 'react';
|
||||
import config from 'config';
|
||||
import { BrowserRouter, Route, Redirect } from 'react-router-dom';
|
||||
import { history } from '@/_helpers';
|
||||
|
|
@ -86,7 +86,7 @@ class App extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Suspense fallback={null}>
|
||||
<BrowserRouter history={history} basename={window.public_config?.SUB_PATH || '/'}>
|
||||
<div className={`main-wrapper ${darkMode ? 'theme-dark' : ''}`}>
|
||||
{updateAvailable && (
|
||||
|
|
@ -261,7 +261,7 @@ class App extends React.Component {
|
|||
</div>
|
||||
</BrowserRouter>
|
||||
<Toast toastOptions={toastOptions} />
|
||||
</>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import { toast } from 'react-hot-toast';
|
|||
import GoogleSSOLoginButton from '@ee/components/LoginPage/GoogleSSOLoginButton';
|
||||
import GitSSOLoginButton from '@ee/components/LoginPage/GitSSOLoginButton';
|
||||
import { ShowLoading } from '@/_components';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
class ConfirmationPage extends React.Component {
|
||||
class ConfirmationPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -150,30 +151,33 @@ class ConfirmationPage extends React.Component {
|
|||
) : (
|
||||
<div className="card-body">
|
||||
<h2 className="card-title text-center mb-4" data-cy="card-title">
|
||||
Set up your account
|
||||
{this.props.t('confirmationPage.setupAccount', 'Set up your account')}
|
||||
</h2>
|
||||
{this.state.configs?.enable_sign_up && (
|
||||
<div className="d-flex flex-column align-items-center separator-bottom">
|
||||
{this.state.configs?.google?.enabled && (
|
||||
<GoogleSSOLoginButton
|
||||
text="Sign up with Google"
|
||||
text={this.props.t('confirmationPage.signupWithGoogle', 'Sign up with Google')}
|
||||
configs={this.state.configs?.google?.configs}
|
||||
configId={this.state.configs?.google?.config_id}
|
||||
/>
|
||||
)}
|
||||
{this.state.configs?.git?.enabled && (
|
||||
<GitSSOLoginButton text="Sign up with GitHub" configs={this.state.configs?.git?.configs} />
|
||||
<GitSSOLoginButton
|
||||
text={this.props.t('confirmationPage.signupWithGitHub', 'Sign up with GitHub')}
|
||||
configs={this.state.configs?.git?.configs}
|
||||
/>
|
||||
)}
|
||||
<div className="mt-2 separator">
|
||||
<h2>
|
||||
<span>OR</span>
|
||||
<span>{this.props.t('confirmationPage.or', 'OR')}</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="first-name-label">
|
||||
First name
|
||||
{this.props.t('confirmationPage.firstName', 'First name')}
|
||||
</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
|
|
@ -189,7 +193,7 @@ class ConfirmationPage extends React.Component {
|
|||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="last-name-label">
|
||||
Last name
|
||||
{this.props.t('confirmationPage.lastName', 'Last name')}
|
||||
</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
|
|
@ -205,7 +209,7 @@ class ConfirmationPage extends React.Component {
|
|||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="company-label">
|
||||
Company
|
||||
{this.props.t('confirmationPage.company', 'Company')}
|
||||
</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
|
|
@ -221,7 +225,7 @@ class ConfirmationPage extends React.Component {
|
|||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="form-label" data-cy="role-label">
|
||||
Role
|
||||
{this.props.t('confirmationPage.role', 'Role')}
|
||||
</div>
|
||||
<select
|
||||
className="form-select"
|
||||
|
|
@ -231,14 +235,14 @@ class ConfirmationPage extends React.Component {
|
|||
data-cy="role-options"
|
||||
>
|
||||
<option value="" disabled>
|
||||
Please select
|
||||
{this.props.t('confirmationPage.pleaseSelect', 'Please select')}
|
||||
</option>
|
||||
{roleOptions}
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="password-label">
|
||||
Password
|
||||
{this.props.t('confirmationPage.password', 'Password')}
|
||||
</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
|
|
@ -254,7 +258,7 @@ class ConfirmationPage extends React.Component {
|
|||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="confirm-password-label">
|
||||
Confirm Password
|
||||
{this.props.t('confirmationPage.confirmPassword', 'Confirm Password')}
|
||||
</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
|
|
@ -270,8 +274,11 @@ class ConfirmationPage extends React.Component {
|
|||
</div>
|
||||
<div className="form-footer">
|
||||
<p data-cy="terms-and-condition-info">
|
||||
By clicking the button below, you agree to our{' '}
|
||||
<a href="https://tooljet.io/terms">Terms and Conditions</a>.
|
||||
{this.props.t('confirmationPage.clickAndAgree', 'By clicking the button below, you agree to our')}{' '}
|
||||
<a href="https://tooljet.io/terms">
|
||||
{this.props.t('confirmationPage.termsAndConditions', 'Terms and Conditions')}
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<button
|
||||
className={`btn mt-2 btn-primary w-100 ${isLoading ? ' btn-loading' : ''}`}
|
||||
|
|
@ -279,7 +286,7 @@ class ConfirmationPage extends React.Component {
|
|||
disabled={isLoading}
|
||||
data-cy="finish-setup-button"
|
||||
>
|
||||
Finish account setup
|
||||
{this.props.t('confirmationPage.finishAccountSetup', 'Finish account setup')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -291,4 +298,4 @@ class ConfirmationPage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { ConfirmationPage };
|
||||
export const ConfirmationPage = withTranslation()(ConfirmationPageComponent);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import { toast } from 'react-hot-toast';
|
|||
import GoogleSSOLoginButton from '@ee/components/LoginPage/GoogleSSOLoginButton';
|
||||
import GitSSOLoginButton from '@ee/components/LoginPage/GitSSOLoginButton';
|
||||
import { ShowLoading } from '@/_components';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
class OrganizationInvitationPage extends React.Component {
|
||||
class OrganizationInvitationPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -101,7 +102,7 @@ class OrganizationInvitationPage extends React.Component {
|
|||
{!this.single_organization ? (
|
||||
<>
|
||||
<h2 className="card-title text-center mb-2" data-cy="card-title">
|
||||
Already have an account?
|
||||
{this.props.t('confirmationPage.accountExists', 'Already have an account?')}
|
||||
</h2>
|
||||
<div className="mb-3">
|
||||
<button
|
||||
|
|
@ -110,37 +111,40 @@ class OrganizationInvitationPage extends React.Component {
|
|||
disabled={isLoading}
|
||||
data-cy="accept-invite-button"
|
||||
>
|
||||
Accept invite
|
||||
{this.props.t('confirmationPage.acceptInvite', 'Accept invite')}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h2 className="card-title text-center mb-4" data-cy="card-title">
|
||||
Set up your account
|
||||
{this.props.t('confirmationPage.setupAccount', 'Set up your account')}
|
||||
</h2>
|
||||
{this.state.configs?.enable_sign_up && (
|
||||
<div className="d-flex flex-column align-items-center separator-bottom">
|
||||
{this.state.configs?.google?.enabled && (
|
||||
<GoogleSSOLoginButton
|
||||
text="Sign up with Google"
|
||||
text={this.props.t('confirmationPage.signupWithGoogle', 'Sign up with Google')}
|
||||
configs={this.state.configs?.google?.configs}
|
||||
configId={this.state.configs?.google?.config_id}
|
||||
/>
|
||||
)}
|
||||
{this.state.configs?.git?.enabled && (
|
||||
<GitSSOLoginButton text="Sign up with GitHub" configs={this.state.configs?.git?.configs} />
|
||||
<GitSSOLoginButton
|
||||
text={this.props.t('confirmationPage.signupWithGitHub', 'Sign up with GitHub')}
|
||||
configs={this.state.configs?.git?.configs}
|
||||
/>
|
||||
)}
|
||||
<div className="mt-2 separator">
|
||||
<h2>
|
||||
<span>OR</span>
|
||||
<span>{this.props.t('confirmationPage.or', 'OR')}</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="password-label">
|
||||
Password
|
||||
{this.props.t('confirmationPage.password', 'Password')}
|
||||
</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
|
|
@ -156,7 +160,7 @@ class OrganizationInvitationPage extends React.Component {
|
|||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="confirm-password-label">
|
||||
Confirm Password
|
||||
{this.props.t('confirmationPage.confirmPassword', 'Confirm Password')}
|
||||
</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
|
|
@ -172,8 +176,14 @@ class OrganizationInvitationPage extends React.Component {
|
|||
</div>
|
||||
<div className="form-footer">
|
||||
<p data-cy="terms-and-condition-info">
|
||||
By clicking the button below, you agree to our{' '}
|
||||
<a href="https://tooljet.io/terms">Terms and Conditions</a>.
|
||||
{this.props.t(
|
||||
'confirmationPage.clickAndAgree',
|
||||
'By clicking the button below, you agree to our'
|
||||
)}{' '}
|
||||
<a href="https://tooljet.io/terms">
|
||||
{this.props.t('confirmationPage.termsAndConditions', 'Terms and Conditions')}
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<button
|
||||
className={`btn mt-2 btn-primary w-100 ${isLoading ? ' btn-loading' : ''}`}
|
||||
|
|
@ -181,7 +191,9 @@ class OrganizationInvitationPage extends React.Component {
|
|||
disabled={isLoading}
|
||||
data-cy="finish-setup-button"
|
||||
>
|
||||
Finish account setup and accept invite
|
||||
{this.props.t('confirmationPage.finishAccountSetup', 'Finish account setup')}{' '}
|
||||
{this.props.t('confirmationPage.and', 'and')}{' '}
|
||||
{this.props.t('confirmationPage.acceptInvite', 'accept invite')}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
|
@ -195,4 +207,4 @@ class OrganizationInvitationPage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { OrganizationInvitationPage };
|
||||
export const OrganizationInvitationPage = withTranslation()(OrganizationInvitationPageComponent);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { appVersionService } from '@/_services';
|
|||
import { Confirm } from './Viewer/Confirm';
|
||||
import Select from '../_ui/Select';
|
||||
import defaultStyle from '../_ui/Select/styles';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const AppVersionsManager = function AppVersionsManager({
|
||||
appId,
|
||||
editingVersion,
|
||||
|
|
@ -13,6 +15,7 @@ export const AppVersionsManager = function AppVersionsManager({
|
|||
showCreateVersionModalPrompt,
|
||||
closeCreateVersionModalPrompt,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [showDropDown, setShowDropDown] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [isCreatingVersion, setIsCreatingVersion] = useState(false);
|
||||
|
|
@ -173,7 +176,7 @@ export const AppVersionsManager = function AppVersionsManager({
|
|||
|
||||
return (
|
||||
<div ref={wrapperRef} className="input-group app-version-menu">
|
||||
<span className="input-group-text app-version-menu-sm">Version</span>
|
||||
<span className="input-group-text app-version-menu-sm">{t('editor.appVersionManager.version', 'Version')}</span>
|
||||
<span
|
||||
className={`app-version-name form-select app-version-menu-sm ${appVersions ? '' : 'disabled'}`}
|
||||
onClick={() => {
|
||||
|
|
@ -198,8 +201,10 @@ export const AppVersionsManager = function AppVersionsManager({
|
|||
>
|
||||
<div className="col-md-4">{version.name}</div>
|
||||
<div className="released-subtext">
|
||||
<img src={'assets/images/icons/editor/deploy-rocket.svg'} />
|
||||
<span className="px-1">Currently Released</span>
|
||||
<img src={'/assets/images/icons/editor/deploy-rocket.svg'} />
|
||||
<span className="px-1">
|
||||
{t('editor.appVersionManager.currentlyReleased', 'Currently Released')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dropdown-divider m-0"></div>
|
||||
|
|
@ -271,11 +276,13 @@ export const AppVersionsManager = function AppVersionsManager({
|
|||
setShowModal(true);
|
||||
}}
|
||||
>
|
||||
<span className="color-primary create-link">Create Version</span>
|
||||
<span className="color-primary create-link">
|
||||
{t('editor.appVersionManager.createVersion', 'Create Version')}
|
||||
</span>
|
||||
</div>
|
||||
<Confirm
|
||||
show={showVersionDeletionConfirmation}
|
||||
message={'Do you really want to delete this version?'}
|
||||
message={t('editor.appVersionManager.deleteVersion', 'Do you really want to delete this version?')}
|
||||
confirmButtonLoading={isDeletingVersion}
|
||||
onConfirm={(versionId) => deleteAppVersion(versionId)}
|
||||
queryConfirmationData={deletingVersionId}
|
||||
|
|
@ -297,14 +304,18 @@ export const AppVersionsManager = function AppVersionsManager({
|
|||
showCreateVersionModalPrompt={showCreateVersionModalPrompt}
|
||||
/>
|
||||
</span>
|
||||
<Modal show={showVersionUpdateModal} closeModal={() => setShowVersionUpdateModal(false)} title="Edit version">
|
||||
<Modal
|
||||
show={showVersionUpdateModal}
|
||||
closeModal={() => setShowVersionUpdateModal(false)}
|
||||
title={t('editor.appVersionManager.editVersion', 'Edit Version')}
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col modal-main">
|
||||
<input
|
||||
type="text"
|
||||
onChange={(e) => setVersionName(e.target.value)}
|
||||
className="form-control"
|
||||
placeholder="version name"
|
||||
placeholder={t('editor.appVersionManager.versionName', 'Version name')}
|
||||
disabled={isEditingVersion}
|
||||
value={versionName}
|
||||
maxLength={25}
|
||||
|
|
@ -314,10 +325,10 @@ export const AppVersionsManager = function AppVersionsManager({
|
|||
<div className="row">
|
||||
<div className="col d-flex modal-footer-btn">
|
||||
<button className="btn btn-light" onClick={() => setShowVersionUpdateModal(false)}>
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button className={`btn btn-primary ${isEditingVersion ? 'btn-loading' : ''}`} onClick={editVersionName}>
|
||||
Save
|
||||
{t('globals.save', 'Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -338,6 +349,7 @@ const CreateVersionModal = function CreateVersionModal({
|
|||
appVersions,
|
||||
showCreateVersionModalPrompt,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const handleKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
// eslint-disable-next-line no-undef
|
||||
|
|
@ -379,18 +391,18 @@ const CreateVersionModal = function CreateVersionModal({
|
|||
<Modal
|
||||
show={showModal || showCreateVersionModalPrompt}
|
||||
setShow={setShowModal}
|
||||
title="Create Version"
|
||||
title={t('editor.appVersionManager.createVersion', 'Create Version')}
|
||||
autoFocus={false}
|
||||
closeModal={() => setShowModal(false)}
|
||||
>
|
||||
<div className="mb-3">
|
||||
<div className="col">
|
||||
<label className="form-label">Version Name</label>
|
||||
<label className="form-label">{t('editor.appVersionManager.versionName', 'Version Name')}</label>
|
||||
<input
|
||||
type="text"
|
||||
onChange={(e) => setVersionName(e.target.value)}
|
||||
className="form-control"
|
||||
placeholder="Enter version name"
|
||||
placeholder={t('editor.appVersionManager.enterVersionName', 'Enter version name')}
|
||||
disabled={isCreatingVersion}
|
||||
value={versionName}
|
||||
autoFocus={true}
|
||||
|
|
@ -400,7 +412,7 @@ const CreateVersionModal = function CreateVersionModal({
|
|||
</div>
|
||||
|
||||
<div className="mb-3" style={{ padding: '2rem 0' }}>
|
||||
<label className="form-label">Create version from</label>
|
||||
<label className="form-label">{t('editor.appVersionManager.createVersionFrom', 'Create version from')}</label>
|
||||
<div className="ts-control">
|
||||
<Select
|
||||
options={options}
|
||||
|
|
@ -428,8 +440,11 @@ const CreateVersionModal = function CreateVersionModal({
|
|||
</div>
|
||||
<div className="col">
|
||||
<span>
|
||||
Version already released. Kindly create a new version or switch to a different version to continue
|
||||
making changes.
|
||||
{t(
|
||||
'editor.appVersionManager.versionAlreadyReleased',
|
||||
`Version already released. Kindly create a new version or switch to a different version to continue
|
||||
making changes.`
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -441,13 +456,13 @@ const CreateVersionModal = function CreateVersionModal({
|
|||
<div className="mb-3">
|
||||
<div className="col d-flex modal-footer-btn">
|
||||
<button className="btn btn-light" onClick={() => setShowModal(false)}>
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-primary ${isCreatingVersion ? 'btn-loading' : ''}`}
|
||||
onClick={() => createVersion(versionName, createAppVersionFrom)}
|
||||
>
|
||||
Create Version
|
||||
{t('editor.appVersionManager.createVersion', 'Create Version')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ import {
|
|||
} from './component-properties-resolution';
|
||||
import _ from 'lodash';
|
||||
import { EditorContext } from '@/Editor/Context/EditorContextWrapper';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const AllComponents = {
|
||||
Button,
|
||||
|
|
@ -137,6 +138,7 @@ export const Box = function Box({
|
|||
dataQueries,
|
||||
readOnly,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const backgroundColor = yellow ? 'yellow' : '';
|
||||
|
||||
let styles = {
|
||||
|
|
@ -249,7 +251,12 @@ export const Box = function Box({
|
|||
delay={{ show: 500, hide: 0 }}
|
||||
trigger={inCanvas && !validatedGeneralProperties.tooltip?.trim() ? null : ['hover', 'focus']}
|
||||
overlay={(props) =>
|
||||
renderTooltip({ props, text: inCanvas ? `${validatedGeneralProperties.tooltip}` : `${component.description}` })
|
||||
renderTooltip({
|
||||
props,
|
||||
text: inCanvas
|
||||
? `${validatedGeneralProperties.tooltip}`
|
||||
: `${t(`widget.${component.name}.description`, component.description)}`,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div
|
||||
|
|
@ -314,7 +321,9 @@ export const Box = function Box({
|
|||
}}
|
||||
></div>
|
||||
</center>
|
||||
<span className="component-title">{component.displayName}</span>
|
||||
<span className="component-title">
|
||||
{t(`widget.${component.name}.displayName`, component.displayName)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { componentTypes } from '../WidgetManager/components';
|
|||
import { DataSourceTypes } from '../DataSourceManager/SourceComponents';
|
||||
import { debounce } from 'lodash';
|
||||
import Fuse from 'fuse.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function CodeBuilder({ initialValue, onChange, components, dataQueries }) {
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
|
|
@ -12,6 +13,7 @@ export function CodeBuilder({ initialValue, onChange, components, dataQueries })
|
|||
const [currentValue, setCurrentValue] = useState(initialValue);
|
||||
const [codeMirrorInstance, setCodeMirrorInstance] = useState(null);
|
||||
const [currentWord, setCurrentWord] = useState('');
|
||||
const { t } = useTranslation();
|
||||
|
||||
function computeCurrentWord(value, _cursorPosition) {
|
||||
const sliced = value
|
||||
|
|
@ -134,7 +136,7 @@ export function CodeBuilder({ initialValue, onChange, components, dataQueries })
|
|||
{showDropdown && (
|
||||
<div className="variables-dropdown">
|
||||
<div className="card">
|
||||
<div className="group-header p-2">components</div>
|
||||
<div className="group-header p-2">{t('globals.components', 'components')}</div>
|
||||
<div className="group-body p-2">
|
||||
{Object.keys(components).map((component) => renderComponentVariables(components[component]))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import FxButton from './Elements/FxButton';
|
|||
import { ToolTip } from '../Inspector/Elements/Components/ToolTip';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { EditorContext } from '@/Editor/Context/EditorContextWrapper';
|
||||
import { camelCase } from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const AllElements = {
|
||||
Color,
|
||||
|
|
@ -92,6 +94,7 @@ export function CodeHinter({
|
|||
height: isFocused ? currentHeight : 0,
|
||||
},
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { variablesExposedForPreview } = useContext(EditorContext);
|
||||
|
||||
|
|
@ -264,7 +267,7 @@ export function CodeHinter({
|
|||
{paramLabel && (
|
||||
<div className={`mb-2 field ${options.className}`} data-cy={`${cyLabel}-widget-parameter-label`}>
|
||||
<ToolTip
|
||||
label={paramLabel}
|
||||
label={t(`widget.commonProperties.${camelCase(paramLabel)}`, paramLabel)}
|
||||
meta={fieldMeta}
|
||||
labelClass={`form-label ${darkMode && 'color-whitish-darkmode'}`}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import React from 'react';
|
||||
import FxButton from './FxButton';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const AlignButtons = ({ value, onChange, forceCodeBox, meta }) => {
|
||||
function handleOptionChanged(event) {
|
||||
onChange(event.currentTarget.value);
|
||||
}
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="row fx-container">
|
||||
|
|
@ -29,7 +31,7 @@ export const AlignButtons = ({ value, onChange, forceCodeBox, meta }) => {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="tooltiptext">Left</span>
|
||||
<span className="tooltiptext">{t('globals.left', 'Left')}</span>
|
||||
</label>
|
||||
|
||||
<label className="radio-img">
|
||||
|
|
@ -50,7 +52,7 @@ export const AlignButtons = ({ value, onChange, forceCodeBox, meta }) => {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="tooltiptext">Center</span>
|
||||
<span className="tooltiptext">{t('globals.center', 'Center')}</span>
|
||||
</label>
|
||||
|
||||
<label className="radio-img">
|
||||
|
|
@ -71,7 +73,7 @@ export const AlignButtons = ({ value, onChange, forceCodeBox, meta }) => {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="tooltiptext">Right</span>
|
||||
<span className="tooltiptext">{t('globals.right', 'Right')}</span>
|
||||
</label>
|
||||
|
||||
<label className="radio-img">
|
||||
|
|
@ -93,7 +95,7 @@ export const AlignButtons = ({ value, onChange, forceCodeBox, meta }) => {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="tooltiptext">Justified</span>
|
||||
<span className="tooltiptext">{t('globals.justified', 'Justified')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
import { useSpring, animated } from 'react-spring';
|
||||
import { useSpring, animated, useTransition } from 'react-spring';
|
||||
import usePopover from '@/_hooks/use-popover';
|
||||
|
||||
import OptionsIcon from './icons/options.svg';
|
||||
// import OptionsSelectedIcon from './icons/options-selected.svg';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
|
||||
import { commentsService } from '@/_services';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const CommentActions = ({
|
||||
socket,
|
||||
|
|
@ -22,6 +22,7 @@ const CommentActions = ({
|
|||
const [open, trigger, content, setOpen] = usePopover(false);
|
||||
const popoverFadeStyle = useSpring({ opacity: open ? 1 : 0 });
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleDelete = async () => {
|
||||
await commentsService.deleteComment(commentId);
|
||||
|
|
@ -58,11 +59,11 @@ const CommentActions = ({
|
|||
>
|
||||
<div>
|
||||
<div className="comment-action" onClick={handleEdit}>
|
||||
Edit
|
||||
{t('globals.edit', 'Edit')}
|
||||
</div>
|
||||
{/* TODO: Add a popup confirmation on delete */}
|
||||
<div className="comment-action border-top" onClick={handleDelete}>
|
||||
Delete
|
||||
{t('globals.delete', 'Delete')}
|
||||
</div>
|
||||
</div>
|
||||
</animated.div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { isEmpty } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import CommentActions from './CommentActions';
|
||||
|
|
@ -16,6 +16,7 @@ moment.updateLocale('en', {
|
|||
|
||||
const CommentBody = ({ socket, thread, isLoading, setEditComment, setEditCommentId, fetchComments }) => {
|
||||
const bottomRef = React.useRef();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const scrollToBottom = () => {
|
||||
bottomRef?.current?.scrollIntoView({
|
||||
|
|
@ -33,7 +34,10 @@ const CommentBody = ({ socket, thread, isLoading, setEditComment, setEditComment
|
|||
}, []);
|
||||
|
||||
const getContent = () => {
|
||||
if (isEmpty(thread)) return <div className="text-center">There are no comments to display</div>;
|
||||
if (isEmpty(thread))
|
||||
return (
|
||||
<div className="text-center">{t('leftSidebar.Comments.commentBody', 'There are no comments to display')}</div>
|
||||
);
|
||||
|
||||
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import TextareaMentions from '@/_ui/Mentions';
|
|||
import Button from '@/_ui/Button';
|
||||
import usePopover from '@/_hooks/use-popover';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function CommentFooter({ users, editComment = '', setMentionedUsers, editCommentId, setEditCommentId, handleSubmit }) {
|
||||
const [comment, setComment] = React.useState(editComment);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [open, trigger, content, setOpen] = usePopover(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
React.useEffect(() => {
|
||||
setComment(editComment);
|
||||
|
|
@ -44,7 +46,7 @@ function CommentFooter({ users, editComment = '', setMentionedUsers, editComment
|
|||
setMentionedUsers={setMentionedUsers}
|
||||
value={comment}
|
||||
setValue={setComment}
|
||||
placeholder="Type your comment here"
|
||||
placeholder={t('leftSidebar.Comments.typeComment', 'Type your comment here')}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -66,7 +68,7 @@ function CommentFooter({ users, editComment = '', setMentionedUsers, editComment
|
|||
})}
|
||||
>
|
||||
<Button loading={loading} disabled={!comment} className={`m2 btn-sm rounded-2`} onClick={handleClick}>
|
||||
Send
|
||||
{t('globals.send', 'Send')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React, { useState, useCallback, useEffect } from 'react';
|
|||
import { GoogleMap, LoadScript, Marker, Autocomplete } from '@react-google-maps/api';
|
||||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
import { darkModeStyles } from './styles';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Map = function Map({
|
||||
id,
|
||||
|
|
@ -19,6 +20,7 @@ export const Map = function Map({
|
|||
}) {
|
||||
const center = component.definition.properties.initialLocation.value;
|
||||
const defaultMarkerValue = component.definition.properties.defaultMarkers.value;
|
||||
const { t } = useTranslation();
|
||||
|
||||
let defaultMarkers = [];
|
||||
try {
|
||||
|
|
@ -165,7 +167,7 @@ export const Map = function Map({
|
|||
<Autocomplete onPlaceChanged={onPlaceChanged} onLoad={onAutocompleteLoad}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
placeholder={t('globals.search', 'Search')}
|
||||
className={`place-search-input ${darkMode && 'text-light bg-dark dark-theme-placeholder'}`}
|
||||
/>
|
||||
</Autocomplete>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import React from 'react';
|
||||
import SelectSearch from 'react-select-search';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const CustomSelect = ({ options, value, multiple, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
function renderValue(valueProps) {
|
||||
if (valueProps) {
|
||||
return valueProps.value.split(', ').map((value, index) => (
|
||||
|
|
@ -22,7 +25,7 @@ export const CustomSelect = ({ options, value, multiple, onChange }) => {
|
|||
search={false}
|
||||
onChange={onChange}
|
||||
multiple={multiple}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { Datepicker } from './Datepicker';
|
|||
import { GlobalFilter } from './GlobalFilter';
|
||||
var _ = require('lodash');
|
||||
import { EditorContext } from '@/Editor/Context/EditorContextWrapper';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function Table({
|
||||
id,
|
||||
|
|
@ -45,6 +46,8 @@ export function Table({
|
|||
registerAction,
|
||||
properties,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const color =
|
||||
component.definition.styles.textColor.value !== '#000'
|
||||
? component.definition.styles.textColor.value
|
||||
|
|
@ -498,7 +501,7 @@ export function Table({
|
|||
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
disabled={!column.isEditable}
|
||||
/>
|
||||
<div className={`invalid-feedback ${isValid ? '' : 'd-flex'}`}>{validationError}</div>
|
||||
|
|
@ -512,7 +515,7 @@ export function Table({
|
|||
printOptions="on-focus"
|
||||
multiple
|
||||
search={true}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
options={columnOptions.selectOptions}
|
||||
value={cellValue}
|
||||
onChange={(value) => {
|
||||
|
|
@ -1133,7 +1136,7 @@ export function Table({
|
|||
filterColumnChanged(index, value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
<div className="col" style={{ maxWidth: '180px' }}>
|
||||
|
|
@ -1155,7 +1158,7 @@ export function Table({
|
|||
filterOperationChanged(index, value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
<div className="col">
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|||
import config from 'config';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Card } from '@/_ui/card';
|
||||
import { withTranslation, useTranslation } from 'react-i18next';
|
||||
|
||||
class DataSourceManager extends React.Component {
|
||||
class DataSourceManagerComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -132,7 +133,10 @@ class DataSourceManager extends React.Component {
|
|||
datasourceService.save(selectedDataSource.id, appId, name, parsedOptions).then(() => {
|
||||
this.setState({ isSaving: false });
|
||||
this.hideModal();
|
||||
toast.success('Datasource Saved', { position: 'top-center' });
|
||||
toast.success(
|
||||
this.props.t('editor.queryManager.dataSourceManager.toast.success.dataSourceSaved', 'Datasource Saved'),
|
||||
{ position: 'top-center' }
|
||||
);
|
||||
this.props.dataSourcesChanged();
|
||||
});
|
||||
} else {
|
||||
|
|
@ -140,12 +144,21 @@ class DataSourceManager extends React.Component {
|
|||
datasourceService.create(appId, appVersionId, name, kind, parsedOptions).then(() => {
|
||||
this.setState({ isSaving: false });
|
||||
this.hideModal();
|
||||
toast.success('Datasource Added', { position: 'top-center' });
|
||||
toast.success(
|
||||
this.props.t('editor.queryManager.dataSourceManager.toast.success.dataSourceAdded', 'Datasource Added'),
|
||||
{ position: 'top-center' }
|
||||
);
|
||||
this.props.dataSourcesChanged();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
toast.error('The name of datasource should not be empty', { position: 'top-center' });
|
||||
toast.error(
|
||||
this.props.t(
|
||||
'editor.queryManager.dataSourceManager.toast.error.noEmptyDsName',
|
||||
'The name of datasource should not be empty'
|
||||
),
|
||||
{ position: 'top-center' }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -221,7 +234,10 @@ class DataSourceManager extends React.Component {
|
|||
queryString={this.state.queryString}
|
||||
handleBackToAllDatasources={goBacktoAllDatasources}
|
||||
darkMode={this.props.darkMode}
|
||||
placeholder={'Suggest an integration'}
|
||||
placeholder={this.props.t(
|
||||
'editor.queryManager.dataSourceManager.suggestAnIntegration',
|
||||
'Suggest an integration'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -244,7 +260,9 @@ class DataSourceManager extends React.Component {
|
|||
<Tab.Content>
|
||||
{suggestingDatasources ? (
|
||||
<div className="suggestion-container">
|
||||
<h4 className="justify-content-start">Suggest Datasource</h4>
|
||||
<h4 className="justify-content-start">
|
||||
{this.props.t('editor.queryManager.dataSourceManager.suggestDataSource', 'Suggest Datasource')}
|
||||
</h4>
|
||||
{datasourceSuggestionUI()}
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -276,7 +294,10 @@ class DataSourceManager extends React.Component {
|
|||
queryString={this.state.queryString}
|
||||
handleBackToAllDatasources={this.handleBackToAllDatasources}
|
||||
darkMode={this.props.darkMode}
|
||||
placeholder={'Tell us what you were looking for?'}
|
||||
placeholder={this.props.t(
|
||||
'editor.queryManager.dataSourceManager.whatLookingFor',
|
||||
'Tell us what you were looking for?'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -349,10 +370,15 @@ class DataSourceManager extends React.Component {
|
|||
</ListGroup>
|
||||
<div className="datasource-modal-sidebar-footer">
|
||||
<p>
|
||||
<span className="footer-text">Don't see what you were looking for?</span>
|
||||
<span className="footer-text">
|
||||
{this.props.t(
|
||||
'editor.queryManager.dataSourceManager.noResultFound',
|
||||
`Don't see what you were looking for?`
|
||||
)}
|
||||
</span>
|
||||
<br />
|
||||
<span className="link-span" onClick={updateSuggestionState}>
|
||||
Suggest
|
||||
{this.props.t('editor.queryManager.dataSourceManager.suggest', 'Suggest')}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -544,7 +570,11 @@ class DataSourceManager extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!selectedDataSource && <span className="text-muted">Add new datasource</span>}
|
||||
{!selectedDataSource && (
|
||||
<span className="text-muted">
|
||||
{this.props.t('editor.queryManager.dataSourceManager.addNewDataSource', 'Add new datasource')}
|
||||
</span>
|
||||
)}
|
||||
</Modal.Title>
|
||||
<span
|
||||
className={`close-btn mx-4 mt-3 ${this.props.darkMode ? 'dark' : ''}`}
|
||||
|
|
@ -580,12 +610,19 @@ class DataSourceManager extends React.Component {
|
|||
</svg>
|
||||
</div>
|
||||
<div className="col" style={{ maxWidth: '480px' }}>
|
||||
<p>Please white-list our IP address if the data source is not publicly accessible.</p>
|
||||
<p>
|
||||
{this.props.t(
|
||||
'editor.queryManager.dataSourceManager.whiteListIP',
|
||||
'Please white-list our IP address if the data source is not publicly accessible.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
{isCopied ? (
|
||||
<center className="my-2">
|
||||
<span className="copied">Copied</span>
|
||||
<span className="copied">
|
||||
{this.props.t('editor.queryManager.dataSourceManager.copied', 'Copied')}
|
||||
</span>
|
||||
</center>
|
||||
) : (
|
||||
<CopyToClipboard
|
||||
|
|
@ -607,7 +644,7 @@ class DataSourceManager extends React.Component {
|
|||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Copy
|
||||
{this.props.t('editor.queryManager.dataSourceManager.copy', 'Copy')}
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
)}
|
||||
|
|
@ -632,7 +669,7 @@ class DataSourceManager extends React.Component {
|
|||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read documentation
|
||||
{this.props.t('globals.readDocumentation', 'Read documentation')}
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
|
|
@ -651,7 +688,7 @@ class DataSourceManager extends React.Component {
|
|||
variant="primary"
|
||||
onClick={this.createDataSource}
|
||||
>
|
||||
{'Save'}
|
||||
{this.props.t('globals.save', 'Save')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
|
|
@ -666,13 +703,15 @@ class DataSourceManager extends React.Component {
|
|||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read documentation
|
||||
{this.props.t('globals.readDocumentation', 'Read documentation')}
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<Button className="m-2" disabled={isSaving} variant="primary" onClick={this.createDataSource}>
|
||||
{isSaving ? 'Saving...' : 'Save'}
|
||||
{isSaving
|
||||
? this.props.t('editor.queryManager.dataSourceManager.saving' + '...', 'Saving...')
|
||||
: this.props.t('globals.save', 'Save')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
|
|
@ -690,6 +729,7 @@ const EmptyStateContainer = ({
|
|||
darkMode,
|
||||
placeholder,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [inputValue, set] = React.useState(() => '');
|
||||
|
||||
const [status, setStatus] = React.useState(false);
|
||||
|
|
@ -706,17 +746,26 @@ const EmptyStateContainer = ({
|
|||
|
||||
return (
|
||||
<div className="empty">
|
||||
{queryString && !suggestionUI && <h3>No results for "{queryString} "</h3>}
|
||||
{queryString && !suggestionUI && (
|
||||
<h3>
|
||||
{t(
|
||||
`editor.queryManager.dataSourceManager.noResultsFor + "${queryString}"`,
|
||||
`No results for "${queryString}"`
|
||||
)}
|
||||
</h3>
|
||||
)}
|
||||
<center className={`empty-results ${suggestionUI ? 'suggestionUI-results' : ''}`}>
|
||||
<img src="assets/images/icons/no-results.svg" width="150" height="150" />
|
||||
{status ? (
|
||||
<div>
|
||||
<p className="text-success mt-2">Thank you, we've taken a note of that!</p>
|
||||
<p className="text-success mt-2">
|
||||
{t('editor.queryManager.dataSourceManager.noteTaken', `Thank you, we've taken a note of that!`)}
|
||||
</p>
|
||||
<button
|
||||
className={`datasource-modal-button ${darkMode && 'dark-button'}`}
|
||||
onClick={handleBackToAllDatasources}
|
||||
>
|
||||
{'Go to all Datasources'}
|
||||
{t('editor.queryManager.dataSourceManager.goToAllDatasources', 'Go to all Datasources')}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -734,7 +783,7 @@ const EmptyStateContainer = ({
|
|||
</div>
|
||||
<div className="col-auto">
|
||||
<Button className="mt-2" variant="primary" onClick={handleSend}>
|
||||
{'Send'}
|
||||
{t('editor.queryManager.dataSourceManager.send', 'Send')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -746,7 +795,7 @@ const EmptyStateContainer = ({
|
|||
|
||||
const SearchBoxContainer = ({ onChange, onClear, queryString, activeDatasourceList }) => {
|
||||
const [searchText, setSearchText] = React.useState(queryString ?? '');
|
||||
|
||||
const { t } = useTranslation();
|
||||
const handleChange = (e) => {
|
||||
setSearchText(e.target.value);
|
||||
onChange(e.target.value, activeDatasourceList);
|
||||
|
|
@ -832,7 +881,7 @@ const SearchBoxContainer = ({ onChange, onClear, queryString, activeDatasourceLi
|
|||
value={searchText}
|
||||
onChange={handleChange}
|
||||
className="form-control"
|
||||
placeholder="Search"
|
||||
placeholder={t('globals.search', 'Search')}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -840,4 +889,4 @@ const SearchBoxContainer = ({ onChange, onClear, queryString, activeDatasourceLi
|
|||
);
|
||||
};
|
||||
|
||||
export { DataSourceManager };
|
||||
export const DataSourceManager = withTranslation()(DataSourceManagerComponent);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { datasourceService } from '@/_services';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const TestConnection = ({ kind, options, onConnectionTestFailed, darkMode }) => {
|
||||
const [isTesting, setTestingStatus] = useState(false);
|
||||
const [connectionStatus, setConnectionStatus] = useState('unknown');
|
||||
const [buttonText, setButtonText] = useState('Test Connection');
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (isTesting) {
|
||||
|
|
@ -44,9 +46,13 @@ export const TestConnection = ({ kind, options, onConnectionTestFailed, darkMode
|
|||
|
||||
return (
|
||||
<div>
|
||||
{connectionStatus === 'failed' && <span className="badge bg-red-lt">could not connect</span>}
|
||||
{connectionStatus === 'failed' && (
|
||||
<span className="badge bg-red-lt">{t('globals.noConnection', 'could not connect')}</span>
|
||||
)}
|
||||
|
||||
{connectionStatus === 'success' && <span className="badge bg-green-lt">connection verified</span>}
|
||||
{connectionStatus === 'success' && (
|
||||
<span className="badge bg-green-lt">{t('globals.connectionVerifeid', 'connection verified')}</span>
|
||||
)}
|
||||
|
||||
{connectionStatus === 'unknown' && (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -62,11 +62,12 @@ import { initEditorWalkThrough } from '@/_helpers/createWalkThrough';
|
|||
import { EditorContextWrapper } from './Context/EditorContextWrapper';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import Selecto from 'react-selecto';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
setAutoFreeze(false);
|
||||
enablePatches();
|
||||
|
||||
class Editor extends React.Component {
|
||||
class EditorComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -1258,7 +1259,7 @@ class Editor extends React.Component {
|
|||
rel="noreferrer"
|
||||
data-cy="preview-button"
|
||||
>
|
||||
Preview
|
||||
{this.props.t('editor.preview', 'Preview')}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="nav-item dropdown d-none d-md-flex me-2">
|
||||
|
|
@ -1451,7 +1452,7 @@ class Editor extends React.Component {
|
|||
<SearchBoxComponent
|
||||
onChange={this.filterQueries}
|
||||
callback={this.toggleQuerySearch}
|
||||
placeholder={'Search queries'}
|
||||
placeholder={this.props.t('editor.searchQueries', 'Search queries')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1464,7 +1465,7 @@ class Editor extends React.Component {
|
|||
style={{ fontSize: '14px', marginLeft: ' 6px' }}
|
||||
className="py-1 px-3 mt-2 text-muted"
|
||||
>
|
||||
Queries
|
||||
{this.props.t('editor.queries', 'Queries')}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
|
|
@ -1529,7 +1530,7 @@ class Editor extends React.Component {
|
|||
})
|
||||
}
|
||||
>
|
||||
{'Create query'}
|
||||
{this.props.t('editor.createQuery', 'Create query')}
|
||||
</button>
|
||||
</center>
|
||||
</div>
|
||||
|
|
@ -1658,7 +1659,9 @@ class Editor extends React.Component {
|
|||
handleEditorEscapeKeyPress={this.handleEditorEscapeKeyPress}
|
||||
></Inspector>
|
||||
) : (
|
||||
<center className="mt-5 p-2">Please select a component to inspect</center>
|
||||
<center className="mt-5 p-2">
|
||||
{this.props.t('editor.inspectComponent', 'Please select a component to inspect')}
|
||||
</center>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -1687,4 +1690,4 @@ class Editor extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { Editor };
|
||||
export const Editor = withTranslation()(EditorComponent);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
class ErrorBoundary extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
static getDerivedStateFromError(error) {
|
||||
// Update state so the next render will show the fallback UI.
|
||||
|
|
@ -20,11 +20,11 @@ class ErrorBoundary extends Component {
|
|||
render() {
|
||||
if (this.state.hasError) {
|
||||
// You can render any custom fallback UI
|
||||
return this.props.showFallback ? <h2>Something went wrong.</h2> : <div></div>;
|
||||
return this.props.showFallback ? <h2>{this.props.t('errorBoundary', 'Something went wrong.')}</h2> : <div></div>;
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
export default withTranslation()(ErrorBoundary);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ import React, { useState, useEffect } from 'react';
|
|||
import Select from '@/_ui/Select';
|
||||
import defaultStyles from '@/_ui/Select/styles';
|
||||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function GotoApp({ getAllApps, currentState, event, handlerChanged, eventIndex, darkMode }) {
|
||||
const queryParamChangeHandler = (index, key, value) => {
|
||||
event.queryParams[index][key] = value;
|
||||
handlerChanged(eventIndex, 'queryParams', event.queryParams);
|
||||
};
|
||||
const { t } = useTranslation();
|
||||
|
||||
const addQueryParam = () => {
|
||||
if (!event.queryParams) {
|
||||
|
|
@ -53,7 +55,7 @@ export function GotoApp({ getAllApps, currentState, event, handlerChanged, event
|
|||
onChange={(value) => {
|
||||
handlerChanged(eventIndex, 'slug', value);
|
||||
}}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import React from 'react';
|
|||
import Accordion from '@/_ui/Accordion';
|
||||
import { EventManager } from '../EventManager';
|
||||
import { renderElement } from '../Utils';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import i18next from 'i18next';
|
||||
|
||||
export const DefaultComponent = ({ componentMeta, darkMode, ...restProps }) => {
|
||||
const {
|
||||
|
|
@ -64,7 +66,7 @@ export const baseComponentProperties = (
|
|||
let items = [];
|
||||
if (properties.length > 0) {
|
||||
items.push({
|
||||
title: 'Properties',
|
||||
title: `${i18next.t('widget.common.properties', 'Properties')}`,
|
||||
children: properties.map((property) =>
|
||||
renderElement(
|
||||
component,
|
||||
|
|
@ -83,7 +85,7 @@ export const baseComponentProperties = (
|
|||
|
||||
if (events.length > 0) {
|
||||
items.push({
|
||||
title: 'Events',
|
||||
title: `${i18next.t('widget.common.events', 'Events')}`,
|
||||
isOpen: false,
|
||||
children: (
|
||||
<EventManager
|
||||
|
|
@ -102,7 +104,7 @@ export const baseComponentProperties = (
|
|||
|
||||
if (validations.length > 0) {
|
||||
items.push({
|
||||
title: 'Validation',
|
||||
title: `${i18next.t('widget.common.validation', 'Validation')}`,
|
||||
children: validations.map((property) =>
|
||||
renderElement(
|
||||
component,
|
||||
|
|
@ -120,7 +122,7 @@ export const baseComponentProperties = (
|
|||
}
|
||||
|
||||
items.push({
|
||||
title: 'General',
|
||||
title: `${i18next.t('widget.common.general', 'General')}`,
|
||||
isOpen: false,
|
||||
children: (
|
||||
<>
|
||||
|
|
@ -139,7 +141,7 @@ export const baseComponentProperties = (
|
|||
});
|
||||
|
||||
items.push({
|
||||
title: 'Layout',
|
||||
title: `${i18next.t('widget.common.layout', 'Layout')}`,
|
||||
isOpen: false,
|
||||
children: (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import SelectSearch, { fuzzySearch } from 'react-select-search';
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { EventManager } from '../EventManager';
|
||||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
|
||||
class Table extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class TableComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -164,7 +164,7 @@ class Table extends React.Component {
|
|||
<Popover id="popover-basic-2" className={`${this.props.darkMode && 'popover-dark-themed theme-dark'} shadow`}>
|
||||
<Popover.Content>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Column type</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.columnType', 'Column type')}</label>
|
||||
<SelectSearch
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
options={[
|
||||
|
|
@ -187,11 +187,11 @@ class Table extends React.Component {
|
|||
this.onColumnItemChange(index, 'columnType', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
placeholder={this.props.t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Column name</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.columnName', 'Column name')}</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control text-field"
|
||||
|
|
@ -204,7 +204,7 @@ class Table extends React.Component {
|
|||
</div>
|
||||
{(column.columnType === 'string' || column.columnType === undefined || column.columnType === 'default') && (
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Overflow</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.overflow', 'Overflow')}</label>
|
||||
<SelectSearch
|
||||
options={[
|
||||
{ name: 'Wrap', value: 'wrap' },
|
||||
|
|
@ -218,12 +218,12 @@ class Table extends React.Component {
|
|||
this.onColumnItemChange(index, 'textWrap', value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
placeholder={this.props.t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">key</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.key', 'key')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.key}
|
||||
|
|
@ -239,7 +239,7 @@ class Table extends React.Component {
|
|||
{(column.columnType === 'string' || column.columnType === undefined || column.columnType === 'default') && (
|
||||
<div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Text color</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.textColor', 'Text color')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.textColor}
|
||||
|
|
@ -255,9 +255,9 @@ class Table extends React.Component {
|
|||
</div>
|
||||
{column.isEditable && (
|
||||
<div>
|
||||
<div className="hr-text">Validation</div>
|
||||
<div className="hr-text">{this.props.t('widget.Table.validation', 'Validation')}</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Regex</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.regex', 'Regex')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.regex}
|
||||
|
|
@ -270,7 +270,7 @@ class Table extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Min length</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.minLength', 'Min length')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.minLength}
|
||||
|
|
@ -283,7 +283,7 @@ class Table extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Max length</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.maxLength', 'Max length')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.maxLength}
|
||||
|
|
@ -296,7 +296,7 @@ class Table extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Custom rule</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.customRule', 'Custom rule')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.customRule}
|
||||
|
|
@ -352,7 +352,7 @@ class Table extends React.Component {
|
|||
column.columnType === 'radio') && (
|
||||
<div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Values</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.values', 'Values')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.values}
|
||||
|
|
@ -365,7 +365,7 @@ class Table extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Labels</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.labels', 'Labels')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.labels}
|
||||
|
|
@ -384,9 +384,9 @@ class Table extends React.Component {
|
|||
<>
|
||||
{column.isEditable && (
|
||||
<div>
|
||||
<div className="hr-text">Validation</div>
|
||||
<div className="hr-text">{this.props.t('widget.Table.validation', 'Validation')}</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Custom rule</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.customRule', 'Custom Rule')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.customRule}
|
||||
|
|
@ -404,7 +404,7 @@ class Table extends React.Component {
|
|||
)}
|
||||
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Cell background color</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.cellBgColor', 'Cell Background Color')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column.cellBackgroundColor ?? 'inherit'}
|
||||
|
|
@ -419,7 +419,9 @@ class Table extends React.Component {
|
|||
|
||||
{column.columnType === 'datepicker' && (
|
||||
<div>
|
||||
<label className="form-label">Date Display Format</label>
|
||||
<label className="form-label">
|
||||
{this.props.t('widget.Table.dateDisplayformat', 'Date Display Format')}
|
||||
</label>
|
||||
<div className="field mb-2">
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
|
|
@ -432,7 +434,7 @@ class Table extends React.Component {
|
|||
componentName={this.getPopoverFieldSource(column.columnType, 'dateFormat')}
|
||||
/>
|
||||
</div>
|
||||
<label className="form-label">Date Parse Format</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.dateParseformat', 'Date Parse Format')}</label>
|
||||
<div className="field mb-2">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -485,7 +487,7 @@ class Table extends React.Component {
|
|||
}}
|
||||
checked={column.isTimeChecked}
|
||||
/>
|
||||
<span className="form-check-label">show time</span>
|
||||
<span className="form-check-label">{this.props.t('widget.Table.showTime', 'show time')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -498,7 +500,7 @@ class Table extends React.Component {
|
|||
onClick={() => this.onColumnItemChange(index, 'isEditable', !column.isEditable)}
|
||||
checked={column.isEditable}
|
||||
/>
|
||||
<span className="form-check-label">make editable</span>
|
||||
<span className="form-check-label">{this.props.t('widget.Table.makeEditable', 'make editable')}</span>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
|
|
@ -518,7 +520,7 @@ class Table extends React.Component {
|
|||
<Popover id="popover-basic" className={`${this.props.darkMode && 'popover-dark-themed theme-dark'} shadow`}>
|
||||
<Popover.Content>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Button Text</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.buttonText', 'Button Text')}</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control text-field"
|
||||
|
|
@ -530,7 +532,7 @@ class Table extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">Button position</label>
|
||||
<label className="form-label">{this.props.t('widget.Table.buttonPosition', 'Button Position')}</label>
|
||||
<SelectSearch
|
||||
className={`${this.props.darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
options={[
|
||||
|
|
@ -576,7 +578,7 @@ class Table extends React.Component {
|
|||
}}
|
||||
/>
|
||||
<button className="btn btn-sm btn-outline-danger mt-2 col" onClick={() => this.removeAction(index)}>
|
||||
Remove
|
||||
{this.props.t('widget.Table.remove', 'Remove')}
|
||||
</button>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
|
|
@ -717,7 +719,7 @@ class Table extends React.Component {
|
|||
onClick={this.addNewColumn}
|
||||
className="btn btn-sm border-0 font-weight-normal padding-2 col-auto color-primary inspector-add-button"
|
||||
>
|
||||
+ Add column
|
||||
{this.props.t('widget.Table.addColumn', '+ Add column')}
|
||||
</button>
|
||||
</div>
|
||||
<SortableList onSortEnd={this.onSortEnd} className="w-100" draggedItemClassName="dragged">
|
||||
|
|
@ -803,14 +805,16 @@ class Table extends React.Component {
|
|||
onClick={this.addNewAction}
|
||||
className="btn btn-sm border-0 font-weight-normal padding-2 col-auto color-primary inspector-add-button"
|
||||
>
|
||||
+ Add button
|
||||
{this.props.t('widget.Table.addButton', '+ Add button')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>{actions.value.map((action, index) => this.actionButton(action, index))}</div>
|
||||
{actions.value.length === 0 && (
|
||||
<div className="text-center">
|
||||
<small className="color-disabled">This table doesn't have any action buttons</small>
|
||||
<small className="color-disabled">
|
||||
{this.props.t('widget.Table.noActionMessage', "This table doesn't have any action buttons")}
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -896,4 +900,4 @@ class Table extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { Table };
|
||||
export const Table = withTranslation()(TableComponent);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import React from 'react';
|
||||
import { ToolTip } from './Components/ToolTip';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const AlignButtons = ({ param, definition, onChange, paramType, componentMeta }) => {
|
||||
const initialValue = definition ? definition.value : '';
|
||||
const paramMeta = componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
const options = paramMeta.options || {};
|
||||
const { t } = useTranslation();
|
||||
|
||||
function handleOptionChanged(event) {
|
||||
onChange(param, 'value', event.currentTarget.value, paramType);
|
||||
|
|
@ -33,7 +35,7 @@ export const AlignButtons = ({ param, definition, onChange, paramType, component
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="tooltiptext">Left</span>
|
||||
<span className="tooltiptext">{t('globals.left', 'Left')}</span>
|
||||
</label>
|
||||
|
||||
<label className="radio-img">
|
||||
|
|
@ -54,7 +56,7 @@ export const AlignButtons = ({ param, definition, onChange, paramType, component
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="tooltiptext">Center</span>
|
||||
<span className="tooltiptext">{t('globals.center', 'Center')}</span>
|
||||
</label>
|
||||
|
||||
<label className="radio-img">
|
||||
|
|
@ -75,7 +77,7 @@ export const AlignButtons = ({ param, definition, onChange, paramType, component
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="tooltiptext">Right</span>
|
||||
<span className="tooltiptext">{t('globals.right', 'Right')}</span>
|
||||
</label>
|
||||
|
||||
<label className="radio-img">
|
||||
|
|
@ -97,7 +99,7 @@ export const AlignButtons = ({ param, definition, onChange, paramType, component
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="tooltiptext">Justified</span>
|
||||
<span className="tooltiptext">{t('globals.justified', 'Justified')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import React from 'react';
|
||||
import { ToolTip } from './Components/ToolTip';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Select = ({ param, definition, onChange, paramType, componentMeta }) => {
|
||||
const paramMeta = componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
const options = paramMeta.options;
|
||||
const value = definition ? definition.value : '';
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="field mb-3">
|
||||
|
|
@ -17,7 +19,7 @@ export const Select = ({ param, definition, onChange, paramType, componentMeta }
|
|||
search={true}
|
||||
onChange={(newVal) => onChange(param, 'value', newVal, paramType)}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import _ from 'lodash';
|
|||
import { componentTypes } from '../WidgetManager/components';
|
||||
import Select from '@/_ui/Select';
|
||||
import defaultStyles from '@/_ui/Select/styles';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const EventManager = ({
|
||||
component,
|
||||
|
|
@ -24,6 +25,7 @@ export const EventManager = ({
|
|||
popoverPlacement,
|
||||
}) => {
|
||||
const [focusedEventIndex, setFocusedEventIndex] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
let actionOptions = ActionTypes.map((action) => {
|
||||
return { name: action.name, value: action.id };
|
||||
|
|
@ -196,7 +198,7 @@ export const EventManager = ({
|
|||
<Popover.Content>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">
|
||||
<span data-cy="event-label">Event</span>
|
||||
<span data-cy="event-label">{t('editor.inspector.eventManager.event', 'Event')}</span>
|
||||
</div>
|
||||
<div className="col-9" data-cy="event-selection">
|
||||
<Select
|
||||
|
|
@ -205,7 +207,7 @@ export const EventManager = ({
|
|||
value={event.eventId}
|
||||
search={false}
|
||||
onChange={(value) => handlerChanged(index, 'eventId', value)}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
|
|
@ -213,7 +215,7 @@ export const EventManager = ({
|
|||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2">
|
||||
<span data-cy="action-label">Action</span>
|
||||
<span data-cy="action-label">{t('editor.inspector.eventManager.action', 'Action')}</span>
|
||||
</div>
|
||||
<div className="col-9 popover-action-select-search" data-cy="action-selection">
|
||||
<Select
|
||||
|
|
@ -222,7 +224,7 @@ export const EventManager = ({
|
|||
value={event.actionId}
|
||||
search={false}
|
||||
onChange={(value) => handlerChanged(index, 'actionId', value)}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
|
|
@ -231,7 +233,7 @@ export const EventManager = ({
|
|||
|
||||
{actionLookup[event.actionId].options?.length > 0 && (
|
||||
<div className="hr-text" data-cy="action-option">
|
||||
Action options
|
||||
{t('editor.inspector.eventManager.actionOptions', 'Action options')}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
|
|
@ -239,7 +241,7 @@ export const EventManager = ({
|
|||
<>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2" data-cy="message-label">
|
||||
Message
|
||||
{t('editor.inspector.eventManager.message', 'Message')}
|
||||
</div>
|
||||
<div className="col-9" data-cy="alert-message-input-field">
|
||||
<CodeHinter
|
||||
|
|
@ -253,7 +255,7 @@ export const EventManager = ({
|
|||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2" data-cy="alert-type-label">
|
||||
Alert Type
|
||||
{t('editor.inspector.eventManager.alertType', 'Alert Type')}
|
||||
</div>
|
||||
<div className="col-9" data-cy="alert-message-type">
|
||||
<Select
|
||||
|
|
@ -262,7 +264,7 @@ export const EventManager = ({
|
|||
value={event.alertType}
|
||||
search={false}
|
||||
onChange={(value) => handlerChanged(index, 'alertType', value)}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
|
|
@ -273,7 +275,7 @@ export const EventManager = ({
|
|||
|
||||
{event.actionId === 'open-webpage' && (
|
||||
<div className="p-1">
|
||||
<label className="form-label mt-1">URL</label>
|
||||
<label className="form-label mt-1">{t('editor.inspector.eventManager.url', 'URL')}</label>
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
currentState={currentState}
|
||||
|
|
@ -297,7 +299,7 @@ export const EventManager = ({
|
|||
|
||||
{event.actionId === 'show-modal' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Modal</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.modal', 'Modal')}</div>
|
||||
<div className="col-9">
|
||||
<Select
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
|
|
@ -307,7 +309,7 @@ export const EventManager = ({
|
|||
onChange={(value) => {
|
||||
handlerChanged(index, 'modal', value);
|
||||
}}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
|
|
@ -317,7 +319,7 @@ export const EventManager = ({
|
|||
|
||||
{event.actionId === 'close-modal' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Modal</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.modal', 'Modal')}</div>
|
||||
<div className="col-9">
|
||||
<Select
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
|
|
@ -327,7 +329,7 @@ export const EventManager = ({
|
|||
onChange={(value) => {
|
||||
handlerChanged(index, 'modal', value);
|
||||
}}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
|
|
@ -337,7 +339,7 @@ export const EventManager = ({
|
|||
|
||||
{event.actionId === 'copy-to-clipboard' && (
|
||||
<div className="p-1">
|
||||
<label className="form-label mt-1">Text</label>
|
||||
<label className="form-label mt-1">{t('editor.inspector.eventManager.text', 'Text')}</label>
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
currentState={currentState}
|
||||
|
|
@ -349,7 +351,7 @@ export const EventManager = ({
|
|||
|
||||
{event.actionId === 'run-query' && (
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Query</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.query', 'Query')}</div>
|
||||
<div className="col-9">
|
||||
<Select
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
|
|
@ -363,7 +365,7 @@ export const EventManager = ({
|
|||
handlerChanged(index, 'queryId', query.id);
|
||||
handlerChanged(index, 'queryName', query.name);
|
||||
}}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
|
|
@ -374,7 +376,7 @@ export const EventManager = ({
|
|||
{event.actionId === 'set-localstorage-value' && (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Key</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.key', 'Key')}</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
|
|
@ -387,7 +389,7 @@ export const EventManager = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2">Value</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.value', 'Value')}</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
|
|
@ -404,7 +406,7 @@ export const EventManager = ({
|
|||
{event.actionId === 'generate-file' && (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Type</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.type', 'Type')}</div>
|
||||
<div className="col-9">
|
||||
<Select
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
|
|
@ -417,14 +419,14 @@ export const EventManager = ({
|
|||
onChange={(value) => {
|
||||
handlerChanged(index, 'fileType', value);
|
||||
}}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2">File name</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.fileName', 'File name')}</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
|
|
@ -436,7 +438,7 @@ export const EventManager = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2">Data</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.data', 'Data')}</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
|
|
@ -452,7 +454,7 @@ export const EventManager = ({
|
|||
{event.actionId === 'set-table-page' && (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Table</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.table', 'Table')}</div>
|
||||
<div className="col-9">
|
||||
<Select
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'}`}
|
||||
|
|
@ -462,14 +464,14 @@ export const EventManager = ({
|
|||
onChange={(value) => {
|
||||
handlerChanged(index, 'table', value);
|
||||
}}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2">Page index</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.pageIndex', 'Page index')}</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
|
|
@ -486,7 +488,7 @@ export const EventManager = ({
|
|||
{event.actionId === 'set-custom-variable' && (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Key</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.key', 'Key')}</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
|
|
@ -498,7 +500,7 @@ export const EventManager = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2">Value</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.value', 'Value')}</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
|
|
@ -514,7 +516,7 @@ export const EventManager = ({
|
|||
{event.actionId === 'unset-custom-variable' && (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">Key</div>
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.key', 'Key')}</div>
|
||||
<div className="col-9">
|
||||
<CodeHinter
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
|
|
@ -531,7 +533,7 @@ export const EventManager = ({
|
|||
<>
|
||||
<div className="row">
|
||||
<div className="col-3 p-1" data-cy="action-options-component-field-label">
|
||||
Component
|
||||
{t('editor.inspector.eventManager.component', 'Component')}
|
||||
</div>
|
||||
<div className="col-9" data-cy="action-options-component-selection-field">
|
||||
<Select
|
||||
|
|
@ -543,7 +545,7 @@ export const EventManager = ({
|
|||
handlerChanged(index, 'componentSpecificActionHandle', '');
|
||||
handlerChanged(index, 'componentId', value);
|
||||
}}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
|
|
@ -551,7 +553,7 @@ export const EventManager = ({
|
|||
</div>
|
||||
<div className="row mt-2">
|
||||
<div className="col-3 p-1" data-cy="action-options-action-field-label">
|
||||
Action
|
||||
{t('editor.inspector.eventManager.action', 'Action')}
|
||||
</div>
|
||||
<div className="col-9" data-cy="action-options-action-selection-field">
|
||||
<Select
|
||||
|
|
@ -567,7 +569,7 @@ export const EventManager = ({
|
|||
getComponentActionDefaultParams(event?.componentId, value)
|
||||
);
|
||||
}}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
/>
|
||||
|
|
@ -780,12 +782,14 @@ export const EventManager = ({
|
|||
onClick={addHandler}
|
||||
data-cy="add-event-handler"
|
||||
>
|
||||
+ Add event handler
|
||||
{t('editor.inspector.eventManager.addEventHandler', '+ Add event handler')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<small className="color-disabled" data-cy="no-event-handler-message">
|
||||
This {componentName.toLowerCase()} doesn't have any event handlers
|
||||
{t('editor.inspector.eventManager.emptyMessage', "This {{componentName}} doesn't have any event handlers", {
|
||||
componentName: componentName.toLowerCase(),
|
||||
})}
|
||||
</small>
|
||||
</div>
|
||||
</>
|
||||
|
|
@ -800,7 +804,7 @@ export const EventManager = ({
|
|||
onClick={addHandler}
|
||||
data-cy="add-more-event-handler"
|
||||
>
|
||||
+ Add handler
|
||||
{t('editor.inspector.eventManager.addHandler', '+ Add handler')}
|
||||
</button>
|
||||
</div>
|
||||
{renderHandlers(events)}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { FilePicker } from './Components/FilePicker';
|
|||
import { CustomComponent } from './Components/CustomComponent';
|
||||
import useFocus from '@/_hooks/use-focus';
|
||||
import Accordion from '@/_ui/Accordion';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Inspector = ({
|
||||
selectedComponentId,
|
||||
|
|
@ -41,6 +42,7 @@ export const Inspector = ({
|
|||
const componentNameRef = useRef(null);
|
||||
const [newComponentName, setNewComponentName] = useState(component.component.name);
|
||||
const [inputRef, setInputFocus] = useFocus();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys('backspace', () => setWidgetDeleteConfirmation(true));
|
||||
useHotkeys('escape', () => switchSidebarTab(2));
|
||||
|
|
@ -78,12 +80,12 @@ export const Inspector = ({
|
|||
function handleComponentNameChange(newName) {
|
||||
if (component.component.name === newName) return;
|
||||
if (newName.length === 0) {
|
||||
toast.error('Widget name cannot be empty');
|
||||
toast.error(t('widget.common.widgetNameEmptyError', 'Widget name cannot be empty'));
|
||||
return setInputFocus();
|
||||
}
|
||||
|
||||
if (!validateComponentName(newName)) {
|
||||
toast.error('Component name already exists');
|
||||
toast.error(t('widget.common.componentNameExistsError', 'Component name already exists'));
|
||||
return setInputFocus();
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +94,12 @@ export const Inspector = ({
|
|||
newComponent.component.name = newName;
|
||||
componentDefinitionChanged(newComponent);
|
||||
} else {
|
||||
toast.error('Invalid widget name. Should be unique and only include letters, numbers and underscore.');
|
||||
toast.error(
|
||||
t(
|
||||
'widget.common.invalidWidgetName',
|
||||
'Invalid widget name. Should be unique and only include letters, numbers and underscore.'
|
||||
)
|
||||
);
|
||||
setInputFocus();
|
||||
}
|
||||
}
|
||||
|
|
@ -318,7 +325,7 @@ export const Inspector = ({
|
|||
const items = [];
|
||||
|
||||
items.push({
|
||||
title: 'General',
|
||||
title: `${t('widget.common.general', 'General')}`,
|
||||
isOpen: false,
|
||||
children: (
|
||||
<>
|
||||
|
|
@ -359,7 +366,7 @@ export const Inspector = ({
|
|||
/>
|
||||
<div ref={tabsRef}>
|
||||
<Tabs activeKey={key} onSelect={(k) => handleTabSelect(k)} className={`tabs-inspector ${darkMode && 'dark'}`}>
|
||||
<Tab style={{ marginBottom: 100 }} eventKey="properties" title="Properties">
|
||||
<Tab style={{ marginBottom: 100 }} eventKey="properties" title={t('widget.common.properties', 'Properties')}>
|
||||
<div className="header py-1 row">
|
||||
<div>
|
||||
<div className="input-icon">
|
||||
|
|
@ -388,7 +395,7 @@ export const Inspector = ({
|
|||
</div>
|
||||
{getAccordion(componentMeta.component)}
|
||||
</Tab>
|
||||
<Tab eventKey="styles" title="Styles">
|
||||
<Tab eventKey="styles" title={t('widget.common.styles', 'Styles')}>
|
||||
<div style={{ marginBottom: '6rem' }}>
|
||||
<div className="p-3">
|
||||
{Object.keys(componentMeta.styles).map((style) =>
|
||||
|
|
@ -440,7 +447,9 @@ export const Inspector = ({
|
|||
rel="noreferrer"
|
||||
data-cy="widget-documentation-link"
|
||||
>
|
||||
<small>{componentMeta.name} documentation</small>
|
||||
<small>
|
||||
{t('widget.common.documentation', '{{componentMeta}} documentation', { componentMeta: componentMeta.name })}
|
||||
</small>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useState } from 'react';
|
||||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import Collapse from 'react-bootstrap/Collapse';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const QuerySelector = ({ param, definition, eventOptionUpdated, dataQueries, extraData, eventMeta }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
function onChange(value) {
|
||||
const query = dataQueries.find((dataquery) => dataquery.id === value);
|
||||
|
|
@ -42,7 +44,7 @@ export const QuerySelector = ({ param, definition, eventOptionUpdated, dataQueri
|
|||
onChange(value);
|
||||
}}
|
||||
filterOptions={fuzzySearch}
|
||||
placeholder="Select.."
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { getSvgIcon } from '@/_helpers/appUtils';
|
|||
import { datasourceService } from '@/_services';
|
||||
import { ConfirmDialog } from '@/_components';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const LeftSidebarDataSources = ({
|
||||
appId,
|
||||
editingVersionId,
|
||||
|
|
@ -124,12 +124,13 @@ export const LeftSidebarDataSources = ({
|
|||
};
|
||||
|
||||
const LeftSidebarDataSourcesContainer = ({ renderDataSource, dataSources = [], toggleDataSourceManagerModal }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="card-body">
|
||||
<div>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<h5 className="text-muted">Data sources</h5>
|
||||
<h5 className="text-muted">{t('leftSidebar.Sources.dataSources', 'Data sources')}</h5>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<OverlayTrigger
|
||||
|
|
@ -147,7 +148,7 @@ const LeftSidebarDataSourcesContainer = ({ renderDataSource, dataSources = [], t
|
|||
<div className="d-flex w-100">
|
||||
{dataSources.length === 0 ? (
|
||||
<center onClick={() => toggleDataSourceManagerModal(true)} className="p-2 color-primary cursor-pointer">
|
||||
+ add data source
|
||||
{t(`leftSidebar.Sources.addDataSource`, '+ add data source')}
|
||||
</center>
|
||||
) : (
|
||||
<div className="mt-2 w-100">{dataSources?.map((source, idx) => renderDataSource(source, idx))}</div>
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ import { LeftSidebarItem } from './SidebarItem';
|
|||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { SidebarPinnedButton } from './SidebarPinnedButton';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import JSONTreeViewer from '@/_ui/JSONTreeViewer';
|
||||
|
||||
export const LeftSidebarDebugger = ({ darkMode, errors, debuggerActions }) => {
|
||||
const { t } = useTranslation();
|
||||
const [open, trigger, content, popoverPinned, updatePopoverPinnedState] = usePinnedPopover(false);
|
||||
const [errorLogs, setErrorLogs] = React.useState([]);
|
||||
const [unReadErrorCount, setUnReadErrorCount] = React.useState({ read: 0, unread: 0 });
|
||||
|
|
@ -84,7 +86,7 @@ export const LeftSidebarDebugger = ({ darkMode, errors, debuggerActions }) => {
|
|||
<div className="nav-header">
|
||||
<ul className="nav nav-tabs d-flex justify-content-between" data-bs-toggle="tabs">
|
||||
<li className="nav-item">
|
||||
<a className="nav-link active">Errors</a>
|
||||
<a className="nav-link active">{t(`leftSidebar.Debugger.errors`, 'Errors')}</a>
|
||||
</li>
|
||||
<li className="btn-group">
|
||||
{errorLogs.length > 0 && (
|
||||
|
|
@ -94,7 +96,7 @@ export const LeftSidebarDebugger = ({ darkMode, errors, debuggerActions }) => {
|
|||
className="btn btn-light btn-sm m-1 py-1"
|
||||
aria-label="clear button"
|
||||
>
|
||||
<span className="text-muted">clear</span>
|
||||
<span className="text-muted">{t(`leftSidebar.Debugger.clear`, 'clear')}</span>
|
||||
</button>
|
||||
)}
|
||||
<SidebarPinnedButton
|
||||
|
|
@ -109,7 +111,9 @@ export const LeftSidebarDebugger = ({ darkMode, errors, debuggerActions }) => {
|
|||
</div>
|
||||
|
||||
<div className="card-body">
|
||||
{errorLogs.length === 0 && <center className="p-2 text-muted">No errors found.</center>}
|
||||
{errorLogs.length === 0 && (
|
||||
<center className="p-2 text-muted">{t(`leftSidebar.Debugger.noErrors`, 'No errors found.')}</center>
|
||||
)}
|
||||
|
||||
<div className="tab-content">
|
||||
{errorLogs.map((error, index) => (
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { LeftSidebarItem } from './SidebarItem';
|
|||
import FxButton from '../CodeBuilder/Elements/FxButton';
|
||||
import { CodeHinter } from '../CodeBuilder/CodeHinter';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const LeftSidebarGlobalSettings = ({
|
||||
globalSettings,
|
||||
|
|
@ -16,6 +17,7 @@ export const LeftSidebarGlobalSettings = ({
|
|||
is_maintenance_on,
|
||||
currentState,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [open, trigger, content] = usePopover(false);
|
||||
const { hideHeader, canvasMaxWidth, canvasMaxHeight, canvasBackgroundColor, backgroundFxQuery } = globalSettings;
|
||||
const [showPicker, setShowPicker] = React.useState(false);
|
||||
|
|
@ -65,7 +67,7 @@ export const LeftSidebarGlobalSettings = ({
|
|||
<div style={{ marginTop: '1rem' }} className="card-body">
|
||||
<div>
|
||||
<div className="d-flex mb-3">
|
||||
<span>Hide header for launched apps</span>
|
||||
<span>{t('leftSidebar.Settings.hideHeader', 'Hide header for launched apps')}</span>
|
||||
<div className="ms-auto form-check form-switch position-relative">
|
||||
<input
|
||||
className="form-check-input"
|
||||
|
|
@ -76,7 +78,7 @@ export const LeftSidebarGlobalSettings = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="d-flex mb-3">
|
||||
<span>Maintenance mode</span>
|
||||
<span>{t('leftSidebar.Settings.maintenanceMode', 'Maintenance mode')}</span>
|
||||
<div className="ms-auto form-check form-switch position-relative">
|
||||
<input
|
||||
className="form-check-input"
|
||||
|
|
@ -87,7 +89,7 @@ export const LeftSidebarGlobalSettings = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="d-flex mb-3">
|
||||
<span className="w-full m-auto">Max width of canvas</span>
|
||||
<span className="w-full m-auto">{t('leftSidebar.Settings.maxWidthOfCanvas', 'Max width of canvas')}</span>
|
||||
<div className="position-relative">
|
||||
<div className="input-with-icon">
|
||||
<input
|
||||
|
|
@ -104,7 +106,9 @@ export const LeftSidebarGlobalSettings = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="d-flex mb-3">
|
||||
<span className="w-full m-auto">Max height of canvas</span>
|
||||
<span className="w-full m-auto">
|
||||
{t('leftSidebar.Settings.maxHeightOfCanvas', 'Max height of canvas')}
|
||||
</span>
|
||||
<div className="position-relative">
|
||||
<div className="input-with-icon">
|
||||
<input
|
||||
|
|
@ -122,7 +126,9 @@ export const LeftSidebarGlobalSettings = ({
|
|||
</div>
|
||||
</div>
|
||||
<div className="d-flex">
|
||||
<span className="w-full">Background color of canvas</span>
|
||||
<span className="w-full">
|
||||
{t('leftSidebar.Settings.backgroundColorOfCanvas', 'Background color of canvas')}
|
||||
</span>
|
||||
<div className="canvas-codehinter-container">
|
||||
{showPicker && (
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const LeftSidebarItem = ({
|
||||
tip = '',
|
||||
|
|
@ -13,12 +14,13 @@ export const LeftSidebarItem = ({
|
|||
count,
|
||||
...rest
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<OverlayTrigger
|
||||
trigger={['click', 'hover', 'focus']}
|
||||
placement="right"
|
||||
delay={{ show: 800, hide: 100 }}
|
||||
overlay={<Tooltip id="button-tooltip">{tip}</Tooltip>}
|
||||
overlay={<Tooltip id="button-tooltip">{t(`leftSidebar.${text}.tip`, tip)}</Tooltip>}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
|
|
@ -39,7 +41,7 @@ export const LeftSidebarItem = ({
|
|||
</div>
|
||||
)}
|
||||
{badge && <LeftSidebarItem.Badge count={count} />}
|
||||
<p>{text && text}</p>
|
||||
<p>{text && t(`leftSidebar.${text}.text`, text)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|||
import Skeleton from 'react-loading-skeleton';
|
||||
import { debounce } from 'lodash';
|
||||
import Textarea from '@/_ui/Textarea';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
class ManageAppUsers extends React.Component {
|
||||
|
||||
class ManageAppUsersComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -133,7 +135,7 @@ class ManageAppUsers extends React.Component {
|
|||
return (
|
||||
<div>
|
||||
<button className="btn font-500 color-primary btn-sm" onClick={() => this.setState({ showModal: true })}>
|
||||
Share
|
||||
{this.props.t('editor.share', 'Share')}
|
||||
</button>
|
||||
|
||||
<Modal
|
||||
|
|
@ -148,7 +150,7 @@ class ManageAppUsers extends React.Component {
|
|||
contentClassName={this.props.darkMode ? 'theme-dark' : ''}
|
||||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Share</Modal.Title>
|
||||
<Modal.Title>{this.props.t('editor.share', 'Share')}</Modal.Title>
|
||||
<div>
|
||||
<Button variant={this.props.darkMode ? 'secondary' : 'light'} size="sm" onClick={() => this.hideModal()}>
|
||||
x
|
||||
|
|
@ -172,12 +174,16 @@ class ManageAppUsers extends React.Component {
|
|||
checked={this.state.app.is_public}
|
||||
disabled={this.state.ischangingVisibility}
|
||||
/>
|
||||
<span className="form-check-label">Make application public ?</span>
|
||||
<span className="form-check-label">
|
||||
{this.props.t('editor.shareModal.makeApplicationPublic', 'Make application public ?')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="shareable-link mb-3">
|
||||
<label className="form-label">
|
||||
<small>Get shareable link for this application</small>
|
||||
<small>
|
||||
{this.props.t('editor.shareModal.shareableLink', 'Get shareable link for this application')}
|
||||
</small>
|
||||
</label>
|
||||
<div className="input-group">
|
||||
<span className="input-group-text">{appLink}</span>
|
||||
|
|
@ -200,7 +206,9 @@ class ManageAppUsers extends React.Component {
|
|||
</div>
|
||||
<span className="input-group-text">
|
||||
<CopyToClipboard text={shareableLink} onCopy={() => toast.success('Link copied to clipboard')}>
|
||||
<button className="btn btn-secondary btn-sm">Copy</button>
|
||||
<button className="btn btn-secondary btn-sm">
|
||||
{this.props.t('editor.shareModal.copy', 'copy')}
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
</span>
|
||||
<div className="invalid-feedback">{slugError}</div>
|
||||
|
|
@ -209,7 +217,9 @@ class ManageAppUsers extends React.Component {
|
|||
<hr />
|
||||
<div className="shareable-link mb-3">
|
||||
<label className="form-label">
|
||||
<small>Get embeddable link for this application</small>
|
||||
<small>
|
||||
{this.props.t('editor.shareModal.embeddableLink', 'Get embeddable link for this application')}
|
||||
</small>
|
||||
</label>
|
||||
<div className="input-group">
|
||||
<Textarea
|
||||
|
|
@ -228,7 +238,9 @@ class ManageAppUsers extends React.Component {
|
|||
})
|
||||
}
|
||||
>
|
||||
<button className="btn btn-secondary btn-sm">Copy</button>
|
||||
<button className="btn btn-secondary btn-sm">
|
||||
{this.props.t('editor.shareModal.copy', 'copy')}
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -311,7 +323,7 @@ class ManageAppUsers extends React.Component {
|
|||
|
||||
<Modal.Footer>
|
||||
<Link to="/users" target="_blank" className="btn color-primary mt-3">
|
||||
Manage Users
|
||||
{this.props.t('editor.shareModal.manageUsers', 'Manage Users')}
|
||||
</Link>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
|
@ -320,4 +332,4 @@ class ManageAppUsers extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { ManageAppUsers };
|
||||
export const ManageAppUsers = withTranslation()(ManageAppUsersComponent);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import RunjsIcon from '../Icons/runjs.svg';
|
|||
import AddIcon from '../../../assets/images/icons/add-source.svg';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { allSvgs } from '@tooljet/plugins/client';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function DataSourceLister({
|
||||
dataSources,
|
||||
|
|
@ -13,7 +14,7 @@ function DataSourceLister({
|
|||
dataSourceModalHandler,
|
||||
}) {
|
||||
const [allSources] = useState([...dataSources, ...staticDataSources]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const computedStyles = {
|
||||
background: darkMode ? '#2f3c4c' : 'white',
|
||||
color: darkMode ? 'white' : '#1f2936',
|
||||
|
|
@ -45,7 +46,7 @@ function DataSourceLister({
|
|||
})}
|
||||
<div className="query-datasource-card" style={computedStyles} onClick={dataSourceModalHandler}>
|
||||
<AddIcon style={{ height: 25, width: 25, marginTop: '-3px' }} />
|
||||
<p> Add datasource</p>
|
||||
<p>{t('editor.queryManager.addDatasource', 'Add datasource')}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { JSONTree } from 'react-json-tree';
|
||||
import { Tab, ListGroup, Row } from 'react-bootstrap';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const Preview = ({ previewPanelRef, previewLoading, queryPreviewData, theme, darkMode }) => {
|
||||
const { t } = useTranslation();
|
||||
const [key, setKey] = React.useState('raw');
|
||||
const [isJson, setIsJson] = React.useState(false);
|
||||
const tabs = ['Json', 'Raw'];
|
||||
|
|
@ -27,7 +28,7 @@ const Preview = ({ previewPanelRef, previewLoading, queryPreviewData, theme, dar
|
|||
<div>
|
||||
<div className="row preview-header border-top" ref={previewPanelRef}>
|
||||
<div className="py-2" style={{ fontWeight: 600 }}>
|
||||
Preview
|
||||
{t('editor.preview', 'Preview')}
|
||||
</div>
|
||||
</div>
|
||||
<Tab.Container activeKey={key} onSelect={(k) => setKey(k)} defaultActiveKey="raw">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import SelectSearch, { fuzzySearch } from 'react-select-search';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
const operationColorMapping = {
|
||||
get: 'azure',
|
||||
|
|
@ -12,7 +13,7 @@ const operationColorMapping = {
|
|||
head: 'blue',
|
||||
};
|
||||
|
||||
class Openapi extends React.Component {
|
||||
class OpenapiComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { selectedDataSource } = props;
|
||||
|
|
@ -196,14 +197,16 @@ class Openapi extends React.Component {
|
|||
|
||||
return (
|
||||
<div>
|
||||
{!spec && <div className="p-3">Valid OpenAPI Spec is not available!.</div>}
|
||||
{!spec && (
|
||||
<div className="p-3">{this.props.t('openApi.noValidOpenApi', 'Valid OpenAPI Spec is not available!.')}</div>
|
||||
)}
|
||||
|
||||
{options && spec && (
|
||||
<div className="mb-3 mt-2">
|
||||
{baseUrls.length > 0 && (
|
||||
<div className="row g-2">
|
||||
<div className="col-12">
|
||||
<label className="form-label pt-2">Host</label>
|
||||
<label className="form-label pt-2">{this.props.t('globals.host', 'Host')}</label>
|
||||
</div>
|
||||
<div className="col openapi-operation-options">
|
||||
<SelectSearch
|
||||
|
|
@ -213,14 +216,14 @@ class Openapi extends React.Component {
|
|||
onChange={(value) => this.changeHost(value)}
|
||||
filterOptions={fuzzySearch}
|
||||
renderOption={this.renderHostOptions}
|
||||
placeholder="Select a host"
|
||||
placeholder={this.props.t('openApi.selectHost', 'Select a host')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="row g-2">
|
||||
<div className="col-12">
|
||||
<label className="form-label pt-2">Operation</label>
|
||||
<label className="form-label pt-2">{this.props.t('globals.operation', 'Operation')}</label>
|
||||
</div>
|
||||
<div className="col openapi-operation-options">
|
||||
<SelectSearch
|
||||
|
|
@ -230,7 +233,7 @@ class Openapi extends React.Component {
|
|||
onChange={(value) => this.changeOperation(value)}
|
||||
filterOptions={fuzzySearch}
|
||||
renderOption={this.renderOperationOption}
|
||||
placeholder="Select an operation"
|
||||
placeholder={this.props.t('openApi.selectOperation', 'Select an operation')}
|
||||
/>
|
||||
|
||||
{selectedOperation && (
|
||||
|
|
@ -250,7 +253,7 @@ class Openapi extends React.Component {
|
|||
<div className="row mt-2">
|
||||
{headerParams.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<h5 className="text-muted">HEADER</h5>
|
||||
<h5 className="text-muted">{this.props.t('globals.header', 'HEADER')}</h5>
|
||||
{headerParams.map((param) => (
|
||||
<div className="row input-group my-1" key={param.name}>
|
||||
<div className="col-4 field field-width-268">
|
||||
|
|
@ -286,7 +289,7 @@ class Openapi extends React.Component {
|
|||
|
||||
{pathParams.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<h5 className="text-muted">PATH</h5>
|
||||
<h5 className="text-muted">{this.props.t('globals.path', 'PATH')}</h5>
|
||||
{pathParams.map((param) => (
|
||||
<div className="row input-group my-1" key={param.name}>
|
||||
<div className="col-4 field field-width-268">
|
||||
|
|
@ -322,7 +325,7 @@ class Openapi extends React.Component {
|
|||
|
||||
{queryParams.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<h5 className="text-muted">QUERY</h5>
|
||||
<h5 className="text-muted">{this.props.t('globals.query', 'QUERY')}</h5>
|
||||
{queryParams.map((param) => (
|
||||
<div className="row input-group my-1" key={param.name}>
|
||||
<div className="col-4 field field-width-268">
|
||||
|
|
@ -358,7 +361,7 @@ class Openapi extends React.Component {
|
|||
|
||||
{requestBody?.schema?.properties && (
|
||||
<div className="mt-2">
|
||||
<h5 className="text-muted">REQUEST BODY</h5>
|
||||
<h5 className="text-muted">{this.props.t('globals.requestBody', 'REQUEST BODY')}</h5>
|
||||
{Object.keys(requestBody.schema.properties).map((param) => (
|
||||
<div className="row input-group my-1" key={param}>
|
||||
<div className="col-4 field field-width-268">
|
||||
|
|
@ -398,4 +401,4 @@ class Openapi extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { Openapi };
|
||||
export const Openapi = withTranslation()(OpenapiComponent);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import DOMPurify from 'dompurify';
|
|||
import Select from '@/_ui/Select';
|
||||
import { openapiService } from '@/_services';
|
||||
import { CodeHinter } from '../../CodeBuilder/CodeHinter';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
const operationColorMapping = {
|
||||
get: 'azure',
|
||||
|
|
@ -12,7 +13,7 @@ const operationColorMapping = {
|
|||
put: 'yellow',
|
||||
};
|
||||
|
||||
class Stripe extends React.Component {
|
||||
class StripeComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
|
@ -178,7 +179,7 @@ class Stripe extends React.Component {
|
|||
{loadingSpec && (
|
||||
<div className="p-3">
|
||||
<div className="spinner-border spinner-border-sm text-azure mx-2" role="status"></div>
|
||||
Please wait whle we load the OpenAPI specification for Stripe.
|
||||
{this.props.t('stripe', 'Please wait whle we load the OpenAPI specification for Stripe.')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -186,7 +187,7 @@ class Stripe extends React.Component {
|
|||
<div className="mb-3 mt-2">
|
||||
<div className="row g-2">
|
||||
<div className="col-12">
|
||||
<label className="form-label pt-2">Operation</label>
|
||||
<label className="form-label pt-2">{this.props.t('globals.operation', 'Operation')}</label>
|
||||
</div>
|
||||
<div className="col stripe-operation-options" style={{ width: '90px', marginTop: 0 }}>
|
||||
<Select
|
||||
|
|
@ -212,7 +213,7 @@ class Stripe extends React.Component {
|
|||
<div className="row mt-2">
|
||||
{pathParams.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<h5 className="text-muted">PATH</h5>
|
||||
<h5 className="text-muted">{this.props.t('globals.path', 'PATH')}</h5>
|
||||
{pathParams.map((param) => (
|
||||
<div className="row input-group my-1" key={param.name}>
|
||||
<div className="col-4 field field-width-268">
|
||||
|
|
@ -258,7 +259,7 @@ class Stripe extends React.Component {
|
|||
|
||||
{queryParams.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<h5 className="text-muted">QUERY</h5>
|
||||
<h5 className="text-muted">{this.props.t('globals.query', 'QUERY')}</h5>
|
||||
{queryParams.map((param) => (
|
||||
<div className="row input-group my-1" key={param.name}>
|
||||
<div className="col-4 field field-width-268">
|
||||
|
|
@ -304,7 +305,7 @@ class Stripe extends React.Component {
|
|||
|
||||
{requestBody.schema.properties && (
|
||||
<div className="mt-2">
|
||||
<h5 className="text-muted">REQUEST BODY</h5>
|
||||
<h5 className="text-muted">{this.props.t('globals.requestBody', 'REQUEST BODY')}</h5>
|
||||
{Object.keys(requestBody.schema.properties).map((param) => (
|
||||
<div className="row input-group my-1" key={param.name}>
|
||||
<div className="col-4 field field-width-268">
|
||||
|
|
@ -356,4 +357,4 @@ class Stripe extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { Stripe };
|
||||
export const Stripe = withTranslation()(StripeComponent);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import _, { isEmpty, isEqual } from 'lodash';
|
|||
import { Button, ButtonGroup, Dropdown } from 'react-bootstrap';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { allSvgs } from '@tooljet/plugins/client';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
const queryNameRegex = new RegExp('^[A-Za-z0-9_-]*$');
|
||||
|
||||
|
|
@ -23,7 +25,7 @@ const staticDataSources = [
|
|||
{ kind: 'runjs', id: 'runjs', name: 'Run JavaScript code' },
|
||||
];
|
||||
|
||||
let QueryManager = class QueryManager extends React.Component {
|
||||
class QueryManagerComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -448,7 +450,7 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
onClick={() => this.switchCurrentTab(1)}
|
||||
className={currentTab === 1 ? 'nav-link active' : 'nav-link'}
|
||||
>
|
||||
General
|
||||
{this.props.t('editor.queryManager.general', 'General')}
|
||||
</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
|
|
@ -456,7 +458,7 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
onClick={() => this.switchCurrentTab(2)}
|
||||
className={currentTab === 2 ? 'nav-link active' : 'nav-link'}
|
||||
>
|
||||
Advanced
|
||||
{this.props.t('editor.queryManager.advanced', 'Advanced')}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -498,7 +500,7 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
} ${this.state.selectedDataSource ? '' : 'disabled'}`}
|
||||
style={{ width: '72px', height: '28px' }}
|
||||
>
|
||||
Preview
|
||||
{this.props.t('editor.queryManager.preview', 'Preview')}
|
||||
</button>
|
||||
)}
|
||||
{selectedDataSource && (addingQuery || editingQuery) && (
|
||||
|
|
@ -524,14 +526,14 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
this.updateButtonText(dropDownButtonText, false);
|
||||
}}
|
||||
>
|
||||
{dropDownButtonText}
|
||||
{this.props.t(`editor.queryManager.${dropDownButtonText}`, dropDownButtonText)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
onClick={() => {
|
||||
this.updateButtonText(`${dropDownButtonText} & Run`, true);
|
||||
}}
|
||||
>
|
||||
{`${dropDownButtonText} & Run`}
|
||||
{this.props.t(`editor.queryManager.${dropDownButtonText} & Run`, `${dropDownButtonText} & Run`)}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
|
|
@ -580,7 +582,11 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
</svg>
|
||||
</p>
|
||||
)}
|
||||
{!this.state.isSourceSelected && <label className="form-label col-md-3">Select Datasource</label>}{' '}
|
||||
{!this.state.isSourceSelected && (
|
||||
<label className="form-label col-md-3">
|
||||
{this.props.t('editor.queryManager.selectDatasource', 'Select Datasource')}
|
||||
</label>
|
||||
)}{' '}
|
||||
{this?.state?.selectedDataSource?.kind && (
|
||||
<div className="header-query-datasource-card-container">
|
||||
<div
|
||||
|
|
@ -662,7 +668,9 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
onClick={() => this.toggleOption('runOnPageLoad')}
|
||||
checked={this.state.options.runOnPageLoad}
|
||||
/>
|
||||
<span className="form-check-label">Run this query on page load?</span>
|
||||
<span className="form-check-label">
|
||||
{this.props.t('editor.queryManager.runQueryOnPageLoad', 'Run this query on page load?')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="form-check form-switch">
|
||||
<input
|
||||
|
|
@ -671,7 +679,12 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
onClick={() => this.toggleOption('requestConfirmation')}
|
||||
checked={this.state.options.requestConfirmation}
|
||||
/>
|
||||
<span className="form-check-label">Request confirmation before running query?</span>
|
||||
<span className="form-check-label">
|
||||
{this.props.t(
|
||||
'editor.queryManager.confirmBeforeQueryRun',
|
||||
'Request confirmation before running query?'
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="form-check form-switch">
|
||||
|
|
@ -681,13 +694,17 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
onClick={() => this.toggleOption('showSuccessNotification')}
|
||||
checked={this.state.options.showSuccessNotification}
|
||||
/>
|
||||
<span className="form-check-label">Show notification on success?</span>
|
||||
<span className="form-check-label">
|
||||
{this.props.t('editor.queryManager.notificationOnSuccess', 'Show notification on success?')}
|
||||
</span>
|
||||
</div>
|
||||
{this.state.options.showSuccessNotification && (
|
||||
<div>
|
||||
<div className="row mt-3">
|
||||
<div className="col-auto">
|
||||
<label className="form-label p-2">Success Message</label>
|
||||
<label className="form-label p-2">
|
||||
{this.props.t('editor.queryManager.successMessage', 'Success Message')}
|
||||
</label>
|
||||
</div>
|
||||
<div className="col">
|
||||
<CodeHinter
|
||||
|
|
@ -696,14 +713,19 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
height="36px"
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
onChange={(value) => this.optionchanged('successMessage', value)}
|
||||
placeholder="Query ran successfully"
|
||||
placeholder={this.props.t(
|
||||
'editor.queryManager.queryRanSuccessfully',
|
||||
'Query ran successfully'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mt-3">
|
||||
<div className="col-auto">
|
||||
<label className="form-label p-2">Notification duration (s)</label>
|
||||
<label className="form-label p-2">
|
||||
{this.props.t('editor.queryManager.notificationDuration', 'Notification duration (s)')}
|
||||
</label>
|
||||
</div>
|
||||
<div className="col">
|
||||
<input
|
||||
|
|
@ -719,7 +741,7 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="hr-text hr-text-left">Events</div>
|
||||
<div className="hr-text hr-text-left">{this.props.t('editor.queryManager.events', 'Events')}</div>
|
||||
|
||||
<div className="query-manager-events">
|
||||
<EventManager
|
||||
|
|
@ -740,7 +762,6 @@ let QueryManager = class QueryManager extends React.Component {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
QueryManager = React.memo(QueryManager);
|
||||
export { QueryManager };
|
||||
export const QueryManager = withTranslation()(React.memo(QueryManagerComponent));
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ import 'codemirror/addon/search/match-highlighter';
|
|||
import 'codemirror/addon/hint/show-hint.css';
|
||||
import { CodeHinter } from '../CodeBuilder/CodeHinter';
|
||||
import { Popover, OverlayTrigger } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Transformation = ({ changeOption, currentState, options, darkMode }) => {
|
||||
const { t } = useTranslation();
|
||||
const defaultValue =
|
||||
options.transformation ||
|
||||
`// write your code here
|
||||
|
|
@ -17,7 +19,6 @@ return data.filter(row => row.amount > 1000);`;
|
|||
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const [enableTransformation, setEnableTransformation] = useState(() => options.enableTransformation);
|
||||
|
||||
// let suggestions = useMemo(() => getSuggestionKeys(currentState), [currentState.components, currentState.queries]);
|
||||
function codeChanged(value) {
|
||||
setValue(() => value);
|
||||
|
|
@ -32,11 +33,13 @@ return data.filter(row => row.amount > 1000);`;
|
|||
const popover = (
|
||||
<Popover id="transformation-popover-container">
|
||||
<p className="transformation-popover">
|
||||
Transformations can be used to transform the results of queries. All the app variables are accessible from
|
||||
transformers and supports JS libraries such as Lodash & Moment.
|
||||
{t(
|
||||
'editor.queryManager.transformation.transformationToolTip',
|
||||
'Transformations can be used to transform the results of queries. All the app variables are accessible from transformers and supports JS libraries such as Lodash & Moment.'
|
||||
)}
|
||||
<br />
|
||||
<a href="https://docs.tooljet.io/docs/tutorial/transformations" target="_blank" rel="noreferrer">
|
||||
Read documentation
|
||||
{t('globals.readDocumentation', 'Read documentation')}
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
|
|
@ -64,7 +67,7 @@ return data.filter(row => row.amount > 1000);`;
|
|||
}}
|
||||
className="form-check-label mx-1"
|
||||
>
|
||||
Transformations
|
||||
{t('editor.queryManager.transformation.transformations', 'Transformations')}
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import { appService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const ReleaseVersionButton = function DeployVersionButton({
|
||||
appId,
|
||||
|
|
@ -12,7 +13,7 @@ export const ReleaseVersionButton = function DeployVersionButton({
|
|||
saveEditingVersion,
|
||||
}) {
|
||||
const [isReleasing, setIsReleasing] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const releaseVersion = (editingVersion) => {
|
||||
setIsReleasing(true);
|
||||
saveEditingVersion();
|
||||
|
|
@ -41,7 +42,7 @@ export const ReleaseVersionButton = function DeployVersionButton({
|
|||
className={`btn btn-primary btn-sm ${isVersionReleased ? 'disabled' : ''} ${isReleasing ? 'btn-loading' : ''}`}
|
||||
onClick={() => releaseVersion(editingVersion)}
|
||||
>
|
||||
Release
|
||||
{t('editor.release', 'Release')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ import { DarkModeToggle } from '@/_components/DarkModeToggle';
|
|||
import LogoIcon from './Icons/logo.svg';
|
||||
import { DataSourceTypes } from './DataSourceManager/SourceComponents';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
class Viewer extends React.Component {
|
||||
|
||||
class ViewerComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -231,7 +233,7 @@ class Viewer extends React.Component {
|
|||
<div className="maintenance_container">
|
||||
<div className="card">
|
||||
<div className="card-body" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<h3>Sorry!. This app is under maintenance</h3>
|
||||
<h3>{this.props.t('viewer', 'Sorry!. This app is under maintenance')}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -327,4 +329,4 @@ class Viewer extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { Viewer };
|
||||
export const Viewer = withTranslation()(ViewerComponent);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function Confirm({ show, message, onConfirm, onCancel, queryConfirmationData, darkMode }) {
|
||||
const [showModal, setShow] = useState(show);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
setShow(show);
|
||||
|
|
@ -32,10 +34,10 @@ export function Confirm({ show, message, onConfirm, onCancel, queryConfirmationD
|
|||
<Modal.Body>{message}</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={handleClose}>
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleConfirm}>
|
||||
Yes
|
||||
{t('globals.yes', 'Yes')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import React, { useState } from 'react';
|
|||
import { DraggableBox } from './DraggableBox';
|
||||
import Fuse from 'fuse.js';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const WidgetManager = function WidgetManager({ componentTypes, zoomLevel, currentLayout, darkMode }) {
|
||||
const [filteredComponents, setFilteredComponents] = useState(componentTypes);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const { t } = useTranslation();
|
||||
|
||||
function handleSearchQueryChange(e) {
|
||||
const { value } = e.target;
|
||||
|
|
@ -53,9 +55,12 @@ export const WidgetManager = function WidgetManager({ componentTypes, zoomLevel,
|
|||
{/* <div class="empty-img">
|
||||
<img src="./static/illustrations/undraw_printing_invoices_5r4r.svg" height="128" alt="" />
|
||||
</div> */}
|
||||
<p className="empty-title">No results found</p>
|
||||
<p className="empty-title">{t('widgetManager.noResults', 'No results found')}</p>
|
||||
<p className={`empty-subtitle ${darkMode ? 'text-white-50' : 'text-secondary'}`}>
|
||||
Try adjusting your search or filter to find what you're looking for.
|
||||
{t(
|
||||
'widgetManager.tryAdjustingFilterMessage',
|
||||
"Try adjusting your search or filter to find what you're looking for."
|
||||
)}
|
||||
</p>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-azure mt-3"
|
||||
|
|
@ -64,16 +69,16 @@ export const WidgetManager = function WidgetManager({ componentTypes, zoomLevel,
|
|||
setSearchQuery('');
|
||||
}}
|
||||
>
|
||||
clear query
|
||||
{t('widgetManager.clearQuery', 'clear query')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const commonSection = { title: 'commonly used', items: [] };
|
||||
const layoutsSection = { title: 'layouts', items: [] };
|
||||
const formSection = { title: 'forms', items: [] };
|
||||
const integrationSection = { title: 'integrations', items: [] };
|
||||
const otherSection = { title: 'others', items: [] };
|
||||
const commonSection = { title: t('widgetManager.commonlyUsed', 'commonly used'), items: [] };
|
||||
const layoutsSection = { title: t('widgetManager.layouts', 'layouts'), items: [] };
|
||||
const formSection = { title: t('widgetManager.forms', 'forms'), items: [] };
|
||||
const integrationSection = { title: t('widgetManager.integrations', 'integrations'), items: [] };
|
||||
const otherSection = { title: t('widgetManager.others', 'others'), items: [] };
|
||||
const allWidgets = [];
|
||||
|
||||
const commonItems = ['Table', 'Chart', 'Button', 'Text', 'Datepicker'];
|
||||
|
|
@ -126,7 +131,7 @@ export const WidgetManager = function WidgetManager({ componentTypes, zoomLevel,
|
|||
<input
|
||||
type="text"
|
||||
className={`form-control mt-3 mb-2 ${darkMode && 'dark-theme-placeholder'}`}
|
||||
placeholder="Search…"
|
||||
placeholder={t('globals.search', 'Search') + '...'}
|
||||
value={searchQuery}
|
||||
onChange={(e) => handleSearchQueryChange(e)}
|
||||
data-cy="widget-search-box"
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { Link } from 'react-router-dom';
|
|||
import { toast } from 'react-hot-toast';
|
||||
import { validateEmail } from '../_helpers/utils';
|
||||
import { authenticationService } from '@/_services';
|
||||
|
||||
class ForgotPassword extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class ForgotPasswordComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -59,15 +59,17 @@ class ForgotPassword extends React.Component {
|
|||
</div>
|
||||
<form className="card card-md" action="." method="get" autoComplete="off">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title text-center mb-4">Forgot Password</h2>
|
||||
<h2 className="card-title text-center mb-4">
|
||||
{this.props.t('loginSignupPage.forgotPassword', 'Forgot Password')}
|
||||
</h2>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Email address</label>
|
||||
<label className="form-label">{this.props.t('loginSignupPage.emailAddress', 'Email address')}</label>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="email"
|
||||
type="email"
|
||||
className="form-control"
|
||||
placeholder="Enter email"
|
||||
placeholder={this.props.t('loginSignupPage.enterEmail', 'Enter email')}
|
||||
data-testid="emailField"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -78,15 +80,15 @@ class ForgotPassword extends React.Component {
|
|||
onClick={this.handleClick}
|
||||
disabled={isLoading || !this.state.email}
|
||||
>
|
||||
Reset Password
|
||||
{this.props.t('loginSignupPage.resetPassword', 'Reset Password')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div className="text-center text-muted mt-3">
|
||||
Don't have account yet?
|
||||
{this.props.t('loginSignupPage.dontHaveAccount', `Don't have account yet?`)}
|
||||
<Link to={'/signup'} tabIndex="-1">
|
||||
Sign up
|
||||
{this.props.t('loginSignupPage.signUp', `Sign up`)}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -95,4 +97,4 @@ class ForgotPassword extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { ForgotPassword };
|
||||
export const ForgotPassword = withTranslation()(ForgotPasswordComponent);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import useHover from '@/_hooks/useHover';
|
|||
import configs from './Configs/AppIcon.json';
|
||||
import { Link } from 'react-router-dom';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const { defaultIcon } = configs;
|
||||
|
||||
export default function AppCard({
|
||||
|
|
@ -25,6 +25,7 @@ export default function AppCard({
|
|||
const [hoverRef, isHovered] = useHover();
|
||||
const [focused, setFocused] = useState(false);
|
||||
const [isMenuOpen, setMenuOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onMenuToggle = useCallback(
|
||||
(status) => {
|
||||
|
|
@ -115,7 +116,7 @@ export default function AppCard({
|
|||
<ToolTip message="Open in app builder">
|
||||
<Link to={`/apps/${app.id}`}>
|
||||
<button type="button" className="btn btn-sm btn-light edit-button" data-cy="edit-button">
|
||||
Edit
|
||||
{t('globals.edit', 'Edit')}
|
||||
</button>
|
||||
</Link>
|
||||
</ToolTip>
|
||||
|
|
@ -124,7 +125,9 @@ export default function AppCard({
|
|||
<div className={`col-${canUpdate ? '6' : '12'} ps-1`}>
|
||||
<ToolTip
|
||||
message={
|
||||
app?.current_version_id === null ? 'App does not have a deployed version' : 'Open in app viewer'
|
||||
app?.current_version_id === null
|
||||
? t('homePage.appCard.noDeployedVersion', 'App does not have a deployed version')
|
||||
: t('homePage.appCard.openInAppViewer', 'Open in app viewer')
|
||||
}
|
||||
>
|
||||
<span>
|
||||
|
|
@ -141,7 +144,9 @@ export default function AppCard({
|
|||
}}
|
||||
data-cy="launch-button"
|
||||
>
|
||||
{app?.is_maintenance_on ? 'Maintenance' : 'Launch'}
|
||||
{app?.is_maintenance_on
|
||||
? t('homePage.appCard.maintenance', 'Maintenance')
|
||||
: t('homePage.appCard.launch', 'Launch')}
|
||||
</button>
|
||||
</span>
|
||||
</ToolTip>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import AppCard from './AppCard';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const AppList = (props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div style={{ minHeight: '600px' }} className="app-list">
|
||||
{props.isLoading && (
|
||||
|
|
@ -54,7 +56,7 @@ const AppList = (props) => {
|
|||
{!props.isLoading && props.meta.total_count === 0 && !(props.currentFolder && props.currentFolder.id) && (
|
||||
<div>
|
||||
<span className={`d-block text-center text-body pt-5 ${props.darkMode && 'text-white-50'}`}>
|
||||
No Applications found
|
||||
{t('homePage.noApplicationFound', 'No Applications found')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -70,7 +72,7 @@ const AppList = (props) => {
|
|||
className={`d-block text-center text-body ${props.darkMode && 'text-white-50'}`}
|
||||
data-cy="empty-folder-text"
|
||||
>
|
||||
This folder is empty
|
||||
{t('homePage.thisFolderIsEmpty', 'This folder is empty')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Popover from 'react-bootstrap/Popover';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const AppMenu = function AppMenu({
|
||||
deleteApp,
|
||||
|
|
@ -18,6 +19,7 @@ export const AppMenu = function AppMenu({
|
|||
const closeMenu = () => {
|
||||
document.body.click();
|
||||
};
|
||||
const { t } = useTranslation();
|
||||
const Field = ({ text, onClick, customClass }) => {
|
||||
return (
|
||||
<div className={`field mb-3${customClass ? ` ${customClass}` : ''}`}>
|
||||
|
|
@ -45,19 +47,36 @@ export const AppMenu = function AppMenu({
|
|||
<Popover id="popover-app-menu" className={darkMode && 'popover-dark-themed'}>
|
||||
<Popover.Content bsPrefix="popover-body">
|
||||
<div data-cy="card-options">
|
||||
{canUpdateApp && <Field text="Change icon" onClick={() => openAppActionModal('change-icon')} />}
|
||||
{canUpdateApp && (
|
||||
<Field
|
||||
text={t('homePage.appCard.changeIcon', 'Change Icon')}
|
||||
onClick={() => openAppActionModal('change-icon')}
|
||||
/>
|
||||
)}
|
||||
{canCreateApp && (
|
||||
<>
|
||||
<Field text="Add to folder" onClick={() => openAppActionModal('add-to-folder')} />
|
||||
<Field
|
||||
text={t('homePage.appCard.addToFolder', 'Add to folder')}
|
||||
onClick={() => openAppActionModal('add-to-folder')}
|
||||
/>
|
||||
|
||||
{currentFolder.id && (
|
||||
<Field text="Remove from folder" onClick={() => openAppActionModal('remove-app-from-folder')} />
|
||||
<Field
|
||||
text={t('homePage.appCard.removeFromFolder', 'Remove from folder')}
|
||||
onClick={() => openAppActionModal('remove-app-from-folder')}
|
||||
/>
|
||||
)}
|
||||
<Field text="Clone app" onClick={cloneApp} />
|
||||
<Field text="Export app" onClick={exportApp} />
|
||||
<Field text={t('homePage.appCard.cloneApp', 'Clone app')} onClick={cloneApp} />
|
||||
<Field text={t('homePage.appCard.exportApp', 'Export app')} onClick={exportApp} />
|
||||
</>
|
||||
)}
|
||||
{canDeleteApp && <Field text="Delete app" customClass="field__danger" onClick={deleteApp} />}
|
||||
{canDeleteApp && (
|
||||
<Field
|
||||
text={t('homePage.appCard.deleteApp', 'Delete app')}
|
||||
customClass="field__danger"
|
||||
onClick={deleteApp}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import TemplateLibraryModal from './TemplateLibraryModal/';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const BlankPage = function BlankPage({
|
||||
createApp,
|
||||
|
|
@ -12,6 +13,7 @@ export const BlankPage = function BlankPage({
|
|||
hideTemplateLibraryModal,
|
||||
viewTemplateLibraryModal,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<div className="page-wrapper">
|
||||
|
|
@ -31,11 +33,13 @@ export const BlankPage = function BlankPage({
|
|||
style={{ color: darkMode && '#ffffff' }}
|
||||
data-cy="empty-welcome-header"
|
||||
>
|
||||
Welcome to ToolJet!
|
||||
{t('blankPage.welcomeToToolJet', 'Welcome to ToolJet!')}
|
||||
</h3>
|
||||
<p className={`empty-title ${darkMode && 'text-white-50'}`} data-cy="empty-description">
|
||||
You can get started by creating a new application or by creating an application using a template in
|
||||
ToolJet Library.
|
||||
{t(
|
||||
'blankPage.getStartedCreateNewApp',
|
||||
'You can get started by creating a new application or by creating an application using a template in ToolJet Library.'
|
||||
)}
|
||||
</p>
|
||||
<div className="empty-action">
|
||||
<a
|
||||
|
|
@ -43,14 +47,14 @@ export const BlankPage = function BlankPage({
|
|||
className={`btn btn-primary ${creatingApp ? 'btn-loading' : ''}`}
|
||||
data-cy="create-new-application"
|
||||
>
|
||||
Create new application
|
||||
{t('homePage.header.createNewApplication', 'Create new application')}
|
||||
</a>
|
||||
<a
|
||||
className={`btn empty-import-button ${isImportingApp ? 'btn-loading' : ''}`}
|
||||
onChange={handleImportApp}
|
||||
>
|
||||
<label style={{ visibility: isImportingApp ? 'hidden' : 'visible' }} data-cy="import-an-application">
|
||||
Import an application
|
||||
{t('blankPage.importApplication', 'Import an application')}
|
||||
<input type="file" ref={fileInput} style={{ display: 'none' }} />
|
||||
</label>
|
||||
</a>
|
||||
|
|
@ -60,7 +64,7 @@ export const BlankPage = function BlankPage({
|
|||
style={{ marginLeft: '24px' }}
|
||||
data-cy="choose-from-template"
|
||||
>
|
||||
Choose from template
|
||||
{t('homePage.header.chooseFromTemplate', 'Choose from template')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Popover from 'react-bootstrap/Popover';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const FolderMenu = function FolderMenu({
|
||||
deleteFolder,
|
||||
|
|
@ -29,7 +30,7 @@ export const FolderMenu = function FolderMenu({
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
|
|
@ -40,8 +41,16 @@ export const FolderMenu = function FolderMenu({
|
|||
<Popover id="popover-app-menu" className={darkMode && 'popover-dark-themed'} data-cy="folder-card">
|
||||
<Popover.Content bsPrefix="popover-body">
|
||||
<div>
|
||||
{canUpdateFolder && <Field text="Edit folder" onClick={editFolder} />}
|
||||
{canDeleteFolder && <Field text="Delete folder" customClass="field__danger" onClick={deleteFolder} />}
|
||||
{canUpdateFolder && (
|
||||
<Field text={t('homePage.foldersSection.editFolder', 'Edit folder')} onClick={editFolder} />
|
||||
)}
|
||||
{canDeleteFolder && (
|
||||
<Field
|
||||
text={t('homePage.foldersSection.deleteFolder', 'Delete folder')}
|
||||
customClass="field__danger"
|
||||
onClick={deleteFolder}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import Modal from './Modal';
|
|||
import { FolderMenu } from './FolderMenu';
|
||||
import useHover from '@/_hooks/useHover';
|
||||
import { ConfirmDialog } from '@/_components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Folders = function Folders({
|
||||
folders,
|
||||
|
|
@ -21,7 +22,7 @@ export const Folders = function Folders({
|
|||
const [isMenuOpen, setMenuOpen] = useState(false);
|
||||
const [hoverRef, isHovered] = useHover();
|
||||
const [focused, setFocused] = useState(false);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const onMenuToggle = useCallback(
|
||||
(status) => {
|
||||
setMenuOpen(!!status);
|
||||
|
|
@ -141,7 +142,10 @@ export const Folders = function Folders({
|
|||
<div className="w-100 px-3 pe-lg-4 folder-list">
|
||||
<ConfirmDialog
|
||||
show={showDeleteConfirmation}
|
||||
message={`Are you sure you want to delete the folder? Apps within the folder will not be deleted.`}
|
||||
message={t(
|
||||
'homePage.foldersSection.wishToDeleteFolder',
|
||||
`Are you sure you want to delete the folder? Apps within the folder will not be deleted.`
|
||||
)}
|
||||
confirmButtonLoading={isDeleting}
|
||||
onConfirm={() => executeDeletion()}
|
||||
onCancel={() => cancelDeleteDialog()}
|
||||
|
|
@ -159,12 +163,12 @@ export const Folders = function Folders({
|
|||
onClick={() => handleFolderChange({})}
|
||||
data-cy="all-applications-link"
|
||||
>
|
||||
All applications
|
||||
{t('homePage.foldersSection.allApplications', 'All applications')}
|
||||
</a>
|
||||
<hr></hr>
|
||||
<div className="d-flex justify-content-between mb-3">
|
||||
<div className="folder-info" data-cy="folder-info">
|
||||
Folders
|
||||
{t('homePage.foldersSection.folders', 'Folders')}
|
||||
</div>
|
||||
{canCreateFolder && (
|
||||
<div
|
||||
|
|
@ -175,7 +179,7 @@ export const Folders = function Folders({
|
|||
}}
|
||||
data-cy="create-new-folder-button"
|
||||
>
|
||||
+ Create new folder
|
||||
{t('homePage.foldersSection.createNewFolder', '+ Create new folder')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -235,14 +239,21 @@ export const Folders = function Folders({
|
|||
))
|
||||
: !isLoading && (
|
||||
<div className="folder-info" data-cy="folder-info-text">
|
||||
You haven't created any folders. Use folders to organize your apps
|
||||
{t(
|
||||
'homePage.foldersSection.noFolders',
|
||||
`You haven't created any folders. Use folders to organize your apps`
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
show={showForm || showUpdateForm}
|
||||
closeModal={() => (showUpdateForm ? setShowUpdateForm(false) : setShowForm(false))}
|
||||
title={showUpdateForm ? 'Update Folder' : 'Create folder'}
|
||||
title={
|
||||
showUpdateForm
|
||||
? t('homePage.foldersSection.updateFolder', 'Update Folder')
|
||||
: t('homePage.foldersSection.createFolder', 'Create folder')
|
||||
}
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col modal-main">
|
||||
|
|
@ -250,7 +261,7 @@ export const Folders = function Folders({
|
|||
type="text"
|
||||
onChange={(e) => setNewFolderName(e.target.value)}
|
||||
className="form-control"
|
||||
placeholder="folder name"
|
||||
placeholder={t('homePage.foldersSection.folderName', 'folder name')}
|
||||
disabled={isCreating || isUpdating}
|
||||
value={newFolderName}
|
||||
maxLength={25}
|
||||
|
|
@ -265,14 +276,16 @@ export const Folders = function Folders({
|
|||
onClick={() => (showUpdateForm ? setShowUpdateForm(false) : setShowForm(false))}
|
||||
data-cy="cancel-button"
|
||||
>
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-primary ${isCreating || isUpdating ? 'btn-loading' : ''}`}
|
||||
onClick={showUpdateForm ? executeEditFolder : saveFolder}
|
||||
data-cy={`${showUpdateForm ? 'update-folder' : 'create-folder'}-button`}
|
||||
>
|
||||
{showUpdateForm ? 'Update folder' : 'Create folder'}
|
||||
{showUpdateForm
|
||||
? t('homePage.foldersSection.updateFolder', 'Update Folder')
|
||||
: t('homePage.foldersSection.createFolder', 'Create folder')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { SearchBox } from '@/_components/SearchBox';
|
||||
import { Button, ButtonGroup, Dropdown } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function Header({
|
||||
folderName,
|
||||
|
|
@ -14,6 +15,7 @@ export default function Header({
|
|||
fileInput,
|
||||
darkMode,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-4">
|
||||
|
|
@ -22,7 +24,7 @@ export default function Header({
|
|||
</h2>
|
||||
</div>
|
||||
<div className="col-8 ms-auto d-print-none d-flex flex-row justify-content-end">
|
||||
<SearchBox onSubmit={onSearchSubmit} darkMode={darkMode} />
|
||||
<SearchBox onSubmit={onSearchSubmit} darkMode={darkMode} placeholder={t('globals.search', 'Search')} />
|
||||
{canCreateApp() && (
|
||||
<>
|
||||
{canCreateApp() && (
|
||||
|
|
@ -33,13 +35,15 @@ export default function Header({
|
|||
data-cy="create-new-app-button"
|
||||
>
|
||||
{isImportingApp && <span className="spinner-border spinner-border-sm mx-2" role="status"></span>}
|
||||
Create new application
|
||||
{t('homePage.header.createNewApplication', 'Create new application')}
|
||||
</Button>
|
||||
<Dropdown.Toggle split className="btn btn-primary d-none d-lg-inline mb-3 " />
|
||||
<Dropdown.Menu className="import-lg-position">
|
||||
<Dropdown.Item onClick={showTemplateLibraryModal}>Choose from template</Dropdown.Item>
|
||||
<Dropdown.Item onClick={showTemplateLibraryModal}>
|
||||
{t('homePage.header.chooseFromTemplate', 'Choose from template')}
|
||||
</Dropdown.Item>
|
||||
<label className="homepage-dropdown-style" onChange={handleImportApp}>
|
||||
Import
|
||||
{t('homePage.header.import', 'Import')}
|
||||
<input type="file" accept=".json" ref={fileInput} style={{ display: 'none' }} />
|
||||
</label>
|
||||
</Dropdown.Menu>
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ import Modal from './Modal';
|
|||
import SelectSearch from 'react-select-search';
|
||||
import Fuse from 'fuse.js';
|
||||
import configs from './Configs/AppIcon.json';
|
||||
|
||||
import { withTranslation } from 'react-i18next';
|
||||
const { iconList, defaultIcon } = configs;
|
||||
|
||||
class HomePage extends React.Component {
|
||||
class HomePageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -505,7 +505,10 @@ class HomePage extends React.Component {
|
|||
<div className="wrapper home-page">
|
||||
<ConfirmDialog
|
||||
show={showAppDeletionConfirmation}
|
||||
message={'The app and the associated data will be permanently deleted, do you want to continue?'}
|
||||
message={this.props.t(
|
||||
'homePage.deleteAppAndData',
|
||||
'The app and the associated data will be permanently deleted, do you want to continue?'
|
||||
)}
|
||||
confirmButtonLoading={isDeletingApp}
|
||||
onConfirm={() => this.executeAppDeletion()}
|
||||
onCancel={() => this.cancelDeleteAppDialog()}
|
||||
|
|
@ -514,7 +517,10 @@ class HomePage extends React.Component {
|
|||
|
||||
<ConfirmDialog
|
||||
show={showRemoveAppFromFolderConfirmation}
|
||||
message={'The app will be removed from this folder, do you want to continue?'}
|
||||
message={this.props.t(
|
||||
'homePage.removeAppFromFolder',
|
||||
'The app will be removed from this folder, do you want to continue?'
|
||||
)}
|
||||
confirmButtonLoading={isDeletingAppFromFolder}
|
||||
onConfirm={() => this.removeAppFromFolder()}
|
||||
onCancel={() =>
|
||||
|
|
@ -529,14 +535,14 @@ class HomePage extends React.Component {
|
|||
<Modal
|
||||
show={showAddToFolderModal && !!appOperations.selectedApp}
|
||||
closeModal={() => this.setState({ showAddToFolderModal: false, appOperations: {} })}
|
||||
title="Add to folder"
|
||||
title={this.props.t('homePage.appCard.addToFolder', 'Add to folder')}
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col modal-main">
|
||||
<div className="mb-3" data-cy="move-selected-app-to-text">
|
||||
<span>Move</span>
|
||||
<span>{this.props.t('homePage.appCard.move', 'Move')}</span>
|
||||
<strong>{` "${appOperations?.selectedApp?.name}" `}</strong>
|
||||
<span>to</span>
|
||||
<span>{this.props.t('homePage.appCard.to', 'to')}</span>
|
||||
</div>
|
||||
<div data-cy="select-folder">
|
||||
<SelectSearch
|
||||
|
|
@ -552,7 +558,7 @@ class HomePage extends React.Component {
|
|||
value={appOperations?.selectedFolder}
|
||||
emptyMessage={this.state.folders === 0 ? 'No folders present' : 'Not found'}
|
||||
filterOptions={this.customFuzzySearch}
|
||||
placeholder="Select folder"
|
||||
placeholder={this.props.t('homePage.appCard.selectFolder', 'Select folder')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -564,14 +570,14 @@ class HomePage extends React.Component {
|
|||
onClick={() => this.setState({ showAddToFolderModal: false, appOperations: {} })}
|
||||
data-cy="cancel-button"
|
||||
>
|
||||
Cancel
|
||||
{this.props.t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-primary ${appOperations?.isAdding ? 'btn-loading' : ''}`}
|
||||
onClick={this.addAppToFolder}
|
||||
data-cy="add-to-folder-button"
|
||||
>
|
||||
Add to folder
|
||||
{this.props.t('homePage.appCard.addToFolder', 'Add to folder')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -580,7 +586,7 @@ class HomePage extends React.Component {
|
|||
<Modal
|
||||
show={showChangeIconModal && !!appOperations.selectedApp}
|
||||
closeModal={() => this.setState({ showChangeIconModal: false, appOperations: {} })}
|
||||
title="Change Icon"
|
||||
title={this.props.t('homePage.appCard.changeIcon', 'Change Icon')}
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col modal-main icon-change-modal">
|
||||
|
|
@ -594,14 +600,14 @@ class HomePage extends React.Component {
|
|||
onClick={() => this.setState({ showChangeIconModal: false, appOperations: {} })}
|
||||
data-cy="cancel-button"
|
||||
>
|
||||
Cancel
|
||||
{this.props.t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-primary ${appOperations?.isAdding ? 'btn-loading' : ''}`}
|
||||
onClick={this.changeIcon}
|
||||
data-cy="change-button"
|
||||
>
|
||||
Change
|
||||
{this.props.t('homePage.change', 'Change')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -697,4 +703,4 @@ class HomePage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { HomePage };
|
||||
export const HomePage = withTranslation()(HomePageComponent);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function TemplateCard() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="template-card-wrapper">
|
||||
<div className="template-icon-wrapper"></div>
|
||||
<div className="template-card-details">
|
||||
<p className="template-card-title">Lead generetion</p>
|
||||
<p className="template-card-title">{t('homePage.templateCard.leadGeneration', 'Lead generation')}</p>
|
||||
<div className="template-action-wrapper">
|
||||
<p>Use</p>
|
||||
<p>Preview</p>
|
||||
<p>{t('homePage.templateCard.use', 'Use')}</p>
|
||||
<p>{t('homePage.templateCard.preview', 'Preview')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { ListGroup } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function AppList(props) {
|
||||
const { apps, selectedApp, selectApp } = props;
|
||||
|
|
@ -31,6 +32,7 @@ export default function AppList(props) {
|
|||
|
||||
const SearchBoxContainer = ({ onChange, queryString }) => {
|
||||
const [searchText, setSearchText] = React.useState(queryString ?? '');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = (e) => {
|
||||
setSearchText(e.target.value);
|
||||
|
|
@ -103,7 +105,13 @@ const SearchBoxContainer = ({ onChange, queryString }) => {
|
|||
</svg>
|
||||
</span>
|
||||
)}
|
||||
<input type="text" value={searchText} onChange={handleChange} className="form-control" placeholder="Search" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchText}
|
||||
onChange={handleChange}
|
||||
className="form-control"
|
||||
placeholder={t('globals.search', 'Search')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { toast } from 'react-hot-toast';
|
|||
import _ from 'lodash';
|
||||
import TemplateDisplay from './TemplateDisplay';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const identifyUniqueCategories = (templates) =>
|
||||
['all', ...new Set(_.map(templates, 'category'))].map((categoryId) => ({
|
||||
|
|
@ -22,6 +23,7 @@ export default function TemplateLibraryModal(props) {
|
|||
(app) => selectedCategory.id === 'all' || app.category === selectedCategory.id
|
||||
);
|
||||
const [selectedApp, selectApp] = useState(undefined);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
selectApp(filteredApps[0]);
|
||||
|
|
@ -76,7 +78,7 @@ export default function TemplateLibraryModal(props) {
|
|||
centered
|
||||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Select template</Modal.Title>
|
||||
<Modal.Title>{t('homePage.templateLibraryModal.select', 'Select template')}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Container fluid>
|
||||
|
|
@ -106,7 +108,7 @@ export default function TemplateLibraryModal(props) {
|
|||
>
|
||||
<div className="d-flex flex-row align-items-center" style={{ height: '100%' }}>
|
||||
<Button variant="outline-primary" onClick={props.onCloseButtonClick}>
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</Button>
|
||||
<a
|
||||
href="#"
|
||||
|
|
@ -115,7 +117,7 @@ export default function TemplateLibraryModal(props) {
|
|||
deployApp();
|
||||
}}
|
||||
>
|
||||
Create application from template
|
||||
{t('homePage.templateLibraryModal.createAppfromTemplate', 'Create application from template')}
|
||||
</a>
|
||||
</div>
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import GoogleSSOLoginButton from '@ee/components/LoginPage/GoogleSSOLoginButton'
|
|||
import GitSSOLoginButton from '@ee/components/LoginPage/GitSSOLoginButton';
|
||||
import { validateEmail } from '../_helpers/utils';
|
||||
import { ShowLoading } from '@/_components';
|
||||
class LoginPage extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class LoginPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
|
@ -150,32 +151,42 @@ class LoginPage extends React.Component {
|
|||
<ShowLoading />
|
||||
) : (
|
||||
<div className="card-body">
|
||||
{!configs && <div className="text-center">No login methods enabled for this workspace</div>}
|
||||
{!configs && (
|
||||
<div className="text-center">
|
||||
{this.props.t(
|
||||
'loginSignupPage.noLoginMethodsEnabled',
|
||||
'No login methods enabled for this workspace'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{configs?.form?.enabled && (
|
||||
<div>
|
||||
<h2 className="card-title text-center mb-4" data-cy="login-page-header">
|
||||
Login to {this.single_organization ? 'your account' : configs?.name || 'your account'}
|
||||
{this.props.t('loginSignupPage.loginTo', 'Login to')}{' '}
|
||||
{this.single_organization
|
||||
? this.props.t('loginSignupPage.yourAccount', 'your account')
|
||||
: configs?.name || this.props.t('loginSignupPage.yourAccount', 'your account')}
|
||||
</h2>
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="email-label">
|
||||
Email address
|
||||
{this.props.t('loginSignupPage.emailAddress', 'Email address')}
|
||||
</label>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="email"
|
||||
type="email"
|
||||
className="form-control"
|
||||
placeholder="Email"
|
||||
placeholder={this.props.t('loginSignupPage.enterEmail', 'Enter email')}
|
||||
data-testid="emailField"
|
||||
data-cy="email-text-field"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label className="form-label" data-cy="password-label">
|
||||
Password
|
||||
{this.props.t('loginSignupPage.password', 'Password')}
|
||||
<span className="form-label-description">
|
||||
<Link to={'/forgot-password'} tabIndex="-1" data-cy="forgot-password-link">
|
||||
Forgot password
|
||||
{this.props.t('loginSignupPage.forgotPassword', 'Forgot Password')}
|
||||
</Link>
|
||||
</span>
|
||||
</label>
|
||||
|
|
@ -185,7 +196,7 @@ class LoginPage extends React.Component {
|
|||
name="password"
|
||||
type={this.state.showPassword ? 'text' : 'password'}
|
||||
className="form-control"
|
||||
placeholder="Password"
|
||||
placeholder={this.props.t('loginSignupPage.password', 'Password')}
|
||||
autoComplete="off"
|
||||
data-testid="passwordField"
|
||||
data-cy="password-text-field"
|
||||
|
|
@ -207,7 +218,7 @@ class LoginPage extends React.Component {
|
|||
htmlFor="check-input"
|
||||
data-cy="show-password-label"
|
||||
>
|
||||
show password
|
||||
{this.props.t('loginSignupPage.showPassword', 'show password')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -224,7 +235,7 @@ class LoginPage extends React.Component {
|
|||
onClick={this.authUser}
|
||||
data-cy="login-button"
|
||||
>
|
||||
Sign in
|
||||
{this.props.t('loginSignupPage.signIn', 'Sign in')}
|
||||
</button>
|
||||
)}
|
||||
{this.state.configs?.google?.enabled && (
|
||||
|
|
@ -240,9 +251,9 @@ class LoginPage extends React.Component {
|
|||
</form>
|
||||
{!this.organizationId && configs?.form?.enabled && configs?.form?.enable_sign_up && (
|
||||
<div className="text-center text-secondary mt-3" data-cy="sign-up-message">
|
||||
Don't have account yet?
|
||||
{this.props.t('loginSignupPage.dontHaveAccount', `Don't have account yet?`)}
|
||||
<Link to={'/signup'} tabIndex="-1" data-cy="sign-up-link">
|
||||
Sign up
|
||||
{this.props.t('loginSignupPage.signUp', `Sign up`)}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -257,4 +268,4 @@ class LoginPage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { LoginPage };
|
||||
export const LoginPage = withTranslation()(LoginPageComponent);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ import { groupPermissionService } from '../_services/groupPermission.service';
|
|||
import { Header } from '@/_components';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
class ManageGroupPermissionResources extends React.Component {
|
||||
class ManageGroupPermissionResourcesComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -322,14 +323,22 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
{isLoadingGroup ? (
|
||||
<ol className="breadcrumb" aria-label="breadcrumbs">
|
||||
<li className="breadcrumb-item">
|
||||
<a href="#">User groups</a>
|
||||
<a href="#">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.userGroup',
|
||||
'User group'
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
) : (
|
||||
<ol className="breadcrumb" aria-label="breadcrumbs">
|
||||
<li className="breadcrumb-item">
|
||||
<Link to="/groups" data-cy="user-groups">
|
||||
User groups
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.userGroup',
|
||||
'User group'
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
<li className="breadcrumb-item">
|
||||
|
|
@ -353,21 +362,24 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
className={cx('nav-item nav-link', { active: currentTab === 'apps' })}
|
||||
data-cy="apps-link"
|
||||
>
|
||||
Apps
|
||||
{this.props.t('header.organization.menus.manageGroups.permissionResources.apps', 'Apps')}
|
||||
</a>
|
||||
<a
|
||||
onClick={() => this.setState({ currentTab: 'users' })}
|
||||
className={cx('nav-item nav-link', { active: currentTab === 'users' })}
|
||||
data-cy="users-link"
|
||||
>
|
||||
Users
|
||||
{this.props.t('header.organization.menus.manageGroups.permissionResources.users', 'Users')}
|
||||
</a>
|
||||
<a
|
||||
onClick={() => this.setState({ currentTab: 'permissions' })}
|
||||
className={cx('nav-item nav-link', { active: currentTab === 'permissions' })}
|
||||
data-cy="permissions-link"
|
||||
>
|
||||
Permissions
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.permissions',
|
||||
'Permissions'
|
||||
)}
|
||||
</a>
|
||||
</nav>
|
||||
<div className="card-body">
|
||||
|
|
@ -386,7 +398,10 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
filterOptions={fuzzySearch}
|
||||
onChange={(value) => this.setSelectedApps(value)}
|
||||
printOptions="on-focus"
|
||||
placeholder="Select apps to add to the group"
|
||||
placeholder={this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.addAppsToGroup',
|
||||
'Select apps to add to the group'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
|
|
@ -397,7 +412,7 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
onClick={() => this.addSelectedAppsToGroup(groupPermission.id, selectedAppIds)}
|
||||
data-cy="add-button"
|
||||
>
|
||||
Add
|
||||
{this.props.t('globals.add', 'Add')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -407,8 +422,18 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
<table className="table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-cy="name-header">Name</th>
|
||||
<th data-cy="permissions-header">Permissions</th>
|
||||
<th data-cy="name-header">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.name',
|
||||
'Name'
|
||||
)}
|
||||
</th>
|
||||
<th data-cy="permissions-header">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.permissions',
|
||||
'Permissions'
|
||||
)}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -443,7 +468,9 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
disabled={groupPermission.group === 'admin'}
|
||||
checked={this.canAppGroupPermission(app, groupPermission.id, 'view')}
|
||||
/>
|
||||
<span className="form-check-label">View</span>
|
||||
<span className="form-check-label">
|
||||
{this.props.t('globals.view', 'view')}
|
||||
</span>
|
||||
</label>
|
||||
<label className="form-check form-check-inline">
|
||||
<input
|
||||
|
|
@ -455,7 +482,9 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
disabled={groupPermission.group === 'admin'}
|
||||
checked={this.canAppGroupPermission(app, groupPermission.id, 'edit')}
|
||||
/>
|
||||
<span className="form-check-label">Edit</span>
|
||||
<span className="form-check-label">
|
||||
{this.props.t('globals.edit', 'Edit')}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
{this.canAppGroupPermission(app, groupPermission.id, 'view') && (
|
||||
|
|
@ -492,7 +521,7 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
}}
|
||||
data-cy="delete-link"
|
||||
>
|
||||
Delete
|
||||
{this.props.t('globals.delete', 'Delete')}
|
||||
</Link>
|
||||
)}
|
||||
</td>
|
||||
|
|
@ -519,7 +548,10 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
value={selectedUserIds}
|
||||
onChange={(value) => this.setSelectedUsers(value)}
|
||||
printOptions="on-focus"
|
||||
placeholder="Select users to add to the group"
|
||||
placeholder={this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.addUsersToGroup',
|
||||
'Select users to add to the group'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
|
|
@ -529,7 +561,7 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
}`}
|
||||
onClick={() => this.addSelectedUsersToGroup(groupPermission.id, selectedUserIds)}
|
||||
>
|
||||
Add
|
||||
{this.props.t('globals.add', 'Add')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -539,8 +571,18 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
<table className="table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-cy="name-header">Name</th>
|
||||
<th data-cy="email-header">Email</th>
|
||||
<th data-cy="name-header">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.name',
|
||||
'name'
|
||||
)}
|
||||
</th>
|
||||
<th data-cy="email-header">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.email',
|
||||
'email'
|
||||
)}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -572,7 +614,7 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
this.removeUserFromGroup(groupPermission.id, user.id);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
{this.props.t('globals.delete', 'Delete')}
|
||||
</Link>
|
||||
)}
|
||||
</td>
|
||||
|
|
@ -592,8 +634,18 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
<table className="table table-vcenter">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-cy="resource-header">Resource</th>
|
||||
<th data-cy="permissions-header">Permissions</th>
|
||||
<th data-cy="resource-header">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.resource',
|
||||
'Resource'
|
||||
)}
|
||||
</th>
|
||||
<th data-cy="permissions-header">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.permissions',
|
||||
'Permissions'
|
||||
)}
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -615,7 +667,12 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
) : (
|
||||
<>
|
||||
<tr>
|
||||
<td data-cy="resource-apps">Apps</td>
|
||||
<td data-cy="resource-apps">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.apps',
|
||||
'Apps'
|
||||
)}
|
||||
</td>
|
||||
<td className="text-muted">
|
||||
<div>
|
||||
<label className="form-check form-check-inline">
|
||||
|
|
@ -632,7 +689,7 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
data-cy="app-create-checkbox"
|
||||
/>
|
||||
<span className="form-check-label" data-cy="app-create-label">
|
||||
Create
|
||||
{this.props.t('globals.create', 'Create')}
|
||||
</span>
|
||||
</label>
|
||||
<label className="form-check form-check-inline">
|
||||
|
|
@ -649,7 +706,7 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
data-cy="app-delete-checkbox"
|
||||
/>
|
||||
<span className="form-check-label" data-cy="app-delete-label">
|
||||
Delete
|
||||
{this.props.t('globals.delete', 'Delete')}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -658,7 +715,12 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
</tr>
|
||||
|
||||
<tr>
|
||||
<td data-cy="resource-folders">Folders</td>
|
||||
<td data-cy="resource-folders">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.folder',
|
||||
'Folder'
|
||||
)}
|
||||
</td>
|
||||
<td className="text-muted">
|
||||
<div>
|
||||
<label className="form-check form-check-inline">
|
||||
|
|
@ -677,7 +739,10 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
data-cy="folder-create-checkbox"
|
||||
/>
|
||||
<span className="form-check-label" data-cy="folder-create-label">
|
||||
Create/Update/Delete
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.createUpdateDelete',
|
||||
'Create/Update/Delete'
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -685,7 +750,7 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Environment variables</td>
|
||||
<td>{this.props.t('globals.environmentVar', 'Environment variables')}</td>
|
||||
<td className="text-muted">
|
||||
<div>
|
||||
<label className="form-check form-check-inline">
|
||||
|
|
@ -702,7 +767,12 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
checked={orgEnvironmentPermission}
|
||||
disabled={groupPermission.group === 'admin'}
|
||||
/>
|
||||
<span className="form-check-label">Create/Update/Delete</span>
|
||||
<span className="form-check-label">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissionResources.createUpdateDelete',
|
||||
'Create/Update/Delete'
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -726,4 +796,4 @@ class ManageGroupPermissionResources extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { ManageGroupPermissionResources };
|
||||
export const ManageGroupPermissionResources = withTranslation()(ManageGroupPermissionResourcesComponent);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { groupPermissionService } from '../_services/groupPermission.service';
|
|||
import { Header, ConfirmDialog } from '@/_components';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
class ManageGroupPermissions extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class ManageGroupPermissionsComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -184,7 +184,7 @@ class ManageGroupPermissions extends React.Component {
|
|||
<div className="col">
|
||||
<div className="page-pretitle"></div>
|
||||
<h2 className="page-title" data-cy="user-groups-title">
|
||||
User Groups
|
||||
{this.props.t('header.organization.menus.manageGroups.permissions.userGroups', 'User Groups')}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="col-auto ms-auto d-print-none">
|
||||
|
|
@ -194,7 +194,10 @@ class ManageGroupPermissions extends React.Component {
|
|||
onClick={() => this.setState({ showNewGroupForm: true, isSaveBtnDisabled: true })}
|
||||
data-cy="create-new-group-button"
|
||||
>
|
||||
Create new group
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageGroups.permissions.createNewGroup',
|
||||
'Create new group'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -208,7 +211,12 @@ class ManageGroupPermissions extends React.Component {
|
|||
<div className="card">
|
||||
<div className="card-header">
|
||||
<h3 className="card-title" data-cy="card-title">
|
||||
{showGroupNameUpdateForm ? 'Update group' : 'Add new group'}
|
||||
{showGroupNameUpdateForm
|
||||
? this.props.t('header.organization.menus.manageGroups.permissions.updateGroup', 'Update group')
|
||||
: this.props.t(
|
||||
'header.organization.menus.manageGroups.permissions.addNewGroup',
|
||||
'Add new group'
|
||||
)}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
|
|
@ -229,7 +237,10 @@ class ManageGroupPermissions extends React.Component {
|
|||
type="text"
|
||||
required
|
||||
className="form-control"
|
||||
placeholder="Enter Name"
|
||||
placeholder={this.props.t(
|
||||
'header.organization.menus.manageGroups.permissions.enterName',
|
||||
'Enter Name'
|
||||
)}
|
||||
onChange={(e) => {
|
||||
this.changeNewGroupName(e.target.value);
|
||||
}}
|
||||
|
|
@ -253,7 +264,7 @@ class ManageGroupPermissions extends React.Component {
|
|||
disabled={creatingGroup}
|
||||
data-cy="cancel-button"
|
||||
>
|
||||
Cancel
|
||||
{this.props.t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
|
|
@ -263,7 +274,12 @@ class ManageGroupPermissions extends React.Component {
|
|||
disabled={creatingGroup || this.state.isSaveBtnDisabled}
|
||||
data-cy="create-group-button"
|
||||
>
|
||||
{showGroupNameUpdateForm ? 'Save' : 'Create Group'}
|
||||
{showGroupNameUpdateForm
|
||||
? this.props.t('globals.save', 'Save')
|
||||
: this.props.t(
|
||||
'header.organization.menus.manageGroups.permissions.createGroup',
|
||||
'Create Group'
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -278,7 +294,9 @@ class ManageGroupPermissions extends React.Component {
|
|||
<table data-testid="usersTable" className="table table-vcenter" disabled={true}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-cy="table-header">Name</th>
|
||||
<th data-cy="table-header">
|
||||
{this.props.t('header.organization.menus.manageGroups.permissions.name', 'Name')}
|
||||
</th>
|
||||
<th className="w-1"></th>
|
||||
<th className="w-1"></th>
|
||||
</tr>
|
||||
|
|
@ -314,14 +332,14 @@ class ManageGroupPermissions extends React.Component {
|
|||
{permissionGroup.group !== 'admin' && permissionGroup.group !== 'all_users' && (
|
||||
<div className="user-group-actions">
|
||||
<Link onClick={() => this.updateGroupName(permissionGroup)} data-cy="update-link">
|
||||
Update
|
||||
{this.props.t('globals.update', 'Update')}
|
||||
</Link>
|
||||
<Link
|
||||
className="text-danger"
|
||||
onClick={() => this.deleteGroup(permissionGroup.id)}
|
||||
data-cy="delete-link"
|
||||
>
|
||||
Delete
|
||||
{this.props.t('globals.delete', 'Delete')}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -342,4 +360,5 @@ class ManageGroupPermissions extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { ManageGroupPermissions };
|
||||
export const ManageGroupPermissions = withTranslation()(ManageGroupPermissionsComponent);
|
||||
// export { ManageGroupPermissions };
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import { Header } from '@/_components';
|
|||
import { toast } from 'react-hot-toast';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import urlJoin from 'url-join';
|
||||
class ManageOrgUsers extends React.Component {
|
||||
class ManageOrgUsersComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -188,7 +189,7 @@ class ManageOrgUsers extends React.Component {
|
|||
<div className="col">
|
||||
<div className="page-pretitle"></div>
|
||||
<h2 className="page-title" data-cy="users-page-title">
|
||||
Users & Permissions
|
||||
{this.props.t('header.organization.menus.manageUsers.usersAndPermission', 'Users & Permissions')}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="col-auto ms-auto d-print-none">
|
||||
|
|
@ -198,7 +199,7 @@ class ManageOrgUsers extends React.Component {
|
|||
onClick={() => this.setState({ showNewUserForm: true })}
|
||||
data-cy="invite-new-user"
|
||||
>
|
||||
Invite new user
|
||||
{this.props.t('header.organization.menus.manageUsers.inviteNewUser', 'Invite new user')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -212,7 +213,7 @@ class ManageOrgUsers extends React.Component {
|
|||
<div className="card">
|
||||
<div className="card-header">
|
||||
<h3 className="card-title" data-cy="add-new-user">
|
||||
Add new user
|
||||
{this.props.t('header.organization.menus.manageUsers.addNewUser', 'Add new user')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
|
|
@ -223,7 +224,10 @@ class ManageOrgUsers extends React.Component {
|
|||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter First Name"
|
||||
placeholder={this.props.t(
|
||||
'header.organization.menus.manageUsers.enterFirstName',
|
||||
'Enter First Name'
|
||||
)}
|
||||
name="firstName"
|
||||
onChange={this.changeNewUserOption.bind(this, 'firstName')}
|
||||
value={this.state.fields['firstName']}
|
||||
|
|
@ -237,7 +241,10 @@ class ManageOrgUsers extends React.Component {
|
|||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter Last Name"
|
||||
placeholder={this.props.t(
|
||||
'header.organization.menus.manageUsers.enterLastName',
|
||||
'Enter Last Name'
|
||||
)}
|
||||
name="lastName"
|
||||
onChange={this.changeNewUserOption.bind(this, 'lastName')}
|
||||
value={this.state.fields['lastName']}
|
||||
|
|
@ -251,14 +258,17 @@ class ManageOrgUsers extends React.Component {
|
|||
</div>
|
||||
<div className="form-group mb-3 ">
|
||||
<label className="form-label" data-cy="email-label">
|
||||
Email address
|
||||
{this.props.t('header.organization.menus.manageUsers.emailAddress', 'Email Address')}
|
||||
</label>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
aria-describedby="emailHelp"
|
||||
placeholder="Enter email"
|
||||
placeholder={this.props.t(
|
||||
'header.organization.menus.manageUsers.enterEmail',
|
||||
'Enter Email'
|
||||
)}
|
||||
name="email"
|
||||
onChange={this.changeNewUserOption.bind(this, 'email')}
|
||||
value={this.state.fields['email']}
|
||||
|
|
@ -281,7 +291,7 @@ class ManageOrgUsers extends React.Component {
|
|||
}
|
||||
data-cy="cancel-button"
|
||||
>
|
||||
Cancel
|
||||
{this.props.t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
|
|
@ -289,7 +299,7 @@ class ManageOrgUsers extends React.Component {
|
|||
disabled={creatingUser}
|
||||
data-cy="create-user-button"
|
||||
>
|
||||
Create User
|
||||
{this.props.t('header.organization.menus.manageUsers.createUser', 'Create User')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -311,9 +321,15 @@ class ManageOrgUsers extends React.Component {
|
|||
<table data-testid="usersTable" className="table table-vcenter" disabled={true}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-cy="name-title">Name</th>
|
||||
<th data-cy="email-title">Email</th>
|
||||
<th data-cy="status-title">Status</th>
|
||||
<th data-cy="name-title">
|
||||
{this.props.t('header.organization.menus.manageUsers.name', 'Name')}
|
||||
</th>
|
||||
<th data-cy="email-title">
|
||||
{this.props.t('header.organization.menus.manageUsers.email', 'Email')}
|
||||
</th>
|
||||
<th data-cy="status-title">
|
||||
{this.props.t('header.organization.menus.manageUsers.status', 'Status')}
|
||||
</th>
|
||||
<th className="w-1"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -420,7 +436,9 @@ class ManageOrgUsers extends React.Component {
|
|||
}}
|
||||
data-cy="user-state"
|
||||
>
|
||||
{user.status === 'archived' ? 'Unarchive' : 'Archive'}
|
||||
{user.status === 'archived'
|
||||
? this.props.t('header.organization.menus.manageUsers.unarchive', 'Unarchive')
|
||||
: this.props.t('header.organization.menus.manageUsers.archive', 'Archive')}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -439,4 +457,4 @@ class ManageOrgUsers extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { ManageOrgUsers };
|
||||
export const ManageOrgUsers = withTranslation()(ManageOrgUsersComponent);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import { toast } from 'react-hot-toast';
|
|||
import ReactTooltip from 'react-tooltip';
|
||||
import VariableForm from './VariableForm';
|
||||
import VariablesTable from './VariablesTable';
|
||||
|
||||
class ManageOrgVars extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class ManageOrgVarsComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.currentUser = authenticationService.currentUserValue;
|
||||
|
|
@ -241,7 +241,10 @@ class ManageOrgVars extends React.Component {
|
|||
|
||||
<ConfirmDialog
|
||||
show={this.state.showVariableDeleteConfirmation}
|
||||
message={'Variable will be deleted, do you want to continue?'}
|
||||
message={this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.envWillBeDeleted',
|
||||
'Variable will be deleted, do you want to continue?'
|
||||
)}
|
||||
onConfirm={() => {
|
||||
this.deleteVariable(this.state.selectedVariableId);
|
||||
}}
|
||||
|
|
@ -259,7 +262,7 @@ class ManageOrgVars extends React.Component {
|
|||
<div className="row align-items-center">
|
||||
<div className="col">
|
||||
<div className="page-pretitle"></div>
|
||||
<h2 className="page-title">Environment Variables</h2>
|
||||
<h2 className="page-title">{this.props.t('globals.environmentVar', 'Environment Variables')}</h2>
|
||||
</div>
|
||||
<div className="col-auto ms-auto d-print-none">
|
||||
{!showVariableForm && this.canCreateVariable() && (
|
||||
|
|
@ -267,7 +270,10 @@ class ManageOrgVars extends React.Component {
|
|||
className="btn btn-primary"
|
||||
onClick={() => this.setState({ showVariableForm: true, errors: {} })}
|
||||
>
|
||||
Add new variable
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.addNewVariable',
|
||||
'Add new variable'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -302,8 +308,10 @@ class ManageOrgVars extends React.Component {
|
|||
/>
|
||||
) : (
|
||||
<span className="no-vars-text">
|
||||
You haven't configured any environment variables, press the '<b>Add new variable</b>'
|
||||
button to create one
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.noEnvConfig',
|
||||
`You haven't configured any environment variables, press the 'Add new variable' button to create one`
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -315,4 +323,4 @@ class ManageOrgVars extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { ManageOrgVars };
|
||||
export const ManageOrgVars = withTranslation()(ManageOrgVarsComponent);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import Select from '@/_ui/Select';
|
||||
|
||||
export default class VariableForm extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class VariableForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
|
@ -11,18 +11,33 @@ export default class VariableForm extends React.Component {
|
|||
<div className="container-xl">
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
<h3 className="card-title">{!this.props.selectedVariableId ? 'Add new variable' : 'Update variable'}</h3>
|
||||
<h3 className="card-title">
|
||||
{!this.props.selectedVariableId
|
||||
? this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.variableForm.addNewVariable',
|
||||
'Add new variable'
|
||||
)
|
||||
: this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.variableForm.updatevariable',
|
||||
'Update variable'
|
||||
)}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<form onSubmit={this.props.createOrUpdate} noValidate>
|
||||
<div className="form-group mb-3 ">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<label className="form-label">Name</label>
|
||||
<label className="form-label">
|
||||
{this.props.t('header.organization.menus.manageSSO.environmentVar.variableForm.name', 'Name')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter Variable Name"
|
||||
placeholder={this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.variableForm.enterVariableName',
|
||||
'Enter Variable Name'
|
||||
)}
|
||||
name="variable_name"
|
||||
onChange={this.props.changeNewVariableOption.bind(this, 'variable_name')}
|
||||
value={this.props.fields['variable_name']}
|
||||
|
|
@ -30,11 +45,17 @@ export default class VariableForm extends React.Component {
|
|||
<span className="text-danger">{this.props.errors['variable_name']}</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<label className="form-label">Value</label>
|
||||
<label className="form-label">
|
||||
{' '}
|
||||
{this.props.t('header.organization.menus.manageSSO.environmentVar.variableForm.value', 'Value')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter Value"
|
||||
placeholder={this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.variableForm.enterValue',
|
||||
'Enter Value'
|
||||
)}
|
||||
name="value"
|
||||
onChange={this.props.changeNewVariableOption.bind(this, 'value')}
|
||||
value={this.props.fields['value']}
|
||||
|
|
@ -46,7 +67,9 @@ export default class VariableForm extends React.Component {
|
|||
<div className="form-group mb-3 ">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<label className="form-label">Type</label>
|
||||
<label className="form-label">
|
||||
{this.props.t('header.organization.menus.manageSSO.environmentVar.variableForm.type', 'Type')}
|
||||
</label>
|
||||
{this.props.selectedVariableId ? (
|
||||
<span>{this.props.fields['variable_type']}</span>
|
||||
) : (
|
||||
|
|
@ -63,7 +86,12 @@ export default class VariableForm extends React.Component {
|
|||
)}
|
||||
</div>
|
||||
<div className="col">
|
||||
<label className="form-label">Enable encryption</label>
|
||||
<label className="form-label">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.variableForm.enableEncryption',
|
||||
' Enable encryption'
|
||||
)}
|
||||
</label>
|
||||
<div className="form-check form-switch encryption-input">
|
||||
<input
|
||||
className="form-check-input"
|
||||
|
|
@ -84,14 +112,19 @@ export default class VariableForm extends React.Component {
|
|||
</div>
|
||||
<div className="form-footer">
|
||||
<button type="button" className="btn btn-light mr-2" onClick={() => this.props.onCancelBtnClicked()}>
|
||||
Cancel
|
||||
{this.props.t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className={`btn mx-2 btn-primary ${this.props.addingVar ? 'btn-loading' : ''}`}
|
||||
disabled={this.props.addingVar}
|
||||
>
|
||||
{!this.props.selectedVariableId ? 'Add variable' : 'Save'}
|
||||
{!this.props.selectedVariableId
|
||||
? this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.variableForm.addVariable',
|
||||
'Add variable'
|
||||
)
|
||||
: this.props.t('globals.save', 'Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -101,3 +134,4 @@ export default class VariableForm extends React.Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
export default withTranslation()(VariableForm);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
export default class VariablesTable extends React.Component {
|
||||
class VariablesTable extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -25,9 +26,15 @@ export default class VariablesTable extends React.Component {
|
|||
<table data-testid="variablesTable" className="table table-vcenter" disabled={true}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th>Type</th>
|
||||
<th>
|
||||
{this.props.t('header.organization.menus.manageSSO.environmentVar.variableTable.name', 'Name')}
|
||||
</th>
|
||||
<th>
|
||||
{this.props.t('header.organization.menus.manageSSO.environmentVar.variableTable.value', 'Value')}
|
||||
</th>
|
||||
<th>
|
||||
{this.props.t('header.organization.menus.manageSSO.environmentVar.variableTable.type', 'Type')}
|
||||
</th>
|
||||
{(this.props.canUpdateVariable || this.props.canDeleteVariable) && <th className="w-1"></th>}
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -67,7 +74,12 @@ export default class VariablesTable extends React.Component {
|
|||
width="12"
|
||||
height="12"
|
||||
/>
|
||||
<span className="text-success mx-2">secret</span>
|
||||
<span className="text-success mx-2">
|
||||
{this.props.t(
|
||||
'header.organization.menus.manageSSO.environmentVar.variableTable.secret',
|
||||
'secret'
|
||||
)}
|
||||
</span>
|
||||
</small>
|
||||
) : (
|
||||
variable.value
|
||||
|
|
@ -132,3 +144,4 @@ export default class VariablesTable extends React.Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
export default withTranslation()(VariablesTable);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useState } from 'react';
|
||||
import { organizationService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function Form({ settings, updateData }) {
|
||||
const [enabled, setEnabled] = useState(settings?.enabled || false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const changeStatus = () => {
|
||||
organizationService.editOrganizationConfigs({ type: 'form', enabled: !enabled }).then(
|
||||
|
|
@ -26,9 +28,9 @@ export function Form({ settings, updateData }) {
|
|||
<div className="card-header">
|
||||
<div className="d-flex justify-content-between title-with-toggle">
|
||||
<div className="card-title" data-cy="card-title">
|
||||
Password Login
|
||||
{t('header.organization.menus.manageSSO.passwordLogin', 'Password Login')}
|
||||
<span className={`badge bg-${enabled ? 'green' : 'grey'} ms-1`} data-cy="status-label">
|
||||
{enabled ? 'Enabled' : 'Disabled'}
|
||||
{enabled ? t('globals.enabled', 'Enabled') : t('globals.disabled', 'Disabled')}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { authenticationService, organizationService } from '@/_services';
|
|||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { copyToClipboard } from '@/_helpers/appUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function GeneralSettings({ settings, updateData }) {
|
||||
const isSingleOrganization = window.public_config?.DISABLE_MULTI_WORKSPACE === 'true';
|
||||
|
|
@ -9,6 +10,7 @@ export function GeneralSettings({ settings, updateData }) {
|
|||
const [inheritSSO, setInheritSSO] = useState(settings?.inherit_s_s_o || false);
|
||||
const [domain, setDomain] = useState(settings?.domain || '');
|
||||
const [isSaving, setSaving] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const reset = () => {
|
||||
setEnableSignUp(settings?.enable_sign_up || false);
|
||||
|
|
@ -92,7 +94,7 @@ export function GeneralSettings({ settings, updateData }) {
|
|||
<div className="card">
|
||||
<div className="card-header">
|
||||
<div className="card-title" data-cy="card-title">
|
||||
General Settings
|
||||
{t('header.organization.menus.manageSSO.generalSettings.title', 'General Settings')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
|
|
@ -107,12 +109,15 @@ export function GeneralSettings({ settings, updateData }) {
|
|||
data-cy="form-check-input"
|
||||
/>
|
||||
<span className="form-check-label" data-cy="form-check-label">
|
||||
Enable signup
|
||||
{t('header.organization.menus.manageSSO.generalSettings.enableSignup', 'Enable signup')}
|
||||
</span>
|
||||
</label>
|
||||
<div className="help-text">
|
||||
<div data-cy="general-settings-help-text">
|
||||
New account will be created for user's first time SSO sign in
|
||||
{t(
|
||||
'header.organization.menus.manageSSO.generalSettings.newAccountWillBeCreated',
|
||||
`New account will be created for user's first time SSO sign in`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -128,7 +133,7 @@ export function GeneralSettings({ settings, updateData }) {
|
|||
data-cy="form-check-input"
|
||||
/>
|
||||
<span className="form-check-label" data-cy="form-check-label">
|
||||
Allow default SSO
|
||||
{t('header.organization.menus.manageSSO.generalSettings.allowDefaultSso', `Allow default SSO`)}
|
||||
</span>
|
||||
</label>
|
||||
<div className="d-flex tick-cross-info mb-2">
|
||||
|
|
@ -137,21 +142,23 @@ export function GeneralSettings({ settings, updateData }) {
|
|||
</div>
|
||||
<div className="help-text mt-1">
|
||||
<div data-cy="login-help-text">
|
||||
Allow users to authenticate via default SSO. Default SSO configurations can be overridden by
|
||||
workspace level SSO.
|
||||
{t(
|
||||
'header.organization.menus.manageSSO.generalSettings.ssoAuth',
|
||||
`Allow users to authenticate via default SSO. Default SSO configurations can be overridden by workspace level SSO.`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label" data-cy="allowed-domains-label">
|
||||
Allowed domains
|
||||
{t('header.organization.menus.manageSSO.generalSettings.allowedDomains', `Allowed domains`)}
|
||||
</label>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter Domains"
|
||||
placeholder={t('header.organization.menus.manageSSO.generalSettings.enterDomains', `Enter Domains`)}
|
||||
name="domain"
|
||||
value={domain}
|
||||
onChange={(e) => setDomain(e.target.value)}
|
||||
|
|
@ -160,15 +167,17 @@ export function GeneralSettings({ settings, updateData }) {
|
|||
</div>
|
||||
<div className="help-text mt-1">
|
||||
<div data-cy="login-help-text">
|
||||
Support multiple domains. Enter domain names separated by comma. example:
|
||||
tooljet.com,tooljet.io,yourorganization.com
|
||||
{t(
|
||||
'header.organization.menus.manageSSO.generalSettings.supportMultiDomains',
|
||||
`Support multiple domains. Enter domain names separated by comma. example: tooljet.com,tooljet.io,yourorganization.com`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!isSingleOrganization && (
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label" data-cy="login-url-label">
|
||||
Login URL
|
||||
{t('header.organization.menus.manageSSO.generalSettings.loginUrl', `Login URL`)}
|
||||
</label>
|
||||
|
||||
<div className="flexer-sso-input form-control">
|
||||
|
|
@ -184,13 +193,18 @@ export function GeneralSettings({ settings, updateData }) {
|
|||
/>
|
||||
</div>
|
||||
<div className="help-text mt-1">
|
||||
<div data-cy="login-help-text">Use this URL to login directly to this workspace</div>
|
||||
<div data-cy="login-help-text">
|
||||
{t(
|
||||
'header.organization.menus.manageSSO.generalSettings.workspaceLogin',
|
||||
`Use this URL to login directly to this workspace`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="form-footer">
|
||||
<button type="button" className="btn btn-light mr-2" onClick={reset} data-cy="cancel-button">
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -199,7 +213,7 @@ export function GeneralSettings({ settings, updateData }) {
|
|||
onClick={saveSettings}
|
||||
data-cy="save-button"
|
||||
>
|
||||
Save
|
||||
{t('globals.save', 'Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
|||
import { organizationService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { copyToClipboard } from '@/_helpers/appUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function Git({ settings, updateData }) {
|
||||
const [enabled, setEnabled] = useState(settings?.enabled || false);
|
||||
|
|
@ -10,6 +11,7 @@ export function Git({ settings, updateData }) {
|
|||
const [clientSecret, setClientSecret] = useState(settings?.configs?.client_secret || '');
|
||||
const [isSaving, setSaving] = useState(false);
|
||||
const [configId, setConfigId] = useState(settings?.id);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const reset = () => {
|
||||
setClientId(settings?.configs?.client_id || '');
|
||||
|
|
@ -71,9 +73,9 @@ export function Git({ settings, updateData }) {
|
|||
<div className="card-header">
|
||||
<div className="d-flex justify-content-between title-with-toggle">
|
||||
<div className="card-title" data-cy="card-title">
|
||||
GitHub
|
||||
{t('header.organization.menus.manageSSO.github.title', 'Github')}
|
||||
<span className={`badge bg-${enabled ? 'green' : 'grey'} ms-1`} data-cy="status-label">
|
||||
{enabled ? 'Enabled' : 'Disabled'}
|
||||
{enabled ? t('globals.enabled', 'Enabled') : t('globals.disabled', 'Disabled')}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -93,31 +95,34 @@ export function Git({ settings, updateData }) {
|
|||
<form noValidate>
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label" data-cy="host-name-label">
|
||||
Host Name
|
||||
{t('header.organization.menus.manageSSO.github.hostName', 'Host Name')}
|
||||
</label>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter Host Name"
|
||||
placeholder={t('header.organization.menus.manageSSO.github.enterHostName', 'Enter Host Name')}
|
||||
value={hostName}
|
||||
onChange={(e) => setHostName(e.target.value)}
|
||||
data-cy="host-name-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="help-text mt-2">
|
||||
<div data-cy="general-settings-help-text">Required if GitHub is self hosted</div>
|
||||
<div data-cy="general-settings-help-text">
|
||||
{' '}
|
||||
{t('header.organization.menus.manageSSO.github.requiredGithub', 'Required if GitHub is self hosted')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label" data-cy="client-id-label">
|
||||
Client Id
|
||||
{t('header.organization.menus.manageSSO.github.clientId', ' Client Id')}
|
||||
</label>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter Client Id"
|
||||
placeholder={t('header.organization.menus.manageSSO.github.enterClientId', 'Enter Client Id')}
|
||||
value={clientId}
|
||||
onChange={(e) => setClientId(e.target.value)}
|
||||
data-cy="client-id-input"
|
||||
|
|
@ -126,17 +131,17 @@ export function Git({ settings, updateData }) {
|
|||
</div>
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label" data-cy="client-secret-label">
|
||||
Client Secret
|
||||
{t('header.organization.menus.manageSSO.github.clientSecret', 'Client Secret')}
|
||||
<small className="text-green mx-2" data-cy="encripted-label">
|
||||
<img className="mx-2 encrypted-icon" src="assets/images/icons/padlock.svg" width="12" height="12" />
|
||||
Encrypted
|
||||
{t('header.organization.menus.manageSSO.github.encrypted', 'Encrypted')}
|
||||
</small>
|
||||
</label>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter Client Secret"
|
||||
placeholder={t('header.organization.menus.manageSSO.github.enterClientSecret', 'Enter Client Secret')}
|
||||
value={clientSecret}
|
||||
onChange={(e) => setClientSecret(e.target.value)}
|
||||
data-cy="client-secret-input"
|
||||
|
|
@ -146,7 +151,7 @@ export function Git({ settings, updateData }) {
|
|||
{configId && (
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label" data-cy="redirect-url-label">
|
||||
Redirect URL
|
||||
{t('header.organization.menus.manageSSO.github.redirectUrl', 'Redirect URL')}
|
||||
</label>
|
||||
<div className="flexer-sso-input form-control">
|
||||
<p
|
||||
|
|
@ -165,7 +170,7 @@ export function Git({ settings, updateData }) {
|
|||
)}
|
||||
<div className="form-footer">
|
||||
<button type="button" className="btn btn-light mr-2" onClick={reset} data-cy="cancel-button">
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -174,7 +179,7 @@ export function Git({ settings, updateData }) {
|
|||
onClick={saveSettings}
|
||||
data-cy="save-button"
|
||||
>
|
||||
Save
|
||||
{t('globals.save', 'Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ import React, { useState } from 'react';
|
|||
import { organizationService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { copyToClipboard } from '@/_helpers/appUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function Google({ settings, updateData }) {
|
||||
const [enabled, setEnabled] = useState(settings?.enabled || false);
|
||||
const [clientId, setClientId] = useState(settings?.configs?.client_id || '');
|
||||
const [isSaving, setSaving] = useState(false);
|
||||
const [configId, setConfigId] = useState(settings?.id);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const reset = () => {
|
||||
setClientId(settings?.configs?.client_id || '');
|
||||
|
|
@ -63,9 +65,11 @@ export function Google({ settings, updateData }) {
|
|||
<div className="card-header">
|
||||
<div className="d-flex justify-content-between title-with-toggle">
|
||||
<div className="card-title" data-cy="card-title">
|
||||
Google
|
||||
{t('header.organization.menus.manageSSO.google.title', 'Google')}
|
||||
<span className={`badge bg-${enabled ? 'green' : 'grey'} ms-1`} data-cy="status-label">
|
||||
{enabled ? 'Enabled' : 'Disabled'}
|
||||
{enabled
|
||||
? t('header.organization.menus.manageSSO.google.enabled', 'Enabled')
|
||||
: t('header.organization.menus.manageSSO.google.disabled', 'Disabled')}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -85,13 +89,13 @@ export function Google({ settings, updateData }) {
|
|||
<form noValidate>
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label" data-cy="client-id-label">
|
||||
Client Id
|
||||
{t('header.organization.menus.manageSSO.google.clientId', 'Client Id')}
|
||||
</label>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter Client Id"
|
||||
placeholder={t('header.organization.menus.manageSSO.google.enterClientId', 'Enter Client Id')}
|
||||
value={clientId}
|
||||
onChange={(e) => setClientId(e.target.value)}
|
||||
data-cy="client-id-input"
|
||||
|
|
@ -101,7 +105,7 @@ export function Google({ settings, updateData }) {
|
|||
{configId && (
|
||||
<div className="form-group mb-3">
|
||||
<label className="form-label" data-cy="redirect-url-label">
|
||||
Redirect URL
|
||||
{t('header.organization.menus.manageSSO.google.redirectUrl', 'Redirect URL')}
|
||||
</label>
|
||||
<div className="flexer-sso-input form-control">
|
||||
<p
|
||||
|
|
@ -120,7 +124,7 @@ export function Google({ settings, updateData }) {
|
|||
)}
|
||||
<div className="form-footer">
|
||||
<button type="button" className="btn btn-light mr-2" onClick={reset} data-cy="cancel-button">
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -129,7 +133,7 @@ export function Google({ settings, updateData }) {
|
|||
onClick={saveSettings}
|
||||
data-cy="save-button"
|
||||
>
|
||||
Save
|
||||
{t('globals.save', 'Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { Google } from './Google';
|
|||
import { Loader } from './Loader';
|
||||
import { Git } from './Git';
|
||||
import { Form } from './Form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function ManageSSO({ switchDarkMode, darkMode }) {
|
||||
const menuItems = [
|
||||
|
|
@ -15,6 +16,7 @@ export function ManageSSO({ switchDarkMode, darkMode }) {
|
|||
{ id: 'git', label: 'GitHub' },
|
||||
{ id: 'form', label: 'Password Login' },
|
||||
];
|
||||
const { t } = useTranslation();
|
||||
const changePage = useCallback(
|
||||
(page) => {
|
||||
setCurrentPage(page);
|
||||
|
|
@ -90,7 +92,7 @@ export function ManageSSO({ switchDarkMode, darkMode }) {
|
|||
<div className="col">
|
||||
<div className="page-pretitle"></div>
|
||||
<h2 className="page-title" data-cy="manage-sso-page-title">
|
||||
Manage SSO
|
||||
{t('header.organization.menus.manageSSO.manageSso', 'Manage SSO')}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import React from 'react';
|
|||
import queryString from 'query-string';
|
||||
import { datasourceService } from '@/_services';
|
||||
import { RedirectLoader } from '@/_components';
|
||||
|
||||
class Authorize extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class AuthorizeComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -113,4 +113,4 @@ class Authorize extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { Authorize };
|
||||
export const Authorize = withTranslation()(AuthorizeComponent);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { tooljetService } from '@/_services';
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
|
||||
class OnboardingModal extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class OnboardingModalComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -54,13 +54,15 @@ class OnboardingModal extends React.Component {
|
|||
className={`${this.props.darkMode && 'dark'} onboarding-modal`}
|
||||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title className="text-center">Finish ToolJet installation</Modal.Title>
|
||||
<Modal.Title className="text-center">
|
||||
{this.props.t('onBoarding.finishToolJetInstallation', 'Finish ToolJet installation')}
|
||||
</Modal.Title>
|
||||
<br />
|
||||
</Modal.Header>
|
||||
|
||||
<Modal.Body>
|
||||
<div className="mb-3 mt-2">
|
||||
<label className="form-label">Organization</label>
|
||||
<label className="form-label">{this.props.t('onBoarding.organization', 'Organization')}</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -74,7 +76,7 @@ class OnboardingModal extends React.Component {
|
|||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Name</label>
|
||||
<label className="form-label">{this.props.t('onBoarding.name', 'Name')}</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -88,7 +90,7 @@ class OnboardingModal extends React.Component {
|
|||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Email</label>
|
||||
<label className="form-label">{this.props.t('onBoarding.email', 'Email')}</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -100,19 +102,24 @@ class OnboardingModal extends React.Component {
|
|||
<span className="input-group-text"></span>
|
||||
</div>
|
||||
</div>
|
||||
<small>You will receive updates from the ToolJet team ( 1-2 emails every month, we do not spam )</small>
|
||||
<small>
|
||||
{this.props.t(
|
||||
'onBoarding.receiveUpdatesFromToolJet',
|
||||
'You will receive updates from the ToolJet team ( 1-2 emails every month, we do not spam )'
|
||||
)}
|
||||
</small>
|
||||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
<div className="row w-100 gx-0">
|
||||
<div className="col">
|
||||
<button className={`btn btn-primary`} onClick={this.finishOnboarding}>
|
||||
Finish setup
|
||||
{this.props.t('onBoarding.finishSetup', 'Finish setup')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<a onClick={this.skipOnboard} className="mt-3 text-muted" data-cy="skip-button">
|
||||
Skip
|
||||
{this.props.t('onBoarding.skip', 'Skip')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -122,4 +129,4 @@ class OnboardingModal extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { OnboardingModal };
|
||||
export const OnboardingModal = withTranslation()(OnboardingModalComponent);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RedirectSso = function RedirectSso() {
|
||||
const isSingleOrganization = window.public_config?.DISABLE_MULTI_WORKSPACE === 'true';
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [organization, setOrganization] = useState([]);
|
||||
const [googlessoEnabled, setGoogleSsoEnabled] = useState(false);
|
||||
|
|
@ -42,32 +44,39 @@ export const RedirectSso = function RedirectSso() {
|
|||
</div>
|
||||
<div className="sso-helper-container">
|
||||
<h2 className="sso-helper-header">
|
||||
<span className="gg-album"></span>Upgrading to v1.13.0 and above.
|
||||
<span className="gg-album"></span>
|
||||
{t('redirectSso.upgradingTov1.13.0', 'Upgrading to v1.13.0 and above.')}
|
||||
</h2>
|
||||
<p className="sso-helper-doc">
|
||||
From v1.13.0 we have introduced
|
||||
{t('redirectSso.fromV1.13.0', 'From v1.13.0 we have introduced')}
|
||||
<a style={{ marginLeft: '4px' }} href="https://docs.tooljet.com/docs/tutorial/multiworkspace">
|
||||
Multi-Workspace
|
||||
{t('redirectSso.multiWorkspace', 'Multi-Workspace')}
|
||||
</a>
|
||||
. The Single Sign-On related configurations are moved from environment variables to database. Please refer
|
||||
this
|
||||
.{' '}
|
||||
{t(
|
||||
'redirectSso.singleSignOnConfig',
|
||||
'The Single Sign-On related configurations are moved from environment variables to database. Please refer this'
|
||||
)}
|
||||
<a
|
||||
style={{ marginLeft: '4px', marginRight: '4px' }}
|
||||
href="https://docs.tooljet.com/docs/category/single-sign-on"
|
||||
>
|
||||
Link
|
||||
{t('redirectSso.link', 'Link')}
|
||||
</a>
|
||||
to configure SSO.
|
||||
{t('redirectSso.toConfigureSSO', 'to configure SSO.')}
|
||||
<br />
|
||||
<li>
|
||||
If you have Google or GitHub SSO configurations before upgrade and disabled Multi-Workspace, then the
|
||||
SSO configurations will be migrated while upgrade but you have to re-configure the redirect URL in the
|
||||
SSO provider side. Redirect URLs for each SSO are given below.
|
||||
{t(
|
||||
'redirectSso.haveGoogleGithubSSo',
|
||||
'If you have Google or GitHub SSO configurations before upgrade and disabled Multi-Workspace, then theSSO configurations will be migrated while upgrade but you have to re-configure the redirect URL in the SSO provider side. Redirect URLs for each SSO are given below.1'
|
||||
)}
|
||||
<br />
|
||||
</li>
|
||||
<li>
|
||||
If you have enabled Multi-Workspace, then the SSO configurations will not be migrated while upgrade so
|
||||
you have to re-configure the SSO under the respective workspace.
|
||||
{t(
|
||||
'redirectSso.isMultiWorkspaceEnabled',
|
||||
'If you have enabled Multi-Workspace, then the SSO configurations will not be migrated while upgrade so you have to re-configure the SSO under the respective workspace.1'
|
||||
)}
|
||||
</li>
|
||||
</p>
|
||||
<div className="sso-content-wrapper">
|
||||
|
|
@ -75,18 +84,21 @@ export const RedirectSso = function RedirectSso() {
|
|||
<>
|
||||
<div>
|
||||
<p className="workspace-status">
|
||||
You have Enabled
|
||||
{t('redirectSso.youHaveEnabled', 'You have Enabled')}
|
||||
<a style={{ marginLeft: '4px' }} href="https://docs.tooljet.com/docs/tutorial/multiworkspace">
|
||||
Multi-Workspace
|
||||
{t('redirectSso.multiWorkspace', 'Multi-Workspace')}
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
Please login with password and you can setup sso using workspace
|
||||
{t(
|
||||
'redirectSso.setupSsoWorkspace',
|
||||
'Please login with password and you can setup sso using workspace'
|
||||
)}
|
||||
<a
|
||||
href="https://docs.tooljet.com/docs/user-authentication/general-settings"
|
||||
style={{ marginLeft: '4px' }}
|
||||
>
|
||||
Manage SSO menu.
|
||||
{t('redirectSso.manageSsoMenu', 'Manage SSO menu.')}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -96,13 +108,10 @@ export const RedirectSso = function RedirectSso() {
|
|||
<div>
|
||||
<p className="workspace-status">
|
||||
{' '}
|
||||
<span
|
||||
className="gg-border-all
|
||||
"
|
||||
></span>
|
||||
You have Disabled
|
||||
<span className="gg-border-all"></span>
|
||||
{t('redirectSso.youHaveDisabled', 'You have Disabled')}
|
||||
<a style={{ marginLeft: '4px' }} href="https://docs.tooljet.com/docs/tutorial/multiworkspace">
|
||||
Multi-Workspace.
|
||||
{t('redirectSso.multiWorkspace', 'Multi-Workspace')}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -113,15 +122,20 @@ export const RedirectSso = function RedirectSso() {
|
|||
<>
|
||||
<div>
|
||||
{googlessoEnabled || gitSsoEnabled ? (
|
||||
<p>Please configure redirect url in SSO provider side.</p>
|
||||
<p>
|
||||
{t('redirectSso.configureRedirectUrl', 'Please configure redirect url in SSO provider side.')}
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
Please login with password and you can setup sso using workspace
|
||||
{t(
|
||||
'redirectSso.setupSsoWorkspace',
|
||||
'Please login with password and you can setup sso using workspace'
|
||||
)}
|
||||
<a
|
||||
style={{ marginLeft: '4px' }}
|
||||
href="https://docs.tooljet.com/docs/user-authentication/general-settings"
|
||||
>
|
||||
Manage SSO menu.
|
||||
{t('redirectSso.manageSsoMenu', 'Manage SSO menu.')}
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -129,10 +143,11 @@ export const RedirectSso = function RedirectSso() {
|
|||
<>
|
||||
<p className="sso-type">
|
||||
<span className="">-</span>
|
||||
Google : <a href="https://docs.tooljet.com/docs/sso/google"> Link</a>
|
||||
{t('redirectSso.google', 'Google')} :{' '}
|
||||
<a href="https://docs.tooljet.com/docs/sso/google"> {t('redirectSso.link', 'Link')}</a>
|
||||
</p>
|
||||
<div className="flexer">
|
||||
<span> Redirect URL: </span>
|
||||
<span> {t('redirectSso.redirectUrl', 'Redirect URL:')} </span>
|
||||
<p id="google-url">{`${window.public_config?.TOOLJET_HOST}/sso/google/${organization?.google?.config_id}`}</p>
|
||||
|
||||
<img
|
||||
|
|
@ -151,11 +166,12 @@ export const RedirectSso = function RedirectSso() {
|
|||
<>
|
||||
<p className="sso-type ">
|
||||
<span className="">-</span>
|
||||
GitHub : <a href="https://docs.tooljet.com/docs/sso/github"> Link</a>
|
||||
{t('redirectSso.gitHub', 'GitHub')} :{' '}
|
||||
<a href="https://docs.tooljet.com/docs/sso/github"> {t('redirectSso.link', 'Link')}</a>
|
||||
</p>
|
||||
|
||||
<div className="flexer">
|
||||
<span> Redirect URL :</span>
|
||||
<span> {t('redirectSso.redirectUrl', 'Redirect URL:')}</span>
|
||||
<p id="git-url">{`${window.public_config?.TOOLJET_HOST}/sso/git/${organization?.git?.config_id}`}</p>
|
||||
|
||||
<img
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { authenticationService } from '@/_services';
|
||||
|
||||
class ResetPassword extends React.Component {
|
||||
import { withTranslation } from 'react-i18next';
|
||||
class ResetPasswordComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -59,30 +59,34 @@ class ResetPassword extends React.Component {
|
|||
</div>
|
||||
<form className="card card-md" action="." method="get" autoComplete="off">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title text-center mb-4">Reset Password</h2>
|
||||
<h2 className="card-title text-center mb-4">
|
||||
{this.props.t('loginSignupPage.resetPassword', 'Reset Password')}
|
||||
</h2>
|
||||
<div className="mb-2">
|
||||
<label className="form-label">New Password</label>
|
||||
<label className="form-label">{this.props.t('loginSignupPage.newPassword', 'New Password')}</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="password"
|
||||
type="password"
|
||||
className="form-control"
|
||||
placeholder="Password"
|
||||
placeholder={this.props.t('loginSignupPage.password', 'Password')}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<span className="input-group-text"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label className="form-label">Password Confirmation</label>
|
||||
<label className="form-label">
|
||||
{this.props.t('loginSignupPage.passwordConfirmation', 'Password Confirmation')}
|
||||
</label>
|
||||
<div className="input-group input-group-flat">
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="password_confirmation"
|
||||
type="password"
|
||||
className="form-control"
|
||||
placeholder="Password Confirmation"
|
||||
placeholder={this.props.t('loginSignupPage.passwordConfirmation', 'Password Confirmation')}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<span className="input-group-text"></span>
|
||||
|
|
@ -93,7 +97,7 @@ class ResetPassword extends React.Component {
|
|||
className={`btn btn-primary w-100 ${isLoading ? 'btn-loading' : ''}`}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
Submit
|
||||
{this.props.t('globals.submit', 'Submit')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -104,4 +108,4 @@ class ResetPassword extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { ResetPassword };
|
||||
export const ResetPassword = withTranslation()(ResetPasswordComponent);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { authenticationService, userService } from '@/_services';
|
||||
import { Header } from '@/_components';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function SettingsPage(props) {
|
||||
const [firstName, setFirstName] = React.useState(authenticationService.currentUserValue.first_name);
|
||||
|
|
@ -15,6 +16,7 @@ function SettingsPage(props) {
|
|||
const [passwordChangeInProgress, setPasswordChangeInProgress] = React.useState(false);
|
||||
const [selectedFile, setSelectedFile] = React.useState(null);
|
||||
const focusRef = React.useRef(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const updateDetails = async () => {
|
||||
const firstNameMatch = firstName.match(/^ *$/);
|
||||
|
|
@ -118,7 +120,7 @@ function SettingsPage(props) {
|
|||
<div className="col">
|
||||
<div className="page-pretitle"></div>
|
||||
<h2 className="page-title" data-cy="page-title">
|
||||
Profile Settings
|
||||
{t('header.profileSettingPage.profileSettings', 'Profile Settings')}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -130,7 +132,7 @@ function SettingsPage(props) {
|
|||
<div className="card">
|
||||
<div className="card-header">
|
||||
<h3 className="card-title" data-cy="card-title-profile">
|
||||
Profile
|
||||
{t('header.profileSettingPage.profile', 'Profile')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
|
|
@ -138,13 +140,13 @@ function SettingsPage(props) {
|
|||
<div className="col">
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="first-name-label">
|
||||
First name{' '}
|
||||
{t('header.profileSettingPage.firstName', 'First name')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
name="first-name"
|
||||
placeholder="Enter first name"
|
||||
placeholder={t('header.profileSettingPage.enterFirstName', 'Enter first name')}
|
||||
value={firstName}
|
||||
onChange={(event) => setFirstName(event.target.value)}
|
||||
data-cy="first-name-input"
|
||||
|
|
@ -154,13 +156,13 @@ function SettingsPage(props) {
|
|||
<div className="col">
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="last-name-label">
|
||||
Last name
|
||||
{t('header.profileSettingPage.lastName', 'Last name')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
name="last-name"
|
||||
placeholder="Enter last name"
|
||||
placeholder={t('header.profileSettingPage.enterLastName', 'Enter last name')}
|
||||
value={lastName}
|
||||
onChange={(event) => setLastName(event.target.value)}
|
||||
data-cy="last-name-input"
|
||||
|
|
@ -172,7 +174,7 @@ function SettingsPage(props) {
|
|||
<div className="col">
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="email-label">
|
||||
Email{' '}
|
||||
{t('header.profileSettingPage.email', 'Email')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -187,7 +189,7 @@ function SettingsPage(props) {
|
|||
</div>
|
||||
<div className="col">
|
||||
<div className="mb-3">
|
||||
<div className="form-label">Avatar</div>
|
||||
<div className="form-label">{t('header.profileSettingPage.avatar', 'Avatar')}</div>
|
||||
<input
|
||||
onChange={(e) => {
|
||||
const file = e.target.files[0];
|
||||
|
|
@ -210,7 +212,7 @@ function SettingsPage(props) {
|
|||
onClick={updateDetails}
|
||||
data-cy="update-button"
|
||||
>
|
||||
Update
|
||||
{t('header.profileSettingPage.update', 'Update')}
|
||||
</button>
|
||||
{/* An !important style on theme.scss is making the last child of every .card-body color to #c3c3c3!. */}
|
||||
{/* The div below is a placeholder to prevent it from affecting the button above. */}
|
||||
|
|
@ -221,7 +223,7 @@ function SettingsPage(props) {
|
|||
<div className="card">
|
||||
<div className="card-header">
|
||||
<h3 className="card-title" data-cy="card-title-change-password">
|
||||
Change password
|
||||
{t('header.profileSettingPage.changePassword', 'Change password')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
|
|
@ -229,13 +231,13 @@ function SettingsPage(props) {
|
|||
<div className="col">
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="current-password-label">
|
||||
Current password
|
||||
{t('header.profileSettingPage.currentPassword', 'Current password')}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
name="last-name"
|
||||
placeholder="Enter current password"
|
||||
placeholder={t('header.profileSettingPage.enterCurrentPassword', 'Enter current password')}
|
||||
value={currentpassword}
|
||||
onChange={(event) => setCurrentPassword(event.target.value)}
|
||||
data-cy="current-password-input"
|
||||
|
|
@ -245,13 +247,13 @@ function SettingsPage(props) {
|
|||
<div className="col">
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="new-password-label">
|
||||
New password
|
||||
{t('header.profileSettingPage.newPassword', 'New password')}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
name="last-name"
|
||||
placeholder="Enter new password"
|
||||
placeholder={t('header.profileSettingPage.enterNewPassword', 'Enter new password')}
|
||||
value={newPassword}
|
||||
onChange={(event) => setNewPassword(event.target.value)}
|
||||
onKeyPress={newPasswordKeyPressHandler}
|
||||
|
|
@ -263,13 +265,13 @@ function SettingsPage(props) {
|
|||
<div className="w-50 confirm-input">
|
||||
<div className="mb-3">
|
||||
<label className="form-label" data-cy="new-password-label">
|
||||
Confirm new password
|
||||
{t('header.profileSettingPage.confirmNewPassword', 'Confirm new password')}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
name="last-name"
|
||||
placeholder="Confirm new password"
|
||||
placeholder={t('header.profileSettingPage.confirmNewPassword', 'Confirm new password')}
|
||||
value={confirmPassword}
|
||||
ref={focusRef}
|
||||
onChange={(event) => setConfirmPassword(event.target.value)}
|
||||
|
|
@ -283,7 +285,7 @@ function SettingsPage(props) {
|
|||
onClick={changePassword}
|
||||
data-cy="change-password-button"
|
||||
>
|
||||
Change password
|
||||
{t('header.profileSettingPage.changePassword', 'Change password')}
|
||||
</button>
|
||||
{/* An !important style on theme.scss is making the last child of every .card-body color to #c3c3c3!. */}
|
||||
{/* The div below is a placeholder to prevent it from affecting the button above. */}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ import { Link } from 'react-router-dom';
|
|||
import { validateEmail } from '../_helpers/utils';
|
||||
import GoogleSSOLoginButton from '@ee/components/LoginPage/GoogleSSOLoginButton';
|
||||
import GitSSOLoginButton from '@ee/components/LoginPage/GitSSOLoginButton';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
class SignupPage extends React.Component {
|
||||
class SignupPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
|
@ -14,6 +15,8 @@ class SignupPage extends React.Component {
|
|||
isLoading: false,
|
||||
};
|
||||
|
||||
console.log('window.public_config?.SSO_DISABLE_SIGNUPS--- ', window.public_config?.SSO_DISABLE_SIGNUPS != true);
|
||||
|
||||
this.ssoConfigs = {
|
||||
enableSignUp:
|
||||
window.public_config?.DISABLE_MULTI_WORKSPACE !== 'true' &&
|
||||
|
|
@ -85,7 +88,9 @@ class SignupPage extends React.Component {
|
|||
<form className="card card-md" action="." method="get" autoComplete="off">
|
||||
{!signupSuccess && (
|
||||
<div className="card-body">
|
||||
<h2 className="card-title text-center mb-4">Create a ToolJet account</h2>
|
||||
<h2 className="card-title text-center mb-4">
|
||||
{this.props.t('loginSignupPage.createToolJetAccount', 'Create a ToolJet account')}
|
||||
</h2>
|
||||
{this.ssoConfigs.enableSignUp && (
|
||||
<div className="d-flex flex-column align-items-center separator-bottom">
|
||||
{this.ssoConfigs.configs?.google?.enabled && (
|
||||
|
|
@ -108,29 +113,33 @@ class SignupPage extends React.Component {
|
|||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Email address</label>
|
||||
<label className="form-label">{this.props.t('loginSignupPage.emailAddress', 'Email address')}</label>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name="email"
|
||||
type="email"
|
||||
className="form-control"
|
||||
placeholder="Enter your business email"
|
||||
placeholder={this.props.t('loginSignupPage.enterBusinessEmail', 'Enter your business email')}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-footer">
|
||||
<button className={`btn btn-primary w-100 ${isLoading ? 'btn-loading' : ''}`} onClick={this.signup}>
|
||||
Sign up
|
||||
{this.props.t('loginSignupPage.signUp', 'Sign up')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{signupSuccess && <div className="card-body">Please check your email for confirmation link</div>}
|
||||
{signupSuccess && (
|
||||
<div className="card-body">
|
||||
{this.props.t('loginSignupPage.emailConfirmLink', 'Please check your email for confirmation link')}
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
{!signupSuccess && (
|
||||
<div className="text-center text-muted mt-3">
|
||||
Already have an account?
|
||||
{this.props.t('loginSignupPage.alreadyHaveAnAccount', 'Already have an account?')}
|
||||
<Link to={'/login'} tabIndex="-1">
|
||||
Sign in
|
||||
{this.props.t('loginSignupPage.signIn', 'Sign in')}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -140,4 +149,4 @@ class SignupPage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export { SignupPage };
|
||||
export const SignupPage = withTranslation()(SignupPageComponent);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function ConfirmDialog({ show, message, onConfirm, onCancel, confirmButtonLoading, darkMode }) {
|
||||
const [showModal, setShow] = useState(show);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
setShow(show);
|
||||
|
|
@ -32,7 +34,7 @@ export function ConfirmDialog({ show, message, onConfirm, onCancel, confirmButto
|
|||
<Modal.Body data-cy="modal-message">{message}</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={handleClose} data-cy="cancel-button">
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="danger"
|
||||
|
|
@ -41,7 +43,7 @@ export function ConfirmDialog({ show, message, onConfirm, onCancel, confirmButto
|
|||
onClick={handleConfirm}
|
||||
data-cy="yes-button"
|
||||
>
|
||||
Yes
|
||||
{t('globals.yes', 'Yes')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { useSpring, animated } from 'react-spring';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const DarkModeToggle = function DarkModeToggle({
|
||||
darkMode = false,
|
||||
|
|
@ -11,7 +12,7 @@ export const DarkModeToggle = function DarkModeToggle({
|
|||
const toggleDarkMode = () => {
|
||||
switchDarkMode(!darkMode);
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
const properties = {
|
||||
sun: {
|
||||
r: 9,
|
||||
|
|
@ -49,7 +50,13 @@ export const DarkModeToggle = function DarkModeToggle({
|
|||
<OverlayTrigger
|
||||
placement={tooltipPlacement}
|
||||
delay={{ show: 250, hide: 400 }}
|
||||
overlay={<Tooltip id="button-tooltip">{darkMode ? 'Activate light mode' : 'Activate dark mode'}</Tooltip>}
|
||||
overlay={
|
||||
<Tooltip id="button-tooltip">
|
||||
{darkMode
|
||||
? t('header.darkModeToggle.activateLightMode', 'Activate light mode')
|
||||
: t('header.darkModeToggle.activateDarkMode', 'Activate dark mode')}
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<animated.svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import React, { useState } from 'react';
|
||||
import { datasourceService } from '@/_services';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Radio from '@/_ui/Radio';
|
||||
import Button from '@/_ui/Button';
|
||||
|
||||
const Googlesheets = ({ optionchanged, createDataSource, options, isSaving, selectedDataSource }) => {
|
||||
const [authStatus, setAuthStatus] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
function authGoogle() {
|
||||
const provider = 'googlesheets';
|
||||
|
|
@ -45,22 +47,33 @@ const Googlesheets = ({ optionchanged, createDataSource, options, isSaving, sele
|
|||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<div className="mb-3">
|
||||
<div className="form-label">Authorize</div>
|
||||
<p>If you want your ToolJet apps to modify your Google sheets, make sure to select read and write access</p>
|
||||
<div className="form-label">{t('globals.authorize', 'Authorize')}</div>
|
||||
<p>
|
||||
{t(
|
||||
'googleSheets.enableReadAndWrite',
|
||||
'If you want your ToolJet apps to modify your Google sheets, make sure to select read and write access'
|
||||
)}
|
||||
</p>
|
||||
<div>
|
||||
<Radio
|
||||
checked={options.access_type?.value === 'read'}
|
||||
disabled={authStatus === 'waiting_for_token'}
|
||||
onClick={() => optionchanged('access_type', 'read')}
|
||||
text="Read only"
|
||||
helpText="Your ToolJet apps can only read data from Google sheets"
|
||||
text={t('googleSheets.readOnly', 'Read only')}
|
||||
helpText={t(
|
||||
'googleSheets.readDataFromSheets',
|
||||
'Your ToolJet apps can only read data from Google sheets'
|
||||
)}
|
||||
/>
|
||||
<Radio
|
||||
checked={options.access_type?.value === 'write'}
|
||||
disabled={authStatus === 'waiting_for_token'}
|
||||
onClick={() => optionchanged('access_type', 'write')}
|
||||
text="Read and write"
|
||||
helpText="Your ToolJet apps can read data from sheets, modify sheets, and more."
|
||||
text={t('googleSheets.readWrite', 'Read and write')}
|
||||
helpText={t(
|
||||
'googleSheets.readModifySheets',
|
||||
'Your ToolJet apps can read data from sheets, modify sheets, and more.'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -75,7 +88,7 @@ const Googlesheets = ({ optionchanged, createDataSource, options, isSaving, sele
|
|||
disabled={isSaving}
|
||||
onClick={() => saveDataSource()}
|
||||
>
|
||||
{isSaving ? 'Saving...' : 'Save data source'}
|
||||
{isSaving ? t('globals.saving', 'Saving...') : t('globals.saveDatasource', 'Save data source')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -86,7 +99,8 @@ const Googlesheets = ({ optionchanged, createDataSource, options, isSaving, sele
|
|||
disabled={isSaving}
|
||||
onClick={() => authGoogle()}
|
||||
>
|
||||
{selectedDataSource.id ? 'Reconnect' : 'Connect'} to Google Sheets
|
||||
{selectedDataSource.id ? t('globals.reconnect', 'Reconnect') : t('globals.connect', 'Connect')}{' '}
|
||||
{t('googleSheets.toGoogleSheets', 'to Google Sheets')}
|
||||
</Button>
|
||||
)}
|
||||
</center>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import { DarkModeToggle } from './DarkModeToggle';
|
|||
import LogoIcon from '../Editor/Icons/logo.svg';
|
||||
import { Organization } from './Organization';
|
||||
import { NotificationCenter } from './NotificationCenter';
|
||||
import { LanguageSelection } from './LanguageSelection';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Header = function Header({ switchDarkMode, darkMode }) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
|
@ -14,6 +16,7 @@ export const Header = function Header({ switchDarkMode, darkMode }) {
|
|||
const [avatar, setAvatar] = useState();
|
||||
const { first_name, last_name, avatar_id, admin } = authenticationService.currentUserValue;
|
||||
const currentVersion = localStorage.getItem('currentVersion');
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
setPathName(document.location.pathname);
|
||||
|
|
@ -53,6 +56,9 @@ export const Header = function Header({ switchDarkMode, darkMode }) {
|
|||
<div className="p-1 m-1 d-flex align-items-center" data-cy="mode-toggle">
|
||||
<DarkModeToggle switchDarkMode={switchDarkMode} darkMode={darkMode} />
|
||||
</div>
|
||||
{/* <div className="p-1 m-1 d-flex align-items-center">
|
||||
<LanguageSelection darkMode={darkMode} />
|
||||
</div> */}
|
||||
{config.COMMENT_FEATURE_ENABLE && (
|
||||
<div className="p-1 d-flex align-items-center" data-cy="notification-center">
|
||||
<NotificationCenter />
|
||||
|
|
@ -87,10 +93,10 @@ export const Header = function Header({ switchDarkMode, darkMode }) {
|
|||
</a>
|
||||
<div className="dropdown-menu dropdown-menu-end dropdown-menu-arrow end-0" data-cy="dropdown-menu">
|
||||
<Link data-testid="settingsBtn" to="/settings" className="dropdown-item" data-cy="profile-link">
|
||||
Profile
|
||||
{t('header.profile', 'Profile')}
|
||||
</Link>
|
||||
<Link data-testid="logoutBtn" to="#" onClick={logout} className="dropdown-item" data-cy="logout-link">
|
||||
Logout
|
||||
{t('header.logout', 'Logout')}
|
||||
</Link>
|
||||
{currentVersion && (
|
||||
<Link to="#" className={`dropdown-item pe-none ${darkMode ? 'color-muted-darkmode' : 'color-muted'}`}>
|
||||
|
|
|
|||
179
frontend/src/_components/LanguageSelection.jsx
Normal file
179
frontend/src/_components/LanguageSelection.jsx
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import { SearchBox } from './SearchBox';
|
||||
import { ListGroup } from 'react-bootstrap';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import i18n from 'i18next';
|
||||
import { isEqual } from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const LanguageSelection = ({ darkMode = false, tooltipPlacement = 'bottom' }) => {
|
||||
const [showModal, setShow] = useState(false);
|
||||
const [selectedLang, setLanguage] = useState({});
|
||||
const [filteredLang, setFilteredLang] = useState([]);
|
||||
const languageRef = useRef(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const lang = i18n.language || 'en';
|
||||
(async () => {
|
||||
languageRef.current = await fetch('/assets/translations/languages.json')
|
||||
.then((response) => response.json())
|
||||
.then((data) => data.languageList);
|
||||
const filteredLanguage = languageRef.current.find((ln) => ln.code === lang);
|
||||
if (filteredLanguage === undefined) {
|
||||
setLanguage(languageRef.current.find((ln) => ln.code === 'en'));
|
||||
} else {
|
||||
setLanguage(filteredLanguage);
|
||||
}
|
||||
setFilteredLang(languageRef.current);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const handleClose = () => {
|
||||
setShow(false);
|
||||
};
|
||||
|
||||
const handleOpen = () => {
|
||||
setShow(true);
|
||||
};
|
||||
|
||||
const onLanguageSelection = (lang) => {
|
||||
setLanguage(lang);
|
||||
i18n.changeLanguage(lang.code);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const searchLanguage = (searchText) => {
|
||||
const lowerCaseSearchText = searchText.toLowerCase();
|
||||
const filteredLanguages = languageRef.current.filter(
|
||||
(ln) =>
|
||||
ln.lang.toLowerCase().startsWith(lowerCaseSearchText) ||
|
||||
ln.nativeLang.toLowerCase().startsWith(lowerCaseSearchText) ||
|
||||
ln.code.toLowerCase().startsWith(lowerCaseSearchText)
|
||||
);
|
||||
if (!isEqual(filteredLanguages, filteredLang)) {
|
||||
setFilteredLang(filteredLanguages);
|
||||
}
|
||||
};
|
||||
|
||||
const renderLanguageList = () => {
|
||||
return (
|
||||
<>
|
||||
{filteredLang.length === 0 ? (
|
||||
<ListGroup.Item variant="light" className="no-results-item">
|
||||
No results
|
||||
</ListGroup.Item>
|
||||
) : (
|
||||
<>
|
||||
<ListGroup.Item key={selectedLang.code} action active onClick={() => onLanguageSelection(selectedLang)}>
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
{selectedLang.lang}
|
||||
<p>{selectedLang.nativeLang}</p>
|
||||
</div>
|
||||
<div className="col-auto ms-auto">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="icon icon-tabler icon-tabler-check"
|
||||
width="44"
|
||||
height="44"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="#4d72fa"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M5 12l5 5l10 -10" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
{filteredLang.map((ln) => {
|
||||
if (ln.code === selectedLang.code) return;
|
||||
return (
|
||||
<ListGroup.Item key={ln.code} action onClick={() => onLanguageSelection(ln)}>
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
{ln.lang}
|
||||
<p>{ln.nativeLang}</p>
|
||||
</div>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderModal = () => {
|
||||
return (
|
||||
<Modal
|
||||
show={showModal}
|
||||
onHide={handleClose}
|
||||
size="sm"
|
||||
centered={true}
|
||||
contentClassName={`lang-selection-modal ${darkMode && 'dark'}`}
|
||||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title>{t('header.languageSelection.changeLanguage', 'Change language')}</Modal.Title>
|
||||
<span className={`close-btn mx-4 mt-3 ${darkMode ? 'dark' : ''}`} onClick={handleClose}>
|
||||
<img src="/assets/images/icons/close.svg" width="12" height="12" />
|
||||
</span>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div className="lang-list">
|
||||
<div className="search-box">
|
||||
<SearchBox
|
||||
onSubmit={searchLanguage}
|
||||
width="100%"
|
||||
placeholder={t('header.languageSelection.searchLanguage', 'Search language')}
|
||||
/>
|
||||
</div>
|
||||
<ListGroup>{renderLanguageList()}</ListGroup>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<OverlayTrigger
|
||||
placement={tooltipPlacement}
|
||||
delay={{ show: 250, hide: 400 }}
|
||||
overlay={
|
||||
<Tooltip id="button-tooltip">{t('header.languageSelection.changeLanguage', 'Change language')}</Tooltip>
|
||||
}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="icon icon-tabler icon-tabler-world"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke={darkMode ? '#fff' : '#808080'}
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
onClick={handleOpen}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<line x1="3.6" y1="9" x2="20.4" y2="9" />
|
||||
<line x1="3.6" y1="15" x2="20.4" y2="15" />
|
||||
<path d="M11.5 3a17 17 0 0 0 0 18" />
|
||||
<path d="M12.5 3a17 17 0 0 1 0 18" />
|
||||
</svg>
|
||||
</OverlayTrigger>
|
||||
{showModal && renderModal()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -3,12 +3,13 @@ import { commentNotificationsService } from '@/_services';
|
|||
import { Notification } from './Notification';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const NotificationCenter = () => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [isRead, setIsRead] = React.useState(false);
|
||||
const [commentNotifications, setCommentNotifications] = React.useState([]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
async function fetchData() {
|
||||
setLoading(true);
|
||||
const { data, error } = await commentNotificationsService.findAll(isRead);
|
||||
|
|
@ -70,7 +71,7 @@ export const NotificationCenter = () => {
|
|||
>
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
<h1 className="card-title">Notifications</h1>
|
||||
<h1 className="card-title">{t('header.notificationCenter.notifications', 'Notifications')}</h1>
|
||||
{!loading && commentNotifications?.length > 0 && (
|
||||
<a href="#" onClick={updateAllNotifications} className="text-muted text-decoration-none ms-auto">
|
||||
Mark all as {isRead && 'un'}read
|
||||
|
|
@ -85,9 +86,17 @@ export const NotificationCenter = () => {
|
|||
{!loading && commentNotifications.length === 0 && (
|
||||
<div className="empty">
|
||||
<div className="empty-img pb-3">🔔</div>
|
||||
<p className="empty-title mb-1">You're all caught up!</p>
|
||||
<p className="empty-title mb-1">
|
||||
{t('header.notificationCenter.youAreCaughtUp', `You're all caught up!`)}
|
||||
</p>
|
||||
<p className="empty-subtitle text-muted">
|
||||
You don't have any {!isRead && 'un'}read notifications!
|
||||
{`${t('header.notificationCenter.youDontHaveany', `You don't have any`)} ${
|
||||
!isRead && t('header.notificationCenter.un', 'un')
|
||||
}${t('header.notificationCenter.read', 'read')} ${t(
|
||||
`header.notificationCenter.notifications`,
|
||||
'notifications'
|
||||
).toLowerCase()}!
|
||||
`}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -99,7 +108,10 @@ export const NotificationCenter = () => {
|
|||
</div>
|
||||
<div className="card-footer text-center margin-auto">
|
||||
<a href="#" className="text-muted text-decoration-none" onClick={() => setIsRead(!isRead)}>
|
||||
View {isRead && 'un'}read notifications
|
||||
{`${t('header.notificationCenter.view', 'View')} ${isRead && t('header.notificationCenter.un', 'un')}${t(
|
||||
'header.notificationCenter.read',
|
||||
'read'
|
||||
)} ${t(`header.notificationCenter.notifications`, 'notifications').toLowerCase()}`}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { authenticationService, organizationService } from '@/_services';
|
|||
import Modal from '../HomePage/Modal';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { SearchBox } from './SearchBox';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Organization = function Organization({ darkMode }) {
|
||||
const isSingleOrganization = window.public_config?.DISABLE_MULTI_WORKSPACE === 'true';
|
||||
|
|
@ -17,6 +18,7 @@ export const Organization = function Organization({ darkMode }) {
|
|||
const [getOrgStatus, setGetOrgStatus] = useState('loading');
|
||||
const [isListOrganizations, setIsListOrganizations] = useState(false);
|
||||
const [newOrgName, setNewOrgName] = useState('');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getAvatar = (organization) => {
|
||||
if (!organization) return;
|
||||
|
|
@ -210,11 +212,16 @@ export const Organization = function Organization({ darkMode }) {
|
|||
</svg>
|
||||
</div>
|
||||
<div className="back-btn" onClick={() => setIsListOrganizations(false)}>
|
||||
Back
|
||||
{t('globals.back', 'Back')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="search-box">
|
||||
<SearchBox onSubmit={searchOrganizations} debounceDelay={100} width="14rem" />
|
||||
<SearchBox
|
||||
onSubmit={searchOrganizations}
|
||||
debounceDelay={100}
|
||||
width="14rem"
|
||||
placeholder={t('globals.search', 'Search')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="org-list">
|
||||
|
|
@ -227,7 +234,7 @@ export const Organization = function Organization({ darkMode }) {
|
|||
href="#"
|
||||
className={`btn btn-primary mb-2 ${getOrgStatus === 'loading' ? 'btn-loading' : ''}`}
|
||||
>
|
||||
Load Organizations
|
||||
{t('header.organization.loadOrganizations', 'Load Organizations')}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -251,7 +258,7 @@ export const Organization = function Organization({ darkMode }) {
|
|||
{admin && (
|
||||
<div className="org-edit">
|
||||
<span onClick={showEditModal} data-cy="edit-workspace-name">
|
||||
Edit
|
||||
{t('globals.edit', 'Edit')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -282,25 +289,27 @@ export const Organization = function Organization({ darkMode }) {
|
|||
</div>
|
||||
{!isSingleOrganization && (
|
||||
<div className="dropdown-item org-actions">
|
||||
<div onClick={showCreateModal}>Add workspace</div>
|
||||
<div onClick={showCreateModal}>{t('header.organization.menus.addWorkspace', 'Add workspace')}</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="dropdown-divider"></div>
|
||||
{admin && (
|
||||
<>
|
||||
<Link data-testid="settingsBtn" to="/users" className="dropdown-item" data-cy="manage-users">
|
||||
Manage Users
|
||||
{t('header.organization.menus.menusList.manageUsers', 'Manage Users')}
|
||||
</Link>
|
||||
<Link data-tesid="settingsBtn" to="/groups" className="dropdown-item" data-cy="manage-groups">
|
||||
Manage Groups
|
||||
{t('header.organization.menus.menusList.manageGroups', 'Manage Groups')}
|
||||
</Link>
|
||||
<Link data-tesid="settingsBtn" to="/manage-sso" className="dropdown-item" data-cy="manage-sso">
|
||||
Manage SSO
|
||||
{t('header.organization.menus.menusList.manageSso', 'Manage SSO')}
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
<Link data-tesid="settingsBtn" to="/manage-environment-vars" className="dropdown-item">
|
||||
{admin ? 'Manage Environment Variables' : 'Environment Variables'}
|
||||
{admin
|
||||
? t('header.organization.menus.menusList.manageEnv', 'Manage Environment Variables')
|
||||
: t('globals.environmentVar', 'Environment Variables')}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -322,14 +331,18 @@ export const Organization = function Organization({ darkMode }) {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Modal show={showCreateOrg} closeModal={() => setShowCreateOrg(false)} title="Create workspace">
|
||||
<Modal
|
||||
show={showCreateOrg}
|
||||
closeModal={() => setShowCreateOrg(false)}
|
||||
title={t('header.organization.createWorkspace', 'Create workspace')}
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col modal-main">
|
||||
<input
|
||||
type="text"
|
||||
onChange={(e) => setNewOrgName(e.target.value)}
|
||||
className="form-control"
|
||||
placeholder="workspace name"
|
||||
placeholder={t('header.organization.workspaceName', 'workspace name')}
|
||||
disabled={isCreating}
|
||||
maxLength={25}
|
||||
/>
|
||||
|
|
@ -338,26 +351,30 @@ export const Organization = function Organization({ darkMode }) {
|
|||
<div className="row">
|
||||
<div className="col d-flex modal-footer-btn">
|
||||
<button className="btn btn-light" onClick={() => setShowCreateOrg(false)}>
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button
|
||||
disabled={isCreating}
|
||||
className={`btn btn-primary ${isCreating ? 'btn-loading' : ''}`}
|
||||
onClick={createOrganization}
|
||||
>
|
||||
Create workspace
|
||||
{t('header.organization.createWorkspace', 'Create workspace')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal show={showEditOrg} closeModal={() => setShowEditOrg(false)} title="Edit workspace">
|
||||
<Modal
|
||||
show={showEditOrg}
|
||||
closeModal={() => setShowEditOrg(false)}
|
||||
title={t('header.organization.editWorkspace', 'Edit workspace')}
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col modal-main">
|
||||
<input
|
||||
type="text"
|
||||
onChange={(e) => setNewOrgName(e.target.value)}
|
||||
className="form-control"
|
||||
placeholder="workspace name"
|
||||
placeholder={t('header.organization.workspaceName', 'workspace name')}
|
||||
disabled={isCreating}
|
||||
value={newOrgName}
|
||||
maxLength={25}
|
||||
|
|
@ -367,10 +384,10 @@ export const Organization = function Organization({ darkMode }) {
|
|||
<div className="row">
|
||||
<div className="col d-flex modal-footer-btn">
|
||||
<button className="btn btn-light" onClick={() => setShowEditOrg(false)}>
|
||||
Cancel
|
||||
{t('globals.cancel', 'Cancel')}
|
||||
</button>
|
||||
<button className={`btn btn-primary ${isCreating ? 'btn-loading' : ''}`} onClick={editOrganization}>
|
||||
Save
|
||||
{t('globals.save', 'Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Pagination = function Pagination({ currentPage, count, pageChanged, itemsPerPage = 10, darkMode }) {
|
||||
const { t } = useTranslation();
|
||||
const totalPages = useMemo(() => {
|
||||
return Math.floor((count - 1) / itemsPerPage) + 1;
|
||||
}, [count, itemsPerPage]);
|
||||
|
|
@ -54,7 +56,9 @@ export const Pagination = function Pagination({ currentPage, count, pageChanged,
|
|||
return (
|
||||
<div className={`card-footer d-flex align-items-center px-1 ${darkMode ? ' bg-transparent' : ''}`}>
|
||||
<p className={`m-0 ${darkMode ? 'text-light' : 'text-muted'}`}>
|
||||
Showing <span>{startingAppCount()}</span> to <span>{endingAppCount()}</span> of <span>{count}</span>
|
||||
{t('homePage.pagination.showing', 'Showing')} <span>{startingAppCount()}</span>{' '}
|
||||
{t('homePage.pagination.to', 'to')} <span>{endingAppCount()}</span> {t('homePage.pagination.of', 'of')}{' '}
|
||||
<span>{count}</span>
|
||||
</p>
|
||||
<ul className="pagination m-0 ms-auto">
|
||||
<li className={`page-item ${currentPage === 1 ? 'disabled' : ''}`}>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,13 @@ import React, { useState, useEffect } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import useDebounce from '@/_hooks/useDebounce';
|
||||
|
||||
export function SearchBox({ width = '200px', onSubmit, debounceDelay = 300, darkMode = false }) {
|
||||
export function SearchBox({
|
||||
width = '200px',
|
||||
onSubmit,
|
||||
debounceDelay = 300,
|
||||
darkMode = false,
|
||||
placeholder = 'Search',
|
||||
}) {
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const debouncedSearchTerm = useDebounce(searchText, debounceDelay);
|
||||
const [isFocused, setFocussed] = useState(false);
|
||||
|
|
@ -48,7 +54,7 @@ export function SearchBox({ width = '200px', onSubmit, debounceDelay = 300, dark
|
|||
value={searchText}
|
||||
onChange={handleChange}
|
||||
className={`form-control ${darkMode && 'dark-theme-placeholder'}`}
|
||||
placeholder="Search"
|
||||
placeholder={placeholder}
|
||||
onFocus={() => setFocussed(true)}
|
||||
onBlur={() => setFocussed(false)}
|
||||
data-cy="home-page-search-bar"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import React, { useState } from 'react';
|
||||
import { datasourceService } from '@/_services';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Button from '@/_ui/Button';
|
||||
|
||||
const Slack = ({ optionchanged, createDataSource, options, isSaving, selectedDataSource }) => {
|
||||
const [authStatus, setAuthStatus] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
function authGoogle() {
|
||||
const provider = 'slack';
|
||||
|
|
@ -38,10 +40,12 @@ const Slack = ({ optionchanged, createDataSource, options, isSaving, selectedDat
|
|||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<div className="mb-3">
|
||||
<div className="form-label">Authorize</div>
|
||||
<div className="form-label">{t('slack.authorize', 'Authorize')}</div>
|
||||
<p>
|
||||
ToolJet can connect to Slack and list users, send messages, etc. Please select appropriate permission
|
||||
scopes.
|
||||
{t(
|
||||
'slack.connectToolJetToSlack',
|
||||
'ToolJet can connect to Slack and list users, send messages, etc. Please select appropriate permission scopes.'
|
||||
)}
|
||||
</p>
|
||||
<div>
|
||||
<label className="form-check mt-3">
|
||||
|
|
@ -53,9 +57,12 @@ const Slack = ({ optionchanged, createDataSource, options, isSaving, selectedDat
|
|||
disabled={authStatus === 'waiting_for_token'}
|
||||
/>
|
||||
<span className="form-check-label">
|
||||
chat:write <br />
|
||||
{t('slack.chatWrite', 'chat:write')} <br />
|
||||
<small className="text-muted">
|
||||
Your ToolJet app will be able to list users and send messages to users & channels.
|
||||
{t(
|
||||
'slack.listUsersAndSendMessage',
|
||||
'Your ToolJet app will be able to list users and send messages to users & channels.'
|
||||
)}
|
||||
</small>
|
||||
</span>
|
||||
</label>
|
||||
|
|
@ -72,7 +79,7 @@ const Slack = ({ optionchanged, createDataSource, options, isSaving, selectedDat
|
|||
disabled={isSaving}
|
||||
onClick={() => saveDataSource()}
|
||||
>
|
||||
{isSaving ? 'Saving...' : 'Save data source'}
|
||||
{isSaving ? t('globals.saving', 'Saving...') : t('globals.saveDatasource', 'Save data source')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -83,7 +90,7 @@ const Slack = ({ optionchanged, createDataSource, options, isSaving, selectedDat
|
|||
disabled={isSaving}
|
||||
onClick={() => authGoogle()}
|
||||
>
|
||||
Connect to Slack
|
||||
{t('slack.connectSlack', 'Connect to Slack')}
|
||||
</Button>
|
||||
)}
|
||||
</center>
|
||||
|
|
|
|||
|
|
@ -6118,4 +6118,220 @@ input.hide-input-arrows{
|
|||
color: #C8C6C6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Language Selection Modal
|
||||
.lang-selection-modal {
|
||||
font-weight: 500;
|
||||
|
||||
.list-group{
|
||||
padding: 1rem 1.5rem;
|
||||
padding-top: 0;
|
||||
overflow-y: scroll;
|
||||
height: calc(100% - 68px);
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border: 0;
|
||||
p {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item.active {
|
||||
background-color: #edf1ff;
|
||||
color: #4d72fa;
|
||||
font-weight: 600;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.modal-body{
|
||||
height: 50vh;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.lang-list {
|
||||
height: 100%;
|
||||
.search-box{
|
||||
position: relative;
|
||||
margin: 1rem 1.5rem;
|
||||
}
|
||||
input {
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
.search-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item.active {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lang-selection-modal.dark {
|
||||
.modal-header {
|
||||
border-color: #232e3c !important;
|
||||
}
|
||||
|
||||
.modal-body,
|
||||
.modal-footer,
|
||||
.modal-header,
|
||||
.modal-content {
|
||||
color: white;
|
||||
background-color: #2b394a;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
color: white;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.list-group-item:hover {
|
||||
background-color: #232e3c;
|
||||
}
|
||||
|
||||
.list-group-item.active {
|
||||
background-color: #4d72fa;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.no-results-item {
|
||||
background-color: #2b394a;
|
||||
color: white;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: #2b394a;
|
||||
border-color: #232e3c;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
// Language Selection Modal
|
||||
.lang-selection-modal {
|
||||
font-weight: 500;
|
||||
|
||||
.list-group{
|
||||
padding: 1rem 1.5rem;
|
||||
padding-top: 0;
|
||||
overflow-y: scroll;
|
||||
height: calc(100% - 68px);
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border: 0;
|
||||
p {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item.active {
|
||||
background-color: #edf1ff;
|
||||
color: #4d72fa;
|
||||
font-weight: 600;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.modal-body{
|
||||
height: 50vh;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.lang-list {
|
||||
height: 100%;
|
||||
.search-box{
|
||||
position: relative;
|
||||
margin: 1rem 1.5rem;
|
||||
}
|
||||
input {
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
.search-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item.active {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lang-selection-modal.dark {
|
||||
.modal-header {
|
||||
border-color: #232e3c !important;
|
||||
}
|
||||
|
||||
.modal-body,
|
||||
.modal-footer,
|
||||
.modal-header,
|
||||
.modal-content {
|
||||
color: white;
|
||||
background-color: #2b394a;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
color: white;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.list-group-item:hover {
|
||||
background-color: #232e3c;
|
||||
}
|
||||
|
||||
.list-group-item.active {
|
||||
background-color: #4d72fa;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.no-results-item {
|
||||
background-color: #2b394a;
|
||||
color: white;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: #2b394a;
|
||||
border-color: #232e3c;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const SearchBox = ({ onChange, ...restProps }) => {
|
||||
const { callback, placeholder } = restProps;
|
||||
const [searchText, setSearchText] = React.useState('');
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = (e) => {
|
||||
setSearchText(e.target.value);
|
||||
|
|
@ -94,7 +96,7 @@ export const SearchBox = ({ onChange, ...restProps }) => {
|
|||
value={searchText}
|
||||
onChange={handleChange}
|
||||
className="form-control animate-width-change"
|
||||
placeholder={placeholder ?? 'Search'}
|
||||
placeholder={placeholder ?? t(`globals.search`, 'Search')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
20
frontend/src/i18n.js
Normal file
20
frontend/src/i18n.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// eslint-disable-next-line import/no-unresolved
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
import Backend from 'i18next-http-backend';
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
load: 'languageOnly',
|
||||
fallbackLng: 'en',
|
||||
backend: {
|
||||
loadPath: `/assets/translations/{{lng}}.json`,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
|
@ -5,6 +5,7 @@ import { Integrations } from '@sentry/tracing';
|
|||
import { createBrowserHistory } from 'history';
|
||||
import { appService } from '@/_services';
|
||||
import { App } from './App';
|
||||
import './i18n';
|
||||
|
||||
const AppWithProfiler = Sentry.withProfiler(App);
|
||||
|
||||
|
|
|
|||
6189
package-lock.json
generated
6189
package-lock.json
generated
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue