mirror of
https://github.com/docmost/docmost.git
synced 2026-07-25 10:04:41 +10:00
bug fixes (#2250)
* util * fix page position collation * support fixed toolbar in templates editor * date localization * fix clipped emoji in templates editor * fix page updated time object * fix flickers * fix: remove redundant breadcrumb from destination modal
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { ActionIcon, Group, Menu, Table, Text } from "@mantine/core";
|
||||
import { IconDots, IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { format } from "date-fns";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IApiKey } from "@/ee/api-key";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
|
||||
import React from "react";
|
||||
import NoTableResults from "@/components/common/no-table-results";
|
||||
import { formatLocalized, useDateFnsLocale } from "@/lib/date-locale.ts";
|
||||
|
||||
interface ApiKeyTableProps {
|
||||
apiKeys: IApiKey[];
|
||||
@@ -23,10 +23,11 @@ export function ApiKeyTable({
|
||||
onRevoke,
|
||||
}: ApiKeyTableProps) {
|
||||
const { t } = useTranslation();
|
||||
const locale = useDateFnsLocale();
|
||||
|
||||
const formatDate = (date: Date | string | null) => {
|
||||
if (!date) return t("Never");
|
||||
return format(new Date(date), "MMM dd, yyyy");
|
||||
return formatLocalized(date, "MMM dd, yyyy", "PP", locale);
|
||||
};
|
||||
|
||||
const isExpired = (expiresAt: string | null) => {
|
||||
|
||||
@@ -31,7 +31,7 @@ export function CreateApiKeyModal({
|
||||
onClose,
|
||||
onSuccess,
|
||||
}: CreateApiKeyModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const { t, i18n } = useTranslation();
|
||||
const [expirationOption, setExpirationOption] = useState<string>("30");
|
||||
const createApiKeyMutation = useCreateApiKeyMutation();
|
||||
|
||||
@@ -59,7 +59,7 @@ export function CreateApiKeyModal({
|
||||
const getExpirationLabel = (days: number) => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + days);
|
||||
const formatted = date.toLocaleDateString("en-US", {
|
||||
const formatted = date.toLocaleDateString(i18n.language, {
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
year: "numeric",
|
||||
|
||||
@@ -4,12 +4,13 @@ import {
|
||||
} from "@/ee/billing/queries/billing-query.ts";
|
||||
import { Group, Text, SimpleGrid, Paper } from "@mantine/core";
|
||||
import classes from "./billing.module.css";
|
||||
import { format } from "date-fns";
|
||||
import { formatInterval } from "@/ee/billing/utils.ts";
|
||||
import { formatLocalized, useDateFnsLocale } from "@/lib/date-locale.ts";
|
||||
|
||||
export default function BillingDetails() {
|
||||
const { data: billing } = useBillingQuery();
|
||||
const { data: plans } = useBillingPlans();
|
||||
const locale = useDateFnsLocale();
|
||||
|
||||
if (!billing || !plans) {
|
||||
return null;
|
||||
@@ -75,7 +76,12 @@ export default function BillingDetails() {
|
||||
: "Renewal date"}
|
||||
</Text>
|
||||
<Text fw={700} fz="lg">
|
||||
{format(billing.periodEndAt, "dd MMM, yyyy")}
|
||||
{formatLocalized(
|
||||
billing.periodEndAt,
|
||||
"dd MMM, yyyy",
|
||||
"PP",
|
||||
locale,
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Badge, Table } from "@mantine/core";
|
||||
import { format } from "date-fns";
|
||||
import { useLicenseInfo } from "@/ee/licence/queries/license-query.ts";
|
||||
import { isLicenseExpired } from "@/ee/licence/license.utils.ts";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
import { formatLocalized, useDateFnsLocale } from "@/lib/date-locale.ts";
|
||||
|
||||
export default function LicenseDetails() {
|
||||
const { data: license, isError } = useLicenseInfo();
|
||||
const [workspace] = useAtom(workspaceAtom);
|
||||
const locale = useDateFnsLocale();
|
||||
|
||||
if (!license) {
|
||||
return null;
|
||||
@@ -50,12 +51,16 @@ export default function LicenseDetails() {
|
||||
|
||||
<Table.Tr>
|
||||
<Table.Th>Issued at</Table.Th>
|
||||
<Table.Td>{format(license.issuedAt, "dd MMMM, yyyy")}</Table.Td>
|
||||
<Table.Td>
|
||||
{formatLocalized(license.issuedAt, "dd MMMM, yyyy", "PPP", locale)}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
|
||||
<Table.Tr>
|
||||
<Table.Th>Expires at</Table.Th>
|
||||
<Table.Td>{format(license.expiresAt, "dd MMMM, yyyy")}</Table.Td>
|
||||
<Table.Td>
|
||||
{formatLocalized(license.expiresAt, "dd MMMM, yyyy", "PPP", locale)}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
<Table.Tr>
|
||||
<Table.Th>License ID</Table.Th>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Group, NumberInput, Select, Text } from "@mantine/core";
|
||||
import { DateInput } from "@mantine/dates";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import i18n from "@/i18n.ts";
|
||||
import {
|
||||
ExpirationMode,
|
||||
PeriodUnit,
|
||||
@@ -30,7 +31,7 @@ export function addDays(days: number, from?: Date): Date {
|
||||
|
||||
function formatShortDate(date: Date): string {
|
||||
const crossesYear = date.getFullYear() !== new Date().getFullYear();
|
||||
return date.toLocaleDateString(undefined, {
|
||||
return date.toLocaleDateString(i18n.language, {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
...(crossesYear && { year: "numeric" }),
|
||||
@@ -38,7 +39,7 @@ function formatShortDate(date: Date): string {
|
||||
}
|
||||
|
||||
function formatLongDate(date: Date): string {
|
||||
return date.toLocaleDateString(undefined, {
|
||||
return date.toLocaleDateString(i18n.language, {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import i18n from "@/i18n.ts";
|
||||
import {
|
||||
useMarkObsoleteMutation,
|
||||
usePageVerificationInfoQuery,
|
||||
@@ -197,11 +198,14 @@ function ExpiringManageContent({ pageId, info, onClose }: ManageContentProps) {
|
||||
{info.expiresAt && (
|
||||
<Text size="xs" c="dimmed">
|
||||
{t(status === "expired" ? "Expired {{date}}" : "Expires {{date}}", {
|
||||
date: new Date(info.expiresAt).toLocaleDateString(undefined, {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
}),
|
||||
date: new Date(info.expiresAt).toLocaleDateString(
|
||||
i18n.language,
|
||||
{
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
},
|
||||
),
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
IconShieldCheck,
|
||||
} from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import i18n from "@/i18n.ts";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { extractPageSlugId } from "@/lib";
|
||||
import { usePageQuery } from "@/features/page/queries/page-query";
|
||||
@@ -127,7 +128,7 @@ export function PageVerificationBadge({
|
||||
status === "verified" && verificationInfo?.expiresAt
|
||||
? t("Verified until {{date}}", {
|
||||
date: new Date(verificationInfo.expiresAt).toLocaleDateString(
|
||||
undefined,
|
||||
i18n.language,
|
||||
{ month: "long", day: "numeric", year: "numeric" },
|
||||
),
|
||||
})
|
||||
|
||||
@@ -16,9 +16,10 @@ import {
|
||||
} from "@/ee/page-verification/types/page-verification.types";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar";
|
||||
import { buildPageUrl } from "@/features/page/page.utils";
|
||||
import { format } from "date-fns";
|
||||
import NoTableResults from "@/components/common/no-table-results";
|
||||
import rowClasses from "@/components/ui/clickable-table-row.module.css";
|
||||
import { formatLocalized, useDateFnsLocale } from "@/lib/date-locale.ts";
|
||||
import type { Locale } from "date-fns";
|
||||
|
||||
const MAX_VISIBLE_VERIFIERS = 5;
|
||||
|
||||
@@ -48,7 +49,11 @@ function statusBadge(status: VerificationStatus | null, t: (s: string) => string
|
||||
}
|
||||
}
|
||||
|
||||
function verifiedUntilText(item: IVerificationListItem, t: (s: string) => string): string {
|
||||
function verifiedUntilText(
|
||||
item: IVerificationListItem,
|
||||
t: (s: string) => string,
|
||||
locale: Locale,
|
||||
): string {
|
||||
if (item.type === "qms") {
|
||||
if (item.status === "approved") return t("Indefinitely");
|
||||
return "—";
|
||||
@@ -60,7 +65,7 @@ function verifiedUntilText(item: IVerificationListItem, t: (s: string) => string
|
||||
const now = new Date();
|
||||
|
||||
if (expires <= now) return t("Expired");
|
||||
return format(expires, "MMM d, yyyy");
|
||||
return formatLocalized(expires, "MMM d, yyyy", "PP", locale);
|
||||
}
|
||||
|
||||
function TableSkeleton() {
|
||||
@@ -98,6 +103,7 @@ export default function VerificationListTable({
|
||||
isLoading,
|
||||
}: VerificationListTableProps) {
|
||||
const { t } = useTranslation();
|
||||
const locale = useDateFnsLocale();
|
||||
|
||||
return (
|
||||
<Table.ScrollContainer minWidth={600}>
|
||||
@@ -200,7 +206,7 @@ export default function VerificationListTable({
|
||||
|
||||
<Table.Td>
|
||||
<Text fz="sm" style={{ whiteSpace: "nowrap" }}>
|
||||
{verifiedUntilText(item, t)}
|
||||
{verifiedUntilText(item, t, locale)}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ActionIcon, Group, Menu, Table, Text } from "@mantine/core";
|
||||
import { IconDots, IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { format } from "date-fns";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
|
||||
import React from "react";
|
||||
import NoTableResults from "@/components/common/no-table-results";
|
||||
import { IScimToken } from "@/ee/scim/types/scim-token.types";
|
||||
import { formatLocalized, useDateFnsLocale } from "@/lib/date-locale.ts";
|
||||
|
||||
interface ScimTokenTableProps {
|
||||
tokens: IScimToken[];
|
||||
@@ -21,10 +21,11 @@ export function ScimTokenTable({
|
||||
onRevoke,
|
||||
}: ScimTokenTableProps) {
|
||||
const { t } = useTranslation();
|
||||
const locale = useDateFnsLocale();
|
||||
|
||||
const formatDate = (date: Date | string | null) => {
|
||||
if (!date) return t("Never");
|
||||
return format(new Date(date), "MMM dd, yyyy");
|
||||
return formatLocalized(date, "MMM dd, yyyy", "PP", locale);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
/* The emoji glyph renders larger than its font-size box; let the transparent
|
||||
ActionIcon overflow so it isn't clipped on the edges. */
|
||||
.emojiButton button {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.titleInput {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
|
||||
@@ -32,6 +32,12 @@ import {
|
||||
} from "../queries/template-query";
|
||||
import { useGetSpacesQuery } from "@/features/space/queries/space-query";
|
||||
import useUserRole from "@/hooks/use-user-role";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { userAtom } from "@/features/user/atoms/current-user-atom";
|
||||
import { FixedToolbar } from "@/features/editor/components/fixed-toolbar/fixed-toolbar";
|
||||
import { EditorLinkMenu } from "@/features/editor/components/link/link-menu";
|
||||
import { EditorBubbleMenu } from "@/features/editor/components/bubble-menu/bubble-menu";
|
||||
import { EditorAiMenu } from "@/ee/ai/components/editor/ai-menu/ai-menu";
|
||||
|
||||
import classes from "./template-editor.module.css";
|
||||
|
||||
@@ -39,6 +45,9 @@ export default function TemplateEditor() {
|
||||
const { t } = useTranslation();
|
||||
const { templateId } = useParams<{ templateId: string }>();
|
||||
const { isAdmin: isWorkspaceAdmin } = useUserRole();
|
||||
const user = useAtomValue(userAtom);
|
||||
const editorToolbarEnabled =
|
||||
user?.settings?.preferences?.editorToolbar ?? false;
|
||||
|
||||
const { data: existingTemplate } = useGetTemplateByIdQuery(templateId || "");
|
||||
const { data: spaces } = useGetSpacesQuery({ limit: 100 });
|
||||
@@ -238,6 +247,10 @@ export default function TemplateEditor() {
|
||||
</title>
|
||||
</Helmet>
|
||||
|
||||
{editorToolbarEnabled && editor && (
|
||||
<FixedToolbar editor={editor} templateMode />
|
||||
)}
|
||||
|
||||
<div className={classes.header}>
|
||||
<Container size={900} h="100%" px={0}>
|
||||
<Group justify="space-between" h="100%" wrap="nowrap">
|
||||
@@ -379,6 +392,13 @@ export default function TemplateEditor() {
|
||||
)}
|
||||
</div>
|
||||
<EditorContent editor={editor} />
|
||||
{editor && (
|
||||
<>
|
||||
<EditorAiMenu editor={editor} />
|
||||
<EditorBubbleMenu editor={editor} templateMode />
|
||||
<EditorLinkMenu editor={editor} />
|
||||
</>
|
||||
)}
|
||||
<div style={{ paddingBottom: "20vh" }} />
|
||||
</Container>
|
||||
</>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
useQueryClient,
|
||||
UseQueryResult,
|
||||
InfiniteData,
|
||||
keepPreviousData,
|
||||
} from "@tanstack/react-query";
|
||||
import { useAtom, useStore } from "jotai";
|
||||
import {
|
||||
@@ -35,6 +36,7 @@ export function useGetTemplatesQuery(params?: { spaceId?: string }) {
|
||||
initialPageParam: undefined as string | undefined,
|
||||
getNextPageParam: (lastPage) =>
|
||||
lastPage.meta.hasNextPage ? lastPage.meta.nextCursor : undefined,
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user