translate(frontend): some setting page

This commit is contained in:
이강준
2022-11-27 15:47:02 +09:00
parent 14bffebc55
commit 2c1398e71c
6 changed files with 197 additions and 95 deletions
@@ -3,6 +3,8 @@ import { Fragment, useState } from "react";
import ListBox from "../Listbox"; import ListBox from "../Listbox";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Button from "../buttons/Button"; import Button from "../buttons/Button";
import useTranslation from "next-translate/useTranslation";
import Trans from "next-translate/Trans";
const AddProjectMemberDialog = ({ const AddProjectMemberDialog = ({
isOpen, isOpen,
@@ -14,6 +16,7 @@ const AddProjectMemberDialog = ({
setEmail, setEmail,
}) => { }) => {
const router = useRouter(); const router = useRouter();
const { t } = useTranslation();
return ( return (
<div className="z-50"> <div className="z-50">
@@ -48,56 +51,68 @@ const AddProjectMemberDialog = ({
as="h3" as="h3"
className="text-lg font-medium leading-6 text-gray-400 z-50" className="text-lg font-medium leading-6 text-gray-400 z-50"
> >
Add a member to your project {t(
"settings:add-member-dialog.add-member-to-project"
)}
</Dialog.Title> </Dialog.Title>
) : ( ) : (
<Dialog.Title <Dialog.Title
as="h3" as="h3"
className="text-lg font-medium leading-6 text-gray-400 z-50" className="text-lg font-medium leading-6 text-gray-400 z-50"
> >
All the users in your organization {t(
are already invited. "settings:add-member-dialog.already-all-invited"
)}
</Dialog.Title> </Dialog.Title>
)} )}
<div className="mt-2 mb-4"> <div className="mt-2 mb-4">
{data?.length > 0 ? ( {data?.length > 0 ? (
<div className="flex flex-col"> <div className="flex flex-col">
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
The user will receive an email {t(
with the instructions. "settings:add-member-dialog.user-will-email"
)}
</p> </p>
<div className=""> <div className="">
<button <Trans
type="button" i18nKey="settings:add-member-dialog.looking-add"
className="inline-flex justify-center rounded-md py-1 text-sm text-gray-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2" components={[
onClick={() => <button
router.push( type="button"
"/settings/org/" + className="inline-flex justify-center rounded-md py-1 text-sm text-gray-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
router.query.id onClick={() =>
) router.push(
} "/settings/org/" +
> router
If you are looking to add users to your org, .query
</button> .id
<button )
type="button" }
className="ml-1 inline-flex justify-center rounded-md py-1 text-sm text-gray-500 hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2" />,
onClick={() => <button
router.push( type="button"
"/settings/org/" + className="ml-1 inline-flex justify-center rounded-md py-1 text-sm text-gray-500 hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
router.query.id + onClick={() =>
"?invite" router.push(
) "/settings/org/" +
} router
> .query
click here. .id +
</button> "?invite"
)
}
>
click here.
</button>,
]}
/>
</div> </div>
</div> </div>
) : ( ) : (
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
Add more users to the {t(
organization first. "settings:add-member-dialog.add-user-org-first"
)}
</p> </p>
)} )}
</div> </div>
@@ -117,9 +132,13 @@ const AddProjectMemberDialog = ({
{data?.length > 0 ? ( {data?.length > 0 ? (
<div className="mt-6 flex flex-col justify-start w-max"> <div className="mt-6 flex flex-col justify-start w-max">
<Button <Button
onButtonPressed={submitModal} onButtonPressed={
submitModal
}
color="mineshaft" color="mineshaft"
text="Add Member" text={t(
"settings:add-member-dialog.add-member"
)}
size="md" size="md"
/> />
</div> </div>
@@ -132,7 +151,9 @@ const AddProjectMemberDialog = ({
) )
} }
color="mineshaft" color="mineshaft"
text="Add Users to Organization" text={t(
"settings:add-member-dialog.add-user-to-org"
)}
size="md" size="md"
/> />
)} )}
+2
View File
@@ -9,6 +9,8 @@ module.exports = {
pages: { pages: {
"*": ["common", "nav"], "*": ["common", "nav"],
"rgx:^/(login|signup)": ["auth"], "rgx:^/(login|signup)": ["auth"],
"rgx:^/settings": ["settings"],
"/dashboard/[id]": ["dashboard"], "/dashboard/[id]": ["dashboard"],
"/users/[id]": ["settings"],
}, },
}; };
+50 -2
View File
@@ -17,8 +17,12 @@
"click-to-copy": "Click to copy", "click-to-copy": "Click to copy",
"project-id": "Project ID", "project-id": "Project ID",
"save-changes": "Save Changes", "save-changes": "Save Changes",
"saved": "Saved",
"drop-zone": "Drag and drop your .env file here.", "drop-zone": "Drag and drop your .env file here.",
"drop-zone-keys": "Drag and drop your .env file here to add more keys." "drop-zone-keys": "Drag and drop your .env file here to add more keys.",
"role": "Role",
"role_admin": "admin",
"display-name": "Display Name"
}, },
"nav": { "nav": {
"support": { "support": {
@@ -75,6 +79,12 @@
"step4-download": "Download PDF" "step4-download": "Download PDF"
}, },
"dashboard": { "dashboard": {
"secrets": "Secrets",
"meta": {
"title": "Dashboard | Infiscal",
"og-title": "Manage your .env files in seconds",
"og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files."
},
"search-keys": "Search keys...", "search-keys": "Search keys...",
"add-key": "Add Key", "add-key": "Add Key",
"personal": "Personal", "personal": "Personal",
@@ -82,6 +92,44 @@
"shared": "Shared", "shared": "Shared",
"shared-description": "Shared keys are visible to your whole team", "shared-description": "Shared keys are visible to your whole team",
"make-shared": "Make Shared", "make-shared": "Make Shared",
"make-personal": "Make Personal" "make-personal": "Make Personal",
"check-docs": {
"button": "Check Docs",
"title": "Good job!",
"line1": "Congrats on adding more secrets.",
"line2": "Here is how to connect them to your codebase."
}
},
"settings": {
"meta": {
"members": {
"title": "Project Members",
"head-title": "Project Settings | Infiscal",
"description": "This pages shows the members of the selected project."
},
"org": {
"title": "Organization Settings",
"head-title": "Organization Settings | Infiscal",
"description": "Manage members of your organization. These users could afterwards be formed into projects."
}
},
"search-members": "Search members...",
"search": "Search...",
"add-member": "Add Member",
"org-members": "Organization Members",
"org-members-description": "Manage members of your organization. These users could afterwards be formed into projects.",
"incident-contacts": "Incident Contacts",
"incident-contacts-description": "These contacts will be notified in the unlikely event of a severe incident.",
"no-incident-contacts": "No incident contacts found.",
"add-contact": "Add Contact",
"add-member-dialog": {
"add-member-to-project": "Add a member to your project",
"already-all-invited": "All the users in your organization are already invited.",
"add-user-org-first": "Add more users to the organization first.",
"user-will-email": "The user will receive an email with the instructions.",
"looking-add": "<0>If you are looking to add users to your org,</0><1>click here</1>",
"add-user-to-org": "Add Users to Organization",
"add-member": "Add Member"
}
} }
} }
+13 -10
View File
@@ -376,29 +376,32 @@ export default function Dashboard() {
return data ? ( return data ? (
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white"> <div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
<Head> <Head>
<title>Secrets</title> <title>{t("dashboard:meta.title")}</title>
<link rel="icon" href="/infisical.ico" /> <link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" /> <meta property="og:image" content="/images/message.png" />
<meta <meta
property="og:title" property="og:title"
content="Manage your .env files in seconds" content={t("dashboard:meta.og-title")}
/> />
<meta <meta
name="og:description" name="og:description"
content="Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files." content={t("dashboard:meta.og-description")}
/> />
</Head> </Head>
<div className="flex flex-row"> <div className="flex flex-row">
<div className="w-full max-h-96 pb-2"> <div className="w-full max-h-96 pb-2">
<NavHeader pageName="Secrets" isProjectRelated={true} /> <NavHeader
pageName={t("dashboard:secrets")}
isProjectRelated={true}
/>
{checkDocsPopUpVisible && ( {checkDocsPopUpVisible && (
<BottonRightPopup <BottonRightPopup
buttonText="Check Docs" buttonText={t("dashboard:check-docs.button")}
buttonLink="https://infisical.com/docs/getting-started/introduction" buttonLink="https://infisical.com/docs/getting-started/introduction"
titleText="Good job!" titleText={t("dashboard:check-docs.title")}
emoji="🎉" emoji="🎉"
textLine1="Congrats on adding more secrets." textLine1={t("dashboard:check-docs.line1")}
textLine2="Here is how to connect them to your codebase." textLine2={t("dashboard:check-docs.line2")}
setCheckDocsPopUpVisible={setCheckDocsPopUpVisible} setCheckDocsPopUpVisible={setCheckDocsPopUpVisible}
/> />
)} )}
@@ -456,13 +459,13 @@ export default function Dashboard() {
className={`flex justify-start max-w-sm mt-2`} className={`flex justify-start max-w-sm mt-2`}
> >
<Button <Button
text={t("save-changes")} text={t("common:save-changes")}
onButtonPressed={savePush} onButtonPressed={savePush}
color="primary" color="primary"
size="md" size="md"
active={buttonReady} active={buttonReady}
iconDisabled={faCheck} iconDisabled={faCheck}
textDisabled="Saved" textDisabled={t("common:saved")}
/> />
</div> </div>
)} )}
+50 -29
View File
@@ -2,7 +2,11 @@ import React, { useState, useEffect } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Head from "next/head"; import Head from "next/head";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faMagnifyingGlass, faPlus, faX } from "@fortawesome/free-solid-svg-icons"; import {
faMagnifyingGlass,
faPlus,
faX,
} from "@fortawesome/free-solid-svg-icons";
import { faCheck } from "@fortawesome/free-solid-svg-icons"; import { faCheck } from "@fortawesome/free-solid-svg-icons";
import InputField from "../../../components/basic/InputField"; import InputField from "../../../components/basic/InputField";
import getWorkspaces from "../../api/workspace/getWorkspaces"; import getWorkspaces from "../../api/workspace/getWorkspaces";
@@ -22,6 +26,7 @@ import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscript
import NavHeader from "../../../components/navigation/NavHeader"; import NavHeader from "../../../components/navigation/NavHeader";
import Button from "../../../components/basic/buttons/Button"; import Button from "../../../components/basic/buttons/Button";
import useTranslation from "next-translate/useTranslation";
export default function SettingsOrg() { export default function SettingsOrg() {
const [buttonReady, setButtonReady] = useState(false); const [buttonReady, setButtonReady] = useState(false);
@@ -45,18 +50,20 @@ export default function SettingsOrg() {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [currentPlan, setCurrentPlan] = useState(""); const [currentPlan, setCurrentPlan] = useState("");
const { t } = useTranslation();
useEffect(async () => { useEffect(async () => {
let org = await getOrganization({ let org = await getOrganization({
orgId: localStorage.getItem("orgData.id"), orgId: localStorage.getItem("orgData.id"),
}); });
let orgData = org; let orgData = org;
setOrgName(orgData.name); setOrgName(orgData.name);
let incidentContactsData = await getIncidentContacts(localStorage.getItem("orgData.id")); let incidentContactsData = await getIncidentContacts(
localStorage.getItem("orgData.id")
);
setIncidentContacts( setIncidentContacts(
incidentContactsData?.map( incidentContactsData?.map((contact) => contact.email)
(contact) => contact.email
)
); );
const user = await getUser(); const user = await getUser();
@@ -82,8 +89,10 @@ export default function SettingsOrg() {
publicKey: user.user?.publicKey, publicKey: user.user?.publicKey,
})) }))
); );
const subscriptions = await getOrganizationSubscriptions({orgId: localStorage.getItem("orgData.id")}); const subscriptions = await getOrganizationSubscriptions({
setCurrentPlan(subscriptions.data[0].plan.product) orgId: localStorage.getItem("orgData.id"),
});
setCurrentPlan(subscriptions.data[0].plan.product);
}, []); }, []);
const modifyOrgName = (newName) => { const modifyOrgName = (newName) => {
@@ -127,7 +136,10 @@ export default function SettingsOrg() {
setIncidentContacts( setIncidentContacts(
incidentContacts.filter((contact) => contact != incidentContact) incidentContacts.filter((contact) => contact != incidentContact)
); );
deleteIncidentContact(localStorage.getItem("orgData.id"), incidentContact); deleteIncidentContact(
localStorage.getItem("orgData.id"),
incidentContact
);
}; };
/** /**
@@ -155,12 +167,12 @@ export default function SettingsOrg() {
return ( return (
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white"> <div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
<Head> <Head>
<title>Settings</title> <title>{t("settings:meta.org.head-title")}</title>
<link rel="icon" href="/infisical.ico" /> <link rel="icon" href="/infisical.ico" />
</Head> </Head>
<div className="flex flex-row"> <div className="flex flex-row">
<div className="w-full max-h-screen pb-2 overflow-y-auto"> <div className="w-full max-h-screen pb-2 overflow-y-auto">
<NavHeader pageName="Organization Settings"/> <NavHeader pageName={t("settings:meta.org.title")} />
<AddIncidentContactDialog <AddIncidentContactDialog
isOpen={isAddIncidentContactOpen} isOpen={isAddIncidentContactOpen}
closeModal={closeAddIncidentContactModal} closeModal={closeAddIncidentContactModal}
@@ -171,10 +183,10 @@ export default function SettingsOrg() {
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl"> <div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
<div className="flex flex-col justify-start items-start text-3xl"> <div className="flex flex-col justify-start items-start text-3xl">
<p className="font-semibold mr-4 text-gray-200"> <p className="font-semibold mr-4 text-gray-200">
Organization Settings {t("settings:meta.org.title")}
</p> </p>
<p className="font-normal mr-4 text-gray-400 text-base"> <p className="font-normal mr-4 text-gray-400 text-base">
View and manage your organization here. {t("settings:meta.org.description")}
</p> </p>
</div> </div>
</div> </div>
@@ -184,7 +196,7 @@ export default function SettingsOrg() {
<div className="bg-white/5 rounded-md px-6 py-4 flex flex-col items-start flex flex-col items-start w-full mb-6"> <div className="bg-white/5 rounded-md px-6 py-4 flex flex-col items-start flex flex-col items-start w-full mb-6">
<div className="max-h-28 w-full max-w-md mr-auto"> <div className="max-h-28 w-full max-w-md mr-auto">
<p className="font-semibold mr-4 text-gray-200 text-xl mb-2"> <p className="font-semibold mr-4 text-gray-200 text-xl mb-2">
Display Name {t("common:display-name")}
</p> </p>
<InputField <InputField
// label="Organization Name" // label="Organization Name"
@@ -200,13 +212,15 @@ export default function SettingsOrg() {
className={`flex justify-start max-w-sm mt-4 mb-2`} className={`flex justify-start max-w-sm mt-4 mb-2`}
> >
<Button <Button
text="Save Changes" text={t("common:save-changes")}
onButtonPressed={() => submitChanges(orgName)} onButtonPressed={() =>
submitChanges(orgName)
}
color="mineshaft" color="mineshaft"
size="md" size="md"
active={buttonReady} active={buttonReady}
iconDisabled={faCheck} iconDisabled={faCheck}
textDisabled="Saved" textDisabled={t("common:saved")}
/> />
</div> </div>
</div> </div>
@@ -215,11 +229,10 @@ export default function SettingsOrg() {
</div> </div>
<div className="bg-white/5 rounded-md px-6 pt-6 pb-2 flex flex-col items-start flex flex-col items-start w-full mb-6"> <div className="bg-white/5 rounded-md px-6 pt-6 pb-2 flex flex-col items-start flex flex-col items-start w-full mb-6">
<p className="font-semibold mr-4 text-white text-xl"> <p className="font-semibold mr-4 text-white text-xl">
Organization Members {t("settings:org-members")}
</p> </p>
<p className="mr-4 text-gray-400 mt-2 mb-2"> <p className="mr-4 text-gray-400 mt-2 mb-2">
Manage members of your organization. These users {t("settings:org-members-description")}
could afterwards be formed into projects.
</p> </p>
<AddUserDialog <AddUserDialog
isOpen={isAddUserOpen} isOpen={isAddUserOpen}
@@ -244,12 +257,14 @@ export default function SettingsOrg() {
onChange={(e) => onChange={(e) =>
setSearchUsers(e.target.value) setSearchUsers(e.target.value)
} }
placeholder={"Search members..."} placeholder={t(
"settings:search-members"
)}
/> />
</div> </div>
<div className="mt-2 ml-2 min-w-max flex flex-row items-start justify-start"> <div className="mt-2 ml-2 min-w-max flex flex-row items-start justify-start">
<Button <Button
text="Add Member" text={t("settings:add-member")}
onButtonPressed={openAddUserModal} onButtonPressed={openAddUserModal}
color="mineshaft" color="mineshaft"
size="md" size="md"
@@ -279,17 +294,20 @@ export default function SettingsOrg() {
<div className="flex flex-row max-w-5xl justify-between items-center w-full"> <div className="flex flex-row max-w-5xl justify-between items-center w-full">
<div className="flex flex-col justify-between w-full max-w-3xl"> <div className="flex flex-col justify-between w-full max-w-3xl">
<p className="text-xl font-semibold mb-3 min-w-max"> <p className="text-xl font-semibold mb-3 min-w-max">
Incident Contacts {t("settings:incident-contacts")}
</p> </p>
<p className="text-xs text-gray-500 mb-2 min-w-max"> <p className="text-xs text-gray-500 mb-2 min-w-max">
These contacts will be notified in the {t(
unlikely event of a severe incident. "settings:incident-contacts-description"
)}
</p> </p>
</div> </div>
<div className="mt-4 mb-2 min-w-max flex flex-row items-end justify-end justify-center"> <div className="mt-4 mb-2 min-w-max flex flex-row items-end justify-end justify-center">
<Button <Button
text="Add Contact" text={t("common:add-contact")}
onButtonPressed={openAddIncidentContactModal} onButtonPressed={
openAddIncidentContactModal
}
color="mineshaft" color="mineshaft"
size="md" size="md"
icon={faPlus} icon={faPlus}
@@ -307,7 +325,7 @@ export default function SettingsOrg() {
onChange={(e) => onChange={(e) =>
setSearchIncidentContact(e.target.value) setSearchIncidentContact(e.target.value)
} }
placeholder={"Search..."} placeholder={t("settings:search")}
/> />
</div> </div>
{incidentContacts?.filter((email) => {incidentContacts?.filter((email) =>
@@ -318,7 +336,10 @@ export default function SettingsOrg() {
email.includes(searchIncidentContact) email.includes(searchIncidentContact)
) )
.map((contact) => ( .map((contact) => (
<div key={guidGenerator()} className="flex flex-row items-center justify-between max-w-5xl px-4 py-3 hover:bg-white/5 border-t border-gray-600 w-full"> <div
key={guidGenerator()}
className="flex flex-row items-center justify-between max-w-5xl px-4 py-3 hover:bg-white/5 border-t border-gray-600 w-full"
>
<p className="text-gray-300"> <p className="text-gray-300">
{contact} {contact}
</p> </p>
@@ -339,7 +360,7 @@ export default function SettingsOrg() {
) : ( ) : (
<div className="w-full flex flex-row justify-center mt-6 max-w-4xl ml-6"> <div className="w-full flex flex-row justify-center mt-6 max-w-4xl ml-6">
<p className="text-gray-400"> <p className="text-gray-400">
No incident contacts found. {t("settings:no-incident-contacts")}
</p> </p>
</div> </div>
)} )}
+26 -19
View File
@@ -17,6 +17,8 @@ import getOrganizationUsers from "../api/organization/GetOrgUsers";
import NavHeader from "../../components/navigation/NavHeader"; import NavHeader from "../../components/navigation/NavHeader";
import Button from "../../components/basic/buttons/Button"; import Button from "../../components/basic/buttons/Button";
import useTranslation from "next-translate/useTranslation";
// #TODO: Update all the workspaceIds // #TODO: Update all the workspaceIds
const crypto = require("crypto"); const crypto = require("crypto");
const { const {
@@ -34,6 +36,8 @@ export default function Users() {
const [personalEmail, setPersonalEmail] = useState(""); const [personalEmail, setPersonalEmail] = useState("");
const [searchUsers, setSearchUsers] = useState(""); const [searchUsers, setSearchUsers] = useState("");
const { t } = useTranslation();
const router = useRouter(); const router = useRouter();
let workspaceId; let workspaceId;
@@ -98,15 +102,13 @@ export default function Users() {
firstName: user.user?.firstName, firstName: user.user?.firstName,
lastName: user.user?.lastName, lastName: user.user?.lastName,
email: email:
user.user?.email == null user.user?.email == null ? user.inviteEmail : user.user?.email,
? user.inviteEmail
: user.user?.email,
role: user?.role, role: user?.role,
status: user?.status, status: user?.status,
userId: user.user?._id, userId: user.user?._id,
membershipId: user._id, membershipId: user._id,
publicKey: user.user?.publicKey, publicKey: user.user?.publicKey,
})) }));
setUserList(tempUserList); setUserList(tempUserList);
const orgUsers = await getOrganizationUsers({ const orgUsers = await getOrganizationUsers({
orgId: localStorage.getItem("orgData.id"), orgId: localStorage.getItem("orgData.id"),
@@ -114,28 +116,33 @@ export default function Users() {
setOrgUserList(orgUsers); setOrgUserList(orgUsers);
setEmail( setEmail(
orgUsers orgUsers
?.filter((user) => user.status == "accepted") ?.filter((user) => user.status == "accepted")
.map((user) => user.user.email) .map((user) => user.user.email)
.filter( .filter(
(email) => (email) =>
!tempUserList !tempUserList
?.map((user1) => user1.email) ?.map((user1) => user1.email)
.includes(email) .includes(email)
)[0] )[0]
) );
}, []); }, []);
return userList ? ( return userList ? (
<div className="bg-bunker-800 md:h-screen flex flex-col justify-start"> <div className="bg-bunker-800 md:h-screen flex flex-col justify-start">
<Head> <Head>
<title>Users</title> <title>{t("settings:meta.members.head-title")}</title>
<link rel="icon" href="/infisical.ico" /> <link rel="icon" href="/infisical.ico" />
</Head> </Head>
<NavHeader pageName="Project Members" isProjectRelated={true}/> <NavHeader
pageName={t("settings:meta.members.title")}
isProjectRelated={true}
/>
<div className="flex flex-col justify-start items-start px-6 py-6 pb-4 text-3xl"> <div className="flex flex-col justify-start items-start px-6 py-6 pb-4 text-3xl">
<p className="font-semibold mr-4 text-white">Project Members</p> <p className="font-semibold mr-4 text-white">
{t("settings:meta.members.title")}
</p>
<p className="mr-4 text-base text-gray-400"> <p className="mr-4 text-base text-gray-400">
This pages shows the members of the selected project. {t("settings:meta.members.description")}
</p> </p>
</div> </div>
<AddProjectMemberDialog <AddProjectMemberDialog
@@ -166,12 +173,12 @@ export default function Users() {
className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none" className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none"
value={searchUsers} value={searchUsers}
onChange={(e) => setSearchUsers(e.target.value)} onChange={(e) => setSearchUsers(e.target.value)}
placeholder={"Search members..."} placeholder={t("settings:search-members")}
/> />
</div> </div>
<div className="mt-2 ml-2 min-w-max flex flex-row items-start justify-start mr-4"> <div className="mt-2 ml-2 min-w-max flex flex-row items-start justify-start mr-4">
<Button <Button
text="Add Member" text={t("settings:add-member")}
onButtonPressed={openAddModal} onButtonPressed={openAddModal}
color="mineshaft" color="mineshaft"
size="md" size="md"