mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
Fix task page labels after language changes (#5086)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -3,6 +3,7 @@ task source controls, and GitHub task list co-located so the wiring between the
|
||||
selected repo, the task filters, and the work-item list stays readable in one
|
||||
place while this surface is still evolving. */
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import {
|
||||
AlertCircle,
|
||||
@@ -19,11 +20,8 @@ import {
|
||||
ExternalLink,
|
||||
Eye,
|
||||
Files,
|
||||
Github,
|
||||
Gitlab,
|
||||
GitMerge,
|
||||
GitPullRequest,
|
||||
LayoutGrid,
|
||||
List,
|
||||
LoaderCircle,
|
||||
Lock,
|
||||
@@ -224,26 +222,30 @@ import {
|
||||
resolveVisibleTaskProvider
|
||||
} from '../../../shared/task-providers'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type TaskSource = TaskProvider
|
||||
|
||||
type GitLabTaskFilter = 'opened' | 'merged' | 'closed' | 'all'
|
||||
type GitLabIssueFilter = 'opened' | 'assigned-to-me'
|
||||
|
||||
const GITLAB_MR_FILTERS: { id: GitLabTaskFilter; label: string }[] = [
|
||||
{ id: 'opened', label: translate('auto.components.TaskPage.606a85c774', 'Open') },
|
||||
{ id: 'merged', label: translate('auto.components.TaskPage.37a82eaaf8', 'Merged') },
|
||||
{ id: 'closed', label: translate('auto.components.TaskPage.d09bf34db7', 'Closed') },
|
||||
{ id: 'all', label: translate('auto.components.TaskPage.c2268a9982', 'All') }
|
||||
]
|
||||
|
||||
const GITLAB_ISSUE_FILTERS: { id: GitLabIssueFilter; label: string }[] = [
|
||||
{ id: 'opened', label: translate('auto.components.TaskPage.606a85c774', 'Open') },
|
||||
{
|
||||
id: 'assigned-to-me',
|
||||
label: translate('auto.components.TaskPage.94f0339621', 'Assigned to me')
|
||||
}
|
||||
]
|
||||
import {
|
||||
getGitHubModeButtons,
|
||||
getGitHubTaskKindPresets,
|
||||
getGitLabIssueFilters,
|
||||
getGitLabMRFilters,
|
||||
getJiraPresets,
|
||||
getLinearDisplayProperties,
|
||||
getLinearGroupOptions,
|
||||
getLinearModeOptions,
|
||||
getLinearOrderOptions,
|
||||
getLinearPriorityLabel,
|
||||
getLinearViewOptions,
|
||||
getSourceOptions,
|
||||
type GitHubTaskKind,
|
||||
type GitLabIssueFilter,
|
||||
type GitLabTaskFilter,
|
||||
type JiraPresetId,
|
||||
LinearIcon,
|
||||
type LinearDisplayProperty,
|
||||
type LinearGroupBy,
|
||||
type LinearMode,
|
||||
type LinearOrderBy,
|
||||
type LinearViewMode
|
||||
} from '@/components/task-page-localized-options'
|
||||
|
||||
function isGitLabMRFilter(value: GitLabTaskFilter | GitLabIssueFilter): value is GitLabTaskFilter {
|
||||
return value === 'opened' || value === 'merged' || value === 'closed' || value === 'all'
|
||||
@@ -254,95 +256,6 @@ function isGitLabIssueFilter(
|
||||
): value is GitLabIssueFilter {
|
||||
return value === 'opened' || value === 'assigned-to-me'
|
||||
}
|
||||
type TaskQueryPreset = {
|
||||
id: TaskViewPresetId
|
||||
label: string
|
||||
query: string
|
||||
}
|
||||
type GitHubTaskKind = 'issues' | 'prs'
|
||||
|
||||
const ISSUE_TASK_QUERY_PRESETS: TaskQueryPreset[] = [
|
||||
{
|
||||
id: 'issues',
|
||||
label: translate('auto.components.TaskPage.606a85c774', 'Open'),
|
||||
query: getTaskPresetQuery('issues')
|
||||
},
|
||||
{
|
||||
id: 'my-issues',
|
||||
label: translate('auto.components.TaskPage.94f0339621', 'Assigned to me'),
|
||||
query: getTaskPresetQuery('my-issues')
|
||||
}
|
||||
]
|
||||
|
||||
const PR_TASK_QUERY_PRESETS: TaskQueryPreset[] = [
|
||||
{
|
||||
id: 'prs',
|
||||
label: translate('auto.components.TaskPage.606a85c774', 'Open'),
|
||||
query: getTaskPresetQuery('prs')
|
||||
},
|
||||
{
|
||||
id: 'my-prs',
|
||||
label: translate('auto.components.TaskPage.7698af5263', 'Mine'),
|
||||
query: getTaskPresetQuery('my-prs')
|
||||
},
|
||||
{
|
||||
id: 'review',
|
||||
label: translate('auto.components.TaskPage.524f095d55', 'Needs review'),
|
||||
query: getTaskPresetQuery('review')
|
||||
}
|
||||
]
|
||||
|
||||
function getGitHubTaskKindPresets(kind: GitHubTaskKind): TaskQueryPreset[] {
|
||||
return kind === 'prs' ? PR_TASK_QUERY_PRESETS : ISSUE_TASK_QUERY_PRESETS
|
||||
}
|
||||
|
||||
type SourceOption = {
|
||||
id: TaskSource
|
||||
label: string
|
||||
Icon: (props: { className?: string }) => React.JSX.Element
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
function LinearIcon({ className }: { className?: string }): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden className={className} fill="currentColor">
|
||||
<path d="M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
const SOURCE_OPTIONS: SourceOption[] = [
|
||||
{
|
||||
id: 'github',
|
||||
label: translate('auto.components.TaskPage.acef77f7ca', 'GitHub'),
|
||||
Icon: ({ className }) => <Github className={className} />
|
||||
},
|
||||
{
|
||||
id: 'gitlab',
|
||||
label: translate('auto.components.TaskPage.11a828abf8', 'GitLab'),
|
||||
Icon: ({ className }) => <Gitlab className={className} />
|
||||
},
|
||||
{
|
||||
id: 'linear',
|
||||
label: translate('auto.components.TaskPage.8675cd6188', 'Linear'),
|
||||
Icon: ({ className }) => <LinearIcon className={className} />
|
||||
},
|
||||
{
|
||||
id: 'jira',
|
||||
label: translate('auto.components.TaskPage.9cd11ba218', 'Jira'),
|
||||
Icon: ({ className }) => <JiraIcon className={className} />
|
||||
}
|
||||
]
|
||||
|
||||
type JiraPresetId = 'assigned' | 'reported' | 'all' | 'done'
|
||||
type JiraPreset = { id: JiraPresetId; label: string }
|
||||
|
||||
const JIRA_PRESETS: JiraPreset[] = [
|
||||
{ id: 'assigned', label: translate('auto.components.TaskPage.1301d376f1', 'Assigned') },
|
||||
{ id: 'reported', label: translate('auto.components.TaskPage.bd9965df51', 'Reported') },
|
||||
{ id: 'all', label: translate('auto.components.TaskPage.4b6e40e42c', 'All Open') },
|
||||
{ id: 'done', label: translate('auto.components.TaskPage.18451e99df', 'Done') }
|
||||
]
|
||||
|
||||
const TASK_SEARCH_DEBOUNCE_MS = 300
|
||||
const LINEAR_ITEM_LIMIT = 36
|
||||
@@ -407,14 +320,6 @@ const GITHUB_TASK_STICKY_TITLE_CELL_CLASS = cn(
|
||||
GITHUB_TASK_ROW_HOVER_SURFACE_CLASS
|
||||
)
|
||||
|
||||
type GitHubModeButton = { id: GitHubTaskKind | 'project'; label: string }
|
||||
|
||||
const GITHUB_MODE_BUTTONS: GitHubModeButton[] = [
|
||||
{ id: 'issues', label: translate('auto.components.TaskPage.dfc0c79bd8', 'Issues') },
|
||||
{ id: 'prs', label: translate('auto.components.TaskPage.137e2a8a01', 'PRs') },
|
||||
{ id: 'project', label: translate('auto.components.TaskPage.727069bee5', 'Projects') }
|
||||
]
|
||||
|
||||
function isPRFocusedTaskView(preset: TaskViewPresetId | null, query: string): boolean {
|
||||
if (preset === 'prs' || preset === 'my-prs' || preset === 'review') {
|
||||
return true
|
||||
@@ -483,22 +388,7 @@ function formatRelativeTime(input: string): string {
|
||||
return relativeTimeFormatter.format(diffDays, 'day')
|
||||
}
|
||||
|
||||
// Why: Linear encodes priority as an integer (0–4). Map to human-readable
|
||||
// labels so the table column is scannable without memorising the scale.
|
||||
const LINEAR_PRIORITY_LABELS: Record<number, string> = {
|
||||
0: 'None',
|
||||
1: 'Urgent',
|
||||
2: 'High',
|
||||
3: 'Medium',
|
||||
4: 'Low'
|
||||
}
|
||||
|
||||
type LinearViewMode = 'list' | 'board'
|
||||
type LinearMode = 'issues' | 'projects' | 'views'
|
||||
type LinearProjectTab = 'overview' | 'issues'
|
||||
type LinearGroupBy = 'none' | 'status' | 'assignee' | 'priority' | 'team'
|
||||
type LinearOrderBy = 'priority' | 'updated' | 'identifier'
|
||||
type LinearDisplayProperty = 'state' | 'priority' | 'assignee' | 'team' | 'labels' | 'updated'
|
||||
|
||||
type LinearGroupSection = {
|
||||
key: string
|
||||
@@ -512,27 +402,8 @@ type LinearIssueListRow =
|
||||
|
||||
const LINEAR_BOARD_DRAG_ISSUE_MIME = 'application/x-orca-linear-issue-id'
|
||||
|
||||
const LINEAR_MODE_OPTIONS: { id: LinearMode; label: string }[] = [
|
||||
{ id: 'issues', label: translate('auto.components.TaskPage.dfc0c79bd8', 'Issues') },
|
||||
{ id: 'projects', label: translate('auto.components.TaskPage.727069bee5', 'Projects') },
|
||||
{ id: 'views', label: translate('auto.components.TaskPage.e78ec261ed', 'Views') }
|
||||
]
|
||||
|
||||
const LINEAR_CUSTOM_VIEW_MODELS = ['issue', 'project'] satisfies readonly LinearCustomViewModel[]
|
||||
|
||||
const LINEAR_VIEW_OPTIONS: {
|
||||
id: LinearViewMode
|
||||
label: string
|
||||
Icon: typeof List
|
||||
}[] = [
|
||||
{ id: 'list', label: translate('auto.components.TaskPage.a6f7e93d7f', 'List'), Icon: List },
|
||||
{
|
||||
id: 'board',
|
||||
label: translate('auto.components.TaskPage.d747aed72f', 'Board'),
|
||||
Icon: LayoutGrid
|
||||
}
|
||||
]
|
||||
|
||||
function mergeLinearCollectionResults<T>(
|
||||
results: LinearCollectionResult<T>[]
|
||||
): LinearCollectionResult<T> {
|
||||
@@ -544,29 +415,6 @@ function mergeLinearCollectionResults<T>(
|
||||
}
|
||||
}
|
||||
|
||||
const LINEAR_GROUP_OPTIONS: { id: LinearGroupBy; label: string }[] = [
|
||||
{ id: 'none', label: translate('auto.components.TaskPage.50387522d7', 'No grouping') },
|
||||
{ id: 'status', label: translate('auto.components.TaskPage.154b0fa623', 'Status') },
|
||||
{ id: 'assignee', label: translate('auto.components.TaskPage.d2a876ca53', 'Assignee') },
|
||||
{ id: 'priority', label: translate('auto.components.TaskPage.c8d5bec5f7', 'Priority') },
|
||||
{ id: 'team', label: translate('auto.components.TaskPage.a98cbe7664', 'Team') }
|
||||
]
|
||||
|
||||
const LINEAR_ORDER_OPTIONS: { id: LinearOrderBy; label: string }[] = [
|
||||
{ id: 'priority', label: translate('auto.components.TaskPage.c8d5bec5f7', 'Priority') },
|
||||
{ id: 'updated', label: translate('auto.components.TaskPage.f362667d55', 'Updated') },
|
||||
{ id: 'identifier', label: translate('auto.components.TaskPage.d8a517ad89', 'Identifier') }
|
||||
]
|
||||
|
||||
const LINEAR_DISPLAY_PROPERTIES: { id: LinearDisplayProperty; label: string }[] = [
|
||||
{ id: 'state', label: translate('auto.components.TaskPage.154b0fa623', 'Status') },
|
||||
{ id: 'priority', label: translate('auto.components.TaskPage.c8d5bec5f7', 'Priority') },
|
||||
{ id: 'assignee', label: translate('auto.components.TaskPage.d2a876ca53', 'Assignee') },
|
||||
{ id: 'team', label: translate('auto.components.TaskPage.a98cbe7664', 'Team') },
|
||||
{ id: 'labels', label: translate('auto.components.TaskPage.d0ca4aa1d0', 'Labels') },
|
||||
{ id: 'updated', label: translate('auto.components.TaskPage.f362667d55', 'Updated') }
|
||||
]
|
||||
|
||||
const DEFAULT_LINEAR_DISPLAY_PROPERTIES: LinearDisplayProperty[] = [
|
||||
'state',
|
||||
'priority',
|
||||
@@ -576,10 +424,6 @@ const DEFAULT_LINEAR_DISPLAY_PROPERTIES: LinearDisplayProperty[] = [
|
||||
'updated'
|
||||
]
|
||||
|
||||
function getLinearPriorityLabel(priority: number): string {
|
||||
return LINEAR_PRIORITY_LABELS[priority] ?? `P${priority}`
|
||||
}
|
||||
|
||||
function getLinearStatusSectionState(section: LinearGroupSection): LinearIssue['state'] | null {
|
||||
if (!section.key.startsWith('status:')) {
|
||||
return null
|
||||
@@ -2587,6 +2431,7 @@ const hasUpstreamCandidateDivergence = (
|
||||
!sameGitHubOwnerRepo(s.sources.prs, s.sources.upstreamCandidate)
|
||||
|
||||
export default function TaskPage(): React.JSX.Element {
|
||||
useTranslation()
|
||||
const settings = useAppStore((s) => s.settings)
|
||||
const persistedUIReady = useAppStore((s) => s.persistedUIReady)
|
||||
const taskResumeState = useAppStore((s) => s.taskResumeState)
|
||||
@@ -2744,9 +2589,19 @@ export default function TaskPage(): React.JSX.Element {
|
||||
preflightStatus?.glab?.installed
|
||||
]
|
||||
)
|
||||
const sourceOptions = getSourceOptions()
|
||||
const githubModeButtons = getGitHubModeButtons()
|
||||
const linearModeOptions = getLinearModeOptions()
|
||||
const jiraPresets = getJiraPresets()
|
||||
const gitLabIssueFilters = getGitLabIssueFilters()
|
||||
const gitLabMRFilters = getGitLabMRFilters()
|
||||
const linearViewOptions = getLinearViewOptions()
|
||||
const linearGroupOptions = getLinearGroupOptions()
|
||||
const linearOrderOptions = getLinearOrderOptions()
|
||||
const linearDisplayPropertyOptions = getLinearDisplayProperties()
|
||||
const visibleSourceOptions = useMemo(
|
||||
() => SOURCE_OPTIONS.filter((source) => visibleTaskProviders.includes(source.id)),
|
||||
[visibleTaskProviders]
|
||||
() => sourceOptions.filter((source) => visibleTaskProviders.includes(source.id)),
|
||||
[sourceOptions, visibleTaskProviders]
|
||||
)
|
||||
const hideTaskSource = useCallback(
|
||||
(provider: TaskProvider, label: string) => {
|
||||
@@ -2785,7 +2640,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
const initialTaskQuery = getTaskPresetQuery(defaultTaskViewPreset)
|
||||
|
||||
const preferredTaskSource = pageData.taskSource ?? defaultTaskSource
|
||||
const [taskSource, setTaskSource] = useState<TaskSource>(
|
||||
const [taskSource, setTaskSource] = useState<TaskProvider>(
|
||||
resolveVisibleTaskProvider(preferredTaskSource, visibleTaskProviders)
|
||||
)
|
||||
const taskSourceManuallyChangedRef = useRef(false)
|
||||
@@ -6778,7 +6633,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
{projectModeVisible ? (
|
||||
<div className="flex items-center gap-1 text-xs">
|
||||
{GITHUB_MODE_BUTTONS.map((mode) => {
|
||||
{githubModeButtons.map((mode) => {
|
||||
const active =
|
||||
mode.id === 'project'
|
||||
? githubMode === 'project'
|
||||
@@ -7137,7 +6992,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
'Linear task mode'
|
||||
)}
|
||||
>
|
||||
{LINEAR_MODE_OPTIONS.map((mode) => {
|
||||
{linearModeOptions.map((mode) => {
|
||||
const active = linearMode === mode.id
|
||||
return (
|
||||
<button
|
||||
@@ -7355,7 +7210,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
<div className="rounded-md rounded-b-none border border-border/50 bg-muted/50 p-3 shadow-sm">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{JIRA_PRESETS.map((preset) => {
|
||||
{jiraPresets.map((preset) => {
|
||||
const active = !jiraSearchInput && activeJiraPreset === preset.id
|
||||
return (
|
||||
<button
|
||||
@@ -7564,8 +7419,8 @@ export default function TaskPage(): React.JSX.Element {
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{gitlabView === 'issues' || gitlabView === 'mrs'
|
||||
? (gitlabView === 'issues'
|
||||
? GITLAB_ISSUE_FILTERS
|
||||
: GITLAB_MR_FILTERS
|
||||
? gitLabIssueFilters
|
||||
: gitLabMRFilters
|
||||
).map(({ id, label }) => {
|
||||
const active = activeGitlabFilter === id
|
||||
return (
|
||||
@@ -8968,7 +8823,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
'Linear view mode'
|
||||
)}
|
||||
>
|
||||
{LINEAR_VIEW_OPTIONS.map(({ id, label, Icon }) => {
|
||||
{linearViewOptions.map(({ id, label, Icon }) => {
|
||||
const active = linearViewMode === id
|
||||
return (
|
||||
<Tooltip key={id}>
|
||||
@@ -9019,7 +8874,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
value={linearViewMode}
|
||||
onValueChange={(value) => setLinearViewMode(value as LinearViewMode)}
|
||||
>
|
||||
{LINEAR_VIEW_OPTIONS.map(({ id, label, Icon }) => (
|
||||
{linearViewOptions.map(({ id, label, Icon }) => (
|
||||
<DropdownMenuRadioItem key={id} value={id}>
|
||||
<Icon className="size-3.5" />
|
||||
{label}
|
||||
@@ -9035,7 +8890,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
value={linearGroupBy}
|
||||
onValueChange={(value) => setLinearGroupBy(value as LinearGroupBy)}
|
||||
>
|
||||
{LINEAR_GROUP_OPTIONS.map((option) => (
|
||||
{linearGroupOptions.map((option) => (
|
||||
<DropdownMenuRadioItem key={option.id} value={option.id}>
|
||||
{option.label}
|
||||
</DropdownMenuRadioItem>
|
||||
@@ -9050,7 +8905,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
value={linearOrderBy}
|
||||
onValueChange={(value) => setLinearOrderBy(value as LinearOrderBy)}
|
||||
>
|
||||
{LINEAR_ORDER_OPTIONS.map((option) => (
|
||||
{linearOrderOptions.map((option) => (
|
||||
<DropdownMenuRadioItem key={option.id} value={option.id}>
|
||||
{option.label}
|
||||
</DropdownMenuRadioItem>
|
||||
@@ -9061,7 +8916,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
<Eye className="size-3.5" />
|
||||
{translate('auto.components.TaskPage.a26a48252e', 'Display properties')}
|
||||
</DropdownMenuLabel>
|
||||
{LINEAR_DISPLAY_PROPERTIES.map((property) => (
|
||||
{linearDisplayPropertyOptions.map((property) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={property.id}
|
||||
checked={effectiveLinearDisplayProperties.has(property.id)}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import {
|
||||
getGitHubModeButtons,
|
||||
getGitHubTaskKindPresets,
|
||||
getLinearPriorityLabel
|
||||
} from './task-page-localized-options'
|
||||
|
||||
describe('task-page-localized-options', () => {
|
||||
beforeEach(async () => {
|
||||
await i18n.changeLanguage('en')
|
||||
})
|
||||
|
||||
it('refreshes GitHub task labels when the UI language changes', async () => {
|
||||
expect(getGitHubTaskKindPresets('issues').map((preset) => preset.label)).toEqual([
|
||||
'Open',
|
||||
'Assigned to me'
|
||||
])
|
||||
expect(getGitHubModeButtons().map((button) => button.label)).toEqual([
|
||||
'Issues',
|
||||
'PRs',
|
||||
'Projects'
|
||||
])
|
||||
|
||||
await i18n.changeLanguage('ko')
|
||||
|
||||
expect(getGitHubTaskKindPresets('issues').map((preset) => preset.label)).toEqual([
|
||||
'열기',
|
||||
'나에게 할당됨'
|
||||
])
|
||||
expect(getGitHubModeButtons().map((button) => button.label)).toEqual(['이슈', 'PR', '프로젝트'])
|
||||
|
||||
await i18n.changeLanguage('en')
|
||||
|
||||
expect(getGitHubTaskKindPresets('issues').map((preset) => preset.label)).toEqual([
|
||||
'Open',
|
||||
'Assigned to me'
|
||||
])
|
||||
expect(getGitHubModeButtons().map((button) => button.label)).toEqual([
|
||||
'Issues',
|
||||
'PRs',
|
||||
'Projects'
|
||||
])
|
||||
})
|
||||
|
||||
it('refreshes Linear priority labels when the UI language changes', async () => {
|
||||
expect(getLinearPriorityLabel(0)).toBe('No priority')
|
||||
|
||||
await i18n.changeLanguage('ko')
|
||||
|
||||
expect(getLinearPriorityLabel(0)).toBe('우선순위 없음')
|
||||
|
||||
await i18n.changeLanguage('en')
|
||||
|
||||
expect(getLinearPriorityLabel(0)).toBe('No priority')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,207 @@
|
||||
import React from 'react'
|
||||
import { Github, Gitlab, LayoutGrid, List } from 'lucide-react'
|
||||
|
||||
import { JiraIcon } from '@/components/icons/JiraIcon'
|
||||
import { createLocalizedCatalog } from '@/i18n/localized-catalog'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { getTaskPresetQuery } from '@/lib/new-workspace'
|
||||
import type { TaskProvider, TaskViewPresetId } from '../../../shared/types'
|
||||
|
||||
export type GitLabTaskFilter = 'opened' | 'merged' | 'closed' | 'all'
|
||||
export type GitLabIssueFilter = 'opened' | 'assigned-to-me'
|
||||
|
||||
export type TaskQueryPreset = {
|
||||
id: TaskViewPresetId
|
||||
label: string
|
||||
query: string
|
||||
}
|
||||
|
||||
export type GitHubTaskKind = 'issues' | 'prs'
|
||||
|
||||
export type SourceOption = {
|
||||
id: TaskProvider
|
||||
label: string
|
||||
Icon: (props: { className?: string }) => React.JSX.Element
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export type JiraPresetId = 'assigned' | 'reported' | 'all' | 'done'
|
||||
export type JiraPreset = { id: JiraPresetId; label: string }
|
||||
|
||||
export type GitHubModeButton = { id: GitHubTaskKind | 'project'; label: string }
|
||||
|
||||
export type LinearViewMode = 'list' | 'board'
|
||||
export type LinearMode = 'issues' | 'projects' | 'views'
|
||||
export type LinearGroupBy = 'none' | 'status' | 'assignee' | 'priority' | 'team'
|
||||
export type LinearOrderBy = 'priority' | 'updated' | 'identifier'
|
||||
export type LinearDisplayProperty =
|
||||
| 'state'
|
||||
| 'priority'
|
||||
| 'assignee'
|
||||
| 'team'
|
||||
| 'labels'
|
||||
| 'updated'
|
||||
|
||||
export function LinearIcon({ className }: { className?: string }): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden className={className} fill="currentColor">
|
||||
<path d="M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const getGitLabMRFilters = createLocalizedCatalog(
|
||||
(): { id: GitLabTaskFilter; label: string }[] => [
|
||||
{ id: 'opened', label: translate('auto.components.TaskPage.606a85c774', 'Open') },
|
||||
{ id: 'merged', label: translate('auto.components.TaskPage.37a82eaaf8', 'Merged') },
|
||||
{ id: 'closed', label: translate('auto.components.TaskPage.d09bf34db7', 'Closed') },
|
||||
{ id: 'all', label: translate('auto.components.TaskPage.c2268a9982', 'All') }
|
||||
]
|
||||
)
|
||||
|
||||
export const getGitLabIssueFilters = createLocalizedCatalog(
|
||||
(): { id: GitLabIssueFilter; label: string }[] => [
|
||||
{ id: 'opened', label: translate('auto.components.TaskPage.606a85c774', 'Open') },
|
||||
{
|
||||
id: 'assigned-to-me',
|
||||
label: translate('auto.components.TaskPage.94f0339621', 'Assigned to me')
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
const getIssueTaskQueryPresets = createLocalizedCatalog((): TaskQueryPreset[] => [
|
||||
{
|
||||
id: 'issues',
|
||||
label: translate('auto.components.TaskPage.606a85c774', 'Open'),
|
||||
query: getTaskPresetQuery('issues')
|
||||
},
|
||||
{
|
||||
id: 'my-issues',
|
||||
label: translate('auto.components.TaskPage.94f0339621', 'Assigned to me'),
|
||||
query: getTaskPresetQuery('my-issues')
|
||||
}
|
||||
])
|
||||
|
||||
const getPRTaskQueryPresets = createLocalizedCatalog((): TaskQueryPreset[] => [
|
||||
{
|
||||
id: 'prs',
|
||||
label: translate('auto.components.TaskPage.606a85c774', 'Open'),
|
||||
query: getTaskPresetQuery('prs')
|
||||
},
|
||||
{
|
||||
id: 'my-prs',
|
||||
label: translate('auto.components.TaskPage.7698af5263', 'Mine'),
|
||||
query: getTaskPresetQuery('my-prs')
|
||||
},
|
||||
{
|
||||
id: 'review',
|
||||
label: translate('auto.components.TaskPage.524f095d55', 'Needs review'),
|
||||
query: getTaskPresetQuery('review')
|
||||
}
|
||||
])
|
||||
|
||||
export function getGitHubTaskKindPresets(kind: GitHubTaskKind): TaskQueryPreset[] {
|
||||
return kind === 'prs' ? getPRTaskQueryPresets() : getIssueTaskQueryPresets()
|
||||
}
|
||||
|
||||
export const getSourceOptions = createLocalizedCatalog((): SourceOption[] => [
|
||||
{
|
||||
id: 'github',
|
||||
label: translate('auto.components.TaskPage.acef77f7ca', 'GitHub'),
|
||||
Icon: ({ className }) => <Github className={className} />
|
||||
},
|
||||
{
|
||||
id: 'gitlab',
|
||||
label: translate('auto.components.TaskPage.11a828abf8', 'GitLab'),
|
||||
Icon: ({ className }) => <Gitlab className={className} />
|
||||
},
|
||||
{
|
||||
id: 'linear',
|
||||
label: translate('auto.components.TaskPage.8675cd6188', 'Linear'),
|
||||
Icon: ({ className }) => <LinearIcon className={className} />
|
||||
},
|
||||
{
|
||||
id: 'jira',
|
||||
label: translate('auto.components.TaskPage.9cd11ba218', 'Jira'),
|
||||
Icon: ({ className }) => <JiraIcon className={className} />
|
||||
}
|
||||
])
|
||||
|
||||
export const getJiraPresets = createLocalizedCatalog((): JiraPreset[] => [
|
||||
{ id: 'assigned', label: translate('auto.components.TaskPage.1301d376f1', 'Assigned') },
|
||||
{ id: 'reported', label: translate('auto.components.TaskPage.bd9965df51', 'Reported') },
|
||||
{ id: 'all', label: translate('auto.components.TaskPage.4b6e40e42c', 'All Open') },
|
||||
{ id: 'done', label: translate('auto.components.TaskPage.18451e99df', 'Done') }
|
||||
])
|
||||
|
||||
export const getGitHubModeButtons = createLocalizedCatalog((): GitHubModeButton[] => [
|
||||
{ id: 'issues', label: translate('auto.components.TaskPage.dfc0c79bd8', 'Issues') },
|
||||
{ id: 'prs', label: translate('auto.components.TaskPage.137e2a8a01', 'PRs') },
|
||||
{ id: 'project', label: translate('auto.components.TaskPage.727069bee5', 'Projects') }
|
||||
])
|
||||
|
||||
export const getLinearModeOptions = createLocalizedCatalog(
|
||||
(): { id: LinearMode; label: string }[] => [
|
||||
{ id: 'issues', label: translate('auto.components.TaskPage.dfc0c79bd8', 'Issues') },
|
||||
{ id: 'projects', label: translate('auto.components.TaskPage.727069bee5', 'Projects') },
|
||||
{ id: 'views', label: translate('auto.components.TaskPage.e78ec261ed', 'Views') }
|
||||
]
|
||||
)
|
||||
|
||||
export const getLinearViewOptions = createLocalizedCatalog(
|
||||
(): {
|
||||
id: LinearViewMode
|
||||
label: string
|
||||
Icon: typeof List
|
||||
}[] => [
|
||||
{ id: 'list', label: translate('auto.components.TaskPage.a6f7e93d7f', 'List'), Icon: List },
|
||||
{
|
||||
id: 'board',
|
||||
label: translate('auto.components.TaskPage.d747aed72f', 'Board'),
|
||||
Icon: LayoutGrid
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
export const getLinearGroupOptions = createLocalizedCatalog(
|
||||
(): { id: LinearGroupBy; label: string }[] => [
|
||||
{ id: 'none', label: translate('auto.components.TaskPage.50387522d7', 'No grouping') },
|
||||
{ id: 'status', label: translate('auto.components.TaskPage.154b0fa623', 'Status') },
|
||||
{ id: 'assignee', label: translate('auto.components.TaskPage.d2a876ca53', 'Assignee') },
|
||||
{ id: 'priority', label: translate('auto.components.TaskPage.c8d5bec5f7', 'Priority') },
|
||||
{ id: 'team', label: translate('auto.components.TaskPage.a98cbe7664', 'Team') }
|
||||
]
|
||||
)
|
||||
|
||||
export const getLinearOrderOptions = createLocalizedCatalog(
|
||||
(): { id: LinearOrderBy; label: string }[] => [
|
||||
{ id: 'priority', label: translate('auto.components.TaskPage.c8d5bec5f7', 'Priority') },
|
||||
{ id: 'updated', label: translate('auto.components.TaskPage.f362667d55', 'Updated') },
|
||||
{ id: 'identifier', label: translate('auto.components.TaskPage.d8a517ad89', 'Identifier') }
|
||||
]
|
||||
)
|
||||
|
||||
export const getLinearDisplayProperties = createLocalizedCatalog(
|
||||
(): { id: LinearDisplayProperty; label: string }[] => [
|
||||
{ id: 'state', label: translate('auto.components.TaskPage.154b0fa623', 'Status') },
|
||||
{ id: 'priority', label: translate('auto.components.TaskPage.c8d5bec5f7', 'Priority') },
|
||||
{ id: 'assignee', label: translate('auto.components.TaskPage.d2a876ca53', 'Assignee') },
|
||||
{ id: 'team', label: translate('auto.components.TaskPage.a98cbe7664', 'Team') },
|
||||
{ id: 'labels', label: translate('auto.components.TaskPage.d0ca4aa1d0', 'Labels') },
|
||||
{ id: 'updated', label: translate('auto.components.TaskPage.f362667d55', 'Updated') }
|
||||
]
|
||||
)
|
||||
|
||||
export const getLinearPriorityLabels = createLocalizedCatalog(
|
||||
(): Record<number, string> => ({
|
||||
0: translate('auto.components.TaskPage.713179dfdc', 'No priority'),
|
||||
1: translate('auto.components.TaskPage.f373ab1a4f', 'Urgent'),
|
||||
2: translate('auto.components.TaskPage.345b169f1f', 'High'),
|
||||
3: translate('auto.components.TaskPage.7fd59c18d8', 'Medium'),
|
||||
4: translate('auto.components.TaskPage.69591944e7', 'Low')
|
||||
})
|
||||
)
|
||||
|
||||
export function getLinearPriorityLabel(priority: number): string {
|
||||
return getLinearPriorityLabels()[priority] ?? `P${priority}`
|
||||
}
|
||||
Reference in New Issue
Block a user