Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28ed897902 |
@@ -11,6 +11,7 @@
|
||||
"preview": "react-router build && serve -s build/client -l 3001",
|
||||
"start": "serve -s build/client -l 3001",
|
||||
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist && rm -rf build",
|
||||
"check:i18n": "tsx ../../packages/i18n/scripts/check-app-keys.ts",
|
||||
"check:lint": "eslint . --cache --cache-location node_modules/.cache/eslint/ --max-warnings=485",
|
||||
"check:types": "react-router typegen && tsc --noEmit",
|
||||
"check:format": "prettier . --cache --check",
|
||||
|
||||
@@ -2,7 +2,8 @@ import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { ISSUE_PRIORITY_FILTERS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { PriorityIcon } from "@plane/propel/icons";
|
||||
// local imports
|
||||
import { FilterHeader } from "./helpers/filter-header";
|
||||
@@ -42,7 +43,7 @@ export const FilterPriority = observer(function FilterPriority(props: Props) {
|
||||
isChecked={appliedFilters?.includes(priority.key) ? true : false}
|
||||
onClick={() => handleUpdate(priority.key)}
|
||||
icon={<PriorityIcon priority={priority.key} className="h-3.5 w-3.5" />}
|
||||
title={t(priority.titleTranslationKey)}
|
||||
title={t(priority.titleTranslationKey as KeysWithoutParams<"translation">)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SignalHigh } from "lucide-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// types
|
||||
import { PriorityIcon } from "@plane/propel/icons";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
@@ -29,7 +30,10 @@ export function IssueBlockPriority({
|
||||
if (priority_detail === null) return <></>;
|
||||
|
||||
return (
|
||||
<Tooltip tooltipHeading="Priority" tooltipContent={t(priority_detail?.titleTranslationKey || "")}>
|
||||
<Tooltip
|
||||
tooltipHeading="Priority"
|
||||
tooltipContent={t((priority_detail?.titleTranslationKey || "") as KeysWithoutParams<"translation">)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 border-[0.5px] rounded-sm text-11 px-2 py-0.5",
|
||||
@@ -59,7 +63,11 @@ export function IssueBlockPriority({
|
||||
) : (
|
||||
<SignalHigh className="size-3" />
|
||||
)}
|
||||
{shouldShowName && <span className="pl-2 text-13">{t(priority_detail?.titleTranslationKey || "")}</span>}
|
||||
{shouldShowName && (
|
||||
<span className="pl-2 text-13">
|
||||
{t((priority_detail?.titleTranslationKey || "") as KeysWithoutParams<"translation">)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,8 @@ import { useRouter, useSearchParams } from "next/navigation";
|
||||
// ui
|
||||
import { SITES_ISSUE_LAYOUTS } from "@plane/constants";
|
||||
// plane i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
// helpers
|
||||
import { queryParamGenerator } from "@/helpers/query-param-generator";
|
||||
@@ -47,7 +48,7 @@ export const IssuesLayoutSelection = observer(function IssuesLayoutSelection(pro
|
||||
if (!layoutOptions[layout.key]) return;
|
||||
|
||||
return (
|
||||
<Tooltip key={layout.key} tooltipContent={t(layout.titleTranslationKey)}>
|
||||
<Tooltip key={layout.key} tooltipContent={t(layout.titleTranslationKey as KeysWithoutParams<"translation">)}>
|
||||
<button
|
||||
type="button"
|
||||
className={`group grid h-[22px] w-7 place-items-center overflow-hidden rounded-sm transition-all bg-layer-transparent hover:bg-layer-transparent-hover ${
|
||||
|
||||
@@ -2,7 +2,8 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { LinkIcon } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import {
|
||||
StatePropertyIcon,
|
||||
StateGroupIcon,
|
||||
@@ -100,7 +101,7 @@ export const PeekOverviewIssueProperties = observer(function PeekOverviewIssuePr
|
||||
}`}
|
||||
>
|
||||
{priority && <PriorityIcon priority={priority?.key} size={12} className="flex-shrink-0" />}
|
||||
<span>{t(priority?.titleTranslationKey || "common.none")}</span>
|
||||
<span>{t((priority?.titleTranslationKey || "common.none") as KeysWithoutParams<"translation">)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"preview": "react-router build && PORT=3002 react-router-serve ./build/server/index.js",
|
||||
"start": "PORT=3002 react-router-serve ./build/server/index.js",
|
||||
"clean": "rm -rf .turbo && rm -rf .next && rm -rf .react-router && rm -rf node_modules && rm -rf dist && rm -rf build",
|
||||
"check:i18n": "tsx ../../packages/i18n/scripts/check-app-keys.ts",
|
||||
"check:lint": "eslint . --cache --cache-location node_modules/.cache/eslint/ --max-warnings=932",
|
||||
"check:types": "react-router typegen && tsc --noEmit",
|
||||
"check:format": "prettier . --cache --check",
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useParams } from "next/navigation";
|
||||
// plane constants
|
||||
import { EIssueFilterType, ISSUE_LAYOUTS, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
|
||||
// plane i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// icons
|
||||
import { ChevronDownIcon } from "@plane/propel/icons";
|
||||
// types
|
||||
@@ -103,7 +104,7 @@ export const ProfileIssuesMobileHeader = observer(function ProfileIssuesMobileHe
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<IssueLayoutIcon layout={ISSUE_LAYOUTS[index].key} className="h-3 w-3" />
|
||||
<div className="text-tertiary">{t(layout.i18n_title)}</div>
|
||||
<div className="text-tertiary">{t(layout.i18n_title as KeysWithoutParams<"translation">)}</div>
|
||||
</CustomMenu.MenuItem>
|
||||
);
|
||||
})}
|
||||
|
||||
+3
-2
@@ -3,7 +3,8 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import { EIssueFilterType, ISSUE_LAYOUTS, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { CalendarLayoutIcon, BoardLayoutIcon, ListLayoutIcon, ChevronDownIcon } from "@plane/propel/icons";
|
||||
import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, EIssueLayoutTypes } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
@@ -110,7 +111,7 @@ export const CycleIssuesMobileHeader = observer(function CycleIssuesMobileHeader
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<IssueLayoutIcon layout={ISSUE_LAYOUTS[index].key} className="w-3 h-3" />
|
||||
<div className="text-tertiary">{t(layout.titleTranslationKey)}</div>
|
||||
<div className="text-tertiary">{t(layout.titleTranslationKey as KeysWithoutParams<"translation">)}</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ function ProjectCyclesPage({ params }: Route.ComponentProps) {
|
||||
const resolvedEmptyState = resolvedTheme === "light" ? lightEmptyState : darkEmptyState;
|
||||
const totalCycles = currentProjectCycleIds?.length ?? 0;
|
||||
const project = getProjectById(projectId);
|
||||
const pageTitle = project?.name ? `${project?.name} - ${t("common.cycles", { count: 2 })}` : undefined;
|
||||
const pageTitle = project?.name ? `${project?.name} - ${t("common.cycles")}` : undefined;
|
||||
const hasAdminLevelPermission = allowPermissions([EUserProjectRoles.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||
const hasMemberLevelPermission = allowPermissions(
|
||||
[EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER],
|
||||
|
||||
+3
-2
@@ -3,7 +3,8 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import { EIssueFilterType, ISSUE_LAYOUTS, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { CalendarLayoutIcon, BoardLayoutIcon, ListLayoutIcon, ChevronDownIcon } from "@plane/propel/icons";
|
||||
import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, EIssueLayoutTypes } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
@@ -90,7 +91,7 @@ export const ModuleIssuesMobileHeader = observer(function ModuleIssuesMobileHead
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<IssueLayoutIcon layout={ISSUE_LAYOUTS[index].key} className="h-3 w-3" />
|
||||
<div className="text-tertiary">{t(layout.i18n_title)}</div>
|
||||
<div className="text-tertiary">{t(layout.i18n_title as KeysWithoutParams<"translation">)}</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
|
||||
@@ -130,7 +130,7 @@ export const GlobalIssuesHeader = observer(function GlobalIssuesHeader() {
|
||||
onChange={(value: string) => {
|
||||
router.push(`/${workspaceSlug}/workspace-views/${value}`);
|
||||
}}
|
||||
title={viewDetails?.name ?? t(defaultViewDetails?.i18n_label ?? "")}
|
||||
title={viewDetails?.name ?? (defaultViewDetails?.i18n_label ? t(defaultViewDetails.i18n_label) : "")}
|
||||
icon={
|
||||
<Breadcrumbs.Icon>
|
||||
<ViewsIcon className="size-4 flex-shrink-0 text-tertiary" />
|
||||
|
||||
+5
-2
@@ -1,7 +1,8 @@
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// components
|
||||
import { NotAuthorizedView } from "@/components/auth-screens/not-authorized-view";
|
||||
import { PageHead } from "@/components/core/page-title";
|
||||
@@ -41,7 +42,9 @@ function MembersSettingsPage({ params }: Route.ComponentProps) {
|
||||
return (
|
||||
<SettingsContentWrapper size="lg">
|
||||
<PageHead title={pageTitle} />
|
||||
<SettingsHeading title={t(getProjectSettingsPageLabelI18nKey("members", "common.members"))} />
|
||||
<SettingsHeading
|
||||
title={t(getProjectSettingsPageLabelI18nKey("members", "common.members") as KeysWithoutParams<"translation">)}
|
||||
/>
|
||||
<ProjectSettingsMemberDefaults projectId={projectId} workspaceSlug={workspaceSlug} />
|
||||
<ProjectTeamspaceList projectId={projectId} workspaceSlug={workspaceSlug} />
|
||||
<ProjectMemberList projectId={projectId} workspaceSlug={workspaceSlug} />
|
||||
|
||||
@@ -24,16 +24,16 @@ const WORKSPACE_ACTION_LINKS = [
|
||||
{
|
||||
key: "create_workspace",
|
||||
Icon: CirclePlus,
|
||||
i18n_label: "create_workspace",
|
||||
i18n_label: "create_workspace" as const,
|
||||
href: "/create-workspace",
|
||||
},
|
||||
{
|
||||
key: "invitations",
|
||||
Icon: Mails,
|
||||
i18n_label: "workspace_invites",
|
||||
i18n_label: "workspace_invites" as const,
|
||||
href: "/invitations",
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
function ProjectActionIcons({ type, size, className = "" }: { type: string; size?: number; className?: string }) {
|
||||
const icons = {
|
||||
@@ -107,8 +107,8 @@ export const ProfileLayoutSidebar = observer(function ProfileLayoutSidebar() {
|
||||
.catch(() =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("sign_out.toast.error.title"),
|
||||
message: t("sign_out.toast.error.message"),
|
||||
title: t("auth.sign_out.toast.error.title"),
|
||||
message: t("auth.sign_out.toast.error.message"),
|
||||
})
|
||||
)
|
||||
.finally(() => setIsSigningOut(false));
|
||||
@@ -148,7 +148,7 @@ export const ProfileLayoutSidebar = observer(function ProfileLayoutSidebar() {
|
||||
return (
|
||||
<Link key={link.key} href={link.href} className="block w-full" onClick={handleItemClick}>
|
||||
<Tooltip
|
||||
tooltipContent={t(link.key)}
|
||||
tooltipContent={t(link.i18n_label)}
|
||||
position="right"
|
||||
className="ml-2"
|
||||
disabled={!sidebarCollapsed}
|
||||
@@ -221,7 +221,7 @@ export const ProfileLayoutSidebar = observer(function ProfileLayoutSidebar() {
|
||||
{WORKSPACE_ACTION_LINKS.map((link) => (
|
||||
<Link className="block w-full" key={link.key} href={link.href} onClick={handleItemClick}>
|
||||
<Tooltip
|
||||
tooltipContent={t(link.key)}
|
||||
tooltipContent={t(link.i18n_label)}
|
||||
position="right"
|
||||
className="ml-2"
|
||||
disabled={!sidebarCollapsed}
|
||||
|
||||
@@ -2,7 +2,10 @@ import { observer } from "mobx-react";
|
||||
import { AlertOctagon, BarChart4, CircleDashed, Folder, Microscope, Search } from "lucide-react";
|
||||
// plane imports
|
||||
import { MARKETING_PRICING_PAGE_LINK } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams, PrefixedKeyWithoutParams} from "@plane/i18n";
|
||||
|
||||
type I18nKey = KeysWithoutParams<"translation"> | PrefixedKeyWithoutParams;
|
||||
import { getButtonStyling } from "@plane/propel/button";
|
||||
import { ContentWrapper } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
@@ -20,46 +23,46 @@ import { useUser } from "@/hooks/store/user";
|
||||
|
||||
export const WORKSPACE_ACTIVE_CYCLES_DETAILS = [
|
||||
{
|
||||
key: "10000_feet_view",
|
||||
key: "10000_feet_view" as const,
|
||||
title: "10,000-feet view of all active cycles.",
|
||||
description:
|
||||
"Zoom out to see running cycles across all your projects at once instead of going from Cycle to Cycle in each project.",
|
||||
icon: Folder,
|
||||
},
|
||||
{
|
||||
key: "get_snapshot_of_each_active_cycle",
|
||||
key: "get_snapshot_of_each_active_cycle" as const,
|
||||
title: "Get a snapshot of each active cycle.",
|
||||
description:
|
||||
"Track high-level metrics for all active cycles, see their state of progress, and get a sense of scope against deadlines.",
|
||||
icon: CircleDashed,
|
||||
},
|
||||
{
|
||||
key: "compare_burndowns",
|
||||
key: "compare_burndowns" as const,
|
||||
title: "Compare burndowns.",
|
||||
description: "Monitor how each of your teams are performing with a peek into each cycle’s burndown report.",
|
||||
description: "Monitor how each of your teams are performing with a peek into each cycle's burndown report.",
|
||||
icon: BarChart4,
|
||||
},
|
||||
{
|
||||
key: "quickly_see_make_or_break_issues",
|
||||
key: "quickly_see_make_or_break_issues" as const,
|
||||
title: "Quickly see make-or-break work items. ",
|
||||
description:
|
||||
"Preview high-priority work items for each cycle against due dates. See all of them per cycle in one click.",
|
||||
icon: AlertOctagon,
|
||||
},
|
||||
{
|
||||
key: "zoom_into_cycles_that_need_attention",
|
||||
key: "zoom_into_cycles_that_need_attention" as const,
|
||||
title: "Zoom into cycles that need attention. ",
|
||||
description: "Investigate the state of any cycle that doesn’t conform to expectations in one click.",
|
||||
description: "Investigate the state of any cycle that doesn't conform to expectations in one click.",
|
||||
icon: Search,
|
||||
},
|
||||
{
|
||||
key: "stay_ahead_of_blockers",
|
||||
key: "stay_ahead_of_blockers" as const,
|
||||
title: "Stay ahead of blockers.",
|
||||
description:
|
||||
"Spot challenges from one project to another and see inter-cycle dependencies that aren’t obvious from any other view.",
|
||||
"Spot challenges from one project to another and see inter-cycle dependencies that aren't obvious from any other view.",
|
||||
icon: Microscope,
|
||||
},
|
||||
];
|
||||
] as const;
|
||||
|
||||
export const WorkspaceActiveCyclesUpgrade = observer(function WorkspaceActiveCyclesUpgrade() {
|
||||
const { t } = useTranslation();
|
||||
@@ -118,7 +121,7 @@ export const WorkspaceActiveCyclesUpgrade = observer(function WorkspaceActiveCyc
|
||||
<h3 className="font-medium">{t(item.key)}</h3>
|
||||
<item.icon className="mt-1 h-4 w-4 text-blue-500" />
|
||||
</div>
|
||||
<span className="text-13 text-tertiary">{t(`${item.key}_description`)}</span>
|
||||
<span className="text-13 text-tertiary">{t(`${item.key}_description` as I18nKey)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import type { I_THEME_OPTION } from "@plane/constants";
|
||||
import { THEME_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams, PrefixedKeyWithoutParams} from "@plane/i18n";
|
||||
import { setPromiseToast } from "@plane/propel/toast";
|
||||
// components
|
||||
import { CustomThemeSelector } from "@/components/core/theme/custom-theme-selector";
|
||||
@@ -14,11 +15,13 @@ import { PreferencesSection } from "@/components/preferences/section";
|
||||
// hooks
|
||||
import { useUserProfile } from "@/hooks/store/user";
|
||||
|
||||
type I18nKey = KeysWithoutParams<"translation"> | PrefixedKeyWithoutParams;
|
||||
|
||||
export const ThemeSwitcher = observer(function ThemeSwitcher(props: {
|
||||
option: {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
title: I18nKey;
|
||||
description: I18nKey;
|
||||
};
|
||||
}) {
|
||||
// store hooks
|
||||
|
||||
@@ -2,7 +2,8 @@ import type { FC } from "react";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
// plane imports
|
||||
import { NETWORK_CHOICES, ETabIndices } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams, PrefixedKeyWithoutParams} from "@plane/i18n";
|
||||
import type { IProject } from "@plane/types";
|
||||
import { CustomSelect } from "@plane/ui";
|
||||
import { getTabIndex } from "@plane/utils";
|
||||
@@ -10,6 +11,8 @@ import { getTabIndex } from "@plane/utils";
|
||||
import { MemberDropdown } from "@/components/dropdowns/member/dropdown";
|
||||
import { ProjectNetworkIcon } from "@/components/project/project-network-icon";
|
||||
|
||||
type I18nKey = KeysWithoutParams<"translation"> | PrefixedKeyWithoutParams;
|
||||
|
||||
type Props = {
|
||||
isMobile?: boolean;
|
||||
};
|
||||
@@ -37,7 +40,7 @@ function ProjectAttributes(props: Props) {
|
||||
{currentNetwork ? (
|
||||
<>
|
||||
<ProjectNetworkIcon iconKey={currentNetwork.iconKey} />
|
||||
{t(currentNetwork.i18n_label)}
|
||||
{t(currentNetwork.i18n_label as I18nKey)}
|
||||
</>
|
||||
) : (
|
||||
<span className="text-placeholder">{t("select_network")}</span>
|
||||
@@ -55,8 +58,8 @@ function ProjectAttributes(props: Props) {
|
||||
<div className="flex items-start gap-2">
|
||||
<ProjectNetworkIcon iconKey={network.iconKey} className="h-3.5 w-3.5" />
|
||||
<div className="-mt-1">
|
||||
<p>{t(network.i18n_label)}</p>
|
||||
<p className="text-11 text-placeholder">{t(network.description)}</p>
|
||||
<p>{t(network.i18n_label as I18nKey)}</p>
|
||||
<p className="text-11 text-placeholder">{t(network.description as I18nKey)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CustomSelect.Option>
|
||||
|
||||
@@ -9,7 +9,8 @@ import { Pin, PinOff } from "lucide-react";
|
||||
// plane imports
|
||||
import type { IWorkspaceSidebarNavigationItem } from "@plane/constants";
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import { DragHandle, DropIndicator } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
@@ -189,7 +190,9 @@ export const ExtendedSidebarItem = observer(function ExtendedSidebarItem(props:
|
||||
<Link href={itemHref} onClick={() => handleLinkClick()} className="group flex-grow">
|
||||
<div className="flex items-center gap-1.5 py-[1px]">
|
||||
{icon}
|
||||
<p className="text-13 leading-5 font-medium">{t(item.labelTranslationKey)}</p>
|
||||
<p className="text-13 leading-5 font-medium">
|
||||
{t(item.labelTranslationKey as KeysWithoutParams<"translation">)}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -103,7 +103,7 @@ export const AuthHeader = observer(function AuthHeader(props: TAuthHeader) {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-h4-semibold text-primary">{typeof header === "string" ? t(header) : header}</span>
|
||||
<span className="text-h4-semibold text-primary">{header}</span>
|
||||
<span className="text-h4-semibold text-placeholder">{subHeader}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,6 +15,8 @@ type TAuthEmailForm = {
|
||||
onSubmit: (data: IEmailCheckData) => Promise<void>;
|
||||
};
|
||||
|
||||
const EMAIL_ERROR_KEY = "auth.common.email.errors.invalid" as const;
|
||||
|
||||
export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailForm) {
|
||||
const { onSubmit, defaultEmail } = props;
|
||||
// states
|
||||
@@ -23,7 +25,7 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
const emailError = useMemo(
|
||||
() => (email && !checkEmailValidity(email) ? { email: "auth.common.email.errors.invalid" } : undefined),
|
||||
() => (email && !checkEmailValidity(email) ? { email: EMAIL_ERROR_KEY } : undefined),
|
||||
[email]
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from "react";
|
||||
// plane package imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams, PrefixedKeyWithoutParams} from "@plane/i18n";
|
||||
import { cn } from "@plane/utils";
|
||||
|
||||
type Props = {
|
||||
i18nTitle: string;
|
||||
i18nTitle: KeysWithoutParams<"translation"> | PrefixedKeyWithoutParams;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
@@ -10,18 +10,18 @@ import { useInstance } from "@/hooks/store/use-instance";
|
||||
|
||||
const authContentMap = {
|
||||
[EAuthModes.SIGN_IN]: {
|
||||
pageTitle: "Sign up",
|
||||
text: "auth.common.new_to_plane",
|
||||
linkText: "Sign up",
|
||||
pageTitle: "auth.sign_up.header.step.email.header" as const,
|
||||
text: "auth.common.new_to_plane" as const,
|
||||
linkText: "auth.sign_up.header.step.email.header" as const,
|
||||
linkHref: "/sign-up",
|
||||
},
|
||||
[EAuthModes.SIGN_UP]: {
|
||||
pageTitle: "Sign in",
|
||||
text: "auth.common.already_have_an_account",
|
||||
linkText: "Sign in",
|
||||
pageTitle: "auth.sign_in.header.step.email.header" as const,
|
||||
text: "auth.common.already_have_an_account" as const,
|
||||
linkText: "auth.common.login" as const,
|
||||
linkHref: "/sign-in",
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
type AuthHeaderProps = {
|
||||
type: EAuthModes;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import type { TBaseLayoutType } from "@plane/types";
|
||||
import { cn } from "@plane/utils";
|
||||
@@ -27,7 +28,11 @@ export function LayoutSwitcher(props: Props) {
|
||||
{BASE_LAYOUTS.filter((l) => (layouts ? layouts.includes(l.key) : true)).map((layout) => {
|
||||
const Icon = layout.icon;
|
||||
return (
|
||||
<Tooltip key={layout.key} tooltipContent={t(layout.label)} isMobile={isMobile}>
|
||||
<Tooltip
|
||||
key={layout.key}
|
||||
tooltipContent={t(layout.label as KeysWithoutParams<"translation">)}
|
||||
isMobile={isMobile}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
// plane ui
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams, PrefixedKeyWithoutParams} from "@plane/i18n";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
// plane utils
|
||||
import { cn } from "@plane/utils";
|
||||
|
||||
type I18nKey = KeysWithoutParams<"translation"> | PrefixedKeyWithoutParams;
|
||||
|
||||
type Props = {
|
||||
onChange: (value: number) => void;
|
||||
value: number;
|
||||
accessSpecifiers: {
|
||||
key: number;
|
||||
i18n_label?: string;
|
||||
i18n_label?: I18nKey;
|
||||
label?: string;
|
||||
icon: LucideIcon;
|
||||
}[];
|
||||
|
||||
@@ -3,7 +3,8 @@ import { observer } from "mobx-react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { Transition, Dialog } from "@headlessui/react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams, PrefixedKeyWithoutParams} from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { Input } from "@plane/ui";
|
||||
@@ -27,6 +28,29 @@ const defaultValues: TUniqueCodeValuesForm = { email: "", code: "" };
|
||||
// service initialization
|
||||
const authService = new AuthService();
|
||||
|
||||
type I18nKey = KeysWithoutParams<"translation"> | PrefixedKeyWithoutParams;
|
||||
|
||||
type ChangeEmailTranslationPath =
|
||||
| "title"
|
||||
| "description"
|
||||
| "toasts.success_title"
|
||||
| "toasts.success_message"
|
||||
| "form.email.label"
|
||||
| "form.email.placeholder"
|
||||
| "form.email.errors.required"
|
||||
| "form.email.errors.invalid"
|
||||
| "form.email.errors.exists"
|
||||
| "form.email.errors.validation_failed"
|
||||
| "form.code.label"
|
||||
| "form.code.placeholder"
|
||||
| "form.code.errors.required"
|
||||
| "form.code.errors.invalid"
|
||||
| "form.code.helper_text"
|
||||
| "actions.cancel"
|
||||
| "actions.confirm"
|
||||
| "actions.continue"
|
||||
| "states.sending";
|
||||
|
||||
export const ChangeEmailModal = observer(function ChangeEmailModal(props: Props) {
|
||||
const { isOpen, onClose } = props;
|
||||
// states
|
||||
@@ -34,7 +58,8 @@ export const ChangeEmailModal = observer(function ChangeEmailModal(props: Props)
|
||||
// store hooks
|
||||
const { signOut } = useUser();
|
||||
const { t } = useTranslation();
|
||||
const changeEmailT = (path: string) => t(`account_settings.profile.change_email_modal.${path}`);
|
||||
const changeEmailT = (path: ChangeEmailTranslationPath) =>
|
||||
t(`account_settings.profile.change_email_modal.${path}` as I18nKey);
|
||||
// form info
|
||||
const {
|
||||
handleSubmit,
|
||||
@@ -56,8 +81,8 @@ export const ChangeEmailModal = observer(function ChangeEmailModal(props: Props)
|
||||
await signOut().catch(() =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("sign_out.toast.error.title"),
|
||||
message: t("sign_out.toast.error.message"),
|
||||
title: t("auth.sign_out.toast.error.title"),
|
||||
message: t("auth.sign_out.toast.error.message"),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { Tab } from "@headlessui/react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TWorkItemFilterCondition } from "@plane/shared-state";
|
||||
import type { TCycleDistribution, TCycleEstimateDistribution, TCyclePlotType } from "@plane/types";
|
||||
import { cn, toFilterArray } from "@plane/utils";
|
||||
@@ -137,7 +138,7 @@ export const CycleProgressStats = observer(function CycleProgressStats(props: TC
|
||||
key={stat.key}
|
||||
onClick={() => setCycleTab(stat.key)}
|
||||
>
|
||||
{t(stat.i18n_title)}
|
||||
{t(stat.i18n_title as KeysWithoutParams<"translation">)}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
|
||||
@@ -5,7 +5,8 @@ import { EIssueCommentAccessSpecifier } from "@plane/constants";
|
||||
// editor
|
||||
import type { EditorRefApi } from "@plane/editor";
|
||||
// i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// ui
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
@@ -177,7 +178,7 @@ export function IssueCommentToolbar(props: Props) {
|
||||
disabled={isSubmitButtonDisabled}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{t(submitButtonText)}
|
||||
{t(submitButtonText as KeysWithoutParams<"translation">)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -4,7 +4,8 @@ import { observer } from "mobx-react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// plane imports
|
||||
import type { EditorRefApi, TExtensions } from "@plane/editor";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { EFileAssetType, TNameDescriptionLoader } from "@plane/types";
|
||||
import { getDescriptionPlaceholderI18n } from "@plane/utils";
|
||||
// components
|
||||
@@ -218,7 +219,11 @@ export const DescriptionInput = observer(function DescriptionInput(props: Props)
|
||||
hasUnsavedChanges.current = true;
|
||||
debouncedFormSave();
|
||||
}}
|
||||
placeholder={placeholder ?? ((isFocused, value) => t(getDescriptionPlaceholderI18n(isFocused, value)))}
|
||||
placeholder={
|
||||
placeholder ??
|
||||
((isFocused, value) =>
|
||||
t(getDescriptionPlaceholderI18n(isFocused, value) as KeysWithoutParams<"translation">))
|
||||
}
|
||||
searchMentionCallback={async (payload) =>
|
||||
await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
|
||||
...payload,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Info } from "lucide-react";
|
||||
// plane imports
|
||||
import { EEstimateSystem, ESTIMATE_SYSTEMS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams, PrefixedKeyWithoutParams} from "@plane/i18n";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import type { TEstimateSystemKeys } from "@plane/types";
|
||||
|
||||
type I18nKey = KeysWithoutParams<"translation"> | PrefixedKeyWithoutParams;
|
||||
// components
|
||||
import { convertMinutesToHoursMinutesString } from "@plane/utils";
|
||||
// plane web imports
|
||||
@@ -36,21 +39,22 @@ export function EstimateCreateStageOne(props: TEstimateCreateStageOne) {
|
||||
const currentSystem = system as TEstimateSystemKeys;
|
||||
const isEnabled = isEstimateSystemEnabled(currentSystem);
|
||||
if (!isEnabled) return null;
|
||||
const i18nName = ESTIMATE_SYSTEMS[currentSystem]?.i18n_name;
|
||||
return {
|
||||
label: !ESTIMATE_SYSTEMS[currentSystem]?.is_available ? (
|
||||
<div className="relative flex items-center gap-2 cursor-no-drop text-tertiary">
|
||||
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
|
||||
{i18nName ? t(i18nName as I18nKey) : null}
|
||||
<Tooltip tooltipContent={t("common.coming_soon")}>
|
||||
<Info size={12} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : !isEnabled ? (
|
||||
<div className="relative flex items-center gap-2 cursor-no-drop text-tertiary">
|
||||
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
|
||||
{i18nName ? t(i18nName as I18nKey) : null}
|
||||
<UpgradeBadge />
|
||||
</div>
|
||||
) : (
|
||||
<div>{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}</div>
|
||||
<div>{i18nName ? t(i18nName as I18nKey) : null}</div>
|
||||
),
|
||||
value: system,
|
||||
disabled: !isEnabled,
|
||||
|
||||
@@ -7,7 +7,8 @@ import { Check, Hotel } from "lucide-react";
|
||||
// plane ui
|
||||
import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { useLocalStorage } from "@plane/hooks";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { MembersPropertyIcon, ProjectIcon, CloseIcon } from "@plane/propel/icons";
|
||||
import { cn, getFileURL } from "@plane/utils";
|
||||
// helpers
|
||||
@@ -158,8 +159,10 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
>
|
||||
<span className="text-24 my-auto">{item.icon}</span>
|
||||
</div>
|
||||
<h3 className="text-13 font-medium text-primary mb-2">{t(item.title)}</h3>
|
||||
<p className="text-11 text-tertiary mb-2">{t(item.description)}</p>
|
||||
<h3 className="text-13 font-medium text-primary mb-2">
|
||||
{t(item.title as KeysWithoutParams<"translation">)}
|
||||
</h3>
|
||||
<p className="text-11 text-tertiary mb-2">{t(item.description as KeysWithoutParams<"translation">)}</p>
|
||||
{isStateComplete ? (
|
||||
<div className="flex items-center gap-2 bg-[#17a34a] rounded-full p-1 w-fit">
|
||||
<Check className="size-3 text-accent-primary text-on-color" />
|
||||
@@ -182,7 +185,7 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
}}
|
||||
className={cn("text-accent-primary hover:text-accent-secondary text-13 font-medium", {})}
|
||||
>
|
||||
{t(item.cta.text)}
|
||||
{t(item.cta.text as KeysWithoutParams<"translation">)}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
@@ -190,7 +193,7 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
className="text-accent-primary hover:text-accent-secondary text-13 font-medium text-left"
|
||||
onClick={item.cta.onClick}
|
||||
>
|
||||
{t(item.cta.text)}
|
||||
{t(item.cta.text as KeysWithoutParams<"translation">)}
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { EmptyStateCompact } from "@plane/propel/empty-state";
|
||||
import type { CompactAssetType } from "@plane/propel/empty-state";
|
||||
|
||||
@@ -34,7 +35,11 @@ export function RecentsEmptyState({ type }: { type: string }) {
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center py-10 bg-layer-1 w-full rounded-lg">
|
||||
<EmptyStateCompact assetKey={assetKey} assetClassName="size-20" title={t(text)} />
|
||||
<EmptyStateCompact
|
||||
assetKey={assetKey}
|
||||
assetClassName="size-20"
|
||||
title={t(text as KeysWithoutParams<"translation">)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { createRoot } from "react-dom/client";
|
||||
// plane types
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { InstructionType, TWidgetEntityData } from "@plane/types";
|
||||
// plane ui
|
||||
import { DropIndicator, ToggleSwitch } from "@plane/ui";
|
||||
@@ -129,7 +130,7 @@ export const WidgetItem = observer(function WidgetItem(props: Props) {
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<WidgetItemDragHandle sort_order={widget.sort_order} isDragging={isDragging} />
|
||||
<div>{t(widgetTitle, { count: 1 })}</div>
|
||||
<div>{t(widgetTitle as KeysWithoutParams<"translation">)}</div>
|
||||
</div>
|
||||
<ToggleSwitch
|
||||
value={widget.is_enabled}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { ChevronDownIcon } from "@plane/propel/icons";
|
||||
import type { TRecentActivityFilterKeys } from "@plane/types";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
@@ -26,7 +27,9 @@ export const FiltersDropdown = observer(function FiltersDropdown(props: TFilters
|
||||
setActiveFilter(filter.name);
|
||||
}}
|
||||
>
|
||||
<div className="truncate font-medium text-11 capitalize">{t(filter.i18n_key)}</div>
|
||||
<div className="truncate font-medium text-11 capitalize">
|
||||
{t(filter.i18n_key as KeysWithoutParams<"translation">)}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
));
|
||||
}
|
||||
@@ -39,7 +42,7 @@ export const FiltersDropdown = observer(function FiltersDropdown(props: TFilters
|
||||
placement="bottom-start"
|
||||
customButton={
|
||||
<button className="flex hover:bg-layer-transparent-hover px-2 py-1 rounded-sm gap-1 capitalize border border-subtle">
|
||||
<span className="font-medium text-13 my-auto">{t(title || "")}</span>
|
||||
<span className="font-medium text-13 my-auto">{t((title || "") as KeysWithoutParams<"translation">)}</span>
|
||||
<ChevronDownIcon className={cn("size-3 my-auto text-tertiary hover:text-secondary duration-300")} />
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import { observer } from "mobx-react";
|
||||
// constants
|
||||
// helpers
|
||||
import { INBOX_STATUS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { cn, findHowManyDaysLeft } from "@plane/utils";
|
||||
// store
|
||||
import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
|
||||
@@ -25,9 +26,12 @@ export const InboxIssueStatus = observer(function InboxIssueStatus(props: Props)
|
||||
const isSnoozedDatePassed = inboxIssue.status === 0 && new Date(inboxIssue.snoozed_till ?? "") < new Date();
|
||||
if (!inboxIssueStatusDetail || isSnoozedDatePassed) return <></>;
|
||||
|
||||
const description = t(inboxIssueStatusDetail.i18n_description(), {
|
||||
days: findHowManyDaysLeft(new Date(inboxIssue.snoozed_till ?? "")),
|
||||
});
|
||||
const description =
|
||||
inboxIssue.status === 0
|
||||
? t("inbox_issue.status.snoozed.description", {
|
||||
days: findHowManyDaysLeft(new Date(inboxIssue.snoozed_till ?? "")) ?? 0,
|
||||
})
|
||||
: t(inboxIssueStatusDetail.i18n_description() as KeysWithoutParams<"translation">);
|
||||
const statusIcon = ICON_PROPERTIES[inboxIssue?.status];
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,8 @@ import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { ETabIndices } from "@plane/constants";
|
||||
import type { EditorRefApi } from "@plane/editor";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { EFileAssetType } from "@plane/types";
|
||||
import { Loader } from "@plane/ui";
|
||||
@@ -71,7 +72,9 @@ export const InboxIssueDescription = observer(function InboxIssueDescription(pro
|
||||
projectId={projectId}
|
||||
dragDropEnabled={false}
|
||||
onChange={(_description: object, description_html: string) => handleData("description_html", description_html)}
|
||||
placeholder={(isFocused, description) => t(`${getDescriptionPlaceholderI18n(isFocused, description)}`)}
|
||||
placeholder={(isFocused, description) =>
|
||||
t(getDescriptionPlaceholderI18n(isFocused, description) as KeysWithoutParams<"translation">)
|
||||
}
|
||||
searchMentionCallback={async (payload) =>
|
||||
await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
|
||||
...payload,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import type { TInboxIssueCurrentTab } from "@plane/types";
|
||||
import { EInboxIssueCurrentTab } from "@plane/types";
|
||||
@@ -94,7 +95,7 @@ export const InboxSidebar = observer(function InboxSidebar(props: IInboxSidebarP
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div>{t(option?.i18n_label)}</div>
|
||||
<div>{t(option?.i18n_label as KeysWithoutParams<"translation">)}</div>
|
||||
{option?.key === "open" && currentTab === option?.key && (
|
||||
<div className="rounded-full p-1.5 py-0.5 bg-accent-primary/20 text-accent-primary text-11 font-semibold">
|
||||
{inboxIssuePaginationInfo?.total_results || 0}
|
||||
|
||||
@@ -2,7 +2,8 @@ import type { FC } from "react";
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TIssue, TIssueServiceType } from "@plane/types";
|
||||
import { EIssueServiceType } from "@plane/types";
|
||||
import { Collapsible } from "@plane/ui";
|
||||
@@ -112,7 +113,9 @@ export const RelationsCollapsibleContent = observer(function RelationsCollapsibl
|
||||
relationKey: relationKey,
|
||||
issueIds: issueIds,
|
||||
icon: issueRelationOption?.icon,
|
||||
label: issueRelationOption?.i18n_label ? t(issueRelationOption?.i18n_label) : "",
|
||||
label: issueRelationOption?.i18n_label
|
||||
? t(issueRelationOption?.i18n_label as KeysWithoutParams<"translation">)
|
||||
: "",
|
||||
className: issueRelationOption?.className,
|
||||
};
|
||||
});
|
||||
|
||||
+3
-2
@@ -3,7 +3,8 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Plus } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TIssueServiceType } from "@plane/types";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
// hooks
|
||||
@@ -56,7 +57,7 @@ export const RelationActionButton = observer(function RelationActionButton(props
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{item.icon(12)}
|
||||
<span>{t(item.i18n_label)}</span>
|
||||
<span>{t(item.i18n_label as KeysWithoutParams<"translation">)}</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
);
|
||||
|
||||
@@ -47,7 +47,7 @@ export const useSubIssueOperations = (issueServiceType: TIssueServiceType): TSub
|
||||
message: t("entity.link_copied_to_clipboard", {
|
||||
entity:
|
||||
issueServiceType === EIssueServiceType.ISSUES
|
||||
? t("common.sub_work_items", { count: 1 })
|
||||
? t("common.sub_work_item")
|
||||
: t("issue.label", { count: 1 }),
|
||||
}),
|
||||
});
|
||||
@@ -63,7 +63,7 @@ export const useSubIssueOperations = (issueServiceType: TIssueServiceType): TSub
|
||||
message: t("entity.fetch.failed", {
|
||||
entity:
|
||||
issueServiceType === EIssueServiceType.ISSUES
|
||||
? t("common.sub_work_items", { count: 2 })
|
||||
? t("common.sub_work_items")
|
||||
: t("issue.label", { count: 2 }),
|
||||
}),
|
||||
});
|
||||
|
||||
+3
-2
@@ -4,7 +4,8 @@ import { observer } from "mobx-react";
|
||||
import { Plus } from "lucide-react";
|
||||
// plane imports
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { WorkItemsIcon } from "@plane/propel/icons";
|
||||
import type { TIssue, TIssueServiceType } from "@plane/types";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
@@ -93,7 +94,7 @@ export const SubIssuesActionButton = observer(function SubIssuesActionButton(pro
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{item.icon}
|
||||
<span>{t(item.i18n_label)}</span>
|
||||
<span>{t(item.i18n_label as KeysWithoutParams<"translation">)}</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
|
||||
@@ -2,7 +2,8 @@ import { observer } from "mobx-react";
|
||||
import { Check, ListFilter } from "lucide-react";
|
||||
// plane imports
|
||||
import type { TActivityFilters, TActivityFilterOption } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
import { PopoverMenu } from "@plane/ui";
|
||||
// helper
|
||||
@@ -53,7 +54,7 @@ export const ActivityFilter = observer(function ActivityFilter(props: TActivityF
|
||||
{item.isSelected && <Check className="h-2.5 w-2.5" />}
|
||||
</div>
|
||||
<div className={cn("whitespace-nowrap", item.isSelected ? "text-primary" : "text-secondary")}>
|
||||
{t(item.labelTranslationKey)}
|
||||
{t(item.labelTranslationKey as KeysWithoutParams<"translation">)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -96,7 +96,7 @@ export const IssueDetailQuickActions = observer(function IssueDetailQuickActions
|
||||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
title: t("toast.error "),
|
||||
title: t("toast.error"),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: t("entity.delete.failed", { entity: t("issue.label", { count: 1 }) }),
|
||||
});
|
||||
|
||||
@@ -104,7 +104,7 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
setToast({
|
||||
title: t("common.error.label"),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: t("entity.update.failed", { entity: t("issue.label") }),
|
||||
message: t("entity.update.failed", { entity: t("issue.label", { count: 1 }) }),
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -115,7 +115,7 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
setToast({
|
||||
title: t("common.success"),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
message: t("entity.delete.success", { entity: t("issue.label") }),
|
||||
message: t("entity.delete.success", { entity: t("issue.label", { count: 1 }) }),
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
@@ -126,7 +126,7 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
setToast({
|
||||
title: t("common.error.label"),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: t("entity.delete.failed", { entity: t("issue.label") }),
|
||||
message: t("entity.delete.failed", { entity: t("issue.label", { count: 1 }) }),
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
|
||||
@@ -52,7 +52,7 @@ export const CalendarQuickAddIssueActions = observer(function CalendarQuickAddIs
|
||||
).then(() => addIssuesToView?.(issueIds));
|
||||
|
||||
setPromiseToast(addExistingIssuesPromise, {
|
||||
loading: t("issue.adding", { count: issueIds.length }),
|
||||
loading: t("issue.adding"),
|
||||
success: {
|
||||
title: t("toast.success"),
|
||||
message: () => t("entity.add.success", { entity: t("issue.label", { count: 2 }) }),
|
||||
|
||||
@@ -4,6 +4,8 @@ import { useParams } from "next/navigation";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
|
||||
type TProfileViewId = "activity" | "assigned" | "created" | "subscribed";
|
||||
|
||||
// TODO: If projectViewId changes, everything breaks. Figure out a better way to handle this.
|
||||
export const ProfileViewEmptyState = observer(function ProfileViewEmptyState() {
|
||||
// plane hooks
|
||||
@@ -13,11 +15,13 @@ export const ProfileViewEmptyState = observer(function ProfileViewEmptyState() {
|
||||
|
||||
if (!profileViewId) return null;
|
||||
|
||||
const viewId = profileViewId.toString() as TProfileViewId;
|
||||
|
||||
return (
|
||||
<EmptyStateDetailed
|
||||
assetKey="work-item"
|
||||
title={t(`profile.empty_state.${profileViewId.toString()}.title`)}
|
||||
description={t(`profile.empty_state.${profileViewId.toString()}.description`)}
|
||||
title={t(`profile.empty_state.${viewId}.title`)}
|
||||
description={t(`profile.empty_state.${viewId}.description`)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
+3
-2
@@ -4,7 +4,8 @@ import { useParams } from "next/navigation";
|
||||
// plane constants
|
||||
import { ISSUE_DISPLAY_PROPERTIES } from "@plane/constants";
|
||||
// plane i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// types
|
||||
import type { IIssueDisplayProperties } from "@plane/types";
|
||||
// plane web helpers
|
||||
@@ -83,7 +84,7 @@ export const FilterDisplayProperties = observer(function FilterDisplayProperties
|
||||
})
|
||||
}
|
||||
>
|
||||
{t(displayProperty.titleTranslationKey)}
|
||||
{t(displayProperty.titleTranslationKey as KeysWithoutParams<"translation">)}
|
||||
</button>
|
||||
</>
|
||||
))}
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { IIssueDisplayFilterOptions, TIssueExtraOptions } from "@plane/types";
|
||||
// components
|
||||
import { FilterOption } from "@/components/issues/issue-layouts/filters";
|
||||
@@ -45,7 +46,7 @@ export const FilterExtraOptions = observer(function FilterExtraOptions(props: Pr
|
||||
key={option.key}
|
||||
isChecked={selectedExtraOptions?.[option.key] ? true : false}
|
||||
onClick={() => handleUpdate(option.key, !selectedExtraOptions?.[option.key])}
|
||||
title={t(option.titleTranslationKey)}
|
||||
title={t(option.titleTranslationKey as KeysWithoutParams<"translation">)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { IIssueDisplayFilterOptions, TIssueGroupByOptions } from "@plane/types";
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
|
||||
@@ -47,7 +48,7 @@ export const FilterGroupBy = observer(function FilterGroupBy(props: Props) {
|
||||
key={groupBy?.key}
|
||||
isChecked={selectedGroupBy === groupBy?.key ? true : false}
|
||||
onClick={() => handleUpdate(groupBy.key)}
|
||||
title={t(groupBy.titleTranslationKey)}
|
||||
title={t(groupBy.titleTranslationKey as KeysWithoutParams<"translation">)}
|
||||
multiple={false}
|
||||
/>
|
||||
);
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { ISSUE_ORDER_BY_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TIssueOrderByOptions } from "@plane/types";
|
||||
|
||||
// components
|
||||
@@ -36,7 +37,7 @@ export const FilterOrderBy = observer(function FilterOrderBy(props: Props) {
|
||||
key={orderBy?.key}
|
||||
isChecked={activeOrderBy === orderBy?.key ? true : false}
|
||||
onClick={() => handleUpdate(orderBy.key)}
|
||||
title={t(orderBy.titleTranslationKey)}
|
||||
title={t(orderBy.titleTranslationKey as KeysWithoutParams<"translation">)}
|
||||
multiple={false}
|
||||
/>
|
||||
))}
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { ISSUE_GROUP_BY_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { IIssueDisplayFilterOptions, TIssueGroupByOptions } from "@plane/types";
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
|
||||
@@ -43,7 +44,7 @@ export const FilterSubGroupBy = observer(function FilterSubGroupBy(props: Props)
|
||||
key={subGroupBy?.key}
|
||||
isChecked={selectedSubGroupBy === subGroupBy?.key ? true : false}
|
||||
onClick={() => handleUpdate(subGroupBy.key)}
|
||||
title={t(subGroupBy.titleTranslationKey)}
|
||||
title={t(subGroupBy.titleTranslationKey as KeysWithoutParams<"translation">)}
|
||||
multiple={false}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// plane imports
|
||||
import { ISSUE_LAYOUTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import type { EIssueLayoutTypes } from "@plane/types";
|
||||
import { cn } from "@plane/utils";
|
||||
@@ -28,7 +29,11 @@ export function LayoutSelection(props: Props) {
|
||||
return (
|
||||
<div className="flex items-center gap-1 rounded-md bg-layer-3 p-1">
|
||||
{ISSUE_LAYOUTS.filter((l) => layouts.includes(l.key)).map((layout) => (
|
||||
<Tooltip key={layout.key} tooltipContent={t(layout.i18n_title)} isMobile={isMobile}>
|
||||
<Tooltip
|
||||
key={layout.key}
|
||||
tooltipContent={t(layout.i18n_title as KeysWithoutParams<"translation">)}
|
||||
isMobile={isMobile}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
import { ISSUE_LAYOUTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { ChevronDownIcon } from "@plane/propel/icons";
|
||||
import type { EIssueLayoutTypes } from "@plane/types";
|
||||
@@ -42,7 +43,7 @@ export function MobileLayoutSelection({
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<IssueLayoutIcon layout={layout.key} className="h-3 w-3" />
|
||||
<div className="text-tertiary">{t(layout.i18n_label)}</div>
|
||||
<div className="text-tertiary">{t(layout.i18n_label as KeysWithoutParams<"translation">)}</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useRef } from "react";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
// plane imports
|
||||
import { ISSUE_ORDER_BY_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TIssueOrderByOptions } from "@plane/types";
|
||||
// helpers
|
||||
import { cn } from "@plane/utils";
|
||||
@@ -37,9 +38,12 @@ export function GroupDragOverlay(props: Props) {
|
||||
const messageContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const shouldOverlayBeVisible = isDraggingOverColumn && canOverlayBeVisible;
|
||||
const readableOrderBy = t(
|
||||
ISSUE_ORDER_BY_OPTIONS.find((orderByObj) => orderByObj.key === orderBy)?.titleTranslationKey || ""
|
||||
);
|
||||
const readableOrderBy = ISSUE_ORDER_BY_OPTIONS.find((orderByObj) => orderByObj.key === orderBy)?.titleTranslationKey
|
||||
? t(
|
||||
ISSUE_ORDER_BY_OPTIONS.find((orderByObj) => orderByObj.key === orderBy)!
|
||||
.titleTranslationKey as KeysWithoutParams<"translation">
|
||||
)
|
||||
: "";
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -75,7 +79,7 @@ export function GroupDragOverlay(props: Props) {
|
||||
<>
|
||||
{readableOrderBy && (
|
||||
<span>
|
||||
{t("issue.layouts.ordered_by_label")} <span className="font-semibold">{t(readableOrderBy)}</span>.
|
||||
{t("issue.layouts.ordered_by_label")} <span className="font-semibold">{readableOrderBy}</span>.
|
||||
</span>
|
||||
)}
|
||||
<span>{t("entity.drop_here_to_move", { entity: isEpic ? "epic" : "work item" })}</span>
|
||||
|
||||
@@ -3,7 +3,8 @@ import { ArrowDownWideNarrow, ArrowUpNarrowWide, CheckIcon, ChevronDownIcon, Era
|
||||
// constants
|
||||
import { SPREADSHEET_PROPERTY_DETAILS } from "@plane/constants";
|
||||
// i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// types
|
||||
import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssueOrderByOptions } from "@plane/types";
|
||||
import { CustomMenu, Row } from "@plane/ui";
|
||||
@@ -50,7 +51,9 @@ export function HeaderColumn(props: Props) {
|
||||
<Row className="flex w-full cursor-pointer items-center justify-between gap-1.5 py-2 text-13 text-secondary hover:text-primary">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{<SpreadSheetPropertyIcon iconKey={propertyDetails.icon} className="h-4 w-4 text-placeholder" />}
|
||||
{property === "sub_issue_count" && isEpic ? t("issue.label", { count: 2 }) : t(propertyDetails.i18n_title)}
|
||||
{property === "sub_issue_count" && isEpic
|
||||
? t("issue.label", { count: 2 })
|
||||
: t(propertyDetails.i18n_title as KeysWithoutParams<"translation">)}
|
||||
</div>
|
||||
<div className="ml-3 flex">
|
||||
{activeSortingProperty === property && (
|
||||
|
||||
@@ -6,7 +6,8 @@ import { Sparkle } from "lucide-react";
|
||||
// plane imports
|
||||
import { ETabIndices } from "@plane/constants";
|
||||
import type { EditorRefApi } from "@plane/editor";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { EFileAssetType } from "@plane/types";
|
||||
@@ -190,7 +191,9 @@ export const IssueDescriptionEditor = observer(function IssueDescriptionEditor(p
|
||||
onEnterKeyPress={() => submitBtnRef?.current?.click()}
|
||||
ref={editorRef}
|
||||
tabIndex={getIndex("description_html")}
|
||||
placeholder={(isFocused, description) => t(getDescriptionPlaceholderI18n(isFocused, description))}
|
||||
placeholder={(isFocused, description) =>
|
||||
t(getDescriptionPlaceholderI18n(isFocused, description) as KeysWithoutParams<"translation">)
|
||||
}
|
||||
searchMentionCallback={async (payload) =>
|
||||
await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
|
||||
...payload,
|
||||
|
||||
@@ -5,7 +5,8 @@ import Link from "next/link";
|
||||
import { MoveDiagonal, MoveRight } from "lucide-react";
|
||||
// plane imports
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { CenterPanelIcon, CopyLinkIcon, FullScreenPanelIcon, SidePanelIcon } from "@plane/propel/icons";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
@@ -210,7 +211,7 @@ export const IssuePeekOverviewHeader = observer(function IssuePeekOverviewHeader
|
||||
}`}
|
||||
>
|
||||
<mode.icon className="-my-1 h-4 w-4 flex-shrink-0" />
|
||||
{t(mode.i18n_title)}
|
||||
{t(mode.i18n_title as KeysWithoutParams<"translation">)}
|
||||
</div>
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// ui
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||
@@ -44,7 +45,7 @@ export const WorkspaceDraftIssueQuickActions = observer(function WorkspaceDraftI
|
||||
>
|
||||
{item.icon && <item.icon className={cn("h-3 w-3", item.iconClassName)} />}
|
||||
<div>
|
||||
<h5>{t(item.title || "")}</h5>
|
||||
<h5>{t((item.title || "") as KeysWithoutParams<"translation">)}</h5>
|
||||
{item.description && (
|
||||
<p
|
||||
className={cn("text-tertiary whitespace-pre-line", {
|
||||
|
||||
@@ -5,7 +5,8 @@ import { useSearchParams } from "next/navigation";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
import { EEstimateSystem } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { ChevronUpIcon, ChevronDownIcon } from "@plane/propel/icons";
|
||||
import type { TModulePlotType } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
@@ -134,7 +135,10 @@ export const ModuleAnalyticsProgress = observer(function ModuleAnalyticsProgress
|
||||
value={plotType}
|
||||
label={
|
||||
<span>
|
||||
{t(moduleBurnDownChartOptions.find((v) => v.value === plotType)?.i18n_label || "none")}
|
||||
{t(
|
||||
(moduleBurnDownChartOptions.find((v) => v.value === plotType)?.i18n_label ||
|
||||
"none") as KeysWithoutParams<"translation">
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
onChange={onChange}
|
||||
@@ -142,7 +146,7 @@ export const ModuleAnalyticsProgress = observer(function ModuleAnalyticsProgress
|
||||
>
|
||||
{moduleBurnDownChartOptions.map((item) => (
|
||||
<CustomSelect.Option key={item.value} value={item.value}>
|
||||
{t(item.i18n_label)}
|
||||
{t(item.i18n_label as KeysWithoutParams<"translation">)}
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
</CustomSelect>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TWorkItemFilterCondition } from "@plane/shared-state";
|
||||
import type { TModuleDistribution, TModuleEstimateDistribution, TModulePlotType } from "@plane/types";
|
||||
import { cn, toFilterArray } from "@plane/utils";
|
||||
@@ -135,7 +136,7 @@ export const ModuleProgressStats = observer(function ModuleProgressStats(props:
|
||||
key={stat.key}
|
||||
onClick={() => setModuleTab(stat.key)}
|
||||
>
|
||||
{t(stat.i18n_title)}
|
||||
{t(stat.i18n_title as KeysWithoutParams<"translation">)}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
|
||||
@@ -5,7 +5,8 @@ import { useParams } from "next/navigation";
|
||||
import { GripVertical, X } from "lucide-react";
|
||||
// plane imports
|
||||
import { WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Checkbox, EModalPosition, EModalWidth, ModalCore, Sortable } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
@@ -214,7 +215,7 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
{getSidebarNavigationItemIcon(item.key)}
|
||||
<label className="text-13 text-primary flex-1 cursor-pointer">
|
||||
{t(item.labelTranslationKey)}
|
||||
{t(item.labelTranslationKey as KeysWithoutParams<"translation">)}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -244,7 +245,9 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi
|
||||
/>
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
{icon}
|
||||
<span className="text-13 text-primary">{t(item.labelTranslationKey)}</span>
|
||||
<span className="text-13 text-primary">
|
||||
{t(item.labelTranslationKey as KeysWithoutParams<"translation">)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,8 @@ import React from "react";
|
||||
import { Link } from "react-router";
|
||||
import { MoreHorizontal, Pin } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { SetAsDefaultIcon } from "@plane/propel/icons";
|
||||
import { Menu } from "@plane/propel/menu";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
@@ -48,7 +49,7 @@ export function TabNavigationOverflowMenu({ overflowItems, isActive, tabPreferen
|
||||
<Menu.MenuItem key={`${item.key}-overflow-${itemIsActive ? "active" : "inactive"}`} className="p-0 w-full">
|
||||
<div className="flex items-center justify-between w-full group/menu-item">
|
||||
<Link to={item.href} className="flex-1 min-w-0 w-full p-1">
|
||||
<span className="text-11">{t(item.i18n_key)}</span>
|
||||
<span className="text-11">{t(item.i18n_key as KeysWithoutParams<"translation">)}</span>
|
||||
</Link>
|
||||
<div className="flex items-center">
|
||||
{/* Show Eye icon ONLY for user-hidden items */}
|
||||
|
||||
@@ -3,7 +3,8 @@ import React, { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, useLocation, Link, useNavigate } from "react-router";
|
||||
import { EUserPermissionsLevel, EUserPermissions } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { TabNavigationList, TabNavigationItem } from "@plane/propel/tab-navigation";
|
||||
import type { EUserProjectRoles } from "@plane/types";
|
||||
// hooks
|
||||
@@ -232,7 +233,7 @@ export const TabNavigationRoot = observer(function TabNavigationRoot(props: TTab
|
||||
>
|
||||
<Link to={item.href}>
|
||||
<TabNavigationItem isActive={itemIsActive}>
|
||||
<span>{t(item.i18n_key)}</span>
|
||||
<span>{t(item.i18n_key as KeysWithoutParams<"translation">)}</span>
|
||||
</TabNavigationItem>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Link } from "react-router";
|
||||
import { PinOff } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { ContextMenu } from "@plane/propel/context-menu";
|
||||
import { SetAsDefaultIcon } from "@plane/propel/icons";
|
||||
import { TabNavigationItem } from "@plane/propel/tab-navigation";
|
||||
@@ -43,7 +44,7 @@ export function TabNavigationVisibleItem({
|
||||
<ContextMenu.Trigger>
|
||||
<Link key={`${item.key}-${isActive ? "active" : "inactive"}`} to={item.href}>
|
||||
<TabNavigationItem isActive={isActive}>
|
||||
<span>{t(item.i18n_key)}</span>
|
||||
<span>{t(item.i18n_key as KeysWithoutParams<"translation">)}</span>
|
||||
</TabNavigationItem>
|
||||
</Link>
|
||||
</ContextMenu.Trigger>
|
||||
|
||||
@@ -4,7 +4,8 @@ import type { LucideIcon } from "lucide-react";
|
||||
import { Globe2, Lock } from "lucide-react";
|
||||
// plane imports
|
||||
import { ETabIndices, EPageAccess } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams, PrefixedKeyWithoutParams} from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-icon-picker";
|
||||
import { PageIcon } from "@plane/propel/icons";
|
||||
@@ -23,9 +24,11 @@ type Props = {
|
||||
handleFormSubmit: () => Promise<void>;
|
||||
};
|
||||
|
||||
type I18nKey = KeysWithoutParams<"translation"> | PrefixedKeyWithoutParams;
|
||||
|
||||
const PAGE_ACCESS_SPECIFIERS: {
|
||||
key: EPageAccess;
|
||||
i18n_label: string;
|
||||
i18n_label: I18nKey;
|
||||
icon: LucideIcon;
|
||||
}[] = [
|
||||
{ key: EPageAccess.PUBLIC, i18n_label: "common.access.public", icon: Globe2 },
|
||||
@@ -132,7 +135,7 @@ export function PageForm(props: Props) {
|
||||
accessSpecifiers={PAGE_ACCESS_SPECIFIERS}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
<h6 className="text-11 font-medium">{t(i18n_access_label || "")}</h6>
|
||||
<h6 className="text-11 font-medium">{t((i18n_access_label || "") as KeysWithoutParams<"translation">)}</h6>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button variant="secondary" size="lg" onClick={handleModalClose} tabIndex={getIndex("cancel")}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Tab } from "@headlessui/react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// plane web components
|
||||
import { ORDERED_PAGE_NAVIGATION_TABS_LIST } from "@/plane-web/components/pages/navigation-pane";
|
||||
|
||||
@@ -18,7 +19,7 @@ export function PageNavigationPaneTabsList() {
|
||||
type="button"
|
||||
className="relative z-[1] flex-1 py-1.5 text-13 font-semibold outline-none"
|
||||
>
|
||||
{t(tab.i18n_label)}
|
||||
{t(tab.i18n_label as KeysWithoutParams<"translation">)}
|
||||
</Tab>
|
||||
))}
|
||||
{/* active tab indicator */}
|
||||
|
||||
@@ -24,8 +24,8 @@ export const usePowerKAccountCommands = (): TPowerKCommandConfig[] => {
|
||||
signOut().catch(() =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("sign_out.toast.error.title"),
|
||||
message: t("sign_out.toast.error.message"),
|
||||
title: t("auth.sign_out.toast.error.title"),
|
||||
message: t("auth.sign_out.toast.error.message"),
|
||||
})
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { X } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// local imports
|
||||
import type { TPowerKContextType } from "../../core/types";
|
||||
import { useContextIndicator } from "../../hooks/use-context-indicator";
|
||||
@@ -26,7 +27,9 @@ export function PowerKModalContextIndicator(props: Props) {
|
||||
<div className="w-full px-4 pt-3 pb-2">
|
||||
<div className="max-w-full bg-layer-1 pl-2 pr-1 py-0.5 rounded-sm inline-flex items-center gap-1 truncate">
|
||||
<div className="flex items-center gap-1.5 text-11 font-medium truncate">
|
||||
<span className="shrink-0 text-secondary">{t(contextEntity.i18n_indicator)}</span>
|
||||
<span className="shrink-0 text-secondary">
|
||||
{t(contextEntity.i18n_indicator as KeysWithoutParams<"translation">)}
|
||||
</span>
|
||||
<span className="shrink-0 bg-layer-1 size-1 rounded-full" />
|
||||
<p className="truncate">{contextIndicator}</p>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,8 @@ import React from "react";
|
||||
import { Command } from "cmdk";
|
||||
import { X, Search } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// local imports
|
||||
import type { TPowerKContext, TPowerKPageType } from "../../core/types";
|
||||
import { POWER_K_MODAL_PAGE_DETAILS } from "./constants";
|
||||
@@ -21,7 +22,7 @@ export function PowerKModalHeader(props: Props) {
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const placeholder = activePage
|
||||
? t(POWER_K_MODAL_PAGE_DETAILS[activePage].i18n_placeholder)
|
||||
? t(POWER_K_MODAL_PAGE_DETAILS[activePage].i18n_placeholder as KeysWithoutParams<"translation">)
|
||||
: t("power_k.page_placeholders.default");
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,8 @@ import { observer } from "mobx-react";
|
||||
// plane types
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
// components
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TPowerKContext } from "@/components/power-k/core/types";
|
||||
import { PowerKSettingsMenu } from "@/components/power-k/menus/settings";
|
||||
// hooks
|
||||
@@ -34,7 +35,7 @@ export const PowerKOpenProjectSettingsMenu = observer(function PowerKOpenProject
|
||||
);
|
||||
const settingsListWithIcons = settingsList.map((setting) => ({
|
||||
...setting,
|
||||
label: t(setting.i18n_label),
|
||||
label: t(setting.i18n_label as KeysWithoutParams<"translation">),
|
||||
icon: setting.Icon,
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import { Command } from "cmdk";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// local imports
|
||||
import type { TPowerKCommandConfig, TPowerKCommandGroup, TPowerKContext } from "../../core/types";
|
||||
import { PowerKModalCommandItem } from "../modal/command-item";
|
||||
@@ -45,8 +46,8 @@ export function CommandRenderer(props: Props) {
|
||||
|
||||
const title =
|
||||
groupKey === "contextual" && activeContext
|
||||
? t(CONTEXT_ENTITY_MAP[activeContext].i18n_title)
|
||||
: t(POWER_K_GROUP_I18N_TITLES[groupKey]);
|
||||
? t(CONTEXT_ENTITY_MAP[activeContext].i18n_title as KeysWithoutParams<"translation">)
|
||||
: t(POWER_K_GROUP_I18N_TITLES[groupKey] as KeysWithoutParams<"translation">);
|
||||
|
||||
return (
|
||||
<Command.Group key={groupKey} heading={title}>
|
||||
@@ -55,7 +56,7 @@ export function CommandRenderer(props: Props) {
|
||||
key={command.id}
|
||||
icon={command.icon}
|
||||
iconNode={command.iconNode}
|
||||
label={t(command.i18n_title)}
|
||||
label={t(command.i18n_title as KeysWithoutParams<"translation">)}
|
||||
keySequence={command.keySequence}
|
||||
shortcut={command.shortcut || command.modifierShortcut}
|
||||
onSelect={() => onCommandSelect(command)}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { substringMatch } from "@plane/utils";
|
||||
// components
|
||||
import type { TPowerKCommandConfig, TPowerKCommandGroup } from "@/components/power-k/core/types";
|
||||
@@ -20,7 +21,9 @@ export function ShortcutRenderer(props: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Apply search filter
|
||||
const filteredCommands = commands.filter((command) => substringMatch(t(command.i18n_title), searchQuery));
|
||||
const filteredCommands = commands.filter((command) =>
|
||||
substringMatch(t(command.i18n_title as KeysWithoutParams<"translation">), searchQuery)
|
||||
);
|
||||
|
||||
// Group commands - separate contextual by context type, others by group
|
||||
type GroupedCommands = {
|
||||
@@ -41,7 +44,7 @@ export function ShortcutRenderer(props: Props) {
|
||||
if (!group) {
|
||||
group = {
|
||||
key: contextKey,
|
||||
title: t(CONTEXT_ENTITY_MAP[command.contextType].i18n_title),
|
||||
title: t(CONTEXT_ENTITY_MAP[command.contextType].i18n_title as KeysWithoutParams<"translation">),
|
||||
priority: POWER_K_GROUP_PRIORITY.contextual,
|
||||
commands: [],
|
||||
};
|
||||
@@ -56,7 +59,7 @@ export function ShortcutRenderer(props: Props) {
|
||||
if (!group) {
|
||||
group = {
|
||||
key: groupKey,
|
||||
title: t(POWER_K_GROUP_I18N_TITLES[groupKey as TPowerKCommandGroup]),
|
||||
title: t(POWER_K_GROUP_I18N_TITLES[groupKey as TPowerKCommandGroup] as KeysWithoutParams<"translation">),
|
||||
priority: POWER_K_GROUP_PRIORITY[groupKey as TPowerKCommandGroup],
|
||||
commands: [],
|
||||
};
|
||||
@@ -81,7 +84,9 @@ export function ShortcutRenderer(props: Props) {
|
||||
{group.commands.map((command) => (
|
||||
<div key={command.id} className="mt-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-11 text-secondary text-left">{t(command.i18n_title)}</h4>
|
||||
<h4 className="text-11 text-secondary text-left">
|
||||
{t(command.i18n_title as KeysWithoutParams<"translation">)}
|
||||
</h4>
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
{command.keySequence && <KeySequenceBadge sequence={command.keySequence} />}
|
||||
{(command.shortcut || command.modifierShortcut) && (
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { PREFERENCE_OPTIONS } from "@plane/constants";
|
||||
import { PREFERENCE_COMPONENTS } from "@/plane-web/components/preferences/config";
|
||||
|
||||
type PreferenceComponentKey = keyof typeof PREFERENCE_COMPONENTS;
|
||||
|
||||
export function PreferencesList() {
|
||||
return (
|
||||
<div className="py-6 space-y-6">
|
||||
{PREFERENCE_OPTIONS.map((option) => {
|
||||
const Component = PREFERENCE_COMPONENTS[option.id as keyof typeof PREFERENCE_COMPONENTS];
|
||||
return <Component key={option.id} option={option} />;
|
||||
const key = option.id as PreferenceComponentKey;
|
||||
const Component = PREFERENCE_COMPONENTS[key];
|
||||
if (!Component) return null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return <Component key={option.id} option={option as any} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,8 @@ import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
// ui
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { UserCirclePropertyIcon, CreateIcon, LayerStackIcon } from "@plane/propel/icons";
|
||||
import type { IUserProfileData } from "@plane/types";
|
||||
import { Loader, Card, ECardSpacing, ECardDirection } from "@plane/ui";
|
||||
@@ -50,7 +51,7 @@ export function ProfileStats({ userProfile }: Props) {
|
||||
<card.icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<p className="text-13 text-placeholder">{t(card.i18n_title)}</p>
|
||||
<p className="text-13 text-placeholder">{t(card.i18n_title as KeysWithoutParams<"translation">)}</p>
|
||||
<p className="text-18 font-semibold">{card.value}</p>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -9,7 +9,8 @@ import { Disclosure, Transition } from "@headlessui/react";
|
||||
// plane helpers
|
||||
import { useOutsideClickDetector } from "@plane/hooks";
|
||||
// types
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Logo } from "@plane/propel/emoji-icon-picker";
|
||||
import { ChevronDownIcon } from "@plane/propel/icons";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
@@ -133,7 +134,9 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||
<div className="mt-6 space-y-5">
|
||||
{userDetails.map((detail) => (
|
||||
<div key={detail.i18n_label} className="flex items-center gap-4 text-13">
|
||||
<div className="w-2/5 flex-shrink-0 text-secondary">{t(detail.i18n_label)}</div>
|
||||
<div className="w-2/5 flex-shrink-0 text-secondary">
|
||||
{t(detail.i18n_label as KeysWithoutParams<"translation">)}
|
||||
</div>
|
||||
<div className="w-3/5 break-words font-medium">{detail.value}</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { observer } from "mobx-react";
|
||||
// constants
|
||||
import { NETWORK_CHOICES } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { CloseIcon } from "@plane/propel/icons";
|
||||
|
||||
type Props = {
|
||||
@@ -20,7 +21,7 @@ export const AppliedAccessFilters = observer(function AppliedAccessFilters(props
|
||||
const accessDetails = NETWORK_CHOICES.find((s) => `${s.key}` === status);
|
||||
return (
|
||||
<div key={status} className="flex items-center gap-1 rounded-sm px-1.5 py-1 text-11 bg-layer-1">
|
||||
{accessDetails && t(accessDetails?.i18n_label)}
|
||||
{accessDetails && t(accessDetails?.i18n_label as KeysWithoutParams<"translation">)}
|
||||
{editable && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -92,7 +92,7 @@ export function ProjectAppliedFiltersList(props: Props) {
|
||||
{/* Applied display filters */}
|
||||
{appliedDisplayFilters.length > 0 && (
|
||||
<Tag key="project_display_filters">
|
||||
<span className="text-11 text-tertiary">{t("projects.label", { count: 2 })}</span>
|
||||
<span className="text-11 text-tertiary">{t("workspace_projects.label", { count: 2 })}</span>
|
||||
<AppliedProjectDisplayFilters
|
||||
editable={isEditingAllowed}
|
||||
values={appliedDisplayFilters}
|
||||
|
||||
@@ -2,7 +2,8 @@ import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { NETWORK_CHOICES } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
|
||||
// local imports
|
||||
@@ -39,7 +40,7 @@ export const FilterAccess = observer(function FilterAccess(props: Props) {
|
||||
isChecked={appliedFilters?.includes(`${access.key}`) ? true : false}
|
||||
onClick={() => handleUpdate(`${access.key}`)}
|
||||
icon={<ProjectNetworkIcon iconKey={access.iconKey} />}
|
||||
title={t(access.i18n_label)}
|
||||
title={t(access.i18n_label as KeysWithoutParams<"translation">)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { Info, Lock } from "lucide-react";
|
||||
import { NETWORK_CHOICES, PROJECT_TRACKER_ELEMENTS, PROJECT_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// plane imports
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-icon-picker";
|
||||
@@ -249,7 +250,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||
<span>{watch("identifier")} .</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
{project.network === 0 && <Lock className="h-2.5 w-2.5 text-on-color " />}
|
||||
{currentNetwork && t(currentNetwork?.i18n_label)}
|
||||
{currentNetwork && t(currentNetwork?.i18n_label as KeysWithoutParams<"translation">)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -386,7 +387,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||
{selectedNetwork ? (
|
||||
<>
|
||||
<ProjectNetworkIcon iconKey={selectedNetwork.iconKey} className="h-3.5 w-3.5" />
|
||||
{t(selectedNetwork.i18n_label)}
|
||||
{t(selectedNetwork.i18n_label as KeysWithoutParams<"translation">)}
|
||||
</>
|
||||
) : (
|
||||
<span className="text-placeholder">{t("select_network")}</span>
|
||||
@@ -403,8 +404,10 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||
<div className="flex items-start gap-2">
|
||||
<ProjectNetworkIcon iconKey={network.iconKey} className="h-3.5 w-3.5" />
|
||||
<div className="-mt-1">
|
||||
<p>{t(network.i18n_label)}</p>
|
||||
<p className="text-11 text-placeholder">{t(network.description)}</p>
|
||||
<p>{t(network.i18n_label as KeysWithoutParams<"translation">)}</p>
|
||||
<p className="text-11 text-placeholder">
|
||||
{t(network.description as KeysWithoutParams<"translation">)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CustomSelect.Option>
|
||||
|
||||
@@ -5,7 +5,8 @@ import { ArrowDownWideNarrow, ArrowUpNarrowWide, CheckIcon, ChevronDownIcon, Era
|
||||
import type { IProjectMemberDisplayProperties, TMemberOrderByOptions } from "@plane/constants";
|
||||
import { MEMBER_PROPERTY_DETAILS } from "@plane/constants";
|
||||
// i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// types
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
import type { IMemberFilters } from "@/store/member/utils";
|
||||
@@ -42,7 +43,7 @@ export const MemberHeaderColumn = observer(function MemberHeaderColumn(props: Pr
|
||||
className="!w-full"
|
||||
customButton={
|
||||
<div className="flex w-full cursor-pointer items-center justify-between gap-1.5 py-2 text-13 text-secondary hover:text-primary">
|
||||
<span>{t(propertyDetails.i18n_title)}</span>
|
||||
<span>{t(propertyDetails.i18n_title as KeysWithoutParams<"translation">)}</span>
|
||||
<div className="ml-3 flex">
|
||||
{(activeSortingProperty === propertyDetails.ascendingOrderKey ||
|
||||
activeSortingProperty === propertyDetails.descendingOrderKey) && (
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { PROJECT_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { setPromiseToast } from "@plane/propel/toast";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import type { IProject } from "@plane/types";
|
||||
@@ -69,7 +70,10 @@ export const ProjectFeaturesList = observer(function ProjectFeaturesList(props:
|
||||
<div className="space-y-6">
|
||||
{Object.entries(PROJECT_FEATURES_LIST).map(([featureSectionKey, feature]) => (
|
||||
<div key={featureSectionKey} className="">
|
||||
<SettingsHeading title={t(feature.key)} description={t(`${feature.key}_description`)} />
|
||||
<SettingsHeading
|
||||
title={t(feature.key as KeysWithoutParams<"translation">)}
|
||||
description={t(`${feature.key}_description` as KeysWithoutParams<"translation">)}
|
||||
/>
|
||||
{Object.entries(feature.featureList).map(([featureItemKey, featureItem]) => (
|
||||
<div key={featureItemKey} className="gap-x-8 gap-y-2 border-b border-subtle bg-surface-1 py-4">
|
||||
<div key={featureItemKey} className="flex items-center justify-between">
|
||||
@@ -77,7 +81,9 @@ export const ProjectFeaturesList = observer(function ProjectFeaturesList(props:
|
||||
<div className="flex items-center justify-center rounded-sm bg-surface-2 p-3">{featureItem.icon}</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h4 className="text-13 font-medium leading-5">{t(featureItem.key)}</h4>
|
||||
<h4 className="text-13 font-medium leading-5">
|
||||
{t(featureItem.key as KeysWithoutParams<"translation">)}
|
||||
</h4>
|
||||
{featureItem.isPro && (
|
||||
<Tooltip tooltipContent="Pro feature" position="top">
|
||||
<UpgradeBadge className="rounded-sm" />
|
||||
@@ -85,7 +91,7 @@ export const ProjectFeaturesList = observer(function ProjectFeaturesList(props:
|
||||
)}
|
||||
</div>
|
||||
<p className="text-13 leading-5 tracking-tight text-tertiary">
|
||||
{t(`${featureItem.key}_description`)}
|
||||
{t(`${featureItem.key}_description` as KeysWithoutParams<"translation">)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useRef } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Menu } from "lucide-react";
|
||||
import { useOutsideClickDetector } from "@plane/hooks";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { ChevronRightIcon } from "@plane/propel/icons";
|
||||
import { useUserSettings } from "@/hooks/store/user";
|
||||
|
||||
@@ -39,7 +40,9 @@ export const SettingsMobileNav = observer(function SettingsMobileNav(props: Prop
|
||||
{/* path */}
|
||||
<div className="flex items-center gap-2">
|
||||
<ChevronRightIcon className="size-4 text-tertiary" />
|
||||
<span className="text-13 font-medium text-secondary">{t(activePath)}</span>
|
||||
<span className="text-13 font-medium text-secondary">
|
||||
{t(activePath as KeysWithoutParams<"translation">)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,8 @@ import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { usePathname, useParams } from "next/navigation";
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Loader } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
@@ -66,7 +67,9 @@ export const NavItemChildren = observer(function NavItemChildren(props: { projec
|
||||
"text-11 font-medium"
|
||||
)}
|
||||
>
|
||||
{t(getProjectSettingsPageLabelI18nKey(link.key, link.i18n_label))}
|
||||
{t(
|
||||
getProjectSettingsPageLabelI18nKey(link.key, link.i18n_label) as KeysWithoutParams<"translation">
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
|
||||
@@ -4,7 +4,8 @@ import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { EUserWorkspaceRoles } from "@plane/types";
|
||||
import { cn, joinUrlPath } from "@plane/utils";
|
||||
// hooks
|
||||
@@ -49,7 +50,7 @@ const SettingsSidebarNavItem = observer(function SettingsSidebarNavItem(props: T
|
||||
{setting.icon
|
||||
? setting.icon
|
||||
: actionIcons && actionIcons({ type: setting.key, size: 16, className: "w-4 h-4" })}
|
||||
<div className="text-13 font-medium truncate">{t(setting.i18n_label)}</div>
|
||||
<div className="text-13 font-medium truncate">{t(setting.i18n_label as KeysWithoutParams<"translation">)}</div>
|
||||
</div>
|
||||
{appendItemsToTitle?.(setting.key)}
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { cn } from "@plane/utils";
|
||||
import { SettingsSidebarHeader } from "./header";
|
||||
import type { TSettingItem } from "./nav-item";
|
||||
@@ -51,7 +52,9 @@ export const SettingsSidebar = observer(function SettingsSidebar(props: Settings
|
||||
if (groupedSettings[category].length === 0) return null;
|
||||
return (
|
||||
<div key={category} className="py-3">
|
||||
<span className="text-13 font-semibold text-tertiary capitalize mb-2 px-2">{t(category)}</span>
|
||||
<span className="text-13 font-semibold text-tertiary capitalize mb-2 px-2">
|
||||
{t(category as KeysWithoutParams<"translation">)}
|
||||
</span>
|
||||
<div className="relative flex flex-col gap-0.5 h-full mt-2">
|
||||
{groupedSettings[category].map(
|
||||
(setting) =>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { observer } from "mobx-react";
|
||||
// icons
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { CloseIcon } from "@plane/propel/icons";
|
||||
// constants
|
||||
// helpers
|
||||
@@ -31,7 +32,7 @@ export const AppliedAccessFilters = observer(function AppliedAccessFilters(props
|
||||
|
||||
return (
|
||||
<div key={access} className="flex items-center gap-1 rounded-sm bg-layer-1 py-1 px-1.5 text-11">
|
||||
<span className="normal-case">{t(label)}</span>
|
||||
<span className="normal-case">{t(label as KeysWithoutParams<"translation">)}</span>
|
||||
{editable && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// types
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { TWebhookEventTypes } from "@plane/types";
|
||||
|
||||
type Props = {
|
||||
@@ -36,7 +37,7 @@ export function WebhookOptions(props: Props) {
|
||||
onChange={() => onChange(option.key)}
|
||||
/>
|
||||
<label className="text-13" htmlFor={option.key}>
|
||||
{t(option.i18n_label)}
|
||||
{t(option.i18n_label as KeysWithoutParams<"translation">)}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ export const NotificationItemSnoozeOption = observer(function NotificationItemSn
|
||||
await unSnoozeNotification(workspaceSlug);
|
||||
setToast({
|
||||
title: `${t("common.success")}!`,
|
||||
message: t("notification.toasts.un_snoozed"),
|
||||
message: t("notification.toasts.unsnoozed"),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
WORKSPACE_TRACKER_ELEMENTS,
|
||||
WORKSPACE_TRACKER_EVENTS,
|
||||
} from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { IWorkspace } from "@plane/types";
|
||||
@@ -256,7 +257,9 @@ export const CreateWorkspaceForm = observer(function CreateWorkspaceForm(props:
|
||||
disabled={!isValid}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? t(primaryButtonText.loading) : t(primaryButtonText.default)}
|
||||
{isSubmitting
|
||||
? t(primaryButtonText.loading as KeysWithoutParams<"translation">)
|
||||
: t(primaryButtonText.default as KeysWithoutParams<"translation">)}
|
||||
</Button>
|
||||
{!secondaryButton && (
|
||||
<Button variant="secondary" type="button" size="xl" onClick={() => router.back()}>
|
||||
|
||||
@@ -3,7 +3,8 @@ import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { EUserPermissionsLevel, EUserPermissions } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { CycleIcon, IntakeIcon, ModuleIcon, PageIcon, ViewsIcon, WorkItemsIcon } from "@plane/propel/icons";
|
||||
import type { EUserProjectRoles } from "@plane/types";
|
||||
// plane ui
|
||||
@@ -176,7 +177,7 @@ export const ProjectNavigation = observer(function ProjectNavigation(props: TPro
|
||||
<SidebarNavItem isActive={!!isActive(item)}>
|
||||
<div className="flex items-center gap-1.5 py-[1px]">
|
||||
<item.icon className={`flex-shrink-0 size-4 ${item.name === "Intake" ? "stroke-1" : "stroke-[1.5]"}`} />
|
||||
<span className="text-11 font-medium">{t(item.i18n_key)}</span>
|
||||
<span className="text-11 font-medium">{t(item.i18n_key as KeysWithoutParams<"translation">)}</span>
|
||||
</div>
|
||||
</SidebarNavItem>
|
||||
</Link>
|
||||
|
||||
@@ -5,7 +5,8 @@ import { useParams, usePathname } from "next/navigation";
|
||||
// plane imports
|
||||
import type { IWorkspaceSidebarNavigationItem } from "@plane/constants";
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { joinUrlPath } from "@plane/utils";
|
||||
// components
|
||||
import { SidebarNavItem } from "@/components/sidebar/sidebar-navigation";
|
||||
@@ -66,7 +67,9 @@ export const SidebarItemBase = observer(function SidebarItemBase({
|
||||
<SidebarNavItem isActive={item.highlight(pathname, itemHref)}>
|
||||
<div className="flex items-center gap-1.5 py-[1px]">
|
||||
{icon}
|
||||
<p className="text-13 leading-5 font-medium">{t(item.labelTranslationKey)}</p>
|
||||
<p className="text-13 leading-5 font-medium">
|
||||
{t(item.labelTranslationKey as KeysWithoutParams<"translation">)}
|
||||
</p>
|
||||
</div>
|
||||
{additionalRender?.(item.key, slug)}
|
||||
</SidebarNavItem>
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useParams, usePathname } from "next/navigation";
|
||||
// plane imports
|
||||
import { EUserPermissionsLevel, SIDEBAR_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { EUserWorkspaceRoles } from "@plane/types";
|
||||
// components
|
||||
import { SidebarNavItem } from "@/components/sidebar/sidebar-navigation";
|
||||
@@ -61,7 +62,9 @@ export const SidebarUserMenuItem = observer(function SidebarUserMenuItem(props:
|
||||
<SidebarNavItem isActive={isActive}>
|
||||
<div className="flex items-center gap-1.5 py-[1px]">
|
||||
<item.Icon className="size-4 flex-shrink-0" />
|
||||
<p className="text-13 leading-5 font-medium">{t(item.labelTranslationKey)}</p>
|
||||
<p className="text-13 leading-5 font-medium">
|
||||
{t(item.labelTranslationKey as KeysWithoutParams<"translation">)}
|
||||
</p>
|
||||
</div>
|
||||
{item.key === "notifications" && <NotificationAppSidebarOption workspaceSlug={workspaceSlug.toString()} />}
|
||||
</SidebarNavItem>
|
||||
|
||||
@@ -38,8 +38,8 @@ export const UserMenuRoot = observer(function UserMenuRoot(props: Props) {
|
||||
await signOut().catch(() =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("sign_out.toast.error.title"),
|
||||
message: t("sign_out.toast.error.message"),
|
||||
title: t("auth.sign_out.toast.error.title"),
|
||||
message: t("auth.sign_out.toast.error.message"),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@ import Link from "next/link";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
// plane imports
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import type { EUserWorkspaceRoles } from "@plane/types";
|
||||
import { cn } from "@plane/utils";
|
||||
// components
|
||||
@@ -58,7 +59,9 @@ export const SidebarWorkspaceMenuItem = observer(function SidebarWorkspaceMenuIt
|
||||
"rotate-180": item.key === "active_cycles",
|
||||
})}
|
||||
/>
|
||||
<p className="text-13 leading-5 font-medium">{t(item.labelTranslationKey)}</p>
|
||||
<p className="text-13 leading-5 font-medium">
|
||||
{t(item.labelTranslationKey as KeysWithoutParams<"translation">)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0">
|
||||
<UpgradeBadge />
|
||||
|
||||
@@ -49,8 +49,8 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work
|
||||
await signOut().catch(() =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("sign_out.toast.error.title"),
|
||||
message: t("sign_out.toast.error.message"),
|
||||
title: t("auth.sign_out.toast.error.title"),
|
||||
message: t("auth.sign_out.toast.error.message"),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
// helpers
|
||||
import { truncateText } from "@plane/utils";
|
||||
|
||||
@@ -20,7 +21,9 @@ export const GlobalDefaultViewListItem = observer(function GlobalDefaultViewList
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex flex-col">
|
||||
<p className="truncate text-13 font-medium leading-4">{truncateText(t(view.i18n_label), 75)}</p>
|
||||
<p className="truncate text-13 font-medium leading-4">
|
||||
{truncateText(t(view.i18n_label as KeysWithoutParams<"translation">), 75)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { ExternalLink, LinkIcon } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type {KeysWithoutParams} from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
// ui
|
||||
import type { TStaticViewTypes } from "@plane/types";
|
||||
@@ -75,7 +76,7 @@ export const DefaultWorkspaceViewQuickActions = observer(function DefaultWorkspa
|
||||
>
|
||||
{item.icon && <item.icon className={cn("h-3 w-3", item.iconClassName)} />}
|
||||
<div>
|
||||
<h5>{t(item.title || "")}</h5>
|
||||
<h5>{t((item.title || "") as KeysWithoutParams<"translation">)}</h5>
|
||||
{item.description && (
|
||||
<p
|
||||
className={cn("text-tertiary whitespace-pre-line", {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"preview": "react-router build && serve -s build/client -l 3000",
|
||||
"start": "serve -s build/client -l 3000",
|
||||
"clean": "rm -rf .turbo && rm -rf .next && rm -rf .react-router && rm -rf node_modules && rm -rf dist && rm -rf build",
|
||||
"check:i18n": "tsx ../../packages/i18n/scripts/check-app-keys.ts",
|
||||
"check:lint": "eslint . --cache --cache-location node_modules/.cache/eslint/ --max-warnings=14367",
|
||||
"check:types": "react-router typegen && tsc --noEmit",
|
||||
"check:format": "prettier . --cache --check",
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.mts",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/index.mjs",
|
||||
".": "./dist/index.js",
|
||||
"./package.json": "./package.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +32,10 @@ export enum EChartModels {
|
||||
PROGRESS = "PROGRESS",
|
||||
}
|
||||
|
||||
export const CHART_COLOR_PALETTES: {
|
||||
key: TChartColorScheme;
|
||||
i18n_label: string;
|
||||
light: string[];
|
||||
dark: string[];
|
||||
}[] = [
|
||||
export const CHART_COLOR_PALETTES = [
|
||||
{
|
||||
key: "modern",
|
||||
i18n_label: "dashboards.widget.color_palettes.modern",
|
||||
i18n_label: "dashboards.widget.color_palettes.modern" as const,
|
||||
light: [
|
||||
"#6172E8",
|
||||
"#8B6EDB",
|
||||
@@ -68,7 +63,7 @@ export const CHART_COLOR_PALETTES: {
|
||||
},
|
||||
{
|
||||
key: "horizon",
|
||||
i18n_label: "dashboards.widget.color_palettes.horizon",
|
||||
i18n_label: "dashboards.widget.color_palettes.horizon" as const,
|
||||
light: [
|
||||
"#E76E50",
|
||||
"#289D90",
|
||||
@@ -96,7 +91,7 @@ export const CHART_COLOR_PALETTES: {
|
||||
},
|
||||
{
|
||||
key: "earthen",
|
||||
i18n_label: "dashboards.widget.color_palettes.earthen",
|
||||
i18n_label: "dashboards.widget.color_palettes.earthen" as const,
|
||||
light: [
|
||||
"#386641",
|
||||
"#6A994E",
|
||||
|
||||
@@ -1,40 +1,33 @@
|
||||
// types
|
||||
export const CYCLE_STATUS: {
|
||||
i18n_label: string;
|
||||
value: "current" | "upcoming" | "completed" | "draft";
|
||||
i18n_title: string;
|
||||
color: string;
|
||||
textColor: string;
|
||||
bgColor: string;
|
||||
}[] = [
|
||||
export const CYCLE_STATUS = [
|
||||
{
|
||||
i18n_label: "project_cycles.status.days_left",
|
||||
value: "current",
|
||||
i18n_title: "project_cycles.status.in_progress",
|
||||
i18n_label: "project_cycles.status.days_left" as const,
|
||||
value: "current" as const,
|
||||
i18n_title: "project_cycles.status.in_progress" as const,
|
||||
color: "#F59E0B",
|
||||
textColor: "text-amber-500",
|
||||
bgColor: "bg-amber-50",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_cycles.status.yet_to_start",
|
||||
value: "upcoming",
|
||||
i18n_title: "project_cycles.status.yet_to_start",
|
||||
i18n_label: "project_cycles.status.yet_to_start" as const,
|
||||
value: "upcoming" as const,
|
||||
i18n_title: "project_cycles.status.yet_to_start" as const,
|
||||
color: "#3F76FF",
|
||||
textColor: "text-blue-500",
|
||||
bgColor: "bg-indigo-50",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_cycles.status.completed",
|
||||
value: "completed",
|
||||
i18n_title: "project_cycles.status.completed",
|
||||
i18n_label: "project_cycles.status.completed" as const,
|
||||
value: "completed" as const,
|
||||
i18n_title: "project_cycles.status.completed" as const,
|
||||
color: "#16A34A",
|
||||
textColor: "text-green-600",
|
||||
bgColor: "bg-green-50",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_cycles.status.draft",
|
||||
value: "draft",
|
||||
i18n_title: "project_cycles.status.draft",
|
||||
i18n_label: "project_cycles.status.draft" as const,
|
||||
value: "draft" as const,
|
||||
i18n_title: "project_cycles.status.draft" as const,
|
||||
color: "#525252",
|
||||
textColor: "text-tertiary",
|
||||
bgColor: "bg-surface-2",
|
||||
|
||||
@@ -23,11 +23,11 @@ export const estimateCount = {
|
||||
export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
points: {
|
||||
name: "Points",
|
||||
i18n_name: "project_settings.estimates.systems.points.label",
|
||||
i18n_name: "project_settings.estimates.systems.points.label" as const,
|
||||
templates: {
|
||||
fibonacci: {
|
||||
title: "Fibonacci",
|
||||
i18n_title: "project_settings.estimates.systems.points.fibonacci",
|
||||
i18n_title: "project_settings.estimates.systems.points.fibonacci" as const,
|
||||
values: [
|
||||
{ id: undefined, key: 1, value: "1" },
|
||||
{ id: undefined, key: 2, value: "2" },
|
||||
@@ -39,7 +39,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
linear: {
|
||||
title: "Linear",
|
||||
i18n_title: "project_settings.estimates.systems.points.linear",
|
||||
i18n_title: "project_settings.estimates.systems.points.linear" as const,
|
||||
values: [
|
||||
{ id: undefined, key: 1, value: "1" },
|
||||
{ id: undefined, key: 2, value: "2" },
|
||||
@@ -51,7 +51,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
squares: {
|
||||
title: "Squares",
|
||||
i18n_title: "project_settings.estimates.systems.points.squares",
|
||||
i18n_title: "project_settings.estimates.systems.points.squares" as const,
|
||||
values: [
|
||||
{ id: undefined, key: 1, value: "1" },
|
||||
{ id: undefined, key: 2, value: "4" },
|
||||
@@ -63,7 +63,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
custom: {
|
||||
title: "Custom",
|
||||
i18n_title: "project_settings.estimates.systems.points.custom",
|
||||
i18n_title: "project_settings.estimates.systems.points.custom" as const,
|
||||
values: [
|
||||
{ id: undefined, key: 1, value: "1" },
|
||||
{ id: undefined, key: 2, value: "2" },
|
||||
@@ -76,11 +76,11 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
categories: {
|
||||
name: "Categories",
|
||||
i18n_name: "project_settings.estimates.systems.categories.label",
|
||||
i18n_name: "project_settings.estimates.systems.categories.label" as const,
|
||||
templates: {
|
||||
t_shirt_sizes: {
|
||||
title: "T-Shirt Sizes",
|
||||
i18n_title: "project_settings.estimates.systems.categories.t_shirt_sizes",
|
||||
i18n_title: "project_settings.estimates.systems.categories.t_shirt_sizes" as const,
|
||||
values: [
|
||||
{ id: undefined, key: 1, value: "XS" },
|
||||
{ id: undefined, key: 2, value: "S" },
|
||||
@@ -92,7 +92,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
easy_to_hard: {
|
||||
title: "Easy to hard",
|
||||
i18n_title: "project_settings.estimates.systems.categories.easy_to_hard",
|
||||
i18n_title: "project_settings.estimates.systems.categories.easy_to_hard" as const,
|
||||
values: [
|
||||
{ id: undefined, key: 1, value: "Easy" },
|
||||
{ id: undefined, key: 2, value: "Medium" },
|
||||
@@ -102,7 +102,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
custom: {
|
||||
title: "Custom",
|
||||
i18n_title: "project_settings.estimates.systems.categories.custom",
|
||||
i18n_title: "project_settings.estimates.systems.categories.custom" as const,
|
||||
values: [
|
||||
{ id: undefined, key: 1, value: "Easy" },
|
||||
{ id: undefined, key: 2, value: "Hard" },
|
||||
@@ -115,11 +115,11 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
time: {
|
||||
name: "Time",
|
||||
i18n_name: "project_settings.estimates.systems.time.label",
|
||||
i18n_name: "project_settings.estimates.systems.time.label" as const,
|
||||
templates: {
|
||||
hours: {
|
||||
title: "Hours",
|
||||
i18n_title: "project_settings.estimates.systems.time.hours",
|
||||
i18n_title: "project_settings.estimates.systems.time.hours" as const,
|
||||
values: [
|
||||
{ id: undefined, key: 1, value: "1" },
|
||||
{ id: undefined, key: 2, value: "2" },
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user