[WEB-2870]feat: language support (#6215)
* fix: adding language support package * fix: language support implementation using mobx * fix: adding more languages for support * fix: profile settings translations * feat: added language support for sidebar and user settings * feat: added language support for deactivation modal * fix: added project sync after transfer issues (#6200) * code refactor and improvement (#6203) * chore: package code refactoring * chore: component restructuring and refactor * chore: comment create improvement * refactor: enhance workspace and project wrapper modularity (#6207) * [WEB-2678]feat: added functionality to add labels directly from dropdown (#6211) * enhancement:added functionality to add features directly from dropdown * fix: fixed import order * fix: fixed lint errors * chore: added common component for project activity (#6212) * chore: added common component for project activity * fix: added enum * fix: added enum for initiatives * - Do not clear temp files that are locked. (#6214) - Handle edge cases in sync workspace * fix: labels empty state for drop down (#6216) * refactor: remove cn helper function from the editor package (#6217) * * feat: added language support to issue create modal in sidebar * fix: project activity type * * fix: added missing translations * fix: modified translation for plurals * fix: fixed spanish translation * dev: language type error in space user profile types * fix: type fixes * chore: added alpha tag --------- Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: Akshita Goyal <36129505+gakshita@users.noreply.github.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com> Co-authored-by: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Co-authored-by: gurusinath <gurusainath007@gmail.com>
This commit is contained in:
parent
ade0aa1643
commit
873e4330bc
84 changed files with 2588 additions and 873 deletions
|
|
@ -3,6 +3,7 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// types
|
||||
import { EIssuesStoreType } from "@plane/constants";
|
||||
import type { TBaseIssue, TIssue } from "@plane/types";
|
||||
|
|
@ -54,6 +55,7 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
|
|||
const [uploadedAssetIds, setUploadedAssetIds] = useState<string[]>([]);
|
||||
const [isDuplicateModalOpen, setIsDuplicateModalOpen] = useState(false);
|
||||
// store hooks
|
||||
const { t } = useTranslation();
|
||||
const { captureIssueEvent } = useEventTracker();
|
||||
const { workspaceSlug, projectId: routerProjectId, cycleId, moduleId } = useParams();
|
||||
const { projectsWithCreatePermissions } = useUser();
|
||||
|
|
@ -218,8 +220,8 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
|
|||
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
message: `${is_draft_issue ? "Draft created." : "Issue created successfully."} `,
|
||||
title: t("success"),
|
||||
message: `${is_draft_issue ? t("draft_created") : t("issue_created_successfully")} `,
|
||||
actionItems: !is_draft_issue && response?.project_id && (
|
||||
<CreateIssueToastActionItems
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
|
|
@ -241,8 +243,8 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
|
|||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: `${is_draft_issue ? "Draft issue" : "Issue"} could not be created. Please try again.`,
|
||||
title: t("error"),
|
||||
message: t(is_draft_issue ? "draft_creation_failed" : "issue_creation_failed"),
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_CREATED,
|
||||
|
|
@ -287,8 +289,8 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
|
|||
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
message: "Issue updated successfully.",
|
||||
title: t("success"),
|
||||
message: t("issue_updated_successfully"),
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
|
|
@ -300,8 +302,8 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
|
|||
console.error(error);
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Issue could not be updated. Please try again.",
|
||||
title: t("error"),
|
||||
message: t("issue_could_not_be_updated"),
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import React, { useState } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { Control, Controller } from "react-hook-form";
|
||||
import { LayoutPanelTop } from "lucide-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// types
|
||||
import { ISearchIssueResponse, TIssue } from "@plane/types";
|
||||
// ui
|
||||
|
|
@ -65,6 +66,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
// states
|
||||
const [parentIssueListModalOpen, setParentIssueListModalOpen] = useState(false);
|
||||
// store hooks
|
||||
const { t } = useTranslation();
|
||||
const { areEstimateEnabledByProjectId } = useProjectEstimates();
|
||||
const { getProjectById } = useProject();
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
|
@ -133,7 +135,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
}}
|
||||
buttonVariant={value?.length > 0 ? "transparent-without-text" : "border-with-text"}
|
||||
buttonClassName={value?.length > 0 ? "hover:bg-transparent" : ""}
|
||||
placeholder="Assignees"
|
||||
placeholder={t("assignees")}
|
||||
multiple
|
||||
tabIndex={getIndex("assignee_ids")}
|
||||
/>
|
||||
|
|
@ -172,7 +174,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
}}
|
||||
buttonVariant="border-with-text"
|
||||
maxDate={maxDate ?? undefined}
|
||||
placeholder="Start date"
|
||||
placeholder={t("start_date")}
|
||||
tabIndex={getIndex("start_date")}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -191,7 +193,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
}}
|
||||
buttonVariant="border-with-text"
|
||||
minDate={minDate ?? undefined}
|
||||
placeholder="Due date"
|
||||
placeholder={t("due_date")}
|
||||
tabIndex={getIndex("target_date")}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -209,7 +211,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
onChange(cycleId);
|
||||
handleFormChange();
|
||||
}}
|
||||
placeholder="Cycle"
|
||||
placeholder={t("cycle")}
|
||||
value={value}
|
||||
buttonVariant="border-with-text"
|
||||
tabIndex={getIndex("cycle_id")}
|
||||
|
|
@ -231,7 +233,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
onChange(moduleIds);
|
||||
handleFormChange();
|
||||
}}
|
||||
placeholder="Modules"
|
||||
placeholder={t("modules")}
|
||||
buttonVariant="border-with-text"
|
||||
tabIndex={getIndex("module_ids")}
|
||||
multiple
|
||||
|
|
@ -256,7 +258,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
projectId={projectId}
|
||||
buttonVariant="border-with-text"
|
||||
tabIndex={getIndex("estimate_point")}
|
||||
placeholder="Estimate"
|
||||
placeholder={t("estimate")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -288,7 +290,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
>
|
||||
<>
|
||||
<CustomMenu.MenuItem className="!p-1" onClick={() => setParentIssueListModalOpen(true)}>
|
||||
Change parent issue
|
||||
{t("change_parent_issue")}
|
||||
</CustomMenu.MenuItem>
|
||||
<Controller
|
||||
control={control}
|
||||
|
|
@ -301,7 +303,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
handleFormChange();
|
||||
}}
|
||||
>
|
||||
Remove parent issue
|
||||
{t("remove_parent_issue")}
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -314,7 +316,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
onClick={() => setParentIssueListModalOpen(true)}
|
||||
>
|
||||
<LayoutPanelTop className="h-3 w-3 flex-shrink-0" />
|
||||
<span className="whitespace-nowrap">Add parent</span>
|
||||
<span className="whitespace-nowrap">{t("add_parent")}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import React from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { Control, Controller, FieldErrors } from "react-hook-form";
|
||||
// types
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TIssue } from "@plane/types";
|
||||
// ui
|
||||
import { Input } from "@plane/ui";
|
||||
|
|
@ -25,12 +26,13 @@ export const IssueTitleInput: React.FC<TIssueTitleInputProps> = observer((props)
|
|||
const { control, issueTitleRef, errors, handleFormChange } = props;
|
||||
// store hooks
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { getIndex } = getTabIndex(ETabIndices.ISSUE_FORM, isMobile);
|
||||
|
||||
const validateWhitespace = (value: string) => {
|
||||
if (value.trim() === "") {
|
||||
return "Title is required";
|
||||
return t("title_is_required");
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
|
@ -41,10 +43,10 @@ export const IssueTitleInput: React.FC<TIssueTitleInputProps> = observer((props)
|
|||
name="name"
|
||||
rules={{
|
||||
validate: validateWhitespace,
|
||||
required: "Title is required",
|
||||
required: t("title_is_required"),
|
||||
maxLength: {
|
||||
value: 255,
|
||||
message: "Title should be less than 255 characters",
|
||||
message: t("title_should_be_less_than_255_characters"),
|
||||
},
|
||||
}}
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
|
|
@ -59,7 +61,7 @@ export const IssueTitleInput: React.FC<TIssueTitleInputProps> = observer((props)
|
|||
}}
|
||||
ref={issueTitleRef || ref}
|
||||
hasError={Boolean(errors.name)}
|
||||
placeholder="Title"
|
||||
placeholder={t("title")}
|
||||
className="w-full text-base"
|
||||
tabIndex={getIndex("name")}
|
||||
autoFocus
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import { useForm } from "react-hook-form";
|
|||
// editor
|
||||
import { EIssuesStoreType } from "@plane/constants";
|
||||
import { EditorRefApi } from "@plane/editor";
|
||||
// i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// types
|
||||
import type { TIssue, ISearchIssueResponse, TWorkspaceDraftIssue } from "@plane/types";
|
||||
// hooks
|
||||
|
|
@ -77,6 +79,7 @@ export interface IssueFormProps {
|
|||
}
|
||||
|
||||
export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
data,
|
||||
issueTitleRef,
|
||||
|
|
@ -89,10 +92,10 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
onCreateMoreToggleChange,
|
||||
isDraft,
|
||||
moveToIssue = false,
|
||||
modalTitle,
|
||||
modalTitle = `${data?.id ? t("update") : isDraft ? t("create_a_draft") : t("create_new_issue")}`,
|
||||
primaryButtonText = {
|
||||
default: `${data?.id ? "Update" : isDraft ? "Save to Drafts" : "Save"}`,
|
||||
loading: `${data?.id ? "Updating" : "Saving"}`,
|
||||
default: `${data?.id ? t("update") : isDraft ? t("save_to_drafts") : t("save")}`,
|
||||
loading: `${data?.id ? t("updating") : t("saving")}`,
|
||||
},
|
||||
isDuplicateModalOpen,
|
||||
handleDuplicateIssueModal,
|
||||
|
|
@ -198,8 +201,8 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
if (!editorRef.current?.isEditorReadyToDiscard()) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Editor is not ready to discard changes.",
|
||||
title: t("error"),
|
||||
message: t("editor_is_not_ready_to_discard_changes"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -391,7 +394,11 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
<DeDupeButtonRoot
|
||||
workspaceSlug={workspaceSlug?.toString()}
|
||||
isDuplicateModalOpen={isDuplicateModalOpen}
|
||||
label={`${duplicateIssues.length} duplicate issue${duplicateIssues.length > 1 ? "s" : ""} found!`}
|
||||
label={
|
||||
duplicateIssues.length === 1
|
||||
? `${duplicateIssues.length} ${t("duplicate_issue_found")}`
|
||||
: `${duplicateIssues.length} ${t("duplicate_issues_found")}`
|
||||
}
|
||||
handleOnClick={() => handleDuplicateIssueModal(!isDuplicateModalOpen)}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -491,7 +498,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
role="button"
|
||||
>
|
||||
<ToggleSwitch value={isCreateMoreToggleEnabled} onChange={() => {}} size="sm" />
|
||||
<span className="text-xs">Create more</span>
|
||||
<span className="text-xs">{t("create_more")}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -511,7 +518,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
}}
|
||||
tabIndex={getIndex("discard_button")}
|
||||
>
|
||||
Discard
|
||||
{t("discard")}
|
||||
</Button>
|
||||
<Button
|
||||
variant={moveToIssue ? "neutral-primary" : "primary"}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { useParams } from "next/navigation";
|
|||
import { usePopper } from "react-popper";
|
||||
import { Check, Component, Plus, Search, Tag } from "lucide-react";
|
||||
import { Combobox } from "@headlessui/react";
|
||||
// plane helpers
|
||||
import { useOutsideClickDetector } from "@plane/hooks";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// components
|
||||
import { IssueLabelsList } from "@/components/ui";
|
||||
// helpers
|
||||
|
|
@ -39,6 +39,7 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
|
|||
createLabelEnabled = false,
|
||||
buttonClassName,
|
||||
} = props;
|
||||
const { t } = useTranslation();
|
||||
// router
|
||||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
|
|
@ -131,7 +132,7 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
|
|||
) : (
|
||||
<div className="h-full flex items-center justify-center gap-1 rounded border-[0.5px] border-custom-border-300 px-2 py-1 text-xs hover:bg-custom-background-80">
|
||||
<Tag className="h-3 w-3 flex-shrink-0" />
|
||||
<span>Labels</span>
|
||||
<span>{t("labels")}</span>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
|
|
@ -152,7 +153,7 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
|
|||
ref={inputRef}
|
||||
className="w-full bg-transparent py-1 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none"
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
placeholder="Search"
|
||||
placeholder={t("search")}
|
||||
displayValue={(assigned: any) => assigned?.name}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -232,10 +233,10 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
|
|||
);
|
||||
})
|
||||
) : (
|
||||
<p className="text-custom-text-400 italic py-1 px-1.5">No matching results</p>
|
||||
<p className="text-custom-text-400 italic py-1 px-1.5">{t("no_matching_results")}</p>
|
||||
)
|
||||
) : (
|
||||
<p className="text-custom-text-400 italic py-1 px-1.5">Loading...</p>
|
||||
<p className="text-custom-text-400 italic py-1 px-1.5">{t("loading")}</p>
|
||||
)}
|
||||
{createLabelEnabled && (
|
||||
<button
|
||||
|
|
@ -244,7 +245,7 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
|
|||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
<Plus className="h-3 w-3" aria-hidden="true" />
|
||||
<span className="whitespace-nowrap">Create new label</span>
|
||||
<span className="whitespace-nowrap">{t("create_new_label")}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue