mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-01 01:16:17 +02:00
feat(i18n): translate chat session delete + project resources section
Two features main shipped while this branch was idle never went through the i18n pass: - Chat session delete confirmation dialog (#2115) and history toggle tooltip (#2117): adds session_history.delete_dialog.* and session_history.row_delete_*, plus window.history_show_tooltip / history_back_tooltip. - Project resources sidebar (#1926/#2080/#2111): entire component including toasts, popover form, attach/remove tooltips. New projects.resources subtree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -124,22 +124,25 @@ export function ChatSessionHistory() {
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Delete chat session</AlertDialogTitle>
|
||||
<AlertDialogTitle>{t(($) => $.session_history.delete_dialog.title)}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{pendingDelete?.title
|
||||
? `"${pendingDelete.title}" and its messages will be permanently removed.`
|
||||
: "This chat session and its messages will be permanently removed."}
|
||||
{" "}This action cannot be undone.
|
||||
? t(($) => $.session_history.delete_dialog.description_with_title, { title: pendingDelete.title })
|
||||
: t(($) => $.session_history.delete_dialog.description_default)}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={deleteSession.isPending}>Cancel</AlertDialogCancel>
|
||||
<AlertDialogCancel disabled={deleteSession.isPending}>
|
||||
{t(($) => $.session_history.delete_dialog.cancel)}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={handleConfirmDelete}
|
||||
disabled={deleteSession.isPending}
|
||||
className="bg-destructive text-white hover:bg-destructive/90"
|
||||
>
|
||||
{deleteSession.isPending ? "Deleting..." : "Delete"}
|
||||
{deleteSession.isPending
|
||||
? t(($) => $.session_history.delete_dialog.confirming)
|
||||
: t(($) => $.session_history.delete_dialog.confirm)}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
@@ -228,13 +231,13 @@ function SessionItem({
|
||||
e.stopPropagation();
|
||||
onRequestDelete();
|
||||
}}
|
||||
aria-label="Delete chat session"
|
||||
aria-label={t(($) => $.session_history.row_delete_aria)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Trash2 className="size-3.5" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="left">Delete</TooltipContent>
|
||||
<TooltipContent side="left">{t(($) => $.session_history.row_delete_tooltip)}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -426,7 +426,7 @@ export function ChatWindow() {
|
||||
<History />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">
|
||||
{showHistory ? "Back to chat" : "Chat history"}
|
||||
{showHistory ? t(($) => $.window.history_back_tooltip) : t(($) => $.window.history_show_tooltip)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
|
||||
@@ -31,6 +31,16 @@
|
||||
"minutes": "{{count}}m ago",
|
||||
"hours": "{{count}}h ago",
|
||||
"days": "{{count}}d ago"
|
||||
},
|
||||
"row_delete_aria": "Delete chat session",
|
||||
"row_delete_tooltip": "Delete",
|
||||
"delete_dialog": {
|
||||
"title": "Delete chat session",
|
||||
"description_with_title": "\"{{title}}\" and its messages will be permanently removed. This action cannot be undone.",
|
||||
"description_default": "This chat session and its messages will be permanently removed. This action cannot be undone.",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Delete",
|
||||
"confirming": "Deleting..."
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -46,7 +56,9 @@
|
||||
"untitled": "New chat",
|
||||
"my_agents": "My agents",
|
||||
"others": "Others",
|
||||
"no_agents": "No agents"
|
||||
"no_agents": "No agents",
|
||||
"history_show_tooltip": "Chat history",
|
||||
"history_back_tooltip": "Back to chat"
|
||||
},
|
||||
"empty_state": {
|
||||
"first_time_title": "Chat with your agents",
|
||||
|
||||
@@ -61,6 +61,20 @@
|
||||
"toast_project_deleted": "Project deleted",
|
||||
"toast_move_issue_failed": "Failed to move issue"
|
||||
},
|
||||
"resources": {
|
||||
"section_header": "Resources",
|
||||
"empty": "No resources attached.",
|
||||
"add_button": "Add resource",
|
||||
"popover_title": "Attach a GitHub repo",
|
||||
"attached_badge": "attached",
|
||||
"remove_tooltip": "Remove",
|
||||
"url_placeholder": "https://github.com/owner/repo",
|
||||
"url_submit": "Add",
|
||||
"toast_attached": "Repository attached",
|
||||
"toast_attach_failed": "Failed to attach",
|
||||
"toast_removed": "Resource removed",
|
||||
"toast_remove_failed": "Failed to remove resource"
|
||||
},
|
||||
"delete_dialog": {
|
||||
"title": "Delete project",
|
||||
"description": "This will delete the project. Issues will not be deleted but will be unlinked.",
|
||||
|
||||
@@ -29,6 +29,16 @@
|
||||
"minutes": "{{count}} 分钟前",
|
||||
"hours": "{{count}} 小时前",
|
||||
"days": "{{count}} 天前"
|
||||
},
|
||||
"row_delete_aria": "删除对话",
|
||||
"row_delete_tooltip": "删除",
|
||||
"delete_dialog": {
|
||||
"title": "删除对话",
|
||||
"description_with_title": "\"{{title}}\" 及其消息会被永久删除,无法撤销。",
|
||||
"description_default": "此对话及其消息会被永久删除,无法撤销。",
|
||||
"cancel": "取消",
|
||||
"confirm": "删除",
|
||||
"confirming": "删除中..."
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -44,7 +54,9 @@
|
||||
"untitled": "新对话",
|
||||
"my_agents": "我的智能体",
|
||||
"others": "其他",
|
||||
"no_agents": "暂无智能体"
|
||||
"no_agents": "暂无智能体",
|
||||
"history_show_tooltip": "对话历史",
|
||||
"history_back_tooltip": "返回对话"
|
||||
},
|
||||
"empty_state": {
|
||||
"first_time_title": "和你的智能体对话",
|
||||
|
||||
@@ -61,6 +61,20 @@
|
||||
"toast_project_deleted": "已删除项目",
|
||||
"toast_move_issue_failed": "移动 issue 失败"
|
||||
},
|
||||
"resources": {
|
||||
"section_header": "资源",
|
||||
"empty": "还没有关联资源。",
|
||||
"add_button": "添加资源",
|
||||
"popover_title": "关联 GitHub 仓库",
|
||||
"attached_badge": "已关联",
|
||||
"remove_tooltip": "移除",
|
||||
"url_placeholder": "https://github.com/owner/repo",
|
||||
"url_submit": "添加",
|
||||
"toast_attached": "已关联仓库",
|
||||
"toast_attach_failed": "关联失败",
|
||||
"toast_removed": "已移除资源",
|
||||
"toast_remove_failed": "移除资源失败"
|
||||
},
|
||||
"delete_dialog": {
|
||||
"title": "删除项目",
|
||||
"description": "将删除这个项目。issue 不会被删除,只会从项目中解除关联。",
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
TooltipTrigger,
|
||||
TooltipContent,
|
||||
} from "@multica/ui/components/ui/tooltip";
|
||||
import { useT } from "../../i18n";
|
||||
|
||||
// Project Resources sidebar section.
|
||||
//
|
||||
@@ -33,6 +34,7 @@ import {
|
||||
// so adding a new type means: (1) extend the API validator, (2) add a render
|
||||
// case here. No changes to the schema or query layer.
|
||||
export function ProjectResourcesSection({ projectId }: { projectId: string }) {
|
||||
const { t } = useT("projects");
|
||||
const wsId = useWorkspaceId();
|
||||
const workspace = useCurrentWorkspace();
|
||||
const [open, setOpen] = useState(true);
|
||||
@@ -56,9 +58,9 @@ export function ProjectResourcesSection({ projectId }: { projectId: string }) {
|
||||
resource_type: "github_repo",
|
||||
resource_ref: { url },
|
||||
});
|
||||
toast.success("Repository attached");
|
||||
toast.success(t(($) => $.resources.toast_attached));
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : "Failed to attach";
|
||||
const msg = err instanceof Error ? err.message : t(($) => $.resources.toast_attach_failed);
|
||||
toast.error(msg);
|
||||
}
|
||||
};
|
||||
@@ -66,9 +68,9 @@ export function ProjectResourcesSection({ projectId }: { projectId: string }) {
|
||||
const handleRemove = async (resource: ProjectResource) => {
|
||||
try {
|
||||
await deleteResource.mutateAsync(resource.id);
|
||||
toast.success("Resource removed");
|
||||
toast.success(t(($) => $.resources.toast_removed));
|
||||
} catch {
|
||||
toast.error("Failed to remove resource");
|
||||
toast.error(t(($) => $.resources.toast_remove_failed));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,7 +80,7 @@ export function ProjectResourcesSection({ projectId }: { projectId: string }) {
|
||||
className={`flex w-full items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors mb-2 hover:bg-accent/70 ${open ? "" : "text-muted-foreground hover:text-foreground"}`}
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
Resources
|
||||
{t(($) => $.resources.section_header)}
|
||||
<ChevronRight
|
||||
className={`!size-3 shrink-0 stroke-[2.5] text-muted-foreground transition-transform ${open ? "rotate-90" : ""}`}
|
||||
/>
|
||||
@@ -87,7 +89,7 @@ export function ProjectResourcesSection({ projectId }: { projectId: string }) {
|
||||
<div className="pl-2 space-y-1.5">
|
||||
{resources.length === 0 && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
No resources attached.
|
||||
{t(($) => $.resources.empty)}
|
||||
</p>
|
||||
)}
|
||||
{resources.map((resource) => (
|
||||
@@ -106,13 +108,13 @@ export function ProjectResourcesSection({ projectId }: { projectId: string }) {
|
||||
className="h-7 px-2 text-xs text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
<Plus className="size-3" />
|
||||
Add resource
|
||||
{t(($) => $.resources.add_button)}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<PopoverContent align="start" className="w-72 p-2 space-y-2">
|
||||
<div className="text-xs font-medium text-muted-foreground">
|
||||
Attach a GitHub repo
|
||||
{t(($) => $.resources.popover_title)}
|
||||
</div>
|
||||
{workspace?.repos && workspace.repos.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
@@ -145,7 +147,7 @@ export function ProjectResourcesSection({ projectId }: { projectId: string }) {
|
||||
</Tooltip>
|
||||
{isAttached && (
|
||||
<span className="text-[10px] text-muted-foreground">
|
||||
attached
|
||||
{t(($) => $.resources.attached_badge)}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
@@ -174,6 +176,7 @@ function ResourceRow({
|
||||
resource: ProjectResource;
|
||||
onRemove: () => void;
|
||||
}) {
|
||||
const { t } = useT("projects");
|
||||
if (resource.resource_type === "github_repo") {
|
||||
const ref = resource.resource_ref as GithubRepoResourceRef;
|
||||
return (
|
||||
@@ -198,7 +201,7 @@ function ResourceRow({
|
||||
type="button"
|
||||
onClick={onRemove}
|
||||
className="opacity-0 group-hover:opacity-100 transition-opacity rounded-sm p-0.5 hover:bg-accent"
|
||||
title="Remove"
|
||||
title={t(($) => $.resources.remove_tooltip)}
|
||||
>
|
||||
<Trash2 className="size-3 text-muted-foreground" />
|
||||
</button>
|
||||
@@ -214,7 +217,7 @@ function ResourceRow({
|
||||
type="button"
|
||||
onClick={onRemove}
|
||||
className="rounded-sm p-0.5 hover:bg-accent"
|
||||
title="Remove"
|
||||
title={t(($) => $.resources.remove_tooltip)}
|
||||
>
|
||||
<Trash2 className="size-3" />
|
||||
</button>
|
||||
@@ -227,6 +230,7 @@ function CustomRepoForm({
|
||||
}: {
|
||||
onSubmit: (url: string) => Promise<void> | void;
|
||||
}) {
|
||||
const { t } = useT("projects");
|
||||
const [url, setUrl] = useState("");
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const handle = async (e: React.FormEvent) => {
|
||||
@@ -247,7 +251,7 @@ function CustomRepoForm({
|
||||
type="url"
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
placeholder="https://github.com/owner/repo"
|
||||
placeholder={t(($) => $.resources.url_placeholder)}
|
||||
className="flex-1 bg-transparent text-xs px-2 py-1 outline-none placeholder:text-muted-foreground"
|
||||
/>
|
||||
<Button
|
||||
@@ -257,7 +261,7 @@ function CustomRepoForm({
|
||||
className="h-6 px-2 text-xs"
|
||||
disabled={!url.trim() || submitting}
|
||||
>
|
||||
Add
|
||||
{t(($) => $.resources.url_submit)}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user