refactor: address PR feedback for security and cleanups

- refactor: migrate dangerouslySetInnerHTML(t.raw) to t.rich() to prevent HTML injection vectors
- fix: restore per-status wording for flow activation toasts ("Flow activated.", "Archived.", "Saved as draft.")
- chore: remove unused `tComposer` in message-thread.tsx
- chore: remove unused `ROLE_DESCRIPTIONS` in invite-member-dialog.tsx
This commit is contained in:
Ivan Mercedes 2026-07-08 07:58:33 -04:00
parent de75715cff
commit 4690c4d929
7 changed files with 18 additions and 13 deletions

View File

@ -1144,7 +1144,9 @@
"editorState": {
"saved": "Saved.",
"fixIssues": "Fix the issues below before activating.",
"statusChanged": "Flow is now {status}."
"statusActivated": "Flow activated.",
"statusArchived": "Archived.",
"statusDraft": "Saved as draft."
},
"summary": {
"options": "{count} {count, plural, =1 {option} other {options}}",

View File

@ -174,7 +174,9 @@ export function FlowBuilder() {
</div>
{state.nodes.length === 0 ? (
<div className="border-border bg-card/50 text-muted-foreground rounded-lg border border-dashed p-8 text-center text-sm" dangerouslySetInnerHTML={{ __html: t.raw('nodesEmpty') }} />
<div className="border-border bg-card/50 text-muted-foreground rounded-lg border border-dashed p-8 text-center text-sm">
{t.rich('nodesEmpty', { strong: (chunks) => <strong>{chunks}</strong> })}
</div>
) : (
state.nodes.map((node) => (
<NodeCard

View File

@ -383,7 +383,13 @@ export function FlowEditorProvider({
throw new Error(json.error ?? `Status update failed: ${res.status}`);
}
setStateRaw((s) => ({ ...s, status: next }));
toast.success(t("statusChanged", { status: next }));
toast.success(
next === "active"
? t("statusActivated")
: next === "archived"
? t("statusArchived")
: t("statusDraft")
);
} catch (err) {
const msg = err instanceof Error ? err.message : "Status update failed";
toast.error(msg);

View File

@ -168,7 +168,6 @@ export function MessageThread({
}: MessageThreadProps) {
const t = useTranslations("Inbox.messageThread");
const tTimer = useTranslations("Inbox.sessionTimer");
const tComposer = useTranslations("Inbox.composer");
const tQuote = useTranslations("Inbox.replyQuote");
const { user } = useAuth();

View File

@ -55,12 +55,6 @@ const EXPIRY_OPTIONS = [
{ value: '30', labelKey: 'days30' },
];
const ROLE_DESCRIPTIONS = [
'adminHint',
'agentHint',
'viewerHint',
];
// Server caps label at 80 chars (see src/app/api/account/invitations/route.ts).
// Mirror it on the client so we short-circuit before the round-trip
// rather than letting the user submit and bounce off a 400.

View File

@ -503,7 +503,9 @@ export function MembersTab() {
<p className="mt-2 text-sm text-muted-foreground">
{t('noPendingTitle')}
</p>
<p className="mt-1 text-xs text-muted-foreground" dangerouslySetInnerHTML={{ __html: t.raw('noPendingDesc') }} />
<p className="mt-1 text-xs text-muted-foreground">
{t.rich('noPendingDesc', { bold: (chunks) => <strong>{chunks}</strong> })}
</p>
</CardContent>
</Card>
) : (

View File

@ -653,7 +653,7 @@ export function TemplateManager() {
{form.category === 'Authentication' && (
<div className="flex items-start gap-2 rounded border border-amber-700/40 bg-amber-950/30 px-3 py-2 text-xs text-amber-300">
<AlertCircle className="size-4 mt-0.5 shrink-0" />
<p dangerouslySetInnerHTML={{ __html: t.raw('authWarning') }} />
<p>{t.rich('authWarning', { bold: (chunks) => <strong>{chunks}</strong> })}</p>
</div>
)}
@ -724,7 +724,7 @@ export function TemplateManager() {
{editingId ? (
t('langFixed')
) : (
<span dangerouslySetInnerHTML={{ __html: t.raw('langHint') }} />
<span>{t.rich('langHint', { code: (chunks) => <code>{chunks}</code> })}</span>
)}
</p>
</div>