@tangle-network/sandbox-ui 0.9.0 → 0.10.1

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.
package/dist/chat.js CHANGED
@@ -8,13 +8,13 @@ import {
8
8
  MessageList,
9
9
  ThinkingIndicator,
10
10
  UserMessage
11
- } from "./chunk-JLKYXLFN.js";
11
+ } from "./chunk-WKSGQVLI.js";
12
12
  import "./chunk-54SQQMMM.js";
13
- import "./chunk-EXSOPXIY.js";
13
+ import "./chunk-QOL4ZB24.js";
14
14
  import "./chunk-HRMUF35V.js";
15
15
  import "./chunk-MT5FJ3ZT.js";
16
16
  import "./chunk-BX6AQMUS.js";
17
- import "./chunk-PLTZB5BC.js";
17
+ import "./chunk-ZNCEM5CD.js";
18
18
  import "./chunk-34I7UFSX.js";
19
19
  import "./chunk-T7HMZEVO.js";
20
20
  import "./chunk-ZMNSRDMH.js";
@@ -516,6 +516,10 @@ function XIcon({ className }) {
516
516
  /* @__PURE__ */ jsx7("path", { d: "m6 6 12 12" })
517
517
  ] });
518
518
  }
519
+ function formatNotifDate(raw) {
520
+ const d = new Date(raw);
521
+ return Number.isNaN(d.getTime()) ? raw : d.toLocaleString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
522
+ }
519
523
  function DefaultLink2({
520
524
  href,
521
525
  to,
@@ -546,10 +550,30 @@ function DashboardLayoutInner({
546
550
  LinkComponent = DefaultLink2,
547
551
  footer,
548
552
  railFooter,
549
- profileMenuItems
553
+ profileMenuItems,
554
+ notifications: notifData
550
555
  }) {
551
556
  const Link = LinkComponent;
552
557
  const [mobileMenuOpen, setMobileMenuOpen] = React3.useState(false);
558
+ const [notificationsOpen, setNotificationsOpen] = React3.useState(false);
559
+ const notifRef = React3.useRef(null);
560
+ React3.useEffect(() => {
561
+ if (!notificationsOpen) return;
562
+ const handler = (e) => {
563
+ if (notifRef.current && !notifRef.current.contains(e.target)) {
564
+ setNotificationsOpen(false);
565
+ }
566
+ };
567
+ const keyHandler = (e) => {
568
+ if (e.key === "Escape") setNotificationsOpen(false);
569
+ };
570
+ document.addEventListener("mousedown", handler);
571
+ document.addEventListener("keydown", keyHandler);
572
+ return () => {
573
+ document.removeEventListener("mousedown", handler);
574
+ document.removeEventListener("keydown", keyHandler);
575
+ };
576
+ }, [notificationsOpen]);
553
577
  const { contentMargin, hidden, mode, hasPanels, panelOpen } = useSidebar();
554
578
  const modeSet = React3.useMemo(() => new Set(modeItems), [modeItems]);
555
579
  const sidebarUser = user ? { email: user.email, name: user.name, tier: user.tier, avatarUrl: user.avatarUrl } : void 0;
@@ -640,7 +664,61 @@ function DashboardLayoutInner({
640
664
  ]
641
665
  }
642
666
  ),
643
- /* @__PURE__ */ jsx7("button", { type: "button", className: "text-muted-foreground hover:text-foreground transition-colors p-2 rounded-lg hover:bg-muted/50", children: /* @__PURE__ */ jsx7(Bell, { className: "h-4 w-4" }) })
667
+ /* @__PURE__ */ jsxs6("div", { className: "relative", ref: notifRef, children: [
668
+ /* @__PURE__ */ jsxs6(
669
+ "button",
670
+ {
671
+ type: "button",
672
+ className: "relative text-muted-foreground hover:text-foreground transition-colors p-2 rounded-lg hover:bg-muted/50",
673
+ onClick: () => setNotificationsOpen(!notificationsOpen),
674
+ "aria-label": "Notifications",
675
+ "aria-expanded": notificationsOpen,
676
+ children: [
677
+ /* @__PURE__ */ jsx7(Bell, { className: "h-4 w-4" }),
678
+ (notifData?.unreadCount ?? 0) > 0 && /* @__PURE__ */ jsx7("span", { className: "absolute top-1 right-1 h-2 w-2 rounded-full bg-destructive" })
679
+ ]
680
+ }
681
+ ),
682
+ notificationsOpen && /* @__PURE__ */ jsxs6("div", { className: "absolute right-0 top-full mt-2 w-80 rounded-lg border border-border bg-card shadow-lg z-50", children: [
683
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between border-b border-border px-4 py-3", children: [
684
+ /* @__PURE__ */ jsx7("p", { className: "font-bold text-foreground text-sm", children: "Notifications" }),
685
+ (notifData?.unreadCount ?? 0) > 0 && notifData?.onMarkAllRead && /* @__PURE__ */ jsx7(
686
+ "button",
687
+ {
688
+ type: "button",
689
+ onClick: () => {
690
+ notifData.onMarkAllRead?.();
691
+ },
692
+ className: "text-primary text-xs font-medium hover:underline",
693
+ children: "Mark all read"
694
+ }
695
+ )
696
+ ] }),
697
+ !notifData?.items || notifData.items.length === 0 ? /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center justify-center px-4 py-8 text-center", children: [
698
+ /* @__PURE__ */ jsx7(Bell, { className: "h-8 w-8 text-muted-foreground/40 mb-2" }),
699
+ /* @__PURE__ */ jsx7("p", { className: "text-muted-foreground text-sm", children: "No notifications yet" }),
700
+ /* @__PURE__ */ jsx7("p", { className: "text-muted-foreground/60 text-xs mt-1", children: "We'll notify you about important updates" })
701
+ ] }) : /* @__PURE__ */ jsx7("div", { className: "max-h-80 overflow-y-auto", children: notifData.items.map((n) => /* @__PURE__ */ jsxs6(
702
+ "button",
703
+ {
704
+ type: "button",
705
+ className: cn(
706
+ "w-full text-left px-4 py-3 border-b border-border last:border-0 transition-colors",
707
+ n.read ? "cursor-default" : "bg-primary/5 hover:bg-muted/50"
708
+ ),
709
+ onClick: () => {
710
+ if (!n.read) notifData.onMarkRead?.(n.id);
711
+ },
712
+ children: [
713
+ /* @__PURE__ */ jsx7("p", { className: cn("text-sm", !n.read ? "font-semibold text-foreground" : "text-muted-foreground"), children: n.title }),
714
+ /* @__PURE__ */ jsx7("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-2", children: n.message }),
715
+ /* @__PURE__ */ jsx7("p", { className: "text-[10px] text-muted-foreground/50 mt-1", children: formatNotifDate(n.createdAt) })
716
+ ]
717
+ },
718
+ n.id
719
+ )) })
720
+ ] })
721
+ ] })
644
722
  ] }),
645
723
  /* @__PURE__ */ jsx7(
646
724
  "button",
@@ -10,10 +10,10 @@ import {
10
10
  } from "./chunk-MA7YKRUP.js";
11
11
  import {
12
12
  ChatContainer
13
- } from "./chunk-JLKYXLFN.js";
13
+ } from "./chunk-WKSGQVLI.js";
14
14
  import {
15
15
  OpenUIArtifactRenderer
16
- } from "./chunk-PLTZB5BC.js";
16
+ } from "./chunk-ZNCEM5CD.js";
17
17
  import {
18
18
  FileArtifactPane,
19
19
  FileTree,
@@ -1656,6 +1656,529 @@ function CheckRow({ check }) {
1656
1656
  ] });
1657
1657
  }
1658
1658
 
1659
+ // src/workspace/task-board.tsx
1660
+ import { useMemo as useMemo5 } from "react";
1661
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1662
+ function TaskBoard({
1663
+ items,
1664
+ columns,
1665
+ className,
1666
+ onMoveItem,
1667
+ onClickItem,
1668
+ renderItemMeta,
1669
+ renderColumnAction,
1670
+ renderBadge,
1671
+ columnEmptyState,
1672
+ header
1673
+ }) {
1674
+ const grouped = useMemo5(() => {
1675
+ const map = /* @__PURE__ */ new Map();
1676
+ for (const col of columns) map.set(col.id, []);
1677
+ for (const item of items) {
1678
+ const list = map.get(item.status);
1679
+ if (list) list.push(item);
1680
+ else map.get(columns[columns.length - 1]?.id)?.push(item);
1681
+ }
1682
+ return map;
1683
+ }, [items, columns]);
1684
+ return /* @__PURE__ */ jsxs11("div", { className: cn("flex flex-1 flex-col overflow-hidden", className), children: [
1685
+ header,
1686
+ /* @__PURE__ */ jsx11("div", { className: "flex flex-1 gap-3 overflow-x-auto p-4", children: columns.map((col) => {
1687
+ const colItems = grouped.get(col.id) ?? [];
1688
+ return /* @__PURE__ */ jsxs11(
1689
+ "div",
1690
+ {
1691
+ className: cn(
1692
+ "flex w-72 shrink-0 flex-col rounded-xl border border-border bg-card/50 border-t-2",
1693
+ col.accent ?? "border-t-muted-foreground/30"
1694
+ ),
1695
+ children: [
1696
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between px-4 py-3", children: [
1697
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
1698
+ /* @__PURE__ */ jsx11("h3", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: col.label }),
1699
+ /* @__PURE__ */ jsx11("span", { className: "inline-flex h-5 min-w-[20px] items-center justify-center rounded-full border border-border px-1.5 text-[10px] font-medium text-muted-foreground", children: colItems.length })
1700
+ ] }),
1701
+ renderColumnAction?.(col)
1702
+ ] }),
1703
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-1 flex-col gap-2 overflow-y-auto px-2 pb-2 min-h-[80px]", children: [
1704
+ colItems.length === 0 && columnEmptyState,
1705
+ colItems.map((item) => /* @__PURE__ */ jsxs11(
1706
+ "button",
1707
+ {
1708
+ type: "button",
1709
+ onClick: () => onClickItem?.(item),
1710
+ className: "group w-full rounded-lg border border-border bg-card p-3 text-left transition-colors hover:border-accent/50",
1711
+ children: [
1712
+ /* @__PURE__ */ jsx11("p", { className: "text-sm font-medium text-foreground", children: item.title }),
1713
+ item.description && /* @__PURE__ */ jsx11("p", { className: "mt-1 text-xs text-muted-foreground line-clamp-2", children: item.description }),
1714
+ (item.priority || item.tags?.length) && /* @__PURE__ */ jsxs11("div", { className: "mt-2 flex flex-wrap gap-1.5", children: [
1715
+ item.priority && (renderBadge ? renderBadge(item.priority, "priority") : /* @__PURE__ */ jsx11("span", { className: "rounded-full border border-border px-2 py-0.5 text-[10px] font-medium text-muted-foreground", children: item.priority })),
1716
+ item.tags?.map(
1717
+ (tag) => renderBadge ? /* @__PURE__ */ jsx11("span", { children: renderBadge(tag, "tag") }, tag) : /* @__PURE__ */ jsx11(
1718
+ "span",
1719
+ {
1720
+ className: "rounded-full border border-border px-2 py-0.5 text-[10px] text-muted-foreground",
1721
+ children: tag
1722
+ },
1723
+ tag
1724
+ )
1725
+ )
1726
+ ] }),
1727
+ renderItemMeta?.(item)
1728
+ ]
1729
+ },
1730
+ item.id
1731
+ ))
1732
+ ] })
1733
+ ]
1734
+ },
1735
+ col.id
1736
+ );
1737
+ }) })
1738
+ ] });
1739
+ }
1740
+
1741
+ // src/workspace/calendar-view.tsx
1742
+ import { useState as useState6, useMemo as useMemo6 } from "react";
1743
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1744
+ function toDateKey(d) {
1745
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
1746
+ }
1747
+ function getMonthDays(year, month) {
1748
+ const first = new Date(year, month, 1);
1749
+ const startDay = first.getDay();
1750
+ const days = [];
1751
+ for (let i = startDay - 1; i >= 0; i--) {
1752
+ const d = new Date(year, month, -i);
1753
+ days.push({ date: d, key: toDateKey(d), inMonth: false });
1754
+ }
1755
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
1756
+ for (let i = 1; i <= daysInMonth; i++) {
1757
+ const d = new Date(year, month, i);
1758
+ days.push({ date: d, key: toDateKey(d), inMonth: true });
1759
+ }
1760
+ const remaining = 7 - days.length % 7;
1761
+ if (remaining < 7) {
1762
+ for (let i = 1; i <= remaining; i++) {
1763
+ const d = new Date(year, month + 1, i);
1764
+ days.push({ date: d, key: toDateKey(d), inMonth: false });
1765
+ }
1766
+ }
1767
+ return days;
1768
+ }
1769
+ function CalendarView({
1770
+ events,
1771
+ className,
1772
+ month: controlledMonth,
1773
+ year: controlledYear,
1774
+ onMonthChange,
1775
+ selectedDay: controlledSelectedDay,
1776
+ onSelectDay,
1777
+ onDoubleClickDay,
1778
+ renderEventChip,
1779
+ renderDayDetail,
1780
+ headerLeft,
1781
+ headerRight,
1782
+ showDayPanel = true
1783
+ }) {
1784
+ const today = /* @__PURE__ */ new Date();
1785
+ const todayKey = toDateKey(today);
1786
+ const [internalMonth, setInternalMonth] = useState6({
1787
+ year: today.getFullYear(),
1788
+ month: today.getMonth()
1789
+ });
1790
+ const [internalSelectedDay, setInternalSelectedDay] = useState6(
1791
+ todayKey
1792
+ );
1793
+ const viewYear = controlledYear ?? internalMonth.year;
1794
+ const viewMonth = controlledMonth ?? internalMonth.month;
1795
+ const selectedDay = controlledSelectedDay ?? internalSelectedDay;
1796
+ function setMonth(y, m) {
1797
+ if (onMonthChange) onMonthChange(y, m);
1798
+ else setInternalMonth({ year: y, month: m });
1799
+ }
1800
+ function selectDay(key) {
1801
+ if (onSelectDay) onSelectDay(key);
1802
+ else setInternalSelectedDay(key);
1803
+ }
1804
+ function prevMonth() {
1805
+ const m = viewMonth === 0 ? 11 : viewMonth - 1;
1806
+ const y = viewMonth === 0 ? viewYear - 1 : viewYear;
1807
+ setMonth(y, m);
1808
+ }
1809
+ function nextMonth() {
1810
+ const m = viewMonth === 11 ? 0 : viewMonth + 1;
1811
+ const y = viewMonth === 11 ? viewYear + 1 : viewYear;
1812
+ setMonth(y, m);
1813
+ }
1814
+ const eventsByDate = useMemo6(() => {
1815
+ const map = {};
1816
+ for (const evt of events) {
1817
+ const d = typeof evt.startAt === "string" ? evt.startAt.slice(0, 10) : toDateKey(evt.startAt);
1818
+ if (!map[d]) map[d] = [];
1819
+ map[d].push(evt);
1820
+ }
1821
+ return map;
1822
+ }, [events]);
1823
+ const monthDays = useMemo6(
1824
+ () => getMonthDays(viewYear, viewMonth),
1825
+ [viewYear, viewMonth]
1826
+ );
1827
+ const monthLabel = new Date(viewYear, viewMonth).toLocaleString("en-US", {
1828
+ month: "long",
1829
+ year: "numeric"
1830
+ });
1831
+ const selectedDayEvents = selectedDay ? eventsByDate[selectedDay] ?? [] : [];
1832
+ return /* @__PURE__ */ jsxs12("div", { className: cn("flex flex-1 min-h-0 overflow-hidden", className), children: [
1833
+ /* @__PURE__ */ jsxs12("div", { className: "flex-1 flex flex-col overflow-hidden border-r border-border", children: [
1834
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between border-b border-border px-4 py-2.5 shrink-0", children: [
1835
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
1836
+ headerLeft,
1837
+ /* @__PURE__ */ jsx12(
1838
+ "button",
1839
+ {
1840
+ type: "button",
1841
+ onClick: prevMonth,
1842
+ className: "h-7 w-7 rounded-md hover:bg-muted flex items-center justify-center text-muted-foreground",
1843
+ children: /* @__PURE__ */ jsx12("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx12("path", { d: "m15 18-6-6 6-6" }) })
1844
+ }
1845
+ ),
1846
+ /* @__PURE__ */ jsx12("span", { className: "text-sm font-semibold text-foreground min-w-[160px] text-center", children: monthLabel }),
1847
+ /* @__PURE__ */ jsx12(
1848
+ "button",
1849
+ {
1850
+ type: "button",
1851
+ onClick: nextMonth,
1852
+ className: "h-7 w-7 rounded-md hover:bg-muted flex items-center justify-center text-muted-foreground",
1853
+ children: /* @__PURE__ */ jsx12("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx12("path", { d: "m9 18 6-6-6-6" }) })
1854
+ }
1855
+ ),
1856
+ /* @__PURE__ */ jsx12(
1857
+ "button",
1858
+ {
1859
+ type: "button",
1860
+ onClick: () => {
1861
+ setMonth(today.getFullYear(), today.getMonth());
1862
+ selectDay(todayKey);
1863
+ },
1864
+ className: "h-7 px-2 rounded-md border border-border text-xs font-medium text-muted-foreground hover:text-foreground",
1865
+ children: "Today"
1866
+ }
1867
+ )
1868
+ ] }),
1869
+ headerRight
1870
+ ] }),
1871
+ /* @__PURE__ */ jsx12("div", { className: "grid grid-cols-7 border-b border-border shrink-0", children: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((d) => /* @__PURE__ */ jsx12(
1872
+ "div",
1873
+ {
1874
+ className: "px-2 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground text-center",
1875
+ children: d
1876
+ },
1877
+ d
1878
+ )) }),
1879
+ /* @__PURE__ */ jsx12("div", { className: "flex-1 grid grid-cols-7 auto-rows-fr overflow-y-auto", children: monthDays.map(({ date, key, inMonth }) => {
1880
+ const dayEvents = eventsByDate[key] ?? [];
1881
+ const isToday = key === todayKey;
1882
+ const isSelected = key === selectedDay;
1883
+ return /* @__PURE__ */ jsxs12(
1884
+ "button",
1885
+ {
1886
+ type: "button",
1887
+ onClick: () => selectDay(key),
1888
+ onDoubleClick: () => onDoubleClickDay?.(key),
1889
+ className: cn(
1890
+ "flex flex-col items-start p-1.5 border-b border-r border-border text-left transition-colors min-h-[72px]",
1891
+ !inMonth && "bg-muted/30",
1892
+ isSelected && "bg-primary/5 ring-1 ring-inset ring-primary/20",
1893
+ !isSelected && "hover:bg-muted/50"
1894
+ ),
1895
+ children: [
1896
+ /* @__PURE__ */ jsx12(
1897
+ "span",
1898
+ {
1899
+ className: cn(
1900
+ "text-xs font-medium w-6 h-6 flex items-center justify-center rounded-full mb-0.5",
1901
+ isToday && "bg-primary text-primary-foreground",
1902
+ !isToday && !inMonth && "text-muted-foreground/50",
1903
+ !isToday && inMonth && "text-foreground"
1904
+ ),
1905
+ children: date.getDate()
1906
+ }
1907
+ ),
1908
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap gap-0.5 w-full", children: [
1909
+ dayEvents.slice(0, 3).map(
1910
+ (evt) => renderEventChip ? /* @__PURE__ */ jsx12("span", { children: renderEventChip(evt) }, evt.id) : /* @__PURE__ */ jsx12(
1911
+ "div",
1912
+ {
1913
+ className: "w-full truncate text-[9px] px-1 py-0.5 rounded bg-primary/10 text-primary",
1914
+ title: evt.title,
1915
+ children: evt.title
1916
+ },
1917
+ evt.id
1918
+ )
1919
+ ),
1920
+ dayEvents.length > 3 && /* @__PURE__ */ jsxs12("span", { className: "text-[9px] text-muted-foreground px-1", children: [
1921
+ "+",
1922
+ dayEvents.length - 3,
1923
+ " more"
1924
+ ] })
1925
+ ] })
1926
+ ]
1927
+ },
1928
+ key
1929
+ );
1930
+ }) })
1931
+ ] }),
1932
+ showDayPanel && /* @__PURE__ */ jsx12("div", { className: "w-80 shrink-0 flex flex-col overflow-hidden", children: renderDayDetail ? renderDayDetail(selectedDay ?? todayKey, selectedDayEvents) : /* @__PURE__ */ jsxs12(Fragment2, { children: [
1933
+ /* @__PURE__ */ jsxs12("div", { className: "px-4 py-3 border-b border-border shrink-0", children: [
1934
+ /* @__PURE__ */ jsx12("h3", { className: "text-sm font-semibold text-foreground", children: selectedDay === todayKey ? "Today" : selectedDay ? (/* @__PURE__ */ new Date(selectedDay + "T00:00:00")).toLocaleDateString(
1935
+ "en-US",
1936
+ { weekday: "long", month: "long", day: "numeric" }
1937
+ ) : "Select a day" }),
1938
+ /* @__PURE__ */ jsxs12("p", { className: "text-xs text-muted-foreground", children: [
1939
+ selectedDayEvents.length,
1940
+ " event",
1941
+ selectedDayEvents.length !== 1 ? "s" : ""
1942
+ ] })
1943
+ ] }),
1944
+ /* @__PURE__ */ jsxs12("div", { className: "flex-1 overflow-y-auto p-2 space-y-2", children: [
1945
+ selectedDayEvents.length === 0 && /* @__PURE__ */ jsx12("div", { className: "px-2 py-8 text-center", children: /* @__PURE__ */ jsx12("p", { className: "text-xs text-muted-foreground", children: "No events this day" }) }),
1946
+ selectedDayEvents.map((evt) => /* @__PURE__ */ jsxs12(
1947
+ "div",
1948
+ {
1949
+ className: "rounded-lg border border-border bg-card p-3",
1950
+ children: [
1951
+ /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-foreground", children: evt.title }),
1952
+ evt.type && /* @__PURE__ */ jsx12("span", { className: "mt-1 inline-block rounded-full border border-border px-2 py-0.5 text-[10px] text-muted-foreground", children: evt.type })
1953
+ ]
1954
+ },
1955
+ evt.id
1956
+ ))
1957
+ ] })
1958
+ ] }) })
1959
+ ] });
1960
+ }
1961
+
1962
+ // src/workspace/approval-queue.tsx
1963
+ import { useState as useState7, useMemo as useMemo7 } from "react";
1964
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1965
+ function ApprovalQueue({
1966
+ items,
1967
+ className,
1968
+ onApprove,
1969
+ onReject,
1970
+ renderItemDetail,
1971
+ renderTypeBadge,
1972
+ renderStats,
1973
+ canResolve = true,
1974
+ header,
1975
+ emptyState,
1976
+ showResolved: controlledShowResolved
1977
+ }) {
1978
+ const [rejectingId, setRejectingId] = useState7(null);
1979
+ const [rejectReason, setRejectReason] = useState7("");
1980
+ const [showResolved, setShowResolved] = useState7(controlledShowResolved ?? false);
1981
+ const pending = useMemo7(
1982
+ () => items.filter((a) => a.status === "pending"),
1983
+ [items]
1984
+ );
1985
+ const resolved = useMemo7(
1986
+ () => items.filter((a) => a.status !== "pending"),
1987
+ [items]
1988
+ );
1989
+ const stats = useMemo7(() => {
1990
+ const map = {};
1991
+ for (const item of items) {
1992
+ if (!map[item.type])
1993
+ map[item.type] = { type: item.type, approved: 0, rejected: 0, total: 0, rate: 0 };
1994
+ map[item.type].total++;
1995
+ if (item.status === "approved" || item.status === "executed")
1996
+ map[item.type].approved++;
1997
+ if (item.status === "rejected") map[item.type].rejected++;
1998
+ }
1999
+ for (const s of Object.values(map)) {
2000
+ s.rate = s.total > 0 ? Math.round(s.approved / s.total * 100) : 0;
2001
+ }
2002
+ return Object.values(map);
2003
+ }, [items]);
2004
+ if (items.length === 0) {
2005
+ return /* @__PURE__ */ jsx13("div", { className: cn("flex-1 flex items-center justify-center", className), children: emptyState ?? /* @__PURE__ */ jsx13("div", { className: "text-center py-20", children: /* @__PURE__ */ jsx13("p", { className: "text-sm text-muted-foreground", children: "No proposals yet" }) }) });
2006
+ }
2007
+ return /* @__PURE__ */ jsx13("div", { className: cn("flex-1 overflow-y-auto", className), children: /* @__PURE__ */ jsxs13("div", { className: "max-w-3xl mx-auto p-6", children: [
2008
+ header,
2009
+ stats.length > 0 && /* @__PURE__ */ jsx13("div", { className: "flex gap-3 mb-6 flex-wrap", children: renderStats ? renderStats(stats) : stats.map((s) => /* @__PURE__ */ jsxs13(
2010
+ "div",
2011
+ {
2012
+ className: "flex-1 min-w-[120px] rounded-lg border border-border bg-card p-3",
2013
+ children: [
2014
+ /* @__PURE__ */ jsx13("div", { className: "flex items-center gap-2 mb-1", children: renderTypeBadge ? renderTypeBadge(s.type) : /* @__PURE__ */ jsx13("span", { className: "rounded-full border border-border px-2 py-0.5 text-[10px] font-medium text-muted-foreground", children: s.type }) }),
2015
+ /* @__PURE__ */ jsx13("div", { className: "flex items-baseline gap-1", children: /* @__PURE__ */ jsxs13("span", { className: "text-xl font-semibold text-foreground", children: [
2016
+ s.rate,
2017
+ "%"
2018
+ ] }) }),
2019
+ /* @__PURE__ */ jsxs13("p", { className: "text-[10px] text-muted-foreground", children: [
2020
+ s.approved,
2021
+ "/",
2022
+ s.total,
2023
+ " approved"
2024
+ ] })
2025
+ ]
2026
+ },
2027
+ s.type
2028
+ )) }),
2029
+ pending.length > 0 && /* @__PURE__ */ jsxs13("section", { className: "mb-8", children: [
2030
+ /* @__PURE__ */ jsxs13("h3", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3", children: [
2031
+ "Pending (",
2032
+ pending.length,
2033
+ ")"
2034
+ ] }),
2035
+ /* @__PURE__ */ jsx13("div", { className: "space-y-3", children: pending.map((item) => /* @__PURE__ */ jsxs13(
2036
+ "div",
2037
+ {
2038
+ className: "rounded-lg border border-primary/20 bg-card p-4",
2039
+ children: [
2040
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-start gap-3", children: [
2041
+ /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
2042
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 mb-1", children: [
2043
+ /* @__PURE__ */ jsx13("h4", { className: "text-sm font-medium text-foreground", children: item.title }),
2044
+ renderTypeBadge ? renderTypeBadge(item.type) : /* @__PURE__ */ jsx13("span", { className: "rounded-full border border-border px-2 py-0.5 text-[10px] text-muted-foreground", children: item.type })
2045
+ ] }),
2046
+ item.description && /* @__PURE__ */ jsx13("p", { className: "text-xs text-muted-foreground line-clamp-2", children: item.description }),
2047
+ renderItemDetail?.(item)
2048
+ ] }),
2049
+ canResolve && /* @__PURE__ */ jsxs13("div", { className: "flex gap-1.5 shrink-0", children: [
2050
+ /* @__PURE__ */ jsx13(
2051
+ "button",
2052
+ {
2053
+ type: "button",
2054
+ onClick: () => onApprove?.(item),
2055
+ className: "h-8 px-3 rounded-md border border-border text-xs font-medium text-emerald-500 hover:bg-emerald-500/10 hover:border-emerald-500/30 transition-colors",
2056
+ children: "Approve"
2057
+ }
2058
+ ),
2059
+ /* @__PURE__ */ jsx13(
2060
+ "button",
2061
+ {
2062
+ type: "button",
2063
+ onClick: () => {
2064
+ setRejectingId(
2065
+ rejectingId === item.id ? null : item.id
2066
+ );
2067
+ setRejectReason("");
2068
+ },
2069
+ className: "h-8 px-3 rounded-md border border-border text-xs font-medium text-destructive hover:bg-destructive/10 hover:border-destructive/30 transition-colors",
2070
+ children: "Reject"
2071
+ }
2072
+ )
2073
+ ] })
2074
+ ] }),
2075
+ rejectingId === item.id && /* @__PURE__ */ jsxs13("div", { className: "mt-3 border-t border-border pt-3", children: [
2076
+ /* @__PURE__ */ jsx13(
2077
+ "textarea",
2078
+ {
2079
+ placeholder: "Why are you rejecting this?",
2080
+ value: rejectReason,
2081
+ onChange: (e) => setRejectReason(e.target.value),
2082
+ className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary/40 mb-2",
2083
+ rows: 2
2084
+ }
2085
+ ),
2086
+ /* @__PURE__ */ jsxs13("div", { className: "flex gap-2 justify-end", children: [
2087
+ /* @__PURE__ */ jsx13(
2088
+ "button",
2089
+ {
2090
+ type: "button",
2091
+ onClick: () => {
2092
+ setRejectingId(null);
2093
+ setRejectReason("");
2094
+ },
2095
+ className: "h-7 px-3 rounded-md text-xs text-muted-foreground hover:text-foreground",
2096
+ children: "Cancel"
2097
+ }
2098
+ ),
2099
+ /* @__PURE__ */ jsx13(
2100
+ "button",
2101
+ {
2102
+ type: "button",
2103
+ onClick: () => {
2104
+ onReject?.(item, rejectReason.trim() || void 0);
2105
+ setRejectingId(null);
2106
+ setRejectReason("");
2107
+ },
2108
+ className: "h-7 px-3 rounded-md bg-destructive text-destructive-foreground text-xs font-medium",
2109
+ children: "Submit Rejection"
2110
+ }
2111
+ )
2112
+ ] })
2113
+ ] })
2114
+ ]
2115
+ },
2116
+ item.id
2117
+ )) })
2118
+ ] }),
2119
+ resolved.length > 0 && /* @__PURE__ */ jsxs13("section", { children: [
2120
+ /* @__PURE__ */ jsxs13(
2121
+ "button",
2122
+ {
2123
+ type: "button",
2124
+ onClick: () => setShowResolved(!showResolved),
2125
+ className: "flex items-center gap-2 text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3 hover:text-foreground transition-colors",
2126
+ children: [
2127
+ /* @__PURE__ */ jsx13(
2128
+ "svg",
2129
+ {
2130
+ width: "12",
2131
+ height: "12",
2132
+ viewBox: "0 0 24 24",
2133
+ fill: "none",
2134
+ stroke: "currentColor",
2135
+ strokeWidth: "2",
2136
+ className: cn(
2137
+ "transition-transform",
2138
+ showResolved && "rotate-180"
2139
+ ),
2140
+ children: /* @__PURE__ */ jsx13("path", { d: "m6 9 6 6 6-6" })
2141
+ }
2142
+ ),
2143
+ "Resolved (",
2144
+ resolved.length,
2145
+ ")"
2146
+ ]
2147
+ }
2148
+ ),
2149
+ showResolved && /* @__PURE__ */ jsx13("div", { className: "space-y-2", children: resolved.map((item) => /* @__PURE__ */ jsxs13(
2150
+ "div",
2151
+ {
2152
+ className: "rounded-lg border border-border bg-card p-4 opacity-60",
2153
+ children: [
2154
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 mb-1", children: [
2155
+ /* @__PURE__ */ jsx13("h4", { className: "text-sm font-medium text-foreground", children: item.title }),
2156
+ renderTypeBadge?.(item.type),
2157
+ /* @__PURE__ */ jsx13(
2158
+ "span",
2159
+ {
2160
+ className: cn(
2161
+ "rounded-full px-2 py-0.5 text-[10px] font-medium",
2162
+ item.status === "approved" && "bg-emerald-500/10 text-emerald-500",
2163
+ item.status === "rejected" && "bg-destructive/10 text-destructive",
2164
+ item.status === "executed" && "bg-primary/10 text-primary"
2165
+ ),
2166
+ children: item.status
2167
+ }
2168
+ )
2169
+ ] }),
2170
+ item.meta?.rejectionReason != null && /* @__PURE__ */ jsxs13("p", { className: "mt-1 text-xs text-destructive", children: [
2171
+ "Reason: ",
2172
+ String(item.meta.rejectionReason)
2173
+ ] })
2174
+ ]
2175
+ },
2176
+ item.id
2177
+ )) })
2178
+ ] })
2179
+ ] }) });
2180
+ }
2181
+
1659
2182
  export {
1660
2183
  WorkspaceLayout,
1661
2184
  DirectoryPane,
@@ -1667,5 +2190,8 @@ export {
1667
2190
  SessionActivityMonitor,
1668
2191
  SandboxWorkbench,
1669
2192
  AgentWorkbench,
1670
- AuditResults
2193
+ AuditResults,
2194
+ TaskBoard,
2195
+ CalendarView,
2196
+ ApprovalQueue
1671
2197
  };