fix(web): quadrant matrix i18n

This commit is contained in:
yyh
2026-01-16 18:17:28 +08:00
parent 1c55602445
commit ae8618877b
6 changed files with 92 additions and 62 deletions

View File

@ -3,6 +3,7 @@ import type { FC } from 'react'
import type { QuadrantData } from './types'
import { RiExpandDiagonalLine } from '@remixicon/react'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import FullScreenModal from '@/app/components/base/fullscreen-modal'
import QuadrantCard from './quadrant-card'
@ -13,6 +14,7 @@ type QuadrantMatrixProps = {
}
const QuadrantMatrix: FC<QuadrantMatrixProps> = ({ content }) => {
const { t } = useTranslation()
const [isExpanded, setIsExpanded] = useState(false)
const parsedData = useMemo<QuadrantData | null>(() => {
@ -42,9 +44,9 @@ const QuadrantMatrix: FC<QuadrantMatrixProps> = ({ content }) => {
return (
<div className="flex items-center justify-center rounded-xl bg-components-panel-bg-blur p-8">
<div className="text-center text-text-secondary">
<div className="system-md-semibold mb-2">Invalid Quadrant Data</div>
<div className="system-md-semibold mb-2">{t('quadrantMatrix.invalidData', { ns: 'app' })}</div>
<div className="text-sm text-text-tertiary">
Expected JSON format with q1, q2, q3, q4 arrays
{t('quadrantMatrix.invalidDataDesc', { ns: 'app' })}
</div>
</div>
</div>
@ -93,30 +95,17 @@ const QuadrantMatrix: FC<QuadrantMatrixProps> = ({ content }) => {
<div className="mb-4 flex items-center justify-between">
<div>
<div className="system-md-semibold text-text-primary">
Eisenhower Matrix
{t('quadrantMatrix.title', { ns: 'app' })}
</div>
<div className="text-xs text-text-tertiary">
{totalTasks}
{' '}
task
{totalTasks !== 1 ? 's' : ''}
{' '}
prioritized
{t('quadrantMatrix.taskCount', { ns: 'app', count: totalTasks })}
</div>
</div>
{/* Legend + Expand Button */}
<div className="flex items-center gap-3">
<div className="flex items-center gap-3 text-[11px] text-text-quaternary">
<span>
<span className="font-medium text-blue-600">I</span>
{' '}
= Importance
</span>
<span>
<span className="font-medium text-orange-500">U</span>
{' '}
= Urgency
</span>
<span>{t('quadrantMatrix.legend.importance', { ns: 'app' })}</span>
<span>{t('quadrantMatrix.legend.urgency', { ns: 'app' })}</span>
</div>
<ActionButton onClick={handleExpand}>
<RiExpandDiagonalLine className="h-4 w-4" />
@ -139,28 +128,15 @@ const QuadrantMatrix: FC<QuadrantMatrixProps> = ({ content }) => {
<div className="mb-6 flex items-center justify-between">
<div>
<div className="text-xl font-semibold text-text-primary">
Eisenhower Matrix
{t('quadrantMatrix.title', { ns: 'app' })}
</div>
<div className="text-sm text-text-tertiary">
{totalTasks}
{' '}
task
{totalTasks !== 1 ? 's' : ''}
{' '}
prioritized
{t('quadrantMatrix.taskCount', { ns: 'app', count: totalTasks })}
</div>
</div>
<div className="flex items-center gap-3 text-sm text-text-quaternary">
<span>
<span className="font-medium text-blue-600">I</span>
{' '}
= Importance
</span>
<span>
<span className="font-medium text-orange-500">U</span>
{' '}
= Urgency
</span>
<span>{t('quadrantMatrix.legend.importance', { ns: 'app' })}</span>
<span>{t('quadrantMatrix.legend.urgency', { ns: 'app' })}</span>
</div>
</div>

View File

@ -1,6 +1,7 @@
'use client'
import type { FC } from 'react'
import type { QuadrantConfig, Task } from './types'
import { useTranslation } from 'react-i18next'
import { cn } from '@/utils/classnames'
import TaskItem from './task-item'
@ -17,7 +18,8 @@ const QuadrantCard: FC<QuadrantCardProps> = ({
expanded = false,
maxDisplay = 3,
}) => {
const { number, title, subtitle, bgClass, borderClass, titleClass } = config
const { t } = useTranslation()
const { number, titleKey, subtitleKey, bgClass, borderClass, titleClass } = config
const displayLimit = expanded ? Infinity : maxDisplay
const displayTasks = tasks.slice(0, displayLimit)
const remainingCount = Math.max(0, tasks.length - displayLimit)
@ -43,14 +45,14 @@ const QuadrantCard: FC<QuadrantCardProps> = ({
>
{number}
</span>
<span className={cn('system-sm-semibold', titleClass)}>{title}</span>
<span className={cn('system-sm-semibold', titleClass)}>{t(titleKey, { ns: 'app' })}</span>
{tasks.length > 0 && (
<span className="bg-components-badge-bg-gray rounded-full px-1.5 py-0.5 text-[10px] font-medium text-text-tertiary">
{tasks.length}
</span>
)}
</div>
<div className="text-[11px] text-text-tertiary">{subtitle}</div>
<div className="text-[11px] text-text-tertiary">{t(subtitleKey, { ns: 'app' })}</div>
</div>
{/* Task List */}
@ -61,17 +63,28 @@ const QuadrantCard: FC<QuadrantCardProps> = ({
>
{displayTasks.length > 0
? (
displayTasks.map((task, index) => (
<TaskItem
key={`${task.name}-${index}`}
task={task}
expanded={expanded}
/>
))
displayTasks.map((task) => {
const taskKey = [
task.name,
task.deadline ?? 'no-deadline',
task.importance_score,
task.urgency_score,
task.description ?? '',
task.action_advice ?? '',
].join('|')
return (
<TaskItem
key={taskKey}
task={task}
expanded={expanded}
/>
)
})
)
: (
<div className="flex flex-1 items-center justify-center text-xs text-text-quaternary">
No tasks
{t('quadrantMatrix.noTasks', { ns: 'app' })}
</div>
)}
</div>
@ -79,10 +92,7 @@ const QuadrantCard: FC<QuadrantCardProps> = ({
{/* More indicator (only in non-expanded mode) */}
{!expanded && remainingCount > 0 && (
<div className="mt-2 shrink-0 text-center text-[11px] text-text-tertiary">
+
{remainingCount}
{' '}
more
{t('quadrantMatrix.more', { ns: 'app', count: remainingCount })}
</div>
)}
</div>

View File

@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { Task } from './types'
import { RiCalendarLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { cn } from '@/utils/classnames'
type TaskItemProps = {
@ -11,6 +12,7 @@ type TaskItemProps = {
}
const TaskItem: FC<TaskItemProps> = ({ task, expanded = false, showScores = true }) => {
const { t } = useTranslation()
const { name, description, deadline, importance_score, urgency_score, action_advice } = task
return (
@ -57,7 +59,8 @@ const TaskItem: FC<TaskItemProps> = ({ task, expanded = false, showScores = true
<span className="bg-components-badge-bg-gray inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] text-text-tertiary">
<RiCalendarLine className="h-3 w-3" />
<span>
DDL:
{t('quadrantMatrix.deadline', { ns: 'app' })}
{' '}
{deadline}
</span>
</span>

View File

@ -1,6 +1,7 @@
/**
* Type definitions for Eisenhower Matrix (Task Quadrant) visualization
*/
import type { I18nKeysWithPrefix } from '@/types/i18n'
export type Task = {
name: string
@ -18,11 +19,15 @@ export type QuadrantData = {
q4: Task[] // Not Urgent & Not Important - Don't Do
}
type QuadrantKeyBase = I18nKeysWithPrefix<'app', 'quadrantMatrix.q'>
type QuadrantTitleKey = Extract<QuadrantKeyBase, `${string}.title`>
type QuadrantSubtitleKey = Extract<QuadrantKeyBase, `${string}.subtitle`>
export type QuadrantConfig = {
key: 'q1' | 'q2' | 'q3' | 'q4'
number: number
title: string
subtitle: string
titleKey: QuadrantTitleKey // i18n key for title
subtitleKey: QuadrantSubtitleKey // i18n key for subtitle
bgClass: string
borderClass: string
titleClass: string
@ -35,8 +40,8 @@ export const QUADRANT_CONFIGS: Record<string, QuadrantConfig> = {
q1: {
key: 'q1',
number: 1,
title: 'Do First',
subtitle: 'Urgent & Important',
titleKey: 'quadrantMatrix.q1.title',
subtitleKey: 'quadrantMatrix.q1.subtitle',
bgClass: 'bg-state-destructive-hover',
borderClass: 'border-state-destructive-border',
titleClass: 'text-text-destructive',
@ -44,8 +49,8 @@ export const QUADRANT_CONFIGS: Record<string, QuadrantConfig> = {
q2: {
key: 'q2',
number: 2,
title: 'Schedule',
subtitle: 'Important & Not Urgent',
titleKey: 'quadrantMatrix.q2.title',
subtitleKey: 'quadrantMatrix.q2.subtitle',
bgClass: 'bg-state-accent-hover',
borderClass: 'border-state-accent-border',
titleClass: 'text-text-accent',
@ -53,8 +58,8 @@ export const QUADRANT_CONFIGS: Record<string, QuadrantConfig> = {
q3: {
key: 'q3',
number: 3,
title: 'Delegate',
subtitle: 'Urgent & Not Important',
titleKey: 'quadrantMatrix.q3.title',
subtitleKey: 'quadrantMatrix.q3.subtitle',
bgClass: 'bg-state-warning-hover',
borderClass: 'border-state-warning-border',
titleClass: 'text-text-warning',
@ -62,8 +67,8 @@ export const QUADRANT_CONFIGS: Record<string, QuadrantConfig> = {
q4: {
key: 'q4',
number: 4,
title: 'Don\'t Do',
subtitle: 'Not Urgent & Not Important',
titleKey: 'quadrantMatrix.q4.title',
subtitleKey: 'quadrantMatrix.q4.subtitle',
bgClass: 'bg-components-panel-on-panel-item-bg',
borderClass: 'border-divider-regular',
titleClass: 'text-text-tertiary',

View File

@ -196,6 +196,24 @@
"publishApp.notSet": "Not set",
"publishApp.notSetDesc": "Currently nobody can access the web app. Please set permissions.",
"publishApp.title": "Who can access web app",
"quadrantMatrix.deadline": "DDL:",
"quadrantMatrix.invalidData": "Invalid Quadrant Data",
"quadrantMatrix.invalidDataDesc": "Expected JSON format with q1, q2, q3, q4 arrays",
"quadrantMatrix.legend.importance": "I = Importance",
"quadrantMatrix.legend.urgency": "U = Urgency",
"quadrantMatrix.more": "+{{count}} more",
"quadrantMatrix.noTasks": "No tasks",
"quadrantMatrix.q1.subtitle": "Urgent & Important",
"quadrantMatrix.q1.title": "Do First",
"quadrantMatrix.q2.subtitle": "Important & Not Urgent",
"quadrantMatrix.q2.title": "Schedule",
"quadrantMatrix.q3.subtitle": "Urgent & Not Important",
"quadrantMatrix.q3.title": "Delegate",
"quadrantMatrix.q4.subtitle": "Not Urgent & Not Important",
"quadrantMatrix.q4.title": "Don't Do",
"quadrantMatrix.taskCount_one": "{{count}} task prioritized",
"quadrantMatrix.taskCount_other": "{{count}} tasks prioritized",
"quadrantMatrix.title": "Eisenhower Matrix",
"removeOriginal": "Delete the original app",
"roadmap": "See our roadmap",
"showMyCreatedAppsOnly": "Created by me",

View File

@ -196,6 +196,24 @@
"publishApp.notSet": "未设置",
"publishApp.notSetDesc": "当前任何人都无法访问 Web 应用。请设置访问权限。",
"publishApp.title": "谁可以访问 web 应用",
"quadrantMatrix.deadline": "截止:",
"quadrantMatrix.invalidData": "无效的象限数据",
"quadrantMatrix.invalidDataDesc": "需要包含 q1, q2, q3, q4 数组的 JSON 格式",
"quadrantMatrix.legend.importance": "I = 重要性",
"quadrantMatrix.legend.urgency": "U = 紧急性",
"quadrantMatrix.more": "+{{count}} 更多",
"quadrantMatrix.noTasks": "暂无任务",
"quadrantMatrix.q1.subtitle": "紧急且重要",
"quadrantMatrix.q1.title": "立即执行",
"quadrantMatrix.q2.subtitle": "重要但不紧急",
"quadrantMatrix.q2.title": "计划安排",
"quadrantMatrix.q3.subtitle": "紧急但不重要",
"quadrantMatrix.q3.title": "委派他人",
"quadrantMatrix.q4.subtitle": "不紧急也不重要",
"quadrantMatrix.q4.title": "不要做",
"quadrantMatrix.taskCount_one": "{{count}} 个任务已排序",
"quadrantMatrix.taskCount_other": "{{count}} 个任务已排序",
"quadrantMatrix.title": "艾森豪威尔矩阵",
"removeOriginal": "删除原应用",
"roadmap": "产品路线图",
"showMyCreatedAppsOnly": "我创建的",