feat: language support (#6472)
* chore: ln support modules constants * fix: translation key * chore: empty state refactor (#6404) * chore: asset path helper hook added * chore: detailed and simple empty state component added * chore: section empty state component added * chore: language translation for all empty states * chore: new empty state implementation * improvement: add more translations * improvement: user permissions and workspace draft empty state * chore: update translation structure * chore: inbox empty states * chore: disabled project features empty state * chore: active cycle progress empty state * chore: notification empty state * chore: connections translation * chore: issue comment, relation, bulk delete, and command k empty state translation * chore: project pages empty state and translations * chore: project module and view related empty state * chore: remove project draft related empty state * chore: project cycle, views and archived issues empty state * chore: project cycles related empty state * chore: project settings empty state * chore: profile issue and acitivity empty state * chore: workspace settings realted constants * chore: stickies and home widgets empty state * chore: remove all reference to deprecated empty state component and constnats * chore: add support to ignore theme in resolved asset path hook * chore: minor updates * fix: build errors --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> * fix: language support fo profile (#6461) * fix: ln support fo profile * fix: merge changes * fix: merge changes * [WEB-3165]feat: language support for issues (#6452) * * chore: moved issue constants to packages * chore: restructured issue constants * improvement: added translations to issue constants * chore: updated translation structure * * chore: updated chinese, spanish and french translation * chore: updated translation for issues mobile header * chore: updated spanish translation * chore: removed translation for issue priorities * fix: build errors * chore: minor updates --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * chore: migrated filters.ts to packages (#6459) Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * chore: workspace drafts constant moved to plane constant package * feat: home language support without stickies (#6443) * feat: home language support without stickies * fix: home sidebar * fix: added missing keys * fix: show all btn * fix: recents empty state * chore: translation update * feat: workspace constant language support and refactor (#6462) * chore: workspace constant language support and refactor * chore: workspace constant language support and refactor * chore: code refactor * chore: code refactor * merge conflict * chore: code refactor --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * chore: tab indices constant moved to plane package (#6464) * chore: notification language support and refactor * chore: ln support for inbox constants (#6432) * chore: ln support for inbox constants * fix: snooze duration * fix: enum * fix: translation keys * fix: inbox status icon * fix: status icon * fix: naming --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * fix: ln support for views constants (#6431) * fix: ln support for views constants * fix: added translation * fix: translation keys * fix: access * chore: code refactor * chore: ln support workspace projects constants (#6429) * chore: ln support workspace projects constants * fix: translation key * fix: removed state translation * fix: removed state translation * fi: added translations * Chore: theme language support and refactor (#6465) * chore: themes language support and refactor * chore: theme language support and refactor * fix * [WEB-3173] chore: language support for cycles constant file (#6415) * chore: ln support for cycles constant file * fix: added chinese * fix: lint * fix: translation key * fix: build errors * minor updates * chore: minor translation update * chore: minor translation update * refactor: move labels contants to packages * refactor: move swr, file and error related constants to packages * chore: timezones constant moved to plane package * chore: metadata constant code refactor * chore: code refactor * fix: dashboard constants moved * chore: code refactor (#6478) * refactor: spreadsheet constants * chore: drafts language support (#6485) * chore: workspace drafts language support * chore: code refactor * feat: ln support for notifications (#6486) * feat: ln support for notifications * fix: translations * * refactor: moved page constants to packages (#6480) * fix: removed use-client * chore: removed unnecessary commnets * chore: workspace draft language support (#6490) * chore: workspace drafts language support * chore: code refactor * chore: draft language support * Feat constant event tracker (#6479) * fix: event tracjer constants * fix: constants event tracker * feat: language translation - projects list (#6493) * feat: added translation to projects list page * chore: restructured translation file * chore: module language support (#6499) * chore: module language support added * chore: code refactor * chore: workspace views language support (#6492) * chore: workspace views language support * chore: code refactor * feat: custom analytics language support (#6494) * feat: custom analytics language support * fix: key * fix: refactoring --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * chore: minor improvements * feat: language support for intake (#6498) * feat: language support for intake * fix: key name * refactor: authentications related translations * feat: language support issues (#6501) * enhancement: added translations for issue list view * chore: added translations for issue detail widgets * chore: added missing translations * chore: modified issue to work items * chore: updated translations * Feat: workspace settings language support (#6508) * feat: language support for workspace settings * fix: lint * fix: export title * chore project settings language support (#6502) * chore: project settings language support * chore: code refactor * refactor: workspace creation related translations * chore: renamed issues to work items * fix: build errors * fix: lint * chore: modified translations * chore: remove duplicate * improvement: french translation * chore: chinese translation improvement * fix: japanese translations * chore: added spanish translation * minor improvements * fix: miscelleous language translations * fix: clear_all key * fix: moved user permission constants (#6516) * feat: language support for issues (#6513) * chore: added language support to issue detail widgets * improvement: added translation for issue detail * enhancement: added language trasnlation to issue layouts * chore: translation improvement (#6518) * feat: language support description (#6519) * enhancement: added language support for description * fix: updated keys * chore: renamed issue to work item (#6522) * chore: replace missing issue occurances to work items * fix: build errors * minor improvements * fix: profile links * Feat ln cycles (#6528) * feat: added language support for cycles * feat: added language support for cycles * chore: added core.json * fix: translation keys * fix: translation keys (#6530) * fix: changed sidebar keys * fix: removed extras * fix: updated keys * chore: optimize translation imports * fix: updated keys (#6534) * fix: updated keys * fix-sub work items toasts * chore: add missing translation and minor fixes * chore: code refactor * fix: language support keys (#6553) * minor improvements * minor fixes * fix: remove lucide import from constants package * chore: regenerate all translations * chore: addded chinese and japanese translation files * chore: remove all from translations * fix: added member * fix: language support keys (#6558) * fix: renamed keys * fix: space app * chore: renamed issues to work items * chore: update site manifest * chore: updated translations * fix: lang keys * chore: update translations --------- Co-authored-by: gakshita <akshitagoyal1516@gmail.com> Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> Co-authored-by: Akshita Goyal <36129505+gakshita@users.noreply.github.com> Co-authored-by: Vamsi Krishna <46787868+mathalav55@users.noreply.github.com> Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so> Co-authored-by: Vamsi krishna <matalav55@gmail.com> Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
This commit is contained in:
parent
e244f48776
commit
d36c3acbf7
693 changed files with 18182 additions and 10485 deletions
|
|
@ -15,6 +15,8 @@ import {
|
|||
MoveRight,
|
||||
Copy,
|
||||
} from "lucide-react";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TNameDescriptionLoader } from "@plane/types";
|
||||
import { Button, ControlLink, CustomMenu, Row, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
|
|
@ -34,7 +36,6 @@ import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|||
// hooks
|
||||
import { useUser, useProjectInbox, useProject, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// store types
|
||||
import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
|
||||
|
||||
|
|
@ -71,6 +72,7 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
const { data: currentUser } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const router = useAppRouter();
|
||||
const { getProjectById } = useProject();
|
||||
|
|
@ -172,8 +174,8 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
copyUrlToClipboard(path).then(() =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Link copied",
|
||||
message: "Issue link copied to clipboard",
|
||||
title: t("common.link_copied"),
|
||||
message: t("common.copied_to_clipboard"),
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -243,10 +245,12 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
beforeFormSubmit={handleInboxIssueAccept}
|
||||
withDraftIssueWrapper={false}
|
||||
fetchIssueDetails={false}
|
||||
modalTitle={`Move ${currentProjectDetails?.identifier}-${issue?.sequence_id} to project issues`}
|
||||
modalTitle={t("inbox_issue.actions.move", {
|
||||
value: `${currentProjectDetails?.identifier}-${issue?.sequence_id}`,
|
||||
})}
|
||||
primaryButtonText={{
|
||||
default: "Add to project",
|
||||
loading: "Adding",
|
||||
default: t("add_to_project"),
|
||||
loading: t("adding"),
|
||||
}}
|
||||
/>
|
||||
<DeclineIssueModal
|
||||
|
|
@ -319,11 +323,11 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setAcceptIssueModal(true),
|
||||
"Only project admins can accept issues"
|
||||
t("inbox_issue.errors.accept_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
Accept
|
||||
{t("inbox_issue.actions.accept")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -339,11 +343,11 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setDeclineIssueModal(true),
|
||||
"Only project admins can deny issues"
|
||||
t("inbox_issue.errors.decline_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
Decline
|
||||
{t("inbox_issue.actions.decline")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -356,7 +360,7 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
size="sm"
|
||||
onClick={() => handleCopyIssueLink(issueLink)}
|
||||
>
|
||||
Copy issue link
|
||||
{t("inbox_issue.actions.copy")}
|
||||
</Button>
|
||||
<ControlLink
|
||||
href={`/${workspaceSlug}/projects/${issue?.project_id}/issues/${currentInboxIssueId}`}
|
||||
|
|
@ -366,7 +370,7 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
target="_self"
|
||||
>
|
||||
<Button variant="neutral-primary" prependIcon={<ExternalLink className="h-2.5 w-2.5" />} size="sm">
|
||||
Open issue
|
||||
{t("inbox_issue.actions.open")}
|
||||
</Button>
|
||||
</ControlLink>
|
||||
</div>
|
||||
|
|
@ -380,15 +384,15 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
handleIssueSnoozeAction,
|
||||
"Only project admins can snooze/Un-snooze issues"
|
||||
t("inbox_issue.errors.snooze_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock size={14} strokeWidth={2} />
|
||||
{inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0
|
||||
? "Un-snooze"
|
||||
: "Snooze"}
|
||||
? t("inbox_issue.actions.unsnooze")
|
||||
: t("inbox_issue.actions.snooze")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
|
|
@ -398,27 +402,27 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setSelectDuplicateIssue(true),
|
||||
"Only project admins can mark issues as duplicate"
|
||||
"Only project admins can mark work item as duplicate"
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FileStack size={14} strokeWidth={2} />
|
||||
Mark as duplicate
|
||||
{t("inbox_issue.actions.mark_as_duplicate")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
<CustomMenu.MenuItem onClick={() => handleCopyIssueLink(intakeIssueLink)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Copy size={14} strokeWidth={2} />
|
||||
Copy issue link
|
||||
{t("inbox_issue.actions.copy")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
{canDelete && (
|
||||
<CustomMenu.MenuItem onClick={() => setDeleteIssueModal(true)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Trash2 size={14} strokeWidth={2} />
|
||||
Delete
|
||||
{t("inbox_issue.actions.delete")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
|
|||
<CustomMenu.MenuItem onClick={handleCopyIssueLink}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Link size={14} strokeWidth={2} />
|
||||
Copy issue link
|
||||
Copy work item link
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
|
|
@ -139,7 +139,7 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
|
|||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<ExternalLink size={14} strokeWidth={2} />
|
||||
Open issue
|
||||
Open work item
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
|
|
@ -149,7 +149,7 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
|
|||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
handleIssueSnoozeAction,
|
||||
"Only project admins can snooze/Un-snooze issues"
|
||||
"Only project admins can snooze/Un-snooze work items"
|
||||
)
|
||||
}
|
||||
>
|
||||
|
|
@ -165,7 +165,7 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
|
|||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setSelectDuplicateIssue(true),
|
||||
"Only project admins can mark issues as duplicate"
|
||||
"Only project admins can mark work items as duplicate"
|
||||
)
|
||||
}
|
||||
>
|
||||
|
|
@ -181,7 +181,7 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
|
|||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setAcceptIssueModal(true),
|
||||
"Only project admins can accept issues"
|
||||
"Only project admins can accept work items"
|
||||
)
|
||||
}
|
||||
>
|
||||
|
|
@ -197,7 +197,7 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
|
|||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setDeclineIssueModal(true),
|
||||
"Only project admins can deny issues"
|
||||
"Only project admins can deny work items"
|
||||
)
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Dispatch, SetStateAction, useEffect, useMemo } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
// plane types
|
||||
import { ISSUE_ARCHIVED, ISSUE_DELETED } from "@plane/constants";
|
||||
import { TIssue, TNameDescriptionLoader } from "@plane/types";
|
||||
// plane ui
|
||||
import { Loader, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
|
|
@ -18,7 +19,6 @@ import {
|
|||
IssueAttachmentRoot,
|
||||
} from "@/components/issues";
|
||||
// constants
|
||||
import { ISSUE_ARCHIVED, ISSUE_DELETED } from "@/constants/event-tracker";
|
||||
// helpers
|
||||
import { getTextContent } from "@/helpers/editor.helper";
|
||||
// hooks
|
||||
|
|
@ -91,23 +91,23 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
|
|||
setToast({
|
||||
title: "Success!",
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
message: "Issue deleted successfully",
|
||||
message: "Work item deleted successfully",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_DELETED,
|
||||
payload: { id: _issueId, state: "SUCCESS", element: "Issue detail page" },
|
||||
payload: { id: _issueId, state: "SUCCESS", element: "Work item detail page" },
|
||||
path: pathname,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error in deleting issue:", error);
|
||||
console.log("Error in deleting work item:", error);
|
||||
setToast({
|
||||
title: "Error!",
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: "Issue delete failed",
|
||||
message: "Work item delete failed",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_DELETED,
|
||||
payload: { id: _issueId, state: "FAILED", element: "Issue detail page" },
|
||||
payload: { id: _issueId, state: "FAILED", element: "Work item detail page" },
|
||||
path: pathname,
|
||||
});
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
|
|||
try {
|
||||
await inboxIssue.updateIssue(data);
|
||||
captureIssueEvent({
|
||||
eventName: "Inbox issue updated",
|
||||
eventName: "Inbox work item updated",
|
||||
payload: { ...data, state: "SUCCESS", element: "Inbox" },
|
||||
updates: {
|
||||
changed_property: Object.keys(data).join(","),
|
||||
|
|
@ -126,12 +126,12 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
|
|||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
title: "Issue update failed",
|
||||
title: "Work item update failed",
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: "Issue update failed",
|
||||
message: "Work item update failed",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: "Inbox issue updated",
|
||||
eventName: "Inbox work item updated",
|
||||
payload: { state: "SUCCESS", element: "Inbox" },
|
||||
updates: {
|
||||
changed_property: Object.keys(data).join(","),
|
||||
|
|
@ -146,14 +146,14 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
|
|||
await archiveIssue(workspaceSlug, projectId, issueId);
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_ARCHIVED,
|
||||
payload: { id: issueId, state: "SUCCESS", element: "Issue details page" },
|
||||
payload: { id: issueId, state: "SUCCESS", element: "Work item details page" },
|
||||
path: pathname,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error in archiving issue:", error);
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_ARCHIVED,
|
||||
payload: { id: issueId, state: "FAILED", element: "Issue details page" },
|
||||
payload: { id: issueId, state: "FAILED", element: "Work item details page" },
|
||||
path: pathname,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { FC, useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import useSWR from "swr";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { TNameDescriptionLoader } from "@plane/types";
|
||||
// components
|
||||
import { ContentWrapper } from "@plane/ui";
|
||||
|
|
@ -8,7 +9,6 @@ import { InboxIssueActionsHeader, InboxIssueMainContent } from "@/components/inb
|
|||
// hooks
|
||||
import { useProjectInbox, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type TInboxContentRoot = {
|
||||
workspaceSlug: string;
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { X } from "lucide-react";
|
||||
import { ISSUE_PRIORITIES } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TIssuePriorities } from "@plane/types";
|
||||
import { PriorityIcon, Tag } from "@plane/ui";
|
||||
// constants
|
||||
import { ISSUE_PRIORITIES } from "@/constants/issue";
|
||||
// hooks
|
||||
import { useProjectInbox } from "@/hooks/store";
|
||||
|
||||
export const InboxIssueAppliedFiltersPriority: FC = observer(() => {
|
||||
// hooks
|
||||
const { t } = useTranslation();
|
||||
const { inboxFilters, handleInboxIssueFilters } = useProjectInbox();
|
||||
// derived values
|
||||
const filteredValues = inboxFilters?.priority || [];
|
||||
|
|
@ -26,7 +27,7 @@ export const InboxIssueAppliedFiltersPriority: FC = observer(() => {
|
|||
if (filteredValues.length === 0) return <></>;
|
||||
return (
|
||||
<Tag>
|
||||
<div className="text-xs text-custom-text-200">Priority</div>
|
||||
<div className="text-xs text-custom-text-200">{t("common.priority")}</div>
|
||||
{filteredValues.map((value) => {
|
||||
const optionDetail = currentOptionDetail(value);
|
||||
if (!optionDetail) return <></>;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { X } from "lucide-react";
|
||||
import { TInboxIssueStatus } from "@plane/types";
|
||||
import { INBOX_STATUS, TInboxIssueStatus } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// constants
|
||||
import { Tag } from "@plane/ui";
|
||||
import { INBOX_STATUS } from "@/constants/inbox";
|
||||
// hooks
|
||||
import { useProjectInbox } from "@/hooks/store";
|
||||
import { InboxStatusIcon } from "../../inbox-status-icon";
|
||||
|
||||
export const InboxIssueAppliedFiltersStatus: FC = observer(() => {
|
||||
// hooks
|
||||
const { inboxFilters, handleInboxIssueFilters } = useProjectInbox();
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const filteredValues = inboxFilters?.status || [];
|
||||
const currentOptionDetail = (status: TInboxIssueStatus) => INBOX_STATUS.find((s) => s.status === status) || undefined;
|
||||
|
|
@ -28,9 +30,9 @@ export const InboxIssueAppliedFiltersStatus: FC = observer(() => {
|
|||
return (
|
||||
<div key={value} className="relative flex items-center gap-1 rounded bg-custom-background-80 p-1 text-xs">
|
||||
<div className="w-3 h-3 flex-shrink-0 relative flex justify-center items-center overflow-hidden">
|
||||
<optionDetail.icon className={`w-3 h-3 ${optionDetail?.textColor(false)}`} />
|
||||
<InboxStatusIcon type={optionDetail?.status} />
|
||||
</div>
|
||||
<div className="text-xs truncate">{optionDetail?.title}</div>
|
||||
<div className="text-xs truncate">{t(optionDetail?.i18n_title)}</div>
|
||||
{handleFilterValue(optionDetail?.status).length >= 1 && (
|
||||
<div
|
||||
className="w-3 h-3 flex-shrink-0 relative flex justify-center items-center overflow-hidden cursor-pointer text-custom-text-300 hover:text-custom-text-200 transition-all"
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
import { FC, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { ISSUE_PRIORITIES } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TIssuePriorities } from "@plane/types";
|
||||
import { PriorityIcon } from "@plane/ui";
|
||||
// plane constants
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "@/components/issues";
|
||||
// constants
|
||||
import { ISSUE_PRIORITIES } from "@/constants/issue";
|
||||
// hooks
|
||||
import { useProjectInbox } from "@/hooks/store/use-project-inbox";
|
||||
|
||||
|
|
@ -18,6 +19,7 @@ type Props = {
|
|||
export const FilterPriority: FC<Props> = observer((props) => {
|
||||
const { searchQuery } = props;
|
||||
// hooks
|
||||
const { t } = useTranslation();
|
||||
const { inboxFilters, handleInboxIssueFilters } = useProjectInbox();
|
||||
// states
|
||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||
|
|
@ -32,7 +34,7 @@ export const FilterPriority: FC<Props> = observer((props) => {
|
|||
return (
|
||||
<>
|
||||
<FilterHeader
|
||||
title={`Priority${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
|
||||
title={`${t("common.priority")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
|
||||
isPreviewEnabled={previewEnabled}
|
||||
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
|
||||
/>
|
||||
|
|
@ -49,7 +51,7 @@ export const FilterPriority: FC<Props> = observer((props) => {
|
|||
/>
|
||||
))
|
||||
) : (
|
||||
<p className="text-xs italic text-custom-text-400">No matches found</p>
|
||||
<p className="text-xs italic text-custom-text-400">{t("common.search.no_matches_found")}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { FC, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// types
|
||||
import { TInboxIssueStatus } from "@plane/types";
|
||||
import { INBOX_STATUS, TInboxIssueStatus } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "@/components/issues";
|
||||
// constants
|
||||
import { INBOX_STATUS } from "@/constants/inbox";
|
||||
// hooks
|
||||
import { useProjectInbox } from "@/hooks/store/use-project-inbox";
|
||||
import { InboxStatusIcon } from "../../inbox-status-icon";
|
||||
|
||||
type Props = {
|
||||
searchQuery: string;
|
||||
|
|
@ -17,6 +18,7 @@ export const FilterStatus: FC<Props> = observer((props) => {
|
|||
const { searchQuery } = props;
|
||||
// hooks
|
||||
const { currentTab, inboxFilters, handleInboxIssueFilters } = useProjectInbox();
|
||||
const { t } = useTranslation();
|
||||
// states
|
||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||
// derived values
|
||||
|
|
@ -52,8 +54,8 @@ export const FilterStatus: FC<Props> = observer((props) => {
|
|||
key={status.key}
|
||||
isChecked={filterValue?.includes(status.status) ? true : false}
|
||||
onClick={() => handleStatusFilterSelect(status.status)}
|
||||
icon={<status.icon className={`h-3.5 w-3.5 ${status?.textColor(false)}`} />}
|
||||
title={status.title}
|
||||
icon={<InboxStatusIcon type={status.status} className={`h-3.5 w-3.5`} />}
|
||||
title={t(status.i18n_title)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { ArrowDownWideNarrow, ArrowUpWideNarrow, Check, ChevronDown } from "lucide-react";
|
||||
import { INBOX_ISSUE_ORDER_BY_OPTIONS, INBOX_ISSUE_SORT_BY_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TInboxIssueSortingOrderByKeys, TInboxIssueSortingSortByKeys } from "@plane/types";
|
||||
import { CustomMenu, getButtonStyling } from "@plane/ui";
|
||||
// constants
|
||||
import { INBOX_ISSUE_ORDER_BY_OPTIONS, INBOX_ISSUE_SORT_BY_OPTIONS } from "@/constants/inbox";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
|
|
@ -14,6 +16,7 @@ import useSize from "@/hooks/use-window-size";
|
|||
|
||||
export const InboxIssueOrderByDropdown: FC = observer(() => {
|
||||
// hooks
|
||||
const { t } = useTranslation();
|
||||
const windowSize = useSize();
|
||||
const { inboxSorting, handleInboxIssueSorting } = useProjectInbox();
|
||||
const orderByDetails =
|
||||
|
|
@ -31,8 +34,7 @@ export const InboxIssueOrderByDropdown: FC = observer(() => {
|
|||
) : (
|
||||
<ArrowDownWideNarrow className="size-3 " />
|
||||
)}
|
||||
{orderByDetails?.label || "Order By"}
|
||||
|
||||
{t(orderByDetails?.i18n_label || "inbox_issue.order_by.created_at")}
|
||||
<ChevronDown className="size-3" strokeWidth={2} />
|
||||
</div>
|
||||
);
|
||||
|
|
@ -47,9 +49,9 @@ export const InboxIssueOrderByDropdown: FC = observer(() => {
|
|||
<CustomMenu.MenuItem
|
||||
key={option.key}
|
||||
className="flex items-center justify-between gap-2"
|
||||
onClick={() => handleInboxIssueSorting("order_by", option.key)}
|
||||
onClick={() => handleInboxIssueSorting("order_by", option.key as TInboxIssueSortingOrderByKeys)}
|
||||
>
|
||||
{option.label}
|
||||
{t(option.i18n_label)}
|
||||
{inboxSorting?.order_by?.includes(option.key) && <Check className="size-3" />}
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
|
|
@ -58,9 +60,9 @@ export const InboxIssueOrderByDropdown: FC = observer(() => {
|
|||
<CustomMenu.MenuItem
|
||||
key={option.key}
|
||||
className="flex items-center justify-between gap-2"
|
||||
onClick={() => handleInboxIssueSorting("sort_by", option.key)}
|
||||
onClick={() => handleInboxIssueSorting("sort_by", option.key as TInboxIssueSortingSortByKeys)}
|
||||
>
|
||||
{option.label}
|
||||
{t(option.i18n_label)}
|
||||
{inboxSorting?.sort_by?.includes(option.key) && <Check className="size-3" />}
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// constants
|
||||
import { INBOX_STATUS } from "@/constants/inbox";
|
||||
// helpers
|
||||
import { INBOX_STATUS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { findHowManyDaysLeft } from "@/helpers/date-time.helper";
|
||||
// store
|
||||
import { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
|
||||
import { ICON_PROPERTIES, InboxStatusIcon } from "./inbox-status-icon";
|
||||
|
||||
type Props = {
|
||||
inboxIssue: IInboxIssueStore;
|
||||
|
|
@ -15,28 +18,31 @@ type Props = {
|
|||
|
||||
export const InboxIssueStatus: React.FC<Props> = observer((props) => {
|
||||
const { inboxIssue, iconSize = 16, showDescription = false } = props;
|
||||
//hooks
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const inboxIssueStatusDetail = INBOX_STATUS.find((s) => s.status === inboxIssue.status);
|
||||
|
||||
const isSnoozedDatePassed = inboxIssue.status === 0 && new Date(inboxIssue.snoozed_till ?? "") < new Date();
|
||||
if (!inboxIssueStatusDetail || isSnoozedDatePassed) return <></>;
|
||||
|
||||
const description = inboxIssueStatusDetail.description(new Date(inboxIssue.snoozed_till ?? ""));
|
||||
const description = t(inboxIssueStatusDetail.i18n_description(), {
|
||||
days: findHowManyDaysLeft(new Date(inboxIssue.snoozed_till ?? "")),
|
||||
});
|
||||
const statusIcon = ICON_PROPERTIES[inboxIssue?.status];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
`relative flex flex-col gap-1 p-1.5 py-0.5 rounded ${inboxIssueStatusDetail.textColor(
|
||||
`relative flex flex-col gap-1 p-1.5 py-0.5 rounded ${statusIcon.textColor(
|
||||
isSnoozedDatePassed
|
||||
)} ${inboxIssueStatusDetail.bgColor(isSnoozedDatePassed)}`
|
||||
)} ${statusIcon.bgColor(isSnoozedDatePassed)}`
|
||||
)}
|
||||
>
|
||||
<div className={`flex items-center gap-1`}>
|
||||
<inboxIssueStatusDetail.icon size={iconSize} className="flex-shrink-0" />
|
||||
<InboxStatusIcon type={inboxIssue?.status} size={iconSize} className="flex-shrink-0" renderColor={false} />
|
||||
<div className="font-medium text-xs whitespace-nowrap">
|
||||
{inboxIssue?.status === 0 && inboxIssue?.snoozed_till
|
||||
? inboxIssueStatusDetail.description(inboxIssue?.snoozed_till)
|
||||
: inboxIssueStatusDetail.title}
|
||||
{inboxIssue?.status === 0 && inboxIssue?.snoozed_till ? description : t(inboxIssueStatusDetail.i18n_title)}
|
||||
</div>
|
||||
</div>
|
||||
{showDescription && <div className="text-sm whitespace-nowrap">{description}</div>}
|
||||
|
|
|
|||
46
web/core/components/inbox/inbox-status-icon.tsx
Normal file
46
web/core/components/inbox/inbox-status-icon.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { AlertTriangle, CheckCircle2, Clock, Copy, XCircle } from "lucide-react";
|
||||
import { TInboxIssueStatus, EInboxIssueStatus } from "@plane/constants";
|
||||
import { cn } from "@plane/utils";
|
||||
|
||||
export const ICON_PROPERTIES = {
|
||||
[EInboxIssueStatus.PENDING]: {
|
||||
icon: AlertTriangle,
|
||||
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-[#AB6400]"),
|
||||
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-[#FFF7C2]"),
|
||||
},
|
||||
[EInboxIssueStatus.DECLINED]: {
|
||||
icon: XCircle,
|
||||
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-[#CE2C31]"),
|
||||
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-[#FEEBEC]"),
|
||||
},
|
||||
[EInboxIssueStatus.SNOOZED]: {
|
||||
icon: Clock,
|
||||
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "text-red-500" : "text-custom-text-400"),
|
||||
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "bg-red-500/10" : "bg-[#E0E1E6]"),
|
||||
},
|
||||
[EInboxIssueStatus.ACCEPTED]: {
|
||||
icon: CheckCircle2,
|
||||
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-[#3E9B4F]"),
|
||||
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-[#E9F6E9]"),
|
||||
},
|
||||
[EInboxIssueStatus.DUPLICATE]: {
|
||||
icon: Copy,
|
||||
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-custom-text-200"),
|
||||
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-gray-500/10"),
|
||||
},
|
||||
};
|
||||
export const InboxStatusIcon = ({
|
||||
type,
|
||||
size,
|
||||
className,
|
||||
renderColor = true,
|
||||
}: {
|
||||
type: TInboxIssueStatus;
|
||||
size?: number;
|
||||
className?: string;
|
||||
renderColor?: boolean;
|
||||
}) => {
|
||||
if (type === undefined) return null;
|
||||
const Icon = ICON_PROPERTIES[type];
|
||||
return <Icon.icon size={size} className={cn(`w-3 h-3 ${renderColor && Icon?.textColor(false)}`, className)} />;
|
||||
};
|
||||
|
|
@ -3,16 +3,16 @@
|
|||
import { FC, FormEvent, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
// editor
|
||||
// plane imports
|
||||
import { ETabIndices, ISSUE_CREATED } from "@plane/constants";
|
||||
import { EditorRefApi } from "@plane/editor";
|
||||
// types
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TIssue } from "@plane/types";
|
||||
import { Button, ToggleSwitch, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { InboxIssueTitle, InboxIssueDescription, InboxIssueProperties } from "@/components/inbox/modals/create-modal";
|
||||
// constants
|
||||
import { ISSUE_CREATED } from "@/constants/event-tracker";
|
||||
import { ETabIndices } from "@/constants/tab-indices";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
import { getTabIndex } from "@/helpers/tab-indices.helper";
|
||||
|
|
@ -67,6 +67,7 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
|
|||
const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id;
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { getProjectById } = useProject();
|
||||
const { t } = useTranslation();
|
||||
// states
|
||||
const [createMore, setCreateMore] = useState<boolean>(false);
|
||||
const [formSubmitting, setFormSubmitting] = useState(false);
|
||||
|
|
@ -179,7 +180,7 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
|
|||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: `Success!`,
|
||||
message: "Issue created successfully.",
|
||||
message: "Work item created successfully.",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -213,7 +214,7 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
|
|||
<form ref={formRef} onSubmit={handleFormSubmit} className="flex flex-col w-full">
|
||||
<div className="space-y-5 p-5 rounded-t-lg bg-custom-background-100">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<h3 className="text-xl font-medium text-custom-text-200">Create intake issue</h3>
|
||||
<h3 className="text-xl font-medium text-custom-text-200">{t("inbox_issue.modal.title")}</h3>
|
||||
{duplicateIssues?.length > 0 && (
|
||||
<DeDupeButtonRoot
|
||||
workspaceSlug={workspaceSlug}
|
||||
|
|
@ -251,7 +252,7 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
|
|||
tabIndex={getIndex("create_more")}
|
||||
>
|
||||
<ToggleSwitch value={createMore} 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-3">
|
||||
<Button
|
||||
|
|
@ -271,7 +272,7 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
|
|||
}}
|
||||
tabIndex={getIndex("discard_button")}
|
||||
>
|
||||
Discard
|
||||
{t("discard")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
|
|
@ -282,7 +283,7 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
|
|||
disabled={isTitleLengthMoreThan255Character}
|
||||
tabIndex={getIndex("submit_button")}
|
||||
>
|
||||
{formSubmitting ? "Creating" : "Create Issue"}
|
||||
{formSubmitting ? t("creating") : t("create_work_item")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
|
||||
import { FC, RefObject } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { ETabIndices } from "@plane/constants";
|
||||
// editor
|
||||
import { EditorRefApi } from "@plane/editor";
|
||||
// i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EFileAssetType } from "@plane/types/src/enums";
|
||||
|
|
@ -11,10 +15,8 @@ import { EFileAssetType } from "@plane/types/src/enums";
|
|||
import { Loader } from "@plane/ui";
|
||||
// components
|
||||
import { RichTextEditor } from "@/components/editor/rich-text-editor/rich-text-editor";
|
||||
// constants
|
||||
import { ETabIndices } from "@/constants/tab-indices";
|
||||
// helpers
|
||||
import { getDescriptionPlaceholder } from "@/helpers/issue.helper";
|
||||
import { getDescriptionPlaceholderI18n } from "@/helpers/issue.helper";
|
||||
import { getTabIndex } from "@/helpers/tab-indices.helper";
|
||||
// hooks
|
||||
import { useProjectInbox } from "@/hooks/store";
|
||||
|
|
@ -51,6 +53,10 @@ export const InboxIssueDescription: FC<TInboxIssueDescription> = observer((props
|
|||
onEnterKeyPress,
|
||||
onAssetUpload,
|
||||
} = props;
|
||||
|
||||
// i18n
|
||||
const { t } = useTranslation();
|
||||
|
||||
// hooks
|
||||
const { loader } = useProjectInbox();
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
|
@ -74,7 +80,7 @@ export const InboxIssueDescription: FC<TInboxIssueDescription> = observer((props
|
|||
projectId={projectId}
|
||||
dragDropEnabled={false}
|
||||
onChange={(_description: object, description_html: string) => handleData("description_html", description_html)}
|
||||
placeholder={getDescriptionPlaceholder}
|
||||
placeholder={(isFocused, description) => t(`${getDescriptionPlaceholderI18n(isFocused, description)}`)}
|
||||
searchMentionCallback={async (payload) =>
|
||||
await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
|
||||
...payload,
|
||||
|
|
@ -98,7 +104,7 @@ export const InboxIssueDescription: FC<TInboxIssueDescription> = observer((props
|
|||
onAssetUpload?.(asset_id);
|
||||
return asset_id;
|
||||
} catch (error) {
|
||||
console.log("Error in uploading issue asset:", error);
|
||||
console.log("Error in uploading work item asset:", error);
|
||||
throw new Error("Asset upload failed. Please try again later.");
|
||||
}
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { FC, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { LayoutPanelTop } from "lucide-react";
|
||||
// plane imports
|
||||
import { ETabIndices } from "@plane/constants";
|
||||
import { ISearchIssueResponse, TIssue } from "@plane/types";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
// components
|
||||
|
|
@ -15,8 +17,6 @@ import {
|
|||
} from "@/components/dropdowns";
|
||||
import { ParentIssuesListModal } from "@/components/issues";
|
||||
import { IssueLabelSelect } from "@/components/issues/select";
|
||||
// constants
|
||||
import { ETabIndices } from "@/constants/tab-indices";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper";
|
||||
import { getTabIndex } from "@/helpers/tab-indices.helper";
|
||||
|
|
@ -194,7 +194,7 @@ export const InboxIssueProperties: FC<TInboxIssueProperties> = observer((props)
|
|||
>
|
||||
<>
|
||||
<CustomMenu.MenuItem className="!p-1" onClick={() => setParentIssueModalOpen(true)}>
|
||||
Change parent issue
|
||||
Change parent work item
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
className="!p-1"
|
||||
|
|
@ -203,7 +203,7 @@ export const InboxIssueProperties: FC<TInboxIssueProperties> = observer((props)
|
|||
setSelectedParentIssue(undefined);
|
||||
}}
|
||||
>
|
||||
Remove parent issue
|
||||
Remove parent work item
|
||||
</CustomMenu.MenuItem>
|
||||
</>
|
||||
</CustomMenu>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { ETabIndices } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TIssue } from "@plane/types";
|
||||
import { Input } from "@plane/ui";
|
||||
// constants
|
||||
import { ETabIndices } from "@/constants/tab-indices";
|
||||
// helpers
|
||||
import { getTabIndex } from "@/helpers/tab-indices.helper";
|
||||
// hooks
|
||||
|
|
@ -23,6 +24,7 @@ export const InboxIssueTitle: FC<TInboxIssueTitle> = observer((props) => {
|
|||
const { isMobile } = usePlatformOS();
|
||||
|
||||
const { getIndex } = getTabIndex(ETabIndices.INTAKE_ISSUE_FORM, isMobile);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
|
|
@ -31,13 +33,13 @@ export const InboxIssueTitle: FC<TInboxIssueTitle> = observer((props) => {
|
|||
type="text"
|
||||
value={data?.name}
|
||||
onChange={(e) => handleData("name", e.target.value)}
|
||||
placeholder="Title"
|
||||
placeholder={t("title")}
|
||||
className="w-full text-base"
|
||||
tabIndex={getIndex("name")}
|
||||
required
|
||||
/>
|
||||
{isTitleLengthMoreThan255Character && (
|
||||
<span className="text-xs text-red-500">Title should be less than 255 characters</span>
|
||||
<span className="text-xs text-red-500">{t("title_should_be_less_than_255_characters")}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useState } from "react";
|
||||
// types
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { TIssue } from "@plane/types";
|
||||
// ui
|
||||
import { AlertModalCore } from "@plane/ui";
|
||||
|
|
@ -19,6 +20,7 @@ export const DeclineIssueModal: React.FC<Props> = (props) => {
|
|||
const [isDeclining, setIsDeclining] = useState(false);
|
||||
// store hooks
|
||||
const { getProjectById } = useProject();
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const projectDetails = data.project_id ? getProjectById(data?.project_id) : undefined;
|
||||
|
||||
|
|
@ -38,11 +40,11 @@ export const DeclineIssueModal: React.FC<Props> = (props) => {
|
|||
handleSubmit={handleDecline}
|
||||
isSubmitting={isDeclining}
|
||||
isOpen={isOpen}
|
||||
title="Decline issue"
|
||||
title={t("inbox_issue.modals.decline.title")}
|
||||
// TODO: Need to translate the confirmation message
|
||||
content={
|
||||
<>
|
||||
{" "}
|
||||
Are you sure you want to decline issue{" "}
|
||||
Are you sure you want to decline work item{" "}
|
||||
<span className="break-words font-medium text-custom-text-100">
|
||||
{projectDetails?.identifier}-{data?.sequence_id}
|
||||
</span>
|
||||
|
|
@ -50,8 +52,8 @@ export const DeclineIssueModal: React.FC<Props> = (props) => {
|
|||
</>
|
||||
}
|
||||
primaryButtonText={{
|
||||
loading: "Declining",
|
||||
default: "Decline",
|
||||
loading: t("declining"),
|
||||
default: t("decline"),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// types
|
||||
import { PROJECT_ERROR_MESSAGES } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { TIssue } from "@plane/types";
|
||||
// ui
|
||||
import { AlertModalCore, setToast, TOAST_TYPE } from "@plane/ui";
|
||||
// constants
|
||||
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
||||
// hooks
|
||||
import { useProject } from "@/hooks/store";
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ export const DeleteInboxIssueModal: React.FC<Props> = observer(({ isOpen, onClos
|
|||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
// store hooks
|
||||
const { getProjectById } = useProject();
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const projectDetails = data.project_id ? getProjectById(data?.project_id) : undefined;
|
||||
|
||||
|
|
@ -35,19 +37,19 @@ export const DeleteInboxIssueModal: React.FC<Props> = observer(({ isOpen, onClos
|
|||
.then(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
message: `Issue deleted successfully`,
|
||||
title: `${t("success")!}`,
|
||||
message: `${t("inbox_issue.modals.delete.success")!}`,
|
||||
});
|
||||
})
|
||||
.catch((errors) => {
|
||||
const isPermissionError = errors?.error === "Only admin or creator can delete the issue";
|
||||
const isPermissionError = errors?.error === "Only admin or creator can delete the work item";
|
||||
const currentError = isPermissionError
|
||||
? PROJECT_ERROR_MESSAGES.permissionError
|
||||
: PROJECT_ERROR_MESSAGES.issueDeleteError;
|
||||
setToast({
|
||||
title: currentError.title,
|
||||
title: t(currentError.i18n_title),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: currentError.message,
|
||||
message: currentError.i18n_message && t(currentError.i18n_message),
|
||||
});
|
||||
})
|
||||
.finally(() => handleClose());
|
||||
|
|
@ -59,14 +61,15 @@ export const DeleteInboxIssueModal: React.FC<Props> = observer(({ isOpen, onClos
|
|||
handleSubmit={handleDelete}
|
||||
isSubmitting={isDeleting}
|
||||
isOpen={isOpen}
|
||||
title="Delete issue"
|
||||
title={t("inbox_issue.modals.delete.title")}
|
||||
// TODO: Need to translate the confirmation message
|
||||
content={
|
||||
<>
|
||||
Are you sure you want to delete issue{" "}
|
||||
Are you sure you want to delete work item{" "}
|
||||
<span className="break-words font-medium text-custom-text-100">
|
||||
{projectDetails?.identifier}-{data?.sequence_id}
|
||||
</span>
|
||||
{""}? The issue will only be deleted from the intake and this action cannot be undone.
|
||||
{""}? The work item will only be deleted from the intake and this action cannot be undone.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -4,18 +4,16 @@ import React, { useEffect, useState } from "react";
|
|||
import { useParams } from "next/navigation";
|
||||
import { Search } from "lucide-react";
|
||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
// components
|
||||
// types
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { ISearchIssueResponse } from "@plane/types";
|
||||
// ui
|
||||
import { Loader, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { EmptyState } from "@/components/empty-state";
|
||||
// constants
|
||||
import { EmptyStateType } from "@/constants/empty-state";
|
||||
// components
|
||||
import { SimpleEmptyState } from "@/components/empty-state";
|
||||
// hooks
|
||||
import { useProject } from "@/hooks/store";
|
||||
import useDebounce from "@/hooks/use-debounce";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
// services
|
||||
import { ProjectService } from "@/services/project";
|
||||
|
||||
|
|
@ -30,18 +28,19 @@ const projectService = new ProjectService();
|
|||
|
||||
export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose, onSubmit, value } = props;
|
||||
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
// router
|
||||
const { workspaceSlug, projectId, issueId } = useParams();
|
||||
|
||||
// hooks
|
||||
const { getProjectById } = useProject();
|
||||
|
||||
// states
|
||||
const [query, setQuery] = useState("");
|
||||
const [issues, setIssues] = useState<ISearchIssueResponse[]>([]);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
|
||||
// hooks
|
||||
const { getProjectById } = useProject();
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const debouncedSearchTerm: string = useDebounce(query, 500);
|
||||
const searchResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/search" });
|
||||
const issuesResolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/issues" });
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || !workspaceSlug || !projectId) return;
|
||||
|
|
@ -75,7 +74,9 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
|||
const issueList =
|
||||
filteredIssues.length > 0 ? (
|
||||
<li className="p-2">
|
||||
{query === "" && <h2 className="mb-2 mt-4 px-3 text-xs font-semibold text-custom-text-100">Select issue</h2>}
|
||||
{query === "" && (
|
||||
<h2 className="mb-2 mt-4 px-3 text-xs font-semibold text-custom-text-100">Select work item</h2>
|
||||
)}
|
||||
<ul className="text-sm text-custom-text-100">
|
||||
{filteredIssues.map((issue) => {
|
||||
const stateColor = issue.state__color || "";
|
||||
|
|
@ -110,12 +111,11 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
|
|||
</li>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center px-3 py-8 text-center">
|
||||
<EmptyState
|
||||
type={
|
||||
query === "" ? EmptyStateType.ISSUE_RELATION_EMPTY_STATE : EmptyStateType.ISSUE_RELATION_SEARCH_EMPTY_STATE
|
||||
}
|
||||
layout="screen-simple"
|
||||
/>
|
||||
{query === "" ? (
|
||||
<SimpleEmptyState title={t("issue_relation.empty_state.no_issues.title")} assetPath={issuesResolvedPath} />
|
||||
) : (
|
||||
<SimpleEmptyState title={t("issue_relation.empty_state.search.title")} assetPath={searchResolvedPath} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { FC, Fragment, useState } from "react";
|
|||
import { DayPicker, getDefaultClassNames } from "react-day-picker";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// ui
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/ui";
|
||||
|
||||
export type InboxIssueSnoozeModalProps = {
|
||||
|
|
@ -17,6 +18,8 @@ export const InboxIssueSnoozeModal: FC<InboxIssueSnoozeModalProps> = (props) =>
|
|||
const { isOpen, handleClose, value, onConfirm } = props;
|
||||
// states
|
||||
const [date, setDate] = useState(value || new Date());
|
||||
//hooks
|
||||
const { t } = useTranslation();
|
||||
|
||||
const defaultClassNames = getDefaultClassNames();
|
||||
|
||||
|
|
@ -70,7 +73,7 @@ export const InboxIssueSnoozeModal: FC<InboxIssueSnoozeModalProps> = (props) =>
|
|||
onConfirm(date);
|
||||
}}
|
||||
>
|
||||
Snooze
|
||||
{t("inbox_issue.actions.snooze")}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import { FC, useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { PanelLeft } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Intake } from "@plane/ui";
|
||||
// components
|
||||
import { EmptyState } from "@/components/empty-state";
|
||||
import { SimpleEmptyState } from "@/components/empty-state";
|
||||
import { InboxSidebar, InboxContentRoot } from "@/components/inbox";
|
||||
import { InboxLayoutLoader } from "@/components/ui";
|
||||
// constants
|
||||
import { EmptyStateType } from "@/constants/empty-state";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { EInboxIssueCurrentTab } from "@/helpers/inbox.helper";
|
||||
// hooks
|
||||
import { useProjectInbox } from "@/hooks/store";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
|
||||
type TInboxIssueRoot = {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -26,8 +27,12 @@ export const InboxIssueRoot: FC<TInboxIssueRoot> = observer((props) => {
|
|||
const { workspaceSlug, projectId, inboxIssueId, inboxAccessible, navigationTab } = props;
|
||||
// states
|
||||
const [isMobileSidebar, setIsMobileSidebar] = useState(true);
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// hooks
|
||||
const { loader, error, currentTab, handleCurrentTab, fetchInboxIssues } = useProjectInbox();
|
||||
// derived values
|
||||
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/intake/issue-detail" });
|
||||
|
||||
useEffect(() => {
|
||||
if (!inboxAccessible || !workspaceSlug || !projectId) return;
|
||||
|
|
@ -96,7 +101,7 @@ export const InboxIssueRoot: FC<TInboxIssueRoot> = observer((props) => {
|
|||
/>
|
||||
) : (
|
||||
<div className="w-full h-full relative flex justify-center items-center">
|
||||
<EmptyState type={EmptyStateType.INBOX_DETAIL_EMPTY_STATE} layout="screen-simple" />
|
||||
<SimpleEmptyState title={t("inbox_issue.empty_state.detail.title")} assetPath={resolvedPath} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
import { FC, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TInboxIssueCurrentTab } from "@plane/types";
|
||||
// plane imports
|
||||
import { TInboxIssueCurrentTab } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Header, Loader, EHeaderVariant } from "@plane/ui";
|
||||
// components
|
||||
import { EmptyState } from "@/components/empty-state";
|
||||
import { SimpleEmptyState } from "@/components/empty-state";
|
||||
import { FiltersRoot, InboxIssueAppliedFilters, InboxIssueList } from "@/components/inbox";
|
||||
import { InboxSidebarLoader } from "@/components/ui";
|
||||
// constants
|
||||
import { EmptyStateType } from "@/constants/empty-state";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { EInboxIssueCurrentTab } from "@/helpers/inbox.helper";
|
||||
|
|
@ -17,6 +17,7 @@ import { EInboxIssueCurrentTab } from "@/helpers/inbox.helper";
|
|||
import { useProject, useProjectInbox } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { useIntersectionObserver } from "@/hooks/use-intersection-observer";
|
||||
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
||||
|
||||
type IInboxSidebarProps = {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -25,22 +26,26 @@ type IInboxSidebarProps = {
|
|||
setIsMobileSidebar: (value: boolean) => void;
|
||||
};
|
||||
|
||||
const tabNavigationOptions: { key: TInboxIssueCurrentTab; label: string }[] = [
|
||||
const tabNavigationOptions: { key: TInboxIssueCurrentTab; i18n_label: string }[] = [
|
||||
{
|
||||
key: EInboxIssueCurrentTab.OPEN,
|
||||
label: "Open",
|
||||
i18n_label: "inbox_issue.tabs.open",
|
||||
},
|
||||
{
|
||||
key: EInboxIssueCurrentTab.CLOSED,
|
||||
label: "Closed",
|
||||
i18n_label: "inbox_issue.tabs.closed",
|
||||
},
|
||||
];
|
||||
|
||||
export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
||||
const { workspaceSlug, projectId, inboxIssueId, setIsMobileSidebar } = props;
|
||||
// router
|
||||
const router = useAppRouter();
|
||||
// ref
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [elementRef, setElementRef] = useState<HTMLDivElement | null>(null);
|
||||
// plane hooks
|
||||
const { t } = useTranslation();
|
||||
// store
|
||||
const { currentProjectDetails } = useProject();
|
||||
const {
|
||||
|
|
@ -52,8 +57,11 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||
fetchInboxPaginationIssues,
|
||||
getAppliedFiltersCount,
|
||||
} = useProjectInbox();
|
||||
|
||||
const router = useAppRouter();
|
||||
// derived values
|
||||
const sidebarAssetPath = useResolvedAssetPath({ basePath: "/empty-state/intake/intake-issue" });
|
||||
const sidebarFilterAssetPath = useResolvedAssetPath({
|
||||
basePath: "/empty-state/intake/filter-issue",
|
||||
});
|
||||
|
||||
const fetchNextPages = useCallback(() => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
|
@ -91,7 +99,7 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<div>{option?.label}</div>
|
||||
<div>{t(option?.i18n_label)}</div>
|
||||
{option?.key === "open" && currentTab === option?.key && (
|
||||
<div className="rounded-full p-1.5 py-0.5 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold">
|
||||
{inboxIssuePaginationInfo?.total_results || 0}
|
||||
|
|
@ -128,16 +136,25 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||
/>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full w-full">
|
||||
<EmptyState
|
||||
type={
|
||||
getAppliedFiltersCount > 0
|
||||
? EmptyStateType.INBOX_SIDEBAR_FILTER_EMPTY_STATE
|
||||
: currentTab === EInboxIssueCurrentTab.OPEN
|
||||
? EmptyStateType.INBOX_SIDEBAR_OPEN_TAB
|
||||
: EmptyStateType.INBOX_SIDEBAR_CLOSED_TAB
|
||||
}
|
||||
layout="screen-simple"
|
||||
/>
|
||||
{getAppliedFiltersCount > 0 ? (
|
||||
<SimpleEmptyState
|
||||
title={t("inbox_issue.empty_state.sidebar_filter.title")}
|
||||
description={t("inbox_issue.empty_state.sidebar_filter.description")}
|
||||
assetPath={sidebarFilterAssetPath}
|
||||
/>
|
||||
) : currentTab === EInboxIssueCurrentTab.OPEN ? (
|
||||
<SimpleEmptyState
|
||||
title={t("inbox_issue.empty_state.sidebar_open_tab.title")}
|
||||
description={t("inbox_issue.empty_state.sidebar_open_tab.description")}
|
||||
assetPath={sidebarAssetPath}
|
||||
/>
|
||||
) : (
|
||||
<SimpleEmptyState
|
||||
title={t("inbox_issue.empty_state.sidebar_closed_tab.title")}
|
||||
description={t("inbox_issue.empty_state.sidebar_closed_tab.description")}
|
||||
assetPath={sidebarAssetPath}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div ref={setElementRef}>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue