UI fixes
This commit is contained in:
parent
b08d37fbf0
commit
7d2ff346fa
7 changed files with 113 additions and 55 deletions
47
apps/client/src/components/ui/responsive-settings-row.tsx
Normal file
47
apps/client/src/components/ui/responsive-settings-row.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Box } from "@mantine/core";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface ResponsiveSettingsRowProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ResponsiveSettingsRow({ children }: ResponsiveSettingsRowProps) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "1rem",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResponsiveSettingsContentProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ResponsiveSettingsContent({ children }: ResponsiveSettingsContentProps) {
|
||||||
|
return (
|
||||||
|
<Box style={{ flex: "1 1 300px", minWidth: 0 }}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResponsiveSettingsControlProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ResponsiveSettingsControl({ children }: ResponsiveSettingsControlProps) {
|
||||||
|
return (
|
||||||
|
<Box style={{ flex: "0 0 auto" }}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import { MfaDisableModal } from "@/ee/mfa";
|
||||||
import { MfaBackupCodesModal } from "@/ee/mfa";
|
import { MfaBackupCodesModal } from "@/ee/mfa";
|
||||||
import { isCloud } from "@/lib/config.ts";
|
import { isCloud } from "@/lib/config.ts";
|
||||||
import useLicense from "@/ee/hooks/use-license.tsx";
|
import useLicense from "@/ee/hooks/use-license.tsx";
|
||||||
|
import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row";
|
||||||
|
|
||||||
export function MfaSettings() {
|
export function MfaSettings() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -53,8 +54,8 @@ export function MfaSettings() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Group justify="space-between" wrap="nowrap" gap="xl">
|
<ResponsiveSettingsRow>
|
||||||
<div style={{ minWidth: 0, flex: 1 }}>
|
<ResponsiveSettingsContent>
|
||||||
<Text size="md">{t("2-step verification")}</Text>
|
<Text size="md">{t("2-step verification")}</Text>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{!isMfaEnabled
|
{!isMfaEnabled
|
||||||
|
|
@ -63,44 +64,46 @@ export function MfaSettings() {
|
||||||
)
|
)
|
||||||
: t("Two-factor authentication is active on your account.")}
|
: t("Two-factor authentication is active on your account.")}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</ResponsiveSettingsContent>
|
||||||
|
|
||||||
{!isMfaEnabled ? (
|
<ResponsiveSettingsControl>
|
||||||
<Tooltip
|
{!isMfaEnabled ? (
|
||||||
label={t("Available in enterprise edition")}
|
<Tooltip
|
||||||
disabled={canUseMfa}
|
label={t("Available in enterprise edition")}
|
||||||
>
|
disabled={canUseMfa}
|
||||||
<Button
|
|
||||||
disabled={!canUseMfa}
|
|
||||||
variant="default"
|
|
||||||
onClick={() => setSetupModalOpen(true)}
|
|
||||||
style={{ whiteSpace: "nowrap" }}
|
|
||||||
>
|
>
|
||||||
{t("Add 2FA method")}
|
<Button
|
||||||
</Button>
|
disabled={!canUseMfa}
|
||||||
</Tooltip>
|
variant="default"
|
||||||
) : (
|
onClick={() => setSetupModalOpen(true)}
|
||||||
<Group gap="sm" wrap="nowrap">
|
style={{ whiteSpace: "nowrap" }}
|
||||||
<Button
|
>
|
||||||
variant="default"
|
{t("Add 2FA method")}
|
||||||
size="sm"
|
</Button>
|
||||||
onClick={() => setBackupCodesModalOpen(true)}
|
</Tooltip>
|
||||||
style={{ whiteSpace: "nowrap" }}
|
) : (
|
||||||
>
|
<Group gap="sm" wrap="nowrap">
|
||||||
{t("Backup codes")} ({mfaStatus?.backupCodesCount || 0})
|
<Button
|
||||||
</Button>
|
variant="default"
|
||||||
<Button
|
size="sm"
|
||||||
variant="default"
|
onClick={() => setBackupCodesModalOpen(true)}
|
||||||
size="sm"
|
style={{ whiteSpace: "nowrap" }}
|
||||||
color="red"
|
>
|
||||||
onClick={() => setDisableModalOpen(true)}
|
{t("Backup codes")} ({mfaStatus?.backupCodesCount || 0})
|
||||||
style={{ whiteSpace: "nowrap" }}
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("Disable")}
|
variant="default"
|
||||||
</Button>
|
size="sm"
|
||||||
</Group>
|
color="red"
|
||||||
)}
|
onClick={() => setDisableModalOpen(true)}
|
||||||
</Group>
|
style={{ whiteSpace: "nowrap" }}
|
||||||
|
>
|
||||||
|
{t("Disable")}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
</ResponsiveSettingsControl>
|
||||||
|
</ResponsiveSettingsRow>
|
||||||
|
|
||||||
<MfaSetupModal
|
<MfaSetupModal
|
||||||
opened={setupModalOpen}
|
opened={setupModalOpen}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export default function SsoProviderList() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card shadow="sm" radius="sm">
|
<Card shadow="sm" radius="sm">
|
||||||
<Table.ScrollContainer minWidth={500}>
|
<Table.ScrollContainer minWidth={600}>
|
||||||
<Table verticalSpacing="sm">
|
<Table verticalSpacing="sm">
|
||||||
<Table.Thead>
|
<Table.Thead>
|
||||||
<Table.Tr>
|
<Table.Tr>
|
||||||
|
|
@ -104,7 +104,7 @@ export default function SsoProviderList() {
|
||||||
</Group>
|
</Group>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Badge color={"gray"} variant="light">
|
<Badge color={"gray"} variant="light" style={{ whiteSpace: "nowrap" }}>
|
||||||
{provider.type.toUpperCase()}
|
{provider.type.toUpperCase()}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
|
|
@ -133,6 +133,7 @@ export default function SsoProviderList() {
|
||||||
)}
|
)}
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
|
<Group gap="xs" wrap="nowrap">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="gray"
|
color="gray"
|
||||||
|
|
@ -168,6 +169,7 @@ export default function SsoProviderList() {
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu.Dropdown>
|
</Menu.Dropdown>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
</Group>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
</Table.Tr>
|
</Table.Tr>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,28 @@
|
||||||
import { Group, Text, MantineSize, SegmentedControl } from "@mantine/core";
|
import { Text, MantineSize, SegmentedControl } from "@mantine/core";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
|
import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||||
import { updateUser } from "@/features/user/services/user-service.ts";
|
import { updateUser } from "@/features/user/services/user-service.ts";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { PageEditMode } from "@/features/user/types/user.types.ts";
|
import { PageEditMode } from "@/features/user/types/user.types.ts";
|
||||||
|
import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row";
|
||||||
|
|
||||||
export default function PageStatePref() {
|
export default function PageStatePref() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group justify="space-between" wrap="nowrap" gap="xl">
|
<ResponsiveSettingsRow>
|
||||||
<div>
|
<ResponsiveSettingsContent>
|
||||||
<Text size="md">{t("Default page edit mode")}</Text>
|
<Text size="md">{t("Default page edit mode")}</Text>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{t("Choose your preferred page edit mode. Avoid accidental edits.")}
|
{t("Choose your preferred page edit mode. Avoid accidental edits.")}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</ResponsiveSettingsContent>
|
||||||
|
|
||||||
<PageStateSegmentedControl />
|
<ResponsiveSettingsControl>
|
||||||
</Group>
|
<PageStateSegmentedControl />
|
||||||
|
</ResponsiveSettingsControl>
|
||||||
|
</ResponsiveSettingsRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,27 @@
|
||||||
import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
|
import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||||
import { updateUser } from "@/features/user/services/user-service.ts";
|
import { updateUser } from "@/features/user/services/user-service.ts";
|
||||||
import { Group, MantineSize, Switch, Text } from "@mantine/core";
|
import { MantineSize, Switch, Text } from "@mantine/core";
|
||||||
import { useAtom } from "jotai/index";
|
import { useAtom } from "jotai/index";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { ResponsiveSettingsRow, ResponsiveSettingsContent, ResponsiveSettingsControl } from "@/components/ui/responsive-settings-row";
|
||||||
|
|
||||||
export default function PageWidthPref() {
|
export default function PageWidthPref() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group justify="space-between" wrap="nowrap" gap="xl">
|
<ResponsiveSettingsRow>
|
||||||
<div>
|
<ResponsiveSettingsContent>
|
||||||
<Text size="md">{t("Full page width")}</Text>
|
<Text size="md">{t("Full page width")}</Text>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{t("Choose your preferred page width.")}
|
{t("Choose your preferred page width.")}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</ResponsiveSettingsContent>
|
||||||
|
|
||||||
<PageWidthToggle />
|
<ResponsiveSettingsControl>
|
||||||
</Group>
|
<PageWidthToggle />
|
||||||
|
</ResponsiveSettingsControl>
|
||||||
|
</ResponsiveSettingsRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export default function WorkspaceInvitesTable() {
|
||||||
)}
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<Table.ScrollContainer minWidth={500}>
|
<Table.ScrollContainer minWidth={600}>
|
||||||
<Table highlightOnHover verticalSpacing="sm">
|
<Table highlightOnHover verticalSpacing="sm">
|
||||||
<Table.Thead>
|
<Table.Thead>
|
||||||
<Table.Tr>
|
<Table.Tr>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
useWorkspaceMembersQuery,
|
useWorkspaceMembersQuery,
|
||||||
} from "@/features/workspace/queries/workspace-query.ts";
|
} from "@/features/workspace/queries/workspace-query.ts";
|
||||||
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
|
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
|
||||||
import React, { useCallback, useRef, useState } from "react";
|
import React from "react";
|
||||||
import RoleSelectMenu from "@/components/ui/role-select-menu.tsx";
|
import RoleSelectMenu from "@/components/ui/role-select-menu.tsx";
|
||||||
import {
|
import {
|
||||||
getUserRoleLabel,
|
getUserRoleLabel,
|
||||||
|
|
@ -54,7 +54,7 @@ export default function WorkspaceMembersTable() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SearchInput onSearch={handleSearch} />
|
<SearchInput onSearch={handleSearch} />
|
||||||
<Table.ScrollContainer minWidth={500}>
|
<Table.ScrollContainer minWidth={600}>
|
||||||
<Table highlightOnHover verticalSpacing="sm">
|
<Table highlightOnHover verticalSpacing="sm">
|
||||||
<Table.Thead>
|
<Table.Thead>
|
||||||
<Table.Tr>
|
<Table.Tr>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue