mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-23 02:29:30 -05:00
dialog
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { inviteSchema, type InviteFormData } from "@/Validation/invite";
|
||||
|
||||
export const useInviteForm = () => {
|
||||
const defaults: InviteFormData = {
|
||||
email: "",
|
||||
role: ["user"],
|
||||
};
|
||||
|
||||
return {
|
||||
resolver: zodResolver(inviteSchema),
|
||||
defaults,
|
||||
};
|
||||
};
|
||||
@@ -60,7 +60,7 @@ export const HeaderTeamControls = ({
|
||||
startIcon={<Icon icon={Mail} />}
|
||||
onClick={onInviteClick}
|
||||
>
|
||||
{t("pages.account.team.inviteMember")}
|
||||
{t("common.buttons.inviteMember")}
|
||||
</Button>
|
||||
)}
|
||||
{isSuperAdmin && (
|
||||
@@ -69,7 +69,7 @@ export const HeaderTeamControls = ({
|
||||
color="primary"
|
||||
startIcon={<Icon icon={UserPlus} />}
|
||||
>
|
||||
{t("pages.account.team.addMember")}
|
||||
{t("common.buttons.addMember")}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { Stack } from "@mui/material";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useTheme, FormHelperText, Typography } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { Dialog, TextField, Select, Button } from "@/Components/v2/inputs";
|
||||
import type { UserRole } from "@/Types/User";
|
||||
import { useInviteForm } from "@/Hooks/useInviteForm";
|
||||
import type { InviteFormData } from "@/Validation/invite";
|
||||
import { usePost } from "@/Hooks/UseApi";
|
||||
|
||||
const CLIENT_HOST = import.meta.env.VITE_APP_CLIENT_HOST;
|
||||
|
||||
interface InviteResponse {
|
||||
token: string;
|
||||
}
|
||||
|
||||
interface InviteTeamMemberDialogProps {
|
||||
open: boolean;
|
||||
@@ -16,29 +27,70 @@ export const InviteTeamMemberDialog = ({
|
||||
}: InviteTeamMemberDialogProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { resolver, defaults } = useInviteForm();
|
||||
|
||||
const { control, handleSubmit, reset } = useForm<InviteFormData>({
|
||||
resolver,
|
||||
defaultValues: defaults,
|
||||
values: defaults,
|
||||
});
|
||||
|
||||
const { post: generateToken, loading: generateLoading } = usePost<
|
||||
InviteFormData,
|
||||
InviteResponse
|
||||
>();
|
||||
const { post: sendInvite, loading: sendLoading } = usePost<
|
||||
InviteFormData,
|
||||
InviteResponse
|
||||
>();
|
||||
const [inviteLink, setInviteLink] = useState<string | null>(null);
|
||||
|
||||
const roleOptions: { value: UserRole; label: string }[] = [
|
||||
{ value: "admin", label: t("common.auth.roles.admin") },
|
||||
{ value: "user", label: t("common.auth.roles.user") },
|
||||
];
|
||||
|
||||
const handleGenerateToken = async (data: InviteFormData) => {
|
||||
const result = await generateToken("/invite", data);
|
||||
if (result?.data?.token) {
|
||||
const token = result.data.token;
|
||||
const link = CLIENT_HOST ? `${CLIENT_HOST}/register/${token}` : token;
|
||||
setInviteLink(link);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSendInvite = async (data: InviteFormData) => {
|
||||
const result = await sendInvite("/invite/send", data);
|
||||
if (result?.success) {
|
||||
handleClose();
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
reset();
|
||||
setInviteLink(null);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
title={t("pages.account.team.invite.title")}
|
||||
content={t("pages.account.team.invite.description")}
|
||||
onCancel={onClose}
|
||||
onConfirm={() => {}}
|
||||
onCancel={handleClose}
|
||||
onConfirm={handleSubmit(handleSendInvite)}
|
||||
confirmText={t("common.buttons.sendInvite")}
|
||||
loading={sendLoading || generateLoading}
|
||||
maxWidth="sm"
|
||||
fullWidth
|
||||
confirmText={t("pages.account.team.invite.sendInvite")}
|
||||
additionalButtons={
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {}}
|
||||
onClick={handleSubmit(handleGenerateToken)}
|
||||
loading={generateLoading || sendLoading}
|
||||
>
|
||||
{t("pages.account.team.invite.generateToken")}
|
||||
{t("common.buttons.generateToken")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
@@ -46,27 +98,66 @@ export const InviteTeamMemberDialog = ({
|
||||
gap={theme.spacing(4)}
|
||||
mt={theme.spacing(4)}
|
||||
>
|
||||
<TextField
|
||||
fieldLabel={t("pages.account.team.invite.email.label")}
|
||||
placeholder={t("pages.account.team.invite.email.placeholder")}
|
||||
type="email"
|
||||
fullWidth
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fieldLabel={t("pages.account.team.invite.email.label")}
|
||||
placeholder={t("pages.account.team.invite.email.placeholder")}
|
||||
type="email"
|
||||
fullWidth
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message ?? ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Select
|
||||
fieldLabel={t("pages.account.team.invite.role.label")}
|
||||
placeholder={t("pages.account.team.invite.role.placeholder")}
|
||||
defaultValue="user"
|
||||
fullWidth
|
||||
>
|
||||
{roleOptions.map((option) => (
|
||||
<MenuItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<Controller
|
||||
name="role"
|
||||
control={control}
|
||||
render={({ field: { value, onChange, ...field }, fieldState }) => (
|
||||
<>
|
||||
<Select
|
||||
{...field}
|
||||
value={Array.isArray(value) ? (value[0] ?? "") : ""}
|
||||
onChange={(e) => onChange([e.target.value as UserRole])}
|
||||
fieldLabel={t("pages.account.team.invite.role.label")}
|
||||
placeholder={t("pages.account.team.invite.role.placeholder")}
|
||||
fullWidth
|
||||
error={!!fieldState.error}
|
||||
>
|
||||
{roleOptions.map((option) => (
|
||||
<MenuItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
{fieldState.error && (
|
||||
<FormHelperText error>{fieldState.error.message}</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
{inviteLink && (
|
||||
<>
|
||||
<Typography variant="body2">
|
||||
{t("pages.account.team.invite.linkLabel")}
|
||||
</Typography>
|
||||
<TextField
|
||||
value={inviteLink}
|
||||
fullWidth
|
||||
slotProps={{
|
||||
input: {
|
||||
readOnly: true,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from "zod";
|
||||
import { UserRoles } from "@/Types/User";
|
||||
|
||||
export const inviteSchema = z.object({
|
||||
email: z.email("Please enter a valid email address"),
|
||||
role: z.array(z.enum(UserRoles)).min(1, "Please select a role"),
|
||||
});
|
||||
|
||||
export type InviteFormData = z.infer<typeof inviteSchema>;
|
||||
@@ -167,6 +167,7 @@
|
||||
"uploadSuccess": "Monitors created successfully!"
|
||||
},
|
||||
"bytesSent": "Bytes Sent",
|
||||
"addMember": "Add member",
|
||||
"cancel": "Cancel",
|
||||
"checkHooks": {
|
||||
"failureResolveOne": "Failed to resolve incident."
|
||||
@@ -195,16 +196,20 @@
|
||||
"home": "Home"
|
||||
},
|
||||
"buttons": {
|
||||
"addMember": "Add member",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"configure": "Configure",
|
||||
"confirm": "Confirm",
|
||||
"create": "Create",
|
||||
"delete": "Delete",
|
||||
"generateToken": "Generate token",
|
||||
"incidents": "Incidents",
|
||||
"inviteMember": "Invite member",
|
||||
"pause": "Pause",
|
||||
"resume": "Resume",
|
||||
"save": "Save",
|
||||
"sendInvite": "Send invite",
|
||||
"test": "Test",
|
||||
"testNotifications": "Test notifications",
|
||||
"toggleTheme": "Toggles light & dark",
|
||||
@@ -299,6 +304,7 @@
|
||||
"createMonitor": "Create monitor",
|
||||
"createNew": "Create new",
|
||||
"delete": "Delete",
|
||||
"generateToken": "Generate token",
|
||||
"DeleteAccountButton": "Remove account",
|
||||
"DeleteAccountTitle": "Remove account",
|
||||
"DeleteAccountWarning": "Removing your account means you won't be able to sign in again and all your data will be removed. This isn't reversible.",
|
||||
@@ -459,6 +465,7 @@
|
||||
"discussions": "Discussions",
|
||||
"docs": "Docs",
|
||||
"incidents": "Incidents",
|
||||
"inviteMember": "Invite member",
|
||||
"infrastructure": "Infrastructure",
|
||||
"logOut": "Log out",
|
||||
"logs": "Logs",
|
||||
@@ -559,8 +566,6 @@
|
||||
}
|
||||
},
|
||||
"team": {
|
||||
"addMember": "Add member",
|
||||
"inviteMember": "Invite member",
|
||||
"filter": {
|
||||
"placeholder": "Filter by role",
|
||||
"all": "All roles",
|
||||
@@ -584,8 +589,7 @@
|
||||
"label": "Role",
|
||||
"placeholder": "Select a role"
|
||||
},
|
||||
"sendInvite": "Send invite",
|
||||
"generateToken": "Generate token"
|
||||
"linkLabel": "Invite link"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -675,8 +679,10 @@
|
||||
"actions": {
|
||||
"configure": "Configure",
|
||||
"delete": "Delete",
|
||||
"generateToken": "Generate token",
|
||||
"details": "Details",
|
||||
"incidents": "Incidents",
|
||||
"inviteMember": "Invite member",
|
||||
"openSite": "Open site",
|
||||
"pause": "Pause",
|
||||
"resume": "Resume"
|
||||
@@ -1341,6 +1347,7 @@
|
||||
"teamMember": "Team member"
|
||||
},
|
||||
"save": "Save",
|
||||
"sendInvite": "Send invite",
|
||||
"selectAll": "Select all",
|
||||
"settingsFailedToClearStats": "Failed to clear stats",
|
||||
"settingsFailedToDeleteMonitors": "Failed to delete all monitors",
|
||||
@@ -1467,6 +1474,7 @@
|
||||
"description": "Create a new user and share the credentials with them. This method gives the member immediate access to all monitors.",
|
||||
"title": "Register new team member"
|
||||
},
|
||||
"addMember": "Add member",
|
||||
"cancel": "Cancel",
|
||||
"changeTeamPassword": {
|
||||
"changePasswordMenu": "Reset Password",
|
||||
|
||||
Reference in New Issue
Block a user