@datum-cloud/activity-ui 0.6.0 → 0.7.0
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/components/ActivityDigestSummary.d.ts +15 -0
- package/dist/components/ActivityDigestSummary.d.ts.map +1 -0
- package/dist/components/ActivityFeed.d.ts +11 -2
- package/dist/components/ActivityFeed.d.ts.map +1 -1
- package/dist/components/ActivityFeedFilters.d.ts +9 -1
- package/dist/components/ActivityFeedFilters.d.ts.map +1 -1
- package/dist/components/ActivityFeedGroupItem.d.ts +23 -0
- package/dist/components/ActivityFeedGroupItem.d.ts.map +1 -0
- package/dist/components/ActivityFeedItem.d.ts +8 -1
- package/dist/components/ActivityFeedItem.d.ts.map +1 -1
- package/dist/hooks/useActivityFeed.d.ts.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +471 -114
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +474 -113
- package/dist/index.js.map +1 -1
- package/dist/lib/groupActivities.d.ts +40 -0
- package/dist/lib/groupActivities.d.ts.map +1 -0
- package/dist/lib/verb.d.ts +22 -0
- package/dist/lib/verb.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1188,6 +1188,48 @@ function constructNow(date) {
|
|
|
1188
1188
|
return constructFrom(date, Date.now());
|
|
1189
1189
|
}
|
|
1190
1190
|
|
|
1191
|
+
/**
|
|
1192
|
+
* The {@link isSameDay} function options.
|
|
1193
|
+
*/
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* @name isSameDay
|
|
1197
|
+
* @category Day Helpers
|
|
1198
|
+
* @summary Are the given dates in the same day (and year and month)?
|
|
1199
|
+
*
|
|
1200
|
+
* @description
|
|
1201
|
+
* Are the given dates in the same day (and year and month)?
|
|
1202
|
+
*
|
|
1203
|
+
* @param laterDate - The first date to check
|
|
1204
|
+
* @param earlierDate - The second date to check
|
|
1205
|
+
* @param options - An object with options
|
|
1206
|
+
*
|
|
1207
|
+
* @returns The dates are in the same day (and year and month)
|
|
1208
|
+
*
|
|
1209
|
+
* @example
|
|
1210
|
+
* // Are 4 September 06:00:00 and 4 September 18:00:00 in the same day?
|
|
1211
|
+
* const result = isSameDay(new Date(2014, 8, 4, 6, 0), new Date(2014, 8, 4, 18, 0))
|
|
1212
|
+
* //=> true
|
|
1213
|
+
*
|
|
1214
|
+
* @example
|
|
1215
|
+
* // Are 4 September and 4 October in the same day?
|
|
1216
|
+
* const result = isSameDay(new Date(2014, 8, 4), new Date(2014, 9, 4))
|
|
1217
|
+
* //=> false
|
|
1218
|
+
*
|
|
1219
|
+
* @example
|
|
1220
|
+
* // Are 4 September, 2014 and 4 September, 2015 in the same day?
|
|
1221
|
+
* const result = isSameDay(new Date(2014, 8, 4), new Date(2015, 8, 4))
|
|
1222
|
+
* //=> false
|
|
1223
|
+
*/
|
|
1224
|
+
function isSameDay(laterDate, earlierDate, options) {
|
|
1225
|
+
const [dateLeft_, dateRight_] = normalizeDates(
|
|
1226
|
+
options?.in,
|
|
1227
|
+
laterDate,
|
|
1228
|
+
earlierDate,
|
|
1229
|
+
);
|
|
1230
|
+
return +startOfDay(dateLeft_) === +startOfDay(dateRight_);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1191
1233
|
/**
|
|
1192
1234
|
* @name isDate
|
|
1193
1235
|
* @category Common Helpers
|
|
@@ -3924,6 +3966,36 @@ function formatISO(date, options) {
|
|
|
3924
3966
|
return result;
|
|
3925
3967
|
}
|
|
3926
3968
|
|
|
3969
|
+
/**
|
|
3970
|
+
* The {@link isToday} function options.
|
|
3971
|
+
*/
|
|
3972
|
+
|
|
3973
|
+
/**
|
|
3974
|
+
* @name isToday
|
|
3975
|
+
* @category Day Helpers
|
|
3976
|
+
* @summary Is the given date today?
|
|
3977
|
+
* @pure false
|
|
3978
|
+
*
|
|
3979
|
+
* @description
|
|
3980
|
+
* Is the given date today?
|
|
3981
|
+
*
|
|
3982
|
+
* @param date - The date to check
|
|
3983
|
+
* @param options - An object with options
|
|
3984
|
+
*
|
|
3985
|
+
* @returns The date is today
|
|
3986
|
+
*
|
|
3987
|
+
* @example
|
|
3988
|
+
* // If today is 6 October 2014, is 6 October 14:00:00 today?
|
|
3989
|
+
* const result = isToday(new Date(2014, 9, 6, 14, 0))
|
|
3990
|
+
* //=> true
|
|
3991
|
+
*/
|
|
3992
|
+
function isToday(date, options) {
|
|
3993
|
+
return isSameDay(
|
|
3994
|
+
constructFrom(date, date),
|
|
3995
|
+
constructNow(date),
|
|
3996
|
+
);
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3927
3999
|
/**
|
|
3928
4000
|
* The {@link subDays} function options.
|
|
3929
4001
|
*/
|
|
@@ -3951,6 +4023,36 @@ function subDays(date, amount, options) {
|
|
|
3951
4023
|
return addDays(date, -amount, options);
|
|
3952
4024
|
}
|
|
3953
4025
|
|
|
4026
|
+
/**
|
|
4027
|
+
* The {@link isYesterday} function options.
|
|
4028
|
+
*/
|
|
4029
|
+
|
|
4030
|
+
/**
|
|
4031
|
+
* @name isYesterday
|
|
4032
|
+
* @category Day Helpers
|
|
4033
|
+
* @summary Is the given date yesterday?
|
|
4034
|
+
* @pure false
|
|
4035
|
+
*
|
|
4036
|
+
* @description
|
|
4037
|
+
* Is the given date yesterday?
|
|
4038
|
+
*
|
|
4039
|
+
* @param date - The date to check
|
|
4040
|
+
* @param options - An object with options
|
|
4041
|
+
*
|
|
4042
|
+
* @returns The date is yesterday
|
|
4043
|
+
*
|
|
4044
|
+
* @example
|
|
4045
|
+
* // If today is 6 October 2014, is 5 October 14:00:00 yesterday?
|
|
4046
|
+
* const result = isYesterday(new Date(2014, 9, 5, 14, 0))
|
|
4047
|
+
* //=> true
|
|
4048
|
+
*/
|
|
4049
|
+
function isYesterday(date, options) {
|
|
4050
|
+
return isSameDay(
|
|
4051
|
+
constructFrom(date, date),
|
|
4052
|
+
subDays(constructNow(date), 1),
|
|
4053
|
+
);
|
|
4054
|
+
}
|
|
4055
|
+
|
|
3954
4056
|
/**
|
|
3955
4057
|
* The {@link subHours} function options.
|
|
3956
4058
|
*/
|
|
@@ -7892,7 +7994,7 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
7892
7994
|
*/
|
|
7893
7995
|
|
|
7894
7996
|
|
|
7895
|
-
const __iconNode$
|
|
7997
|
+
const __iconNode$o = [
|
|
7896
7998
|
[
|
|
7897
7999
|
"path",
|
|
7898
8000
|
{
|
|
@@ -7901,7 +8003,7 @@ const __iconNode$n = [
|
|
|
7901
8003
|
}
|
|
7902
8004
|
]
|
|
7903
8005
|
];
|
|
7904
|
-
const Activity = createLucideIcon("activity", __iconNode$
|
|
8006
|
+
const Activity = createLucideIcon("activity", __iconNode$o);
|
|
7905
8007
|
|
|
7906
8008
|
/**
|
|
7907
8009
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -7911,7 +8013,7 @@ const Activity = createLucideIcon("activity", __iconNode$n);
|
|
|
7911
8013
|
*/
|
|
7912
8014
|
|
|
7913
8015
|
|
|
7914
|
-
const __iconNode$
|
|
8016
|
+
const __iconNode$n = [
|
|
7915
8017
|
["path", { d: "M10.268 21a2 2 0 0 0 3.464 0", key: "vwvbt9" }],
|
|
7916
8018
|
[
|
|
7917
8019
|
"path",
|
|
@@ -7921,7 +8023,7 @@ const __iconNode$m = [
|
|
|
7921
8023
|
}
|
|
7922
8024
|
]
|
|
7923
8025
|
];
|
|
7924
|
-
const Bell = createLucideIcon("bell", __iconNode$
|
|
8026
|
+
const Bell = createLucideIcon("bell", __iconNode$n);
|
|
7925
8027
|
|
|
7926
8028
|
/**
|
|
7927
8029
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -7931,8 +8033,8 @@ const Bell = createLucideIcon("bell", __iconNode$m);
|
|
|
7931
8033
|
*/
|
|
7932
8034
|
|
|
7933
8035
|
|
|
7934
|
-
const __iconNode$
|
|
7935
|
-
const Check = createLucideIcon("check", __iconNode$
|
|
8036
|
+
const __iconNode$m = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
|
|
8037
|
+
const Check = createLucideIcon("check", __iconNode$m);
|
|
7936
8038
|
|
|
7937
8039
|
/**
|
|
7938
8040
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -7942,8 +8044,8 @@ const Check = createLucideIcon("check", __iconNode$l);
|
|
|
7942
8044
|
*/
|
|
7943
8045
|
|
|
7944
8046
|
|
|
7945
|
-
const __iconNode$
|
|
7946
|
-
const ChevronDown = createLucideIcon("chevron-down", __iconNode$
|
|
8047
|
+
const __iconNode$l = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
|
|
8048
|
+
const ChevronDown = createLucideIcon("chevron-down", __iconNode$l);
|
|
7947
8049
|
|
|
7948
8050
|
/**
|
|
7949
8051
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -7953,8 +8055,8 @@ const ChevronDown = createLucideIcon("chevron-down", __iconNode$k);
|
|
|
7953
8055
|
*/
|
|
7954
8056
|
|
|
7955
8057
|
|
|
7956
|
-
const __iconNode$
|
|
7957
|
-
const ChevronRight = createLucideIcon("chevron-right", __iconNode$
|
|
8058
|
+
const __iconNode$k = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
|
|
8059
|
+
const ChevronRight = createLucideIcon("chevron-right", __iconNode$k);
|
|
7958
8060
|
|
|
7959
8061
|
/**
|
|
7960
8062
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -7964,8 +8066,8 @@ const ChevronRight = createLucideIcon("chevron-right", __iconNode$j);
|
|
|
7964
8066
|
*/
|
|
7965
8067
|
|
|
7966
8068
|
|
|
7967
|
-
const __iconNode$
|
|
7968
|
-
const ChevronUp = createLucideIcon("chevron-up", __iconNode$
|
|
8069
|
+
const __iconNode$j = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
|
|
8070
|
+
const ChevronUp = createLucideIcon("chevron-up", __iconNode$j);
|
|
7969
8071
|
|
|
7970
8072
|
/**
|
|
7971
8073
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -7975,12 +8077,12 @@ const ChevronUp = createLucideIcon("chevron-up", __iconNode$i);
|
|
|
7975
8077
|
*/
|
|
7976
8078
|
|
|
7977
8079
|
|
|
7978
|
-
const __iconNode$
|
|
8080
|
+
const __iconNode$i = [
|
|
7979
8081
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
7980
8082
|
["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
|
|
7981
8083
|
["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
|
|
7982
8084
|
];
|
|
7983
|
-
const CircleAlert = createLucideIcon("circle-alert", __iconNode$
|
|
8085
|
+
const CircleAlert = createLucideIcon("circle-alert", __iconNode$i);
|
|
7984
8086
|
|
|
7985
8087
|
/**
|
|
7986
8088
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -7990,11 +8092,11 @@ const CircleAlert = createLucideIcon("circle-alert", __iconNode$h);
|
|
|
7990
8092
|
*/
|
|
7991
8093
|
|
|
7992
8094
|
|
|
7993
|
-
const __iconNode$
|
|
8095
|
+
const __iconNode$h = [
|
|
7994
8096
|
["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
|
|
7995
8097
|
["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
|
|
7996
8098
|
];
|
|
7997
|
-
const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$
|
|
8099
|
+
const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$h);
|
|
7998
8100
|
|
|
7999
8101
|
/**
|
|
8000
8102
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8004,11 +8106,11 @@ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$g);
|
|
|
8004
8106
|
*/
|
|
8005
8107
|
|
|
8006
8108
|
|
|
8007
|
-
const __iconNode$
|
|
8109
|
+
const __iconNode$g = [
|
|
8008
8110
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
8009
8111
|
["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
|
|
8010
8112
|
];
|
|
8011
|
-
const CircleCheck = createLucideIcon("circle-check", __iconNode$
|
|
8113
|
+
const CircleCheck = createLucideIcon("circle-check", __iconNode$g);
|
|
8012
8114
|
|
|
8013
8115
|
/**
|
|
8014
8116
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8018,12 +8120,12 @@ const CircleCheck = createLucideIcon("circle-check", __iconNode$f);
|
|
|
8018
8120
|
*/
|
|
8019
8121
|
|
|
8020
8122
|
|
|
8021
|
-
const __iconNode$
|
|
8123
|
+
const __iconNode$f = [
|
|
8022
8124
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
8023
8125
|
["path", { d: "m15 9-6 6", key: "1uzhvr" }],
|
|
8024
8126
|
["path", { d: "m9 9 6 6", key: "z0biqf" }]
|
|
8025
8127
|
];
|
|
8026
|
-
const CircleX = createLucideIcon("circle-x", __iconNode$
|
|
8128
|
+
const CircleX = createLucideIcon("circle-x", __iconNode$f);
|
|
8027
8129
|
|
|
8028
8130
|
/**
|
|
8029
8131
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8033,11 +8135,11 @@ const CircleX = createLucideIcon("circle-x", __iconNode$e);
|
|
|
8033
8135
|
*/
|
|
8034
8136
|
|
|
8035
8137
|
|
|
8036
|
-
const __iconNode$
|
|
8138
|
+
const __iconNode$e = [
|
|
8037
8139
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
8038
8140
|
["path", { d: "M12 6v6l4 2", key: "mmk7yg" }]
|
|
8039
8141
|
];
|
|
8040
|
-
const Clock = createLucideIcon("clock", __iconNode$
|
|
8142
|
+
const Clock = createLucideIcon("clock", __iconNode$e);
|
|
8041
8143
|
|
|
8042
8144
|
/**
|
|
8043
8145
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8047,11 +8149,11 @@ const Clock = createLucideIcon("clock", __iconNode$d);
|
|
|
8047
8149
|
*/
|
|
8048
8150
|
|
|
8049
8151
|
|
|
8050
|
-
const __iconNode$
|
|
8152
|
+
const __iconNode$d = [
|
|
8051
8153
|
["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
|
|
8052
8154
|
["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
|
|
8053
8155
|
];
|
|
8054
|
-
const Copy = createLucideIcon("copy", __iconNode$
|
|
8156
|
+
const Copy = createLucideIcon("copy", __iconNode$d);
|
|
8055
8157
|
|
|
8056
8158
|
/**
|
|
8057
8159
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8061,7 +8163,7 @@ const Copy = createLucideIcon("copy", __iconNode$c);
|
|
|
8061
8163
|
*/
|
|
8062
8164
|
|
|
8063
8165
|
|
|
8064
|
-
const __iconNode$
|
|
8166
|
+
const __iconNode$c = [
|
|
8065
8167
|
[
|
|
8066
8168
|
"path",
|
|
8067
8169
|
{
|
|
@@ -8071,7 +8173,7 @@ const __iconNode$b = [
|
|
|
8071
8173
|
],
|
|
8072
8174
|
["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
|
|
8073
8175
|
];
|
|
8074
|
-
const Eye = createLucideIcon("eye", __iconNode$
|
|
8176
|
+
const Eye = createLucideIcon("eye", __iconNode$c);
|
|
8075
8177
|
|
|
8076
8178
|
/**
|
|
8077
8179
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8081,8 +8183,8 @@ const Eye = createLucideIcon("eye", __iconNode$b);
|
|
|
8081
8183
|
*/
|
|
8082
8184
|
|
|
8083
8185
|
|
|
8084
|
-
const __iconNode$
|
|
8085
|
-
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$
|
|
8186
|
+
const __iconNode$b = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
|
|
8187
|
+
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$b);
|
|
8086
8188
|
|
|
8087
8189
|
/**
|
|
8088
8190
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8092,7 +8194,7 @@ const LoaderCircle = createLucideIcon("loader-circle", __iconNode$a);
|
|
|
8092
8194
|
*/
|
|
8093
8195
|
|
|
8094
8196
|
|
|
8095
|
-
const __iconNode$
|
|
8197
|
+
const __iconNode$a = [
|
|
8096
8198
|
[
|
|
8097
8199
|
"path",
|
|
8098
8200
|
{
|
|
@@ -8102,7 +8204,7 @@ const __iconNode$9 = [
|
|
|
8102
8204
|
],
|
|
8103
8205
|
["path", { d: "m15 5 4 4", key: "1mk7zo" }]
|
|
8104
8206
|
];
|
|
8105
|
-
const Pencil = createLucideIcon("pencil", __iconNode$
|
|
8207
|
+
const Pencil = createLucideIcon("pencil", __iconNode$a);
|
|
8106
8208
|
|
|
8107
8209
|
/**
|
|
8108
8210
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8112,11 +8214,11 @@ const Pencil = createLucideIcon("pencil", __iconNode$9);
|
|
|
8112
8214
|
*/
|
|
8113
8215
|
|
|
8114
8216
|
|
|
8115
|
-
const __iconNode$
|
|
8217
|
+
const __iconNode$9 = [
|
|
8116
8218
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
8117
8219
|
["path", { d: "M12 5v14", key: "s699le" }]
|
|
8118
8220
|
];
|
|
8119
|
-
const Plus = createLucideIcon("plus", __iconNode$
|
|
8221
|
+
const Plus = createLucideIcon("plus", __iconNode$9);
|
|
8120
8222
|
|
|
8121
8223
|
/**
|
|
8122
8224
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8126,13 +8228,13 @@ const Plus = createLucideIcon("plus", __iconNode$8);
|
|
|
8126
8228
|
*/
|
|
8127
8229
|
|
|
8128
8230
|
|
|
8129
|
-
const __iconNode$
|
|
8231
|
+
const __iconNode$8 = [
|
|
8130
8232
|
["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
|
|
8131
8233
|
["path", { d: "M21 3v5h-5", key: "1q7to0" }],
|
|
8132
8234
|
["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
|
|
8133
8235
|
["path", { d: "M8 16H3v5", key: "1cv678" }]
|
|
8134
8236
|
];
|
|
8135
|
-
const RefreshCw = createLucideIcon("refresh-cw", __iconNode$
|
|
8237
|
+
const RefreshCw = createLucideIcon("refresh-cw", __iconNode$8);
|
|
8136
8238
|
|
|
8137
8239
|
/**
|
|
8138
8240
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8142,11 +8244,11 @@ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$7);
|
|
|
8142
8244
|
*/
|
|
8143
8245
|
|
|
8144
8246
|
|
|
8145
|
-
const __iconNode$
|
|
8247
|
+
const __iconNode$7 = [
|
|
8146
8248
|
["path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8", key: "1p45f6" }],
|
|
8147
8249
|
["path", { d: "M21 3v5h-5", key: "1q7to0" }]
|
|
8148
8250
|
];
|
|
8149
|
-
const RotateCw = createLucideIcon("rotate-cw", __iconNode$
|
|
8251
|
+
const RotateCw = createLucideIcon("rotate-cw", __iconNode$7);
|
|
8150
8252
|
|
|
8151
8253
|
/**
|
|
8152
8254
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -8156,11 +8258,32 @@ const RotateCw = createLucideIcon("rotate-cw", __iconNode$6);
|
|
|
8156
8258
|
*/
|
|
8157
8259
|
|
|
8158
8260
|
|
|
8159
|
-
const __iconNode$
|
|
8261
|
+
const __iconNode$6 = [
|
|
8160
8262
|
["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
|
|
8161
8263
|
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
|
|
8162
8264
|
];
|
|
8163
|
-
const Search = createLucideIcon("search", __iconNode$
|
|
8265
|
+
const Search = createLucideIcon("search", __iconNode$6);
|
|
8266
|
+
|
|
8267
|
+
/**
|
|
8268
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8269
|
+
*
|
|
8270
|
+
* This source code is licensed under the ISC license.
|
|
8271
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8272
|
+
*/
|
|
8273
|
+
|
|
8274
|
+
|
|
8275
|
+
const __iconNode$5 = [
|
|
8276
|
+
["path", { d: "M10 5H3", key: "1qgfaw" }],
|
|
8277
|
+
["path", { d: "M12 19H3", key: "yhmn1j" }],
|
|
8278
|
+
["path", { d: "M14 3v4", key: "1sua03" }],
|
|
8279
|
+
["path", { d: "M16 17v4", key: "1q0r14" }],
|
|
8280
|
+
["path", { d: "M21 12h-9", key: "1o4lsq" }],
|
|
8281
|
+
["path", { d: "M21 19h-5", key: "1rlt1p" }],
|
|
8282
|
+
["path", { d: "M21 5h-7", key: "1oszz2" }],
|
|
8283
|
+
["path", { d: "M8 10v4", key: "tgpxqk" }],
|
|
8284
|
+
["path", { d: "M8 12H3", key: "a7s4jb" }]
|
|
8285
|
+
];
|
|
8286
|
+
const SlidersHorizontal = createLucideIcon("sliders-horizontal", __iconNode$5);
|
|
8164
8287
|
|
|
8165
8288
|
/**
|
|
8166
8289
|
* @license lucide-react v0.577.0 - ISC
|
|
@@ -9661,6 +9784,14 @@ function ActionToggle({ value, onChange, className = '', disabled = false, }) {
|
|
|
9661
9784
|
|
|
9662
9785
|
// Debounce delay for filter changes (ms)
|
|
9663
9786
|
const FILTER_DEBOUNCE_MS$1 = 300;
|
|
9787
|
+
// Backoff for watch stream reconnects. Without this, a persistently failing
|
|
9788
|
+
// upstream gets re-opened on every render with no delay — see
|
|
9789
|
+
// https://github.com/datum-cloud/infra/issues/5060, where a single browser
|
|
9790
|
+
// tab reconnected roughly 2.5 times a minute against a stream that kept
|
|
9791
|
+
// aborting, driving the proxy's memory up until the process was OOMKilled.
|
|
9792
|
+
const RECONNECT_BASE_DELAY_MS = 1000;
|
|
9793
|
+
const RECONNECT_MAX_DELAY_MS = 30000;
|
|
9794
|
+
const MAX_RECONNECT_ATTEMPTS = 8;
|
|
9664
9795
|
/**
|
|
9665
9796
|
* Escape a value for safe interpolation into a CEL string literal.
|
|
9666
9797
|
* Prevents injection via values containing backslashes or double quotes.
|
|
@@ -9755,6 +9886,12 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
9755
9886
|
const resourceVersionRef = useRef();
|
|
9756
9887
|
// Track the watch stop function
|
|
9757
9888
|
const watchStopRef = useRef(null);
|
|
9889
|
+
// Consecutive watch failures since the last successful event, used to back
|
|
9890
|
+
// off reconnect attempts. Reset on any real event (including BOOKMARK) and
|
|
9891
|
+
// on a manual startStreaming() call.
|
|
9892
|
+
const reconnectAttemptsRef = useRef(0);
|
|
9893
|
+
// Pending scheduled reconnect, if any.
|
|
9894
|
+
const reconnectTimerRef = useRef(null);
|
|
9758
9895
|
// Track whether streaming should restart after filter change
|
|
9759
9896
|
const shouldRestartStreamingRef = useRef(false);
|
|
9760
9897
|
// Track if we've done the initial load
|
|
@@ -9829,6 +9966,9 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
9829
9966
|
console.error('Watch error:', event.object);
|
|
9830
9967
|
return;
|
|
9831
9968
|
}
|
|
9969
|
+
// A real event (including a BOOKMARK keep-alive) means the connection is
|
|
9970
|
+
// healthy — reset backoff so a later failure starts from the base delay.
|
|
9971
|
+
reconnectAttemptsRef.current = 0;
|
|
9832
9972
|
if (event.type === 'BOOKMARK') {
|
|
9833
9973
|
// Update resource version for resume capability
|
|
9834
9974
|
if (event.object.metadata?.resourceVersion) {
|
|
@@ -9894,15 +10034,13 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
9894
10034
|
setActivities((prev) => prev.filter((a) => a.metadata?.name !== event.object.metadata?.name));
|
|
9895
10035
|
}
|
|
9896
10036
|
}, [filters]);
|
|
9897
|
-
//
|
|
9898
|
-
|
|
10037
|
+
// Open the watch connection. Does not touch userPaused/backoff state —
|
|
10038
|
+
// callers (startStreaming, scheduleReconnect) own that.
|
|
10039
|
+
const connectWatch = useCallback(() => {
|
|
9899
10040
|
if (watchStopRef.current) {
|
|
9900
10041
|
// Already watching
|
|
9901
10042
|
return;
|
|
9902
10043
|
}
|
|
9903
|
-
// Explicit start clears the user-paused flag so the auto-restart
|
|
9904
|
-
// effects can keep the stream alive after subsequent filter changes.
|
|
9905
|
-
setUserPaused(false);
|
|
9906
10044
|
// Clear any previous watch error when starting a new stream
|
|
9907
10045
|
setWatchError(null);
|
|
9908
10046
|
const params = buildWatchParams();
|
|
@@ -9916,6 +10054,7 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
9916
10054
|
setWatchError(streamError);
|
|
9917
10055
|
setIsStreaming(false);
|
|
9918
10056
|
watchStopRef.current = null;
|
|
10057
|
+
reconnectAttemptsRef.current += 1;
|
|
9919
10058
|
},
|
|
9920
10059
|
onClose: () => {
|
|
9921
10060
|
setIsStreaming(false);
|
|
@@ -9926,11 +10065,50 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
9926
10065
|
setIsStreaming(true);
|
|
9927
10066
|
setNewActivitiesCount(0);
|
|
9928
10067
|
}, [client, buildWatchParams, handleWatchEvent]);
|
|
10068
|
+
// Reconnect with exponential backoff. The first attempt (no prior
|
|
10069
|
+
// failures) connects immediately; each subsequent failure doubles the
|
|
10070
|
+
// delay up to RECONNECT_MAX_DELAY_MS. After MAX_RECONNECT_ATTEMPTS
|
|
10071
|
+
// consecutive failures, this stops retrying automatically — the watch
|
|
10072
|
+
// stays stopped (with watchError set) until the user calls
|
|
10073
|
+
// startStreaming() again, which resets the attempt count.
|
|
10074
|
+
const scheduleReconnect = useCallback(() => {
|
|
10075
|
+
if (reconnectTimerRef.current) {
|
|
10076
|
+
// A reconnect is already scheduled.
|
|
10077
|
+
return;
|
|
10078
|
+
}
|
|
10079
|
+
if (reconnectAttemptsRef.current >= MAX_RECONNECT_ATTEMPTS) {
|
|
10080
|
+
return;
|
|
10081
|
+
}
|
|
10082
|
+
const attempt = reconnectAttemptsRef.current;
|
|
10083
|
+
const delay = attempt === 0 ? 0 : Math.min(RECONNECT_MAX_DELAY_MS, RECONNECT_BASE_DELAY_MS * 2 ** (attempt - 1));
|
|
10084
|
+
reconnectTimerRef.current = setTimeout(() => {
|
|
10085
|
+
reconnectTimerRef.current = null;
|
|
10086
|
+
connectWatch();
|
|
10087
|
+
}, delay);
|
|
10088
|
+
}, [connectWatch]);
|
|
10089
|
+
// Start watching for real-time updates. This is the user/effect-facing
|
|
10090
|
+
// entry point: it resets backoff and any pending scheduled reconnect
|
|
10091
|
+
// before connecting, so a manual retry always starts from a clean slate.
|
|
10092
|
+
const startStreaming = useCallback(() => {
|
|
10093
|
+
// Explicit start clears the user-paused flag so the auto-restart
|
|
10094
|
+
// effects can keep the stream alive after subsequent filter changes.
|
|
10095
|
+
setUserPaused(false);
|
|
10096
|
+
reconnectAttemptsRef.current = 0;
|
|
10097
|
+
if (reconnectTimerRef.current) {
|
|
10098
|
+
clearTimeout(reconnectTimerRef.current);
|
|
10099
|
+
reconnectTimerRef.current = null;
|
|
10100
|
+
}
|
|
10101
|
+
connectWatch();
|
|
10102
|
+
}, [connectWatch]);
|
|
9929
10103
|
// Stop watching. Marks the user as paused so the auto-start effects
|
|
9930
10104
|
// below don't immediately re-open the stream this call just closed.
|
|
9931
10105
|
const stopStreaming = useCallback(() => {
|
|
9932
10106
|
setUserPaused(true);
|
|
9933
10107
|
setIsStreaming(false);
|
|
10108
|
+
if (reconnectTimerRef.current) {
|
|
10109
|
+
clearTimeout(reconnectTimerRef.current);
|
|
10110
|
+
reconnectTimerRef.current = null;
|
|
10111
|
+
}
|
|
9934
10112
|
if (watchStopRef.current) {
|
|
9935
10113
|
watchStopRef.current();
|
|
9936
10114
|
watchStopRef.current = null;
|
|
@@ -10093,9 +10271,10 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10093
10271
|
}
|
|
10094
10272
|
};
|
|
10095
10273
|
}, [filters, timeRange]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
10096
|
-
// Auto-start streaming after initial load when enabled
|
|
10097
|
-
//
|
|
10098
|
-
//
|
|
10274
|
+
// Auto-start streaming after initial load when enabled, and reconnect
|
|
10275
|
+
// (with backoff — see scheduleReconnect) whenever a connection drops.
|
|
10276
|
+
// Skipped when the user has explicitly paused — otherwise clicking Pause
|
|
10277
|
+
// would be immediately undone by this effect on the next render.
|
|
10099
10278
|
useEffect(() => {
|
|
10100
10279
|
if (enableStreaming &&
|
|
10101
10280
|
autoStartStreaming &&
|
|
@@ -10103,9 +10282,9 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10103
10282
|
activities.length > 0 &&
|
|
10104
10283
|
!isStreaming &&
|
|
10105
10284
|
!isLoading) {
|
|
10106
|
-
|
|
10285
|
+
scheduleReconnect();
|
|
10107
10286
|
}
|
|
10108
|
-
}, [enableStreaming, autoStartStreaming, userPaused, activities.length, isStreaming, isLoading,
|
|
10287
|
+
}, [enableStreaming, autoStartStreaming, userPaused, activities.length, isStreaming, isLoading, scheduleReconnect]);
|
|
10109
10288
|
// Restart streaming after filter change refresh completes. Also skipped
|
|
10110
10289
|
// when the user has paused — a filter change shouldn't silently resume
|
|
10111
10290
|
// a stream the user intentionally stopped.
|
|
@@ -10117,15 +10296,18 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10117
10296
|
!isStreaming &&
|
|
10118
10297
|
!isLoading) {
|
|
10119
10298
|
shouldRestartStreamingRef.current = false;
|
|
10120
|
-
|
|
10299
|
+
scheduleReconnect();
|
|
10121
10300
|
}
|
|
10122
|
-
}, [enableStreaming, userPaused, activities.length, isStreaming, isLoading,
|
|
10301
|
+
}, [enableStreaming, userPaused, activities.length, isStreaming, isLoading, scheduleReconnect]);
|
|
10123
10302
|
// Cleanup on unmount
|
|
10124
10303
|
useEffect(() => {
|
|
10125
10304
|
return () => {
|
|
10126
10305
|
if (watchStopRef.current) {
|
|
10127
10306
|
watchStopRef.current();
|
|
10128
10307
|
}
|
|
10308
|
+
if (reconnectTimerRef.current) {
|
|
10309
|
+
clearTimeout(reconnectTimerRef.current);
|
|
10310
|
+
}
|
|
10129
10311
|
if (filterDebounceRef.current) {
|
|
10130
10312
|
clearTimeout(filterDebounceRef.current);
|
|
10131
10313
|
}
|
|
@@ -10290,36 +10472,6 @@ function ActivityExpandedDetails({ activity, compact = false, }) {
|
|
|
10290
10472
|
return (jsx(TooltipProvider, { children: compact ? (jsx(DetailPanelShell, { children: body })) : (jsx("div", { className: "mt-4 pt-4 border-t border-border", children: body })) }));
|
|
10291
10473
|
}
|
|
10292
10474
|
|
|
10293
|
-
// Number of columns rendered for the feed variant. Used as the colSpan on
|
|
10294
|
-
// the expanded-detail row so it stretches across the full width.
|
|
10295
|
-
const ACTIVITY_FEED_COLUMN_COUNT = 5;
|
|
10296
|
-
/**
|
|
10297
|
-
* Get avatar initials from actor name
|
|
10298
|
-
*/
|
|
10299
|
-
function getActorInitials(name) {
|
|
10300
|
-
const parts = name.split(/[@\s.]+/).filter(Boolean);
|
|
10301
|
-
if (parts.length === 0)
|
|
10302
|
-
return "?";
|
|
10303
|
-
if (parts.length === 1)
|
|
10304
|
-
return parts[0].charAt(0).toUpperCase();
|
|
10305
|
-
return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
|
|
10306
|
-
}
|
|
10307
|
-
/**
|
|
10308
|
-
* Get Tailwind classes for actor avatar based on actor type
|
|
10309
|
-
*/
|
|
10310
|
-
function getActorAvatarClasses(actorType, compact) {
|
|
10311
|
-
const baseClasses = cn("rounded-full flex items-center justify-center shrink-0 font-semibold", compact ? "w-5 h-5 text-1xs" : "w-6 h-6 text-1xs");
|
|
10312
|
-
switch (actorType) {
|
|
10313
|
-
case "user":
|
|
10314
|
-
return cn(baseClasses, "bg-primary text-primary-foreground");
|
|
10315
|
-
case "controller":
|
|
10316
|
-
return cn(baseClasses, "bg-secondary text-secondary-foreground");
|
|
10317
|
-
case "machine account":
|
|
10318
|
-
return cn(baseClasses, "bg-muted text-muted-foreground");
|
|
10319
|
-
default:
|
|
10320
|
-
return cn(baseClasses, "bg-muted text-muted-foreground");
|
|
10321
|
-
}
|
|
10322
|
-
}
|
|
10323
10475
|
/**
|
|
10324
10476
|
* Extract verb from activity summary (e.g., "alice created HTTPProxy" -> "created")
|
|
10325
10477
|
*/
|
|
@@ -10328,24 +10480,24 @@ function extractVerb(summary) {
|
|
|
10328
10480
|
if (words.length >= 2) {
|
|
10329
10481
|
return words[1].toLowerCase();
|
|
10330
10482
|
}
|
|
10331
|
-
return
|
|
10483
|
+
return 'unknown';
|
|
10332
10484
|
}
|
|
10333
10485
|
/**
|
|
10334
|
-
* Normalize verb to a canonical form for coloring
|
|
10486
|
+
* Normalize a verb to a canonical form used for coloring/icons/grouping.
|
|
10335
10487
|
*/
|
|
10336
10488
|
function normalizeVerb(verb) {
|
|
10337
10489
|
const normalized = verb.toLowerCase();
|
|
10338
|
-
if (normalized.includes(
|
|
10339
|
-
return
|
|
10340
|
-
if (normalized.includes(
|
|
10341
|
-
return
|
|
10342
|
-
if (normalized.includes(
|
|
10343
|
-
normalized.includes(
|
|
10344
|
-
normalized.includes(
|
|
10345
|
-
normalized.includes(
|
|
10346
|
-
normalized.includes(
|
|
10347
|
-
return
|
|
10348
|
-
return
|
|
10490
|
+
if (normalized.includes('create') || normalized.includes('add'))
|
|
10491
|
+
return 'create';
|
|
10492
|
+
if (normalized.includes('delete') || normalized.includes('remove'))
|
|
10493
|
+
return 'delete';
|
|
10494
|
+
if (normalized.includes('update') ||
|
|
10495
|
+
normalized.includes('patch') ||
|
|
10496
|
+
normalized.includes('modify') ||
|
|
10497
|
+
normalized.includes('change') ||
|
|
10498
|
+
normalized.includes('edit'))
|
|
10499
|
+
return 'update';
|
|
10500
|
+
return 'other';
|
|
10349
10501
|
}
|
|
10350
10502
|
/**
|
|
10351
10503
|
* Get icon container + icon color classes based on verb
|
|
@@ -10353,25 +10505,25 @@ function normalizeVerb(verb) {
|
|
|
10353
10505
|
function getActionIconClasses(verb) {
|
|
10354
10506
|
const normalizedVerb = normalizeVerb(verb);
|
|
10355
10507
|
switch (normalizedVerb) {
|
|
10356
|
-
case
|
|
10508
|
+
case 'create':
|
|
10357
10509
|
return {
|
|
10358
|
-
container:
|
|
10359
|
-
icon:
|
|
10510
|
+
container: 'bg-blue-50 dark:bg-blue-950',
|
|
10511
|
+
icon: 'text-blue-500 dark:text-blue-400',
|
|
10360
10512
|
};
|
|
10361
|
-
case
|
|
10513
|
+
case 'update':
|
|
10362
10514
|
return {
|
|
10363
|
-
container:
|
|
10364
|
-
icon:
|
|
10515
|
+
container: 'bg-green-50 dark:bg-green-950',
|
|
10516
|
+
icon: 'text-green-600 dark:text-green-400',
|
|
10365
10517
|
};
|
|
10366
|
-
case
|
|
10518
|
+
case 'delete':
|
|
10367
10519
|
return {
|
|
10368
|
-
container:
|
|
10369
|
-
icon:
|
|
10520
|
+
container: 'bg-red-50 dark:bg-red-950',
|
|
10521
|
+
icon: 'text-red-500 dark:text-red-400',
|
|
10370
10522
|
};
|
|
10371
10523
|
default:
|
|
10372
10524
|
return {
|
|
10373
|
-
container:
|
|
10374
|
-
icon:
|
|
10525
|
+
container: 'bg-slate-100 dark:bg-slate-800',
|
|
10526
|
+
icon: 'text-slate-500 dark:text-slate-400',
|
|
10375
10527
|
};
|
|
10376
10528
|
}
|
|
10377
10529
|
}
|
|
@@ -10381,20 +10533,51 @@ function getActionIconClasses(verb) {
|
|
|
10381
10533
|
function getTimelineIcon(verb) {
|
|
10382
10534
|
const normalizedVerb = normalizeVerb(verb);
|
|
10383
10535
|
switch (normalizedVerb) {
|
|
10384
|
-
case
|
|
10536
|
+
case 'create':
|
|
10385
10537
|
return Plus;
|
|
10386
|
-
case
|
|
10538
|
+
case 'update':
|
|
10387
10539
|
return Pencil;
|
|
10388
|
-
case
|
|
10540
|
+
case 'delete':
|
|
10389
10541
|
return Trash2;
|
|
10390
10542
|
default:
|
|
10391
10543
|
return Activity;
|
|
10392
10544
|
}
|
|
10393
10545
|
}
|
|
10546
|
+
|
|
10547
|
+
// Number of columns rendered for the feed variant. Used as the colSpan on
|
|
10548
|
+
// the expanded-detail row so it stretches across the full width.
|
|
10549
|
+
const ACTIVITY_FEED_COLUMN_COUNT = 5;
|
|
10550
|
+
/**
|
|
10551
|
+
* Get avatar initials from actor name
|
|
10552
|
+
*/
|
|
10553
|
+
function getActorInitials(name) {
|
|
10554
|
+
const parts = name.split(/[@\s.]+/).filter(Boolean);
|
|
10555
|
+
if (parts.length === 0)
|
|
10556
|
+
return "?";
|
|
10557
|
+
if (parts.length === 1)
|
|
10558
|
+
return parts[0].charAt(0).toUpperCase();
|
|
10559
|
+
return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
|
|
10560
|
+
}
|
|
10561
|
+
/**
|
|
10562
|
+
* Get Tailwind classes for actor avatar based on actor type
|
|
10563
|
+
*/
|
|
10564
|
+
function getActorAvatarClasses(actorType, compact) {
|
|
10565
|
+
const baseClasses = cn("rounded-full flex items-center justify-center shrink-0 font-semibold", compact ? "w-5 h-5 text-1xs" : "w-6 h-6 text-1xs");
|
|
10566
|
+
switch (actorType) {
|
|
10567
|
+
case "user":
|
|
10568
|
+
return cn(baseClasses, "bg-primary text-primary-foreground");
|
|
10569
|
+
case "controller":
|
|
10570
|
+
return cn(baseClasses, "bg-secondary text-secondary-foreground");
|
|
10571
|
+
case "machine account":
|
|
10572
|
+
return cn(baseClasses, "bg-muted text-muted-foreground");
|
|
10573
|
+
default:
|
|
10574
|
+
return cn(baseClasses, "bg-muted text-muted-foreground");
|
|
10575
|
+
}
|
|
10576
|
+
}
|
|
10394
10577
|
/**
|
|
10395
10578
|
* ActivityFeedItem renders a single activity in the feed or timeline
|
|
10396
10579
|
*/
|
|
10397
|
-
function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isSelected = false, className = "", compact = false, isNew = false, variant = "feed", isLast = false, defaultExpanded = false, }) {
|
|
10580
|
+
function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isSelected = false, className = "", compact = false, isNew = false, variant = "feed", isLast = false, defaultExpanded = false, density = "default", }) {
|
|
10398
10581
|
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
|
10399
10582
|
const { spec, metadata } = activity;
|
|
10400
10583
|
const { actor, summary, links, tenant } = spec;
|
|
@@ -10415,7 +10598,7 @@ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, ten
|
|
|
10415
10598
|
if (isTimeline) {
|
|
10416
10599
|
const { container: iconBg, icon: iconColor } = getActionIconClasses(verb);
|
|
10417
10600
|
const Icon = getTimelineIcon(verb);
|
|
10418
|
-
return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border", className), children: [jsxs("div", { className: cn("flex items-center gap-3
|
|
10601
|
+
return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border", className), children: [jsxs("div", { className: cn("flex items-center gap-3 cursor-pointer group", density === "compact" ? "py-1.5" : "py-3", isSelected && "bg-muted/40"), onClick: toggleExpand, children: [jsx("div", { className: cn("rounded-md shrink-0 flex items-center justify-center", density === "compact" ? "w-6 h-6" : "w-8 h-8", iconBg, iconColor), children: jsx(Icon, { size: 16, strokeWidth: 2 }) }), jsx("div", { className: "flex-1 min-w-0 text-sm text-foreground leading-snug", children: jsx(ActivityFeedSummary, { summary: summary, links: links, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, resourceLinkContext: { tenant } }) }), tenant && (jsx("div", { className: "shrink-0", children: tenantRenderer ? (tenantRenderer(tenant)) : (jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" })) })), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: timestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", className: "h-5 py-0 px-1 text-base text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? "−" : "+" })] }), isExpanded && (jsx(ActivityExpandedDetails, { activity: activity, tenantLinkResolver: tenantLinkResolver, compact: true }))] }));
|
|
10419
10602
|
}
|
|
10420
10603
|
// Feed variant — flat-column table row. Columns: Actor | Summary |
|
|
10421
10604
|
// Tenant | When | expand toggle. Renders an additional TableRow with a
|
|
@@ -10708,7 +10891,7 @@ const getSelectedPreset$1 = (timeRange) => {
|
|
|
10708
10891
|
/**
|
|
10709
10892
|
* ActivityFeedFilters provides filter controls for the activity feed
|
|
10710
10893
|
*/
|
|
10711
|
-
function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, }) {
|
|
10894
|
+
function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, layout = "inline", }) {
|
|
10712
10895
|
const { resourceKinds, actorNames, apiGroups, resourceNamespaces, error: facetsError, } = useFacets(client, timeRange, filters);
|
|
10713
10896
|
// Log facets error for debugging
|
|
10714
10897
|
if (facetsError) {
|
|
@@ -10928,12 +11111,186 @@ function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTi
|
|
|
10928
11111
|
clearTimeout(searchDebounceRef.current);
|
|
10929
11112
|
onFiltersChangeRef.current({ ...filtersRef.current, search: undefined });
|
|
10930
11113
|
}, []);
|
|
11114
|
+
const searchInput = (jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search activities...", value: searchInputValue, onChange: handleSearchChange, className: "pl-8 h-7 text-xs pr-6" }), searchInputValue && (jsx("button", { onClick: handleSearchClear, className: "absolute right-1.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) }))] }));
|
|
11115
|
+
const changeSourceControl = !hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled }));
|
|
11116
|
+
const filterChips = activeFilterIds.map((filterId) => {
|
|
11117
|
+
const config = FILTER_CONFIGS$1[filterId];
|
|
11118
|
+
return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
|
|
11119
|
+
? getFilterOptions(filterId)
|
|
11120
|
+
: undefined, onValuesChange: (values) => handleFilterChange(filterId, values), onClear: () => handleFilterClear(filterId), onPopoverClose: () => handlePopoverClose(filterId), inputMode: config.inputMode, placeholder: config.placeholder, searchPlaceholder: config.searchPlaceholder, autoOpen: pendingFilter === filterId, disabled: disabled }, filterId));
|
|
11121
|
+
});
|
|
11122
|
+
const addFilterControl = (jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }));
|
|
11123
|
+
const timeRangeControl = (jsx(TimeRangeDropdown, { presets: TIME_PRESETS$1, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() }));
|
|
11124
|
+
if (layout === "compact") {
|
|
11125
|
+
const activeFilterCount = activeFilterIds.length +
|
|
11126
|
+
(!hiddenFilters.includes("changeSource") &&
|
|
11127
|
+
filters.changeSource &&
|
|
11128
|
+
filters.changeSource !== "all"
|
|
11129
|
+
? 1
|
|
11130
|
+
: 0);
|
|
11131
|
+
return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [searchInput, jsxs(Popover, { children: [jsx(PopoverTrigger, { asChild: true, children: jsxs("button", { type: "button", disabled: disabled, className: cn("flex h-7 items-center gap-1.5 rounded-md border border-border bg-background px-2 text-xs", "text-muted-foreground hover:text-foreground hover:border-foreground/50 transition-colors", "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", "disabled:cursor-not-allowed disabled:opacity-50"), children: [jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }), jsx("span", { className: "font-medium", children: "Filters" }), activeFilterCount > 0 && (jsx("span", { className: "ml-0.5 rounded-full bg-primary text-primary-foreground text-[10px] font-semibold leading-none px-1.5 py-0.5", children: activeFilterCount }))] }) }), jsx(PopoverContent, { className: "w-[320px] p-3", sideOffset: 4, align: "start", children: jsxs("div", { className: "flex flex-col gap-3", children: [changeSourceControl, jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [filterChips, addFilterControl] }), extraFilters, timeRangeControl] }) })] })] }) }));
|
|
11132
|
+
}
|
|
10931
11133
|
return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [!hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled })), jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search activities...", value: searchInputValue, onChange: handleSearchChange, className: "h-9 pl-9 pr-9 text-xs md:text-xs" }), searchInputValue && (jsx("button", { onClick: handleSearchClear, className: "absolute right-0 top-0 flex h-9 w-9 items-center justify-center rounded-r-lg text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) }))] }), activeFilterIds.map((filterId) => {
|
|
10932
11134
|
const config = FILTER_CONFIGS$1[filterId];
|
|
10933
11135
|
return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
|
|
10934
11136
|
? getFilterOptions(filterId)
|
|
10935
11137
|
: undefined, onValuesChange: (values) => handleFilterChange(filterId, values), onClear: () => handleFilterClear(filterId), onPopoverClose: () => handlePopoverClose(filterId), inputMode: config.inputMode, placeholder: config.placeholder, searchPlaceholder: config.searchPlaceholder, autoOpen: pendingFilter === filterId, disabled: disabled }, filterId));
|
|
10936
|
-
}), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), extraFilters, jsx("div", { className: "flex-1 min-w-[20px]" }),
|
|
11138
|
+
}), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), extraFilters, jsx("div", { className: "flex-1 min-w-[20px]" }), timeRangeControl] }) }));
|
|
11139
|
+
}
|
|
11140
|
+
|
|
11141
|
+
const KIND_PLURALS = {
|
|
11142
|
+
DNSRecordSet: "DNS records",
|
|
11143
|
+
};
|
|
11144
|
+
function pluralizeKind(kind, count) {
|
|
11145
|
+
if (count === 1)
|
|
11146
|
+
return kind;
|
|
11147
|
+
return KIND_PLURALS[kind] ?? `${kind}s`;
|
|
11148
|
+
}
|
|
11149
|
+
const VERB_LABEL = {
|
|
11150
|
+
create: "created",
|
|
11151
|
+
update: "updated",
|
|
11152
|
+
delete: "deleted",
|
|
11153
|
+
other: "changed",
|
|
11154
|
+
};
|
|
11155
|
+
/**
|
|
11156
|
+
* ActivityFeedGroupItem renders a run of consecutive same verb/kind/actor
|
|
11157
|
+
* activities (see `groupActivities`) as a single collapsed row in the
|
|
11158
|
+
* "digest" timeline. A group of size 1 renders exactly like a normal
|
|
11159
|
+
* `ActivityFeedItem` — no visual change for ungrouped activities. Larger
|
|
11160
|
+
* groups render a synthesized summary ("{actor} created 12 DNS records")
|
|
11161
|
+
* that expands to reveal the individual activities underneath.
|
|
11162
|
+
*/
|
|
11163
|
+
function ActivityFeedGroupItem({ group, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isLast = false, }) {
|
|
11164
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
11165
|
+
if (group.count === 1) {
|
|
11166
|
+
return (jsx(ActivityFeedItem, { activity: group.activities[0], resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: onActorClick, onActivityClick: onActivityClick, variant: "timeline", density: "compact", isLast: isLast }));
|
|
11167
|
+
}
|
|
11168
|
+
const { container: iconBg, icon: iconColor } = getActionIconClasses(group.verb);
|
|
11169
|
+
const Icon = getTimelineIcon(group.verb);
|
|
11170
|
+
const actorVisible = group.actorDisplayName || group.actorName;
|
|
11171
|
+
const summary = `${actorVisible} ${VERB_LABEL[group.verb]} ${group.count} ${pluralizeKind(group.kind, group.count)}`;
|
|
11172
|
+
const toggleExpand = (e) => {
|
|
11173
|
+
e.stopPropagation();
|
|
11174
|
+
setIsExpanded((prev) => !prev);
|
|
11175
|
+
};
|
|
11176
|
+
return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border"), children: [jsxs("div", { className: "flex items-center gap-3 py-1.5 cursor-pointer group", onClick: toggleExpand, children: [jsxs("div", { className: cn("w-6 h-6 rounded-md shrink-0 flex items-center justify-center relative", iconBg, iconColor), children: [jsx(Icon, { size: 14, strokeWidth: 2 }), jsx("span", { className: "absolute -bottom-1 -right-1 rounded-full bg-background border border-border px-1 text-[10px] font-semibold leading-tight text-foreground", children: group.count })] }), jsx("div", { className: "flex-1 min-w-0 text-xs text-foreground leading-normal", children: summary }), group.tenant && (jsx("div", { className: "shrink-0", children: tenantRenderer ? (tenantRenderer(group.tenant)) : (jsx(TenantBadge, { tenant: group.tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" })) })), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: group.latestTimestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", className: "h-5 py-0 px-1 text-base text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? "−" : "+" })] }), isExpanded && (jsx("div", { className: "pl-11 flex flex-col", children: group.activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: onActorClick, onActivityClick: onActivityClick, compact: true, density: "compact", variant: "timeline", isLast: index === group.activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name))) }))] }));
|
|
11177
|
+
}
|
|
11178
|
+
|
|
11179
|
+
/**
|
|
11180
|
+
* Thin stat strip summarizing the activities currently loaded in the feed —
|
|
11181
|
+
* e.g. "18 changes · 3 people · 1 system". Computed client-side over the
|
|
11182
|
+
* loaded/paginated activities; it is a quick-glance headline, not a
|
|
11183
|
+
* full-range aggregate (that would require a backend facet query).
|
|
11184
|
+
*/
|
|
11185
|
+
function ActivityDigestSummary({ activities, className = "" }) {
|
|
11186
|
+
const stats = useMemo(() => {
|
|
11187
|
+
const humanActors = new Set();
|
|
11188
|
+
const systemActors = new Set();
|
|
11189
|
+
for (const activity of activities) {
|
|
11190
|
+
const { actor, changeSource } = activity.spec;
|
|
11191
|
+
if (changeSource === "human") {
|
|
11192
|
+
humanActors.add(actor.name);
|
|
11193
|
+
}
|
|
11194
|
+
else {
|
|
11195
|
+
systemActors.add(actor.name);
|
|
11196
|
+
}
|
|
11197
|
+
}
|
|
11198
|
+
return {
|
|
11199
|
+
total: activities.length,
|
|
11200
|
+
people: humanActors.size,
|
|
11201
|
+
systems: systemActors.size,
|
|
11202
|
+
};
|
|
11203
|
+
}, [activities]);
|
|
11204
|
+
if (stats.total === 0)
|
|
11205
|
+
return null;
|
|
11206
|
+
const parts = [`${stats.total} ${stats.total === 1 ? "change" : "changes"}`];
|
|
11207
|
+
if (stats.people > 0)
|
|
11208
|
+
parts.push(`${stats.people} ${stats.people === 1 ? "person" : "people"}`);
|
|
11209
|
+
if (stats.systems > 0)
|
|
11210
|
+
parts.push(`${stats.systems} ${stats.systems === 1 ? "system" : "systems"}`);
|
|
11211
|
+
return (jsx("div", { className: `px-1 pt-4 pb-2 text-xs text-muted-foreground ${className}`, children: parts.join(" · ") }));
|
|
11212
|
+
}
|
|
11213
|
+
|
|
11214
|
+
function tenantKey(tenant) {
|
|
11215
|
+
if (!tenant)
|
|
11216
|
+
return '';
|
|
11217
|
+
return `${tenant.type}:${tenant.name}`;
|
|
11218
|
+
}
|
|
11219
|
+
function groupSignature(activity) {
|
|
11220
|
+
const { spec } = activity;
|
|
11221
|
+
const verb = normalizeVerb(extractVerb(spec.summary));
|
|
11222
|
+
return [verb, spec.resource.kind, spec.actor.name, tenantKey(spec.tenant)].join('|');
|
|
11223
|
+
}
|
|
11224
|
+
/**
|
|
11225
|
+
* Group consecutive activities that share verb + resource kind + actor +
|
|
11226
|
+
* tenant. Activities are assumed to already be sorted newest-first (as
|
|
11227
|
+
* returned by `useActivityFeed`); only adjacent items are merged, so a
|
|
11228
|
+
* burst of matching activities collapses into one group without needing
|
|
11229
|
+
* any time-window heuristics.
|
|
11230
|
+
*/
|
|
11231
|
+
function groupActivities(activities) {
|
|
11232
|
+
const groups = [];
|
|
11233
|
+
for (const activity of activities) {
|
|
11234
|
+
const signature = groupSignature(activity);
|
|
11235
|
+
const last = groups[groups.length - 1];
|
|
11236
|
+
if (last && last.key === signature) {
|
|
11237
|
+
last.activities.push(activity);
|
|
11238
|
+
last.count += 1;
|
|
11239
|
+
continue;
|
|
11240
|
+
}
|
|
11241
|
+
const { spec } = activity;
|
|
11242
|
+
groups.push({
|
|
11243
|
+
key: signature,
|
|
11244
|
+
verb: normalizeVerb(extractVerb(spec.summary)),
|
|
11245
|
+
kind: spec.resource.kind,
|
|
11246
|
+
actorName: spec.actor.name,
|
|
11247
|
+
actorDisplayName: spec.actor.displayName,
|
|
11248
|
+
tenant: spec.tenant,
|
|
11249
|
+
activities: [activity],
|
|
11250
|
+
count: 1,
|
|
11251
|
+
latestTimestamp: activity.metadata?.creationTimestamp,
|
|
11252
|
+
});
|
|
11253
|
+
}
|
|
11254
|
+
return groups;
|
|
11255
|
+
}
|
|
11256
|
+
function dayLabel(timestamp) {
|
|
11257
|
+
if (!timestamp)
|
|
11258
|
+
return 'Unknown date';
|
|
11259
|
+
const date = new Date(timestamp);
|
|
11260
|
+
if (Number.isNaN(date.getTime()))
|
|
11261
|
+
return 'Unknown date';
|
|
11262
|
+
if (isToday(date))
|
|
11263
|
+
return 'Today';
|
|
11264
|
+
if (isYesterday(date))
|
|
11265
|
+
return 'Yesterday';
|
|
11266
|
+
return format(date, 'MMMM d, yyyy');
|
|
11267
|
+
}
|
|
11268
|
+
/**
|
|
11269
|
+
* Bucket activities into calendar-day sections (Today / Yesterday / a
|
|
11270
|
+
* formatted date), then group consecutive matching activities within each
|
|
11271
|
+
* day. Bucketing by day first (rather than grouping across the whole list)
|
|
11272
|
+
* keeps a group from silently spanning a day boundary.
|
|
11273
|
+
*/
|
|
11274
|
+
function groupActivitiesByDay(activities) {
|
|
11275
|
+
const sections = [];
|
|
11276
|
+
let currentLabel = null;
|
|
11277
|
+
let currentBucket = [];
|
|
11278
|
+
const flush = () => {
|
|
11279
|
+
if (currentLabel !== null && currentBucket.length > 0) {
|
|
11280
|
+
sections.push({ label: currentLabel, groups: groupActivities(currentBucket) });
|
|
11281
|
+
}
|
|
11282
|
+
};
|
|
11283
|
+
for (const activity of activities) {
|
|
11284
|
+
const label = dayLabel(activity.metadata?.creationTimestamp);
|
|
11285
|
+
if (label !== currentLabel) {
|
|
11286
|
+
flush();
|
|
11287
|
+
currentLabel = label;
|
|
11288
|
+
currentBucket = [];
|
|
11289
|
+
}
|
|
11290
|
+
currentBucket.push(activity);
|
|
11291
|
+
}
|
|
11292
|
+
flush();
|
|
11293
|
+
return sections;
|
|
10937
11294
|
}
|
|
10938
11295
|
|
|
10939
11296
|
/**
|
|
@@ -11064,10 +11421,10 @@ function ActivityFeed({ client, initialFilters = { changeSource: "human" }, init
|
|
|
11064
11421
|
// Parent containers must have proper height constraints (h-screen/h-full + overflow-hidden)
|
|
11065
11422
|
const effectiveMaxHeight = maxHeight === "none" ? undefined : maxHeight;
|
|
11066
11423
|
const listClasses = "flex-1 min-h-0 overflow-y-auto flex flex-col";
|
|
11067
|
-
return (jsx(TooltipProvider, { delayDuration: 200, children: jsxs(Card, { className: containerClasses, children: [enableStreaming && (jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center", children: [isStreaming && !watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsxs("span", { className: "relative flex h-2 w-2", children: [jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--success-500)] opacity-75" }), jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-[var(--success-500)]" })] }), jsx("span", { className: "text-xs text-muted-foreground", children: "Streaming activity..." })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "New activities will appear automatically" }) })] }) })), watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "relative flex h-2 w-2", children: jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-destructive" }) }), jsx("span", { className: "text-xs text-destructive", children: "Connection error" })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "Stream connection lost" }) })] }) })), newActivitiesCount > 0 && !watchError && (jsxs(Badge, { variant: "secondary", className: "text-xs", children: ["+", newActivitiesCount, " new"] }))] }), jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: handleStreamingToggle, children: watchError ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: "2", children: [jsx("path", { d: "M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" }), jsx("circle", { cx: "12", cy: "12", r: "3" })] }), "Retry"] })) : isStreaming ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] }), "Pause"] })) : (jsxs(Fragment, { children: [jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("polygon", { points: "5,3 19,12 5,21", fill: "currentColor" }) }), "Resume"] })) })] })), showFilters && (jsx(ActivityFeedFilters, { client: client, filters: filters, timeRange: timeRange, onFiltersChange: handleFiltersChange, onTimeRangeChange: handleTimeRangeChange, disabled: isLoading, hiddenFilters: hiddenFilters })), jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-4", errorFormatter: errorFormatter }), jsx(ApiErrorAlert, { error: watchError, onRetry: startStreaming, className: "mb-4", errorFormatter: errorFormatter }), !policiesLoading && hasPolicies === false && (jsxs("div", { className: "flex flex-col items-center py-12 px-8 text-center bg-muted border border-dashed border-border rounded-xl mb-4", children: [jsx("div", { className: "flex justify-center mb-4 text-muted-foreground", children: jsxs("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" }), jsx("rect", { x: "9", y: "3", width: "6", height: "4", rx: "1" }), jsx("path", { d: "M9 12h6" }), jsx("path", { d: "M9 16h6" })] }) }), jsx("h3", { className: "m-0 mb-2 text-lg font-semibold text-foreground leading-snug", children: "Get started with activity logging" }), jsx("p", { className: "m-0 mb-6 text-sm leading-relaxed text-muted-foreground max-w-[400px]", children: "Activity policies define which resources to track and how to summarize changes. Create your first policy to start seeing activity logs here." }), onCreatePolicy && (jsx(Button, { onClick: onCreatePolicy, children: "Create Policy" }))] })), jsxs("div", { className: listClasses, ref: scrollContainerRef, style: effectiveMaxHeight ? { maxHeight: effectiveMaxHeight } : undefined, children: [!isLoading &&
|
|
11424
|
+
return (jsx(TooltipProvider, { delayDuration: 200, children: jsxs(Card, { className: containerClasses, children: [enableStreaming && (jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center", children: [isStreaming && !watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsxs("span", { className: "relative flex h-2 w-2", children: [jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--success-500)] opacity-75" }), jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-[var(--success-500)]" })] }), jsx("span", { className: "text-xs text-muted-foreground", children: "Streaming activity..." })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "New activities will appear automatically" }) })] }) })), watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "relative flex h-2 w-2", children: jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-destructive" }) }), jsx("span", { className: "text-xs text-destructive", children: "Connection error" })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "Stream connection lost" }) })] }) })), newActivitiesCount > 0 && !watchError && (jsxs(Badge, { variant: "secondary", className: "text-xs", children: ["+", newActivitiesCount, " new"] }))] }), jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: handleStreamingToggle, children: watchError ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: "2", children: [jsx("path", { d: "M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" }), jsx("circle", { cx: "12", cy: "12", r: "3" })] }), "Retry"] })) : isStreaming ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] }), "Pause"] })) : (jsxs(Fragment, { children: [jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("polygon", { points: "5,3 19,12 5,21", fill: "currentColor" }) }), "Resume"] })) })] })), showFilters && (jsx(ActivityFeedFilters, { client: client, filters: filters, timeRange: timeRange, onFiltersChange: handleFiltersChange, onTimeRangeChange: handleTimeRangeChange, disabled: isLoading, hiddenFilters: hiddenFilters, layout: variant === "digest" ? "compact" : "inline" })), variant === "digest" && !isLoading && activities.length > 0 && (jsx(ActivityDigestSummary, { activities: activities })), jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-4", errorFormatter: errorFormatter }), jsx(ApiErrorAlert, { error: watchError, onRetry: startStreaming, className: "mb-4", errorFormatter: errorFormatter }), !policiesLoading && hasPolicies === false && (jsxs("div", { className: "flex flex-col items-center py-12 px-8 text-center bg-muted border border-dashed border-border rounded-xl mb-4", children: [jsx("div", { className: "flex justify-center mb-4 text-muted-foreground", children: jsxs("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" }), jsx("rect", { x: "9", y: "3", width: "6", height: "4", rx: "1" }), jsx("path", { d: "M9 12h6" }), jsx("path", { d: "M9 16h6" })] }) }), jsx("h3", { className: "m-0 mb-2 text-lg font-semibold text-foreground leading-snug", children: "Get started with activity logging" }), jsx("p", { className: "m-0 mb-6 text-sm leading-relaxed text-muted-foreground max-w-[400px]", children: "Activity policies define which resources to track and how to summarize changes. Create your first policy to start seeing activity logs here." }), onCreatePolicy && (jsx(Button, { onClick: onCreatePolicy, children: "Create Policy" }))] })), jsxs("div", { className: listClasses, ref: scrollContainerRef, style: effectiveMaxHeight ? { maxHeight: effectiveMaxHeight } : undefined, children: [!isLoading &&
|
|
11068
11425
|
activities.length === 0 &&
|
|
11069
11426
|
hasPolicies !== false &&
|
|
11070
|
-
variant === "timeline" && (jsxs("div", { className: "py-12 text-center text-muted-foreground", children: [jsx("p", { className: "m-0", children: "No activities found" }), jsx("p", { className: "text-sm text-muted-foreground mt-2 m-0", children: "Try adjusting your filters or time range" })] })), variant === "timeline" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })) : (jsxs(Table, { className: "w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-12", "aria-label": "Actor" }), jsx(TableHead, { children: "Summary" }), jsx(TableHead, { children: "Tenant" }), jsx(TableHead, { className: "w-[170px]", children: "When" }), jsx(TableHead, { className: "w-10", "aria-label": "Expand" })] }) }), jsxs(TableBody, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-3/4" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-5 w-32 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6" }) })] }, `sk-${index}`))) })), !isLoading &&
|
|
11427
|
+
(variant === "timeline" || variant === "digest") && (jsxs("div", { className: "py-12 text-center text-muted-foreground", children: [jsx("p", { className: "m-0", children: "No activities found" }), jsx("p", { className: "text-sm text-muted-foreground mt-2 m-0", children: "Try adjusting your filters or time range" })] })), variant === "digest" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), groupActivitiesByDay(activities).map((section) => (jsxs("div", { className: "flex flex-col", children: [jsx("div", { className: "sticky top-0 z-10 bg-card pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: section.label }), section.groups.map((group, index) => (jsx(ActivityFeedGroupItem, { group: group, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, isLast: index === section.groups.length - 1 }, `${section.label}-${group.key}-${index}`)))] }, section.label)))] })) : variant === "timeline" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })) : (jsxs(Table, { className: "w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-12", "aria-label": "Actor" }), jsx(TableHead, { children: "Summary" }), jsx(TableHead, { children: "Tenant" }), jsx(TableHead, { className: "w-[170px]", children: "When" }), jsx(TableHead, { className: "w-10", "aria-label": "Expand" })] }) }), jsxs(TableBody, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-3/4" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-5 w-32 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6" }) })] }, `sk-${index}`))) })), !isLoading &&
|
|
11071
11428
|
activities.length === 0 &&
|
|
11072
11429
|
hasPolicies !== false && (jsx(TableRow, { children: jsxs(TableCell, { colSpan: ACTIVITY_FEED_COLUMN_COUNT, className: "text-center py-12 text-muted-foreground", children: [jsx("div", { children: "No activities found" }), jsx("div", { className: "text-sm mt-2", children: "Try adjusting your filters or time range" })] }) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })] })), infiniteScroll && hasMore && (jsx("div", { ref: loadMoreTriggerRef, className: "h-px mt-4" })), !infiniteScroll && activities.length > 0 ? (jsxs("div", { className: "flex items-center justify-between gap-4 px-4 py-3 border-t border-border text-sm text-muted-foreground", children: [jsxs("span", { children: [activities.length, " ", activities.length === 1 ? "activity" : "activities", hasMore ? " so far" : ""] }), hasMore ? (jsx(Button, { type: "tertiary", theme: "outline", size: "small", htmlType: "button", onClick: handleLoadMoreClick, disabled: isLoading, children: isLoading ? "Loading…" : "Load more" })) : (jsx("span", { children: "End of results" }))] })) : null] })] }) }));
|
|
11073
11430
|
}
|
|
@@ -16456,5 +16813,5 @@ const POLICY_FILTER_FIELDS = [
|
|
|
16456
16813
|
},
|
|
16457
16814
|
];
|
|
16458
16815
|
|
|
16459
|
-
export { ACTIVITY_FILTER_FIELDS, AUDIT_TEMPLATES, ActionMultiSelect, ActionToggle, ActivityApiClient, ActivityExpandedDetails, ActivityFeed, ActivityFeedFilters, ActivityFeedItem, ActivityFeedItemSkeleton, ActivityFeedSummary, ActivityLayout, ApiError, ApiErrorAlert, AuditEventViewer, AuditLogExpandedDetails, AuditLogFeedItem, AuditLogFilters, AuditLogQueryComponent, Badge, CelEditor, ChangeSourceToggle, Combobox, DateTimeRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, EVENT_FACET_FIELDS, EVENT_FILTER_FIELDS, EVENT_TEMPLATES, EventExpandedDetails, EventFeedItem, EventTypeToggle, EventsFeed, FILTER_FIELDS, FilterBuilder, FilterBuilderWithAutocomplete, MultiCombobox, NetworkError, POLICY_FILTER_FIELDS, PolicyActivityView, PolicyActivityViewSkeleton, PolicyDetailView, PolicyEditView, PolicyEditor, PolicyList, PolicyPreviewPanel, PolicyPreviewResult, PolicyResourceForm, PolicyRuleEditor, PolicyRuleEditorDialog, PolicyRuleList, PolicyRuleListItem, ReindexJobCreate, ReindexJobDetailView, ReindexJobDialog, ReindexJobList, ResourceHistoryView, RulePreviewPanel, SampleInputTemplates, SimpleQueryBuilder, TenantBadge, TimeRangeDropdown, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserSelect, badgeVariants, buildAuditLogCEL, cn, copyToClipboard, defaultErrorFormatter, defaultResourceLinkResolver, extractEvent, extractFieldPaths, extractFieldPathsFromMany, generateShareableUrl, getReindexJobDuration, getReindexJobStatusMessage, isEventRecord, isReindexJobRunning, isReindexJobTerminal, parseActivityFilters, parseApiError, parseEventFilters, parseTimeRange, serializeActivityFilters, serializeEventFilters, useActivityFeed, useAuditLogFacets, useAuditLogQuery, useEventFacets, useEventsFeed, useFacets, usePolicyEditor, usePolicyList, usePolicyPreview, useReindexJobs };
|
|
16816
|
+
export { ACTIVITY_FILTER_FIELDS, AUDIT_TEMPLATES, ActionMultiSelect, ActionToggle, ActivityApiClient, ActivityDigestSummary, ActivityExpandedDetails, ActivityFeed, ActivityFeedFilters, ActivityFeedGroupItem, ActivityFeedItem, ActivityFeedItemSkeleton, ActivityFeedSummary, ActivityLayout, ApiError, ApiErrorAlert, AuditEventViewer, AuditLogExpandedDetails, AuditLogFeedItem, AuditLogFilters, AuditLogQueryComponent, Badge, CelEditor, ChangeSourceToggle, Combobox, DateTimeRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, EVENT_FACET_FIELDS, EVENT_FILTER_FIELDS, EVENT_TEMPLATES, EventExpandedDetails, EventFeedItem, EventTypeToggle, EventsFeed, FILTER_FIELDS, FilterBuilder, FilterBuilderWithAutocomplete, MultiCombobox, NetworkError, POLICY_FILTER_FIELDS, PolicyActivityView, PolicyActivityViewSkeleton, PolicyDetailView, PolicyEditView, PolicyEditor, PolicyList, PolicyPreviewPanel, PolicyPreviewResult, PolicyResourceForm, PolicyRuleEditor, PolicyRuleEditorDialog, PolicyRuleList, PolicyRuleListItem, ReindexJobCreate, ReindexJobDetailView, ReindexJobDialog, ReindexJobList, ResourceHistoryView, RulePreviewPanel, SampleInputTemplates, SimpleQueryBuilder, TenantBadge, TimeRangeDropdown, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserSelect, badgeVariants, buildAuditLogCEL, cn, copyToClipboard, defaultErrorFormatter, defaultResourceLinkResolver, extractEvent, extractFieldPaths, extractFieldPathsFromMany, generateShareableUrl, getReindexJobDuration, getReindexJobStatusMessage, groupActivities, groupActivitiesByDay, isEventRecord, isReindexJobRunning, isReindexJobTerminal, parseActivityFilters, parseApiError, parseEventFilters, parseTimeRange, serializeActivityFilters, serializeEventFilters, useActivityFeed, useAuditLogFacets, useAuditLogQuery, useEventFacets, useEventsFeed, useFacets, usePolicyEditor, usePolicyList, usePolicyPreview, useReindexJobs };
|
|
16460
16817
|
//# sourceMappingURL=index.esm.js.map
|