@agent-native/creative-context 0.7.18 → 0.7.19-nightly-20260901231700

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,6 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
2
+ import { cn } from "@agent-native/toolkit";
3
+ import { ShareDisclosureSection } from "@agent-native/toolkit/sharing";
1
4
  import {
2
5
  Badge,
3
6
  Button,
@@ -10,13 +13,8 @@ import {
10
13
  SelectValue,
11
14
  Sheet,
12
15
  SheetContent,
13
- SheetDescription,
14
16
  SheetHeader,
15
17
  SheetTitle,
16
- Tabs,
17
- TabsContent,
18
- TabsList,
19
- TabsTrigger,
20
18
  Textarea,
21
19
  } from "@agent-native/toolkit/ui";
22
20
  import {
@@ -24,7 +22,6 @@ import {
24
22
  IconFileText,
25
23
  IconLink,
26
24
  IconPlus,
27
- IconShieldCheck,
28
25
  IconX,
29
26
  } from "@tabler/icons-react";
30
27
  import { useEffect, useState } from "react";
@@ -38,7 +35,6 @@ import {
38
35
  useManageCreativeContext,
39
36
  type CreativeContextMembership,
40
37
  type CreativeContextMembershipRank,
41
- type CreativeContextPolicy,
42
38
  type CreativeContextSummary,
43
39
  } from "./actions.js";
44
40
 
@@ -83,6 +79,7 @@ export function normalizeCreativeContextResources(
83
79
  }
84
80
 
85
81
  const VISIBILITY_RANK = { private: 0, org: 1, public: 2 } as const;
82
+ type CreativeContextTranslate = ReturnType<typeof useT>;
86
83
 
87
84
  export function requiresBroaderPublication(
88
85
  resource: CreativeContextResourceDescriptor,
@@ -111,27 +108,6 @@ export function creativeContextSafePreviewUrl(url: string | undefined) {
111
108
  }
112
109
  }
113
110
 
114
- function policyCopy(policy: CreativeContextPolicy) {
115
- switch (policy) {
116
- case "review":
117
- return "New resources wait for reviewer approval before they are reused.";
118
- case "admins-only":
119
- return "Only administrators can approve or remove resources.";
120
- default:
121
- return "New resources are published after submission.";
122
- }
123
- }
124
-
125
- function formatResourceTimestamp(value: string | undefined) {
126
- if (!value) return null;
127
- const date = new Date(value);
128
- if (!Number.isFinite(date.getTime())) return null;
129
- return new Intl.DateTimeFormat(undefined, {
130
- dateStyle: "medium",
131
- timeStyle: "short",
132
- }).format(date);
133
- }
134
-
135
111
  function ResourcePreview({
136
112
  resource,
137
113
  }: {
@@ -143,13 +119,13 @@ function ResourcePreview({
143
119
  <img
144
120
  src={imageUrl}
145
121
  alt={resource.preview?.alt ?? ""}
146
- className="size-11 rounded-md border border-border object-cover"
122
+ className="size-9 rounded-md border border-border object-cover"
147
123
  />
148
124
  );
149
125
  }
150
126
  return (
151
- <div className="flex size-11 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground">
152
- <IconFileText className="size-5" />
127
+ <div className="flex size-9 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground">
128
+ <IconFileText className="size-4" />
153
129
  </div>
154
130
  );
155
131
  }
@@ -210,6 +186,7 @@ export async function submitCreativeContextResources({
210
186
 
211
187
  function MembershipRow({
212
188
  membership,
189
+ t,
213
190
  updateAvailable,
214
191
  canReview,
215
192
  canWithdraw,
@@ -218,6 +195,7 @@ function MembershipRow({
218
195
  onAction,
219
196
  }: {
220
197
  membership: CreativeContextMembership;
198
+ t: CreativeContextTranslate;
221
199
  updateAvailable: boolean;
222
200
  canReview: boolean;
223
201
  canWithdraw: boolean;
@@ -228,45 +206,71 @@ function MembershipRow({
228
206
  ) => void;
229
207
  }) {
230
208
  const pending = Boolean(membership.pendingSubmissionId);
209
+ const rankLabel = {
210
+ canonical: t("creativeContext.share.canonical", {
211
+ defaultValue: "Canonical",
212
+ }),
213
+ exemplar: t("creativeContext.exemplar"),
214
+ normal: t("creativeContext.share.reference", {
215
+ defaultValue: "Reference",
216
+ }),
217
+ }[membership.rank];
231
218
  return (
232
- <article className="flex flex-wrap items-center justify-between gap-3 rounded-md border border-border p-3">
233
- <div>
234
- <p className="text-sm font-medium">
235
- {pending ? "Pending resource" : "Published resource"}
236
- </p>
237
- <p className="mt-0.5 text-xs text-muted-foreground">
238
- {membership.rank}{" "}
239
- {membership.purpose ? `· ${membership.purpose}` : ""}
240
- </p>
219
+ <article className="flex flex-wrap items-center justify-between gap-2 border-t border-border/60 py-2.5 first:border-t-0">
220
+ <div className="flex min-w-0 items-center gap-2">
221
+ <Badge variant={pending ? "outline" : "secondary"} className="shrink-0">
222
+ {pending
223
+ ? t("creativeContext.share.pendingResource", {
224
+ defaultValue: "Pending resource",
225
+ })
226
+ : t("creativeContext.share.publishedResource", {
227
+ defaultValue: "Published resource",
228
+ })}
229
+ </Badge>
230
+ <span className="min-w-0 truncate text-xs text-muted-foreground">
231
+ {membership.purpose ?? rankLabel}
232
+ </span>
233
+ {updateAvailable ? (
234
+ <span className="shrink-0 text-xs text-muted-foreground">
235
+ {t("creativeContext.updateAvailable")}
236
+ </span>
237
+ ) : null}
241
238
  </div>
242
- <div className="flex flex-wrap gap-2">
239
+ <div className="flex flex-wrap justify-end gap-1">
243
240
  {pending ? (
244
- <Badge variant="outline">Pending review</Badge>
241
+ <Badge variant="outline">
242
+ {t("creativeContext.share.pendingReview", {
243
+ defaultValue: "Pending review",
244
+ })}
245
+ </Badge>
245
246
  ) : (
246
- <Badge variant="secondary">Published</Badge>
247
+ <span className="sr-only">
248
+ {t("creativeContext.share.published", {
249
+ defaultValue: "Published",
250
+ })}
251
+ </span>
247
252
  )}
248
- {updateAvailable ? (
249
- <Badge variant="outline">Update available</Badge>
250
- ) : null}
251
253
  {pending && canWithdraw ? (
252
254
  <Button
253
255
  type="button"
254
256
  variant="outline"
255
257
  size="sm"
258
+ className="h-8 px-2 text-xs"
256
259
  disabled={busy}
257
260
  onClick={() => onAction("withdraw")}
258
261
  >
259
- Withdraw
262
+ {t("creativeContext.share.withdraw", { defaultValue: "Withdraw" })}
260
263
  </Button>
261
264
  ) : null}
262
265
  {pending && canReview ? (
263
266
  <Button
264
267
  type="button"
265
268
  size="sm"
269
+ className="h-8 px-2 text-xs"
266
270
  disabled={busy}
267
271
  onClick={() => onAction("approve")}
268
272
  >
269
- <IconCheck /> Approve
273
+ <IconCheck /> {t("creativeContext.approve")}
270
274
  </Button>
271
275
  ) : null}
272
276
  {pending && canReview ? (
@@ -274,10 +278,13 @@ function MembershipRow({
274
278
  type="button"
275
279
  size="sm"
276
280
  variant="outline"
281
+ className="h-8 px-2 text-xs"
277
282
  disabled={busy}
278
283
  onClick={() => onAction("request-changes")}
279
284
  >
280
- Request changes
285
+ {t("creativeContext.share.requestChanges", {
286
+ defaultValue: "Request changes",
287
+ })}
281
288
  </Button>
282
289
  ) : null}
283
290
  {canRemove ? (
@@ -285,10 +292,12 @@ function MembershipRow({
285
292
  type="button"
286
293
  size="sm"
287
294
  variant="ghost"
295
+ className="h-8 px-2 text-xs"
288
296
  disabled={busy}
289
297
  onClick={() => onAction("remove")}
290
298
  >
291
- <IconX /> Remove
299
+ <IconX />{" "}
300
+ {t("creativeContext.share.remove", { defaultValue: "Remove" })}
292
301
  </Button>
293
302
  ) : null}
294
303
  </div>
@@ -299,16 +308,24 @@ function MembershipRow({
299
308
  function ContextSelect({
300
309
  contexts,
301
310
  contextId,
311
+ t,
302
312
  onValueChange,
313
+ disabled = false,
303
314
  }: {
304
315
  contexts: CreativeContextSummary[];
305
316
  contextId: string;
317
+ t: CreativeContextTranslate;
306
318
  onValueChange: (value: string) => void;
319
+ disabled?: boolean;
307
320
  }) {
308
321
  return (
309
322
  <Select value={contextId} onValueChange={onValueChange}>
310
- <SelectTrigger>
311
- <SelectValue placeholder="Choose a context" />
323
+ <SelectTrigger className="w-full" disabled={disabled}>
324
+ <SelectValue
325
+ placeholder={t("creativeContext.share.chooseContext", {
326
+ defaultValue: "Choose a context",
327
+ })}
328
+ />
312
329
  </SelectTrigger>
313
330
  {/* This tab is embedded inside ShareButton's high z-index popover
314
331
  (see z-[100010]+ overrides in design/content/slides toolbars).
@@ -332,6 +349,7 @@ export function CreativeContextShareTab({
332
349
  resources,
333
350
  className,
334
351
  }: CreativeContextShareTabProps) {
352
+ const t = useT();
335
353
  const contextsQuery = useCreativeContexts();
336
354
  const manageContext = useManageCreativeContext();
337
355
  const manageMembership = useManageContextMembership();
@@ -341,7 +359,6 @@ export function CreativeContextShareTab({
341
359
  resources,
342
360
  );
343
361
  const primaryResource = selectedResources[0];
344
- const updatedAt = formatResourceTimestamp(primaryResource?.updatedAt);
345
362
  const [contextId, setContextId] = useState("");
346
363
  const membershipsQuery = useContextMemberships(
347
364
  contextId ? { contextId } : null,
@@ -360,7 +377,9 @@ export function CreativeContextShareTab({
360
377
  useState(false);
361
378
  const busy = manageContext.isPending || manageMembership.isPending;
362
379
  const selectedContext = contexts.find((context) => context.id === contextId);
363
- const canCreateContext = contexts.some((context) => context.access.canAdmin);
380
+ const canCreateContext =
381
+ contextsQuery.data?.canCreateContext === true &&
382
+ contexts.some((context) => context.access.canAdmin);
364
383
  const needsBroaderPublicationConfirmation = selectedResources.some((item) =>
365
384
  requiresBroaderPublication(item, selectedContext),
366
385
  );
@@ -398,12 +417,28 @@ export function CreativeContextShareTab({
398
417
  setConfirmedBroaderPublication(false);
399
418
  setSubmitSummary(
400
419
  result.failed
401
- ? `${result.submitted} submitted; ${result.failed} could not be submitted.`
402
- : `${result.submitted} ${result.submitted === 1 ? "resource" : "resources"} submitted.`,
420
+ ? t("creativeContext.share.partialSubmission", {
421
+ submitted: result.submitted,
422
+ failed: result.failed,
423
+ defaultValue:
424
+ "{{submitted}} submitted; {{failed}} could not be submitted.",
425
+ })
426
+ : t(
427
+ result.submitted === 1
428
+ ? "creativeContext.share.resourceSubmitted"
429
+ : "creativeContext.share.resourcesSubmitted",
430
+ {
431
+ count: result.submitted,
432
+ defaultValue:
433
+ result.submitted === 1
434
+ ? "{{count}} resource submitted."
435
+ : "{{count}} resources submitted.",
436
+ },
437
+ ),
403
438
  );
404
439
  await refresh();
405
440
  } catch {
406
- setError("Could not submit this resource to the selected context.");
441
+ setError(t("creativeContext.submitUpdateFailed"));
407
442
  }
408
443
  }
409
444
 
@@ -421,7 +456,7 @@ export function CreativeContextShareTab({
421
456
  await contextsQuery.refetch();
422
457
  if (result.context?.id) setContextId(result.context.id);
423
458
  } catch {
424
- setError("Could not create a context.");
459
+ setError(t("creativeContext.saveFailed"));
425
460
  }
426
461
  }
427
462
 
@@ -439,97 +474,83 @@ export function CreativeContextShareTab({
439
474
  });
440
475
  await refresh();
441
476
  } catch {
442
- setError("Could not update this context membership.");
477
+ setError(t("creativeContext.saveFailed"));
443
478
  }
444
479
  }
445
480
 
446
481
  return (
447
- <section className={className} aria-label="Creative context">
448
- <div className="flex items-start gap-3 border-b border-border/70 pb-4">
449
- {primaryResource ? (
482
+ <section
483
+ className={cn("space-y-3", className)}
484
+ aria-label={t("creativeContext.share.title", {
485
+ defaultValue: "Creative context",
486
+ })}
487
+ >
488
+ {primaryResource ? (
489
+ <div className="flex min-w-0 items-center gap-2 border-b border-border/70 pb-3">
450
490
  <ResourcePreview resource={primaryResource} />
451
- ) : null}
452
- <div className="min-w-0">
453
- <p className="truncate text-sm font-medium">
454
- {selectedResources.length === 1
455
- ? primaryResource?.title
456
- : `${selectedResources.length} selected resources`}
457
- </p>
458
- <p className="mt-0.5 text-xs text-muted-foreground">
491
+ <p className="min-w-0 truncate text-sm font-medium">
459
492
  {selectedResources.length === 1
460
- ? (primaryResource?.preview?.label ??
461
- primaryResource?.resourceType)
462
- : "Each resource is submitted separately"}
493
+ ? primaryResource.title
494
+ : t("creativeContext.share.selectedResources", {
495
+ count: selectedResources.length,
496
+ defaultValue: "{{count}} selected resources",
497
+ })}
463
498
  </p>
464
- {selectedResources.length === 1 && updatedAt ? (
465
- <p className="mt-0.5 text-xs text-muted-foreground">
466
- Current version · {updatedAt}
467
- </p>
468
- ) : null}
469
499
  </div>
470
- </div>
471
- <Tabs defaultValue="contexts" className="mt-4">
472
- <TabsList className="w-full justify-start">
473
- <TabsTrigger value="contexts">Contexts</TabsTrigger>
474
- <TabsTrigger value="policy">Policy</TabsTrigger>
475
- </TabsList>
476
- <TabsContent value="contexts" className="space-y-3">
477
- <ContextSelect
478
- contexts={contexts}
479
- contextId={contextId}
480
- onValueChange={setContextId}
481
- />
482
- {selectedContext ? (
483
- <p className="text-xs text-muted-foreground">
484
- {selectedContext.description ||
485
- `${selectedContext.memberCount} published resources`}{" "}
486
- · {policyCopy(selectedContext.approvalPolicy)}
487
- </p>
488
- ) : (
489
- <p className="text-sm text-muted-foreground">
490
- No contexts are available yet.
491
- </p>
492
- )}
493
- {contextId && selectedResources.length === 1 ? (
494
- <div className="space-y-2">
495
- {memberships.map((membership) => (
496
- <MembershipRow
497
- key={membership.id}
498
- membership={membership}
499
- updateAvailable={Boolean(
500
- primaryResource?.updatedAt &&
501
- membership.publishedItem?.sourceModifiedAt &&
502
- primaryResource.updatedAt !==
503
- membership.publishedItem.sourceModifiedAt,
504
- )}
505
- canReview={selectedContext?.access.canReview === true}
506
- canWithdraw={
507
- selectedContext?.access.canReview === true ||
508
- selectedContext?.access.canSubmit === true
509
- }
510
- canRemove={selectedContext?.access.canAdmin === true}
511
- busy={busy}
512
- onAction={(operation) => void act(membership.id, operation)}
513
- />
514
- ))}
515
- {!memberships.length && !membershipsQuery.isLoading ? (
516
- <p className="text-sm text-muted-foreground">
517
- This resource has not been submitted to this context.
518
- </p>
519
- ) : null}
520
- </div>
521
- ) : null}
522
- {contextId && selectedResources.length ? (
523
- <div className="rounded-md border border-dashed border-border p-3">
524
- <p className="text-sm font-medium">
525
- {memberships.some((membership) => membership.publishedItem)
526
- ? "Submit update for "
527
- : "Add "}
528
- {selectedResources.length === 1
529
- ? "this resource"
530
- : `${selectedResources.length} resources`}
531
- </p>
532
- <div className="mt-2 grid gap-2 sm:grid-cols-2">
500
+ ) : null}
501
+ {contexts.length ? (
502
+ <ContextSelect
503
+ contexts={contexts}
504
+ contextId={contextId}
505
+ t={t}
506
+ onValueChange={setContextId}
507
+ disabled={busy}
508
+ />
509
+ ) : (
510
+ <p className="text-sm text-muted-foreground">
511
+ {t("creativeContext.share.noContexts", {
512
+ defaultValue: "No contexts are available yet.",
513
+ })}
514
+ </p>
515
+ )}
516
+ {contextId && selectedResources.length === 1 && memberships.length ? (
517
+ <div>
518
+ {memberships.map((membership) => (
519
+ <MembershipRow
520
+ key={membership.id}
521
+ membership={membership}
522
+ t={t}
523
+ updateAvailable={Boolean(
524
+ primaryResource?.updatedAt &&
525
+ membership.publishedItem?.sourceModifiedAt &&
526
+ primaryResource.updatedAt !==
527
+ membership.publishedItem.sourceModifiedAt,
528
+ )}
529
+ canReview={selectedContext?.access.canReview === true}
530
+ canWithdraw={
531
+ selectedContext?.access.canReview === true ||
532
+ selectedContext?.access.canSubmit === true
533
+ }
534
+ canRemove={selectedContext?.access.canAdmin === true}
535
+ busy={busy}
536
+ onAction={(operation) => void act(membership.id, operation)}
537
+ />
538
+ ))}
539
+ </div>
540
+ ) : null}
541
+ {contextId && selectedResources.length ? (
542
+ <div className="border-t border-border/60 pt-3">
543
+ <div className="flex items-start gap-2">
544
+ <ShareDisclosureSection
545
+ label={
546
+ memberships.some((membership) => membership.publishedItem)
547
+ ? t("creativeContext.submitUpdate")
548
+ : t("creativeContext.addToContext")
549
+ }
550
+ className="min-w-0 flex-1"
551
+ contentClassName="space-y-2"
552
+ >
553
+ <div className="grid gap-2 sm:grid-cols-2">
533
554
  <Select
534
555
  value={rank}
535
556
  onValueChange={(value) =>
@@ -543,94 +564,103 @@ export function CreativeContextShareTab({
543
564
  data-agent-native-share-overlay=""
544
565
  className="z-[100020]"
545
566
  >
546
- <SelectItem value="canonical">Canonical</SelectItem>
547
- <SelectItem value="exemplar">Exemplar</SelectItem>
548
- <SelectItem value="normal">Reference</SelectItem>
567
+ <SelectItem value="canonical">
568
+ {t("creativeContext.share.canonical", {
569
+ defaultValue: "Canonical",
570
+ })}
571
+ </SelectItem>
572
+ <SelectItem value="exemplar">
573
+ {t("creativeContext.exemplar")}
574
+ </SelectItem>
575
+ <SelectItem value="normal">
576
+ {t("creativeContext.share.reference", {
577
+ defaultValue: "Reference",
578
+ })}
579
+ </SelectItem>
549
580
  </SelectContent>
550
581
  </Select>
551
582
  <Input
552
583
  value={purpose}
553
584
  onChange={(event) => setPurpose(event.target.value)}
554
- placeholder="Purpose"
585
+ placeholder={t("creativeContext.share.purpose", {
586
+ defaultValue: "Purpose",
587
+ })}
555
588
  />
556
589
  </div>
557
590
  <Textarea
558
- className="mt-2"
559
591
  value={note}
560
592
  onChange={(event) => setNote(event.target.value)}
561
- placeholder="Note for reviewers"
593
+ placeholder={t("creativeContext.share.reviewerNote", {
594
+ defaultValue: "Note for reviewers",
595
+ })}
562
596
  rows={2}
563
597
  />
564
- {needsBroaderPublicationConfirmation ? (
565
- <label className="mt-3 flex items-start gap-2 text-xs text-muted-foreground">
566
- <Checkbox
567
- checked={confirmedBroaderPublication}
568
- onCheckedChange={(checked) =>
569
- setConfirmedBroaderPublication(checked === true)
570
- }
571
- />
572
- <span>
573
- This context is shared more broadly than this resource.
574
- Publishing creates a governed copy available to the
575
- context's audience.
576
- </span>
577
- </label>
578
- ) : null}
579
- <Button
580
- type="button"
581
- className="mt-3"
582
- size="sm"
583
- disabled={
584
- busy ||
585
- selectedContext?.access.canSubmit !== true ||
586
- (needsBroaderPublicationConfirmation &&
587
- !confirmedBroaderPublication)
598
+ </ShareDisclosureSection>
599
+ <Button
600
+ type="button"
601
+ size="sm"
602
+ className="shrink-0"
603
+ disabled={
604
+ busy ||
605
+ selectedContext?.access.canSubmit !== true ||
606
+ (needsBroaderPublicationConfirmation &&
607
+ !confirmedBroaderPublication)
608
+ }
609
+ onClick={() => void submit()}
610
+ >
611
+ <IconLink />{" "}
612
+ {t("creativeContext.share.submit", { defaultValue: "Submit" })}
613
+ </Button>
614
+ </div>
615
+ {needsBroaderPublicationConfirmation ? (
616
+ <label className="flex items-start gap-2 text-xs text-muted-foreground">
617
+ <Checkbox
618
+ checked={confirmedBroaderPublication}
619
+ onCheckedChange={(checked) =>
620
+ setConfirmedBroaderPublication(checked === true)
588
621
  }
589
- onClick={() => void submit()}
590
- >
591
- <IconLink /> Submit
592
- </Button>
593
- </div>
594
- ) : null}
595
- {canCreateContext ? (
596
- <div className="flex gap-2 border-t border-border/60 pt-3">
597
- <Input
598
- value={newContextName}
599
- onChange={(event) => setNewContextName(event.target.value)}
600
- placeholder="New context name"
601
622
  />
602
- <Button
603
- type="button"
604
- variant="outline"
605
- size="sm"
606
- disabled={busy || !newContextName.trim()}
607
- onClick={() => void createContext()}
608
- >
609
- <IconPlus /> New
610
- </Button>
611
- </div>
623
+ <span>
624
+ {t("creativeContext.share.broaderPublication", {
625
+ defaultValue:
626
+ "This context is shared more broadly than this resource. Publishing creates a governed copy available to the context's audience.",
627
+ })}
628
+ </span>
629
+ </label>
612
630
  ) : null}
613
- {error ? <p className="text-xs text-destructive">{error}</p> : null}
614
- {submitSummary ? (
615
- <p className="text-xs text-muted-foreground">{submitSummary}</p>
616
- ) : null}
617
- </TabsContent>
618
- <TabsContent
619
- value="policy"
620
- className="space-y-3 text-sm text-muted-foreground"
631
+ </div>
632
+ ) : null}
633
+ {canCreateContext ? (
634
+ <ShareDisclosureSection
635
+ label={t("creativeContext.share.newContextName", {
636
+ defaultValue: "New context name",
637
+ })}
621
638
  >
622
- <div className="flex gap-2 rounded-md border border-border p-3">
623
- <IconShieldCheck className="mt-0.5 size-4 shrink-0" />
624
- <p>
625
- Open contexts publish submitted resources, review contexts wait
626
- for approval, and admins-only contexts require an administrator.
627
- </p>
639
+ <div className="flex gap-2">
640
+ <Input
641
+ value={newContextName}
642
+ onChange={(event) => setNewContextName(event.target.value)}
643
+ placeholder={t("creativeContext.share.newContextName", {
644
+ defaultValue: "New context name",
645
+ })}
646
+ />
647
+ <Button
648
+ type="button"
649
+ variant="outline"
650
+ size="sm"
651
+ disabled={busy || !newContextName.trim()}
652
+ onClick={() => void createContext()}
653
+ >
654
+ <IconPlus />{" "}
655
+ {t("creativeContext.share.new", { defaultValue: "New" })}
656
+ </Button>
628
657
  </div>
629
- {selectedContext ? (
630
- <p>{policyCopy(selectedContext.approvalPolicy)}</p>
631
- ) : null}
632
- </TabsContent>
633
- </Tabs>
658
+ </ShareDisclosureSection>
659
+ ) : null}
660
+ {error ? <p className="text-xs text-destructive">{error}</p> : null}
661
+ {submitSummary ? (
662
+ <p className="text-xs text-muted-foreground">{submitSummary}</p>
663
+ ) : null}
634
664
  </section>
635
665
  );
636
666
  }
@@ -645,14 +675,16 @@ export function CreativeContextShareSheet({
645
675
  open: boolean;
646
676
  onOpenChange: (open: boolean) => void;
647
677
  }) {
678
+ const t = useT();
648
679
  return (
649
680
  <Sheet open={open} onOpenChange={onOpenChange}>
650
681
  <SheetContent side="right" className="w-full overflow-y-auto sm:max-w-lg">
651
682
  <SheetHeader>
652
- <SheetTitle>Creative context</SheetTitle>
653
- <SheetDescription>
654
- Place this resource in a governed context for future reuse.
655
- </SheetDescription>
683
+ <SheetTitle>
684
+ {t("creativeContext.share.title", {
685
+ defaultValue: "Creative context",
686
+ })}
687
+ </SheetTitle>
656
688
  </SheetHeader>
657
689
  <CreativeContextShareTab
658
690
  resource={resource}