@burdenoff/microfe-subscriberbot 2026.827.1 → 2026.1014.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/subscriberbot/admin/AdminDashboardPage.d.ts.map +1 -1
- package/dist/subscriberbot/admin/AdminDashboardPage.js +80 -83
- package/dist/subscriberbot/admin/AdminDashboardPage.js.map +1 -1
- package/dist/subscriberbot/hooks/index.d.ts +1 -0
- package/dist/subscriberbot/hooks/index.d.ts.map +1 -1
- package/dist/subscriberbot/hooks/index.js +1 -0
- package/dist/subscriberbot/hooks/useTr.d.ts +17 -0
- package/dist/subscriberbot/hooks/useTr.d.ts.map +1 -0
- package/dist/subscriberbot/hooks/useTr.js +14 -0
- package/dist/subscriberbot/hooks/useTr.js.map +1 -0
- package/dist/subscriberbot/pages/AssetVaultPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/AssetVaultPage.js +111 -109
- package/dist/subscriberbot/pages/AssetVaultPage.js.map +1 -1
- package/dist/subscriberbot/pages/DashboardPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/DashboardPage.js +113 -105
- package/dist/subscriberbot/pages/DashboardPage.js.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.js +404 -369
- package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
- package/dist/subscriberbot/pages/InsightsPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/InsightsPage.js +94 -93
- package/dist/subscriberbot/pages/InsightsPage.js.map +1 -1
- package/dist/subscriberbot/pages/InvoicesPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/InvoicesPage.js +360 -366
- package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.js +209 -193
- package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +268 -236
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentNewPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentNewPage.js +96 -76
- package/dist/subscriberbot/pages/agents/AgentNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +145 -161
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +514 -441
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderNewPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js +167 -129
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +171 -218
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +340 -279
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +185 -166
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +153 -182
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,247 +8,222 @@ import { ErrorView as l, LoadingView as u } from "../../components/StateViews.js
|
|
|
8
8
|
import { MiniProgress as d } from "../../components/viz.js";
|
|
9
9
|
import "../../components/index.js";
|
|
10
10
|
import { useRelationships as f } from "../../hooks/useSubscriberBotApi.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
11
|
+
import { useTr as p } from "../../hooks/useTr.js";
|
|
12
|
+
import { useMemo as m, useState as h } from "react";
|
|
13
|
+
import { ChevronRight as g, Plus as _, Repeat2 as v, Search as y } from "lucide-react";
|
|
14
|
+
import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
15
|
+
import { GlassCard as C, PagePurpose as w } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/subscriberbot/pages/relationships/RelationshipsListPage.tsx
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
label: "All kinds"
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
value: "SUBSCRIPTION",
|
|
56
|
-
label: "Subscription"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
value: "MEMBERSHIP",
|
|
60
|
-
label: "Membership"
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
value: "LICENSE",
|
|
64
|
-
label: "License"
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
value: "CONTRACT",
|
|
68
|
-
label: "Contract"
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
value: "NEWSLETTER",
|
|
72
|
-
label: "Newsletter"
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
value: "RECURRING_PAYMENT",
|
|
76
|
-
label: "Recurring payment"
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
value: "ENROLLMENT",
|
|
80
|
-
label: "Enrollment"
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
value: "TRIAL",
|
|
84
|
-
label: "Trial"
|
|
85
|
-
}
|
|
86
|
-
], D = "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2.5 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]";
|
|
87
|
-
function O(e) {
|
|
17
|
+
var T = 20, E = [
|
|
18
|
+
"ACTIVE",
|
|
19
|
+
"TRIAL",
|
|
20
|
+
"PAUSED",
|
|
21
|
+
"PAST_DUE",
|
|
22
|
+
"LAPSED",
|
|
23
|
+
"EXPIRED",
|
|
24
|
+
"CANCELLED"
|
|
25
|
+
], D = [
|
|
26
|
+
"SUBSCRIPTION",
|
|
27
|
+
"MEMBERSHIP",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"CONTRACT",
|
|
30
|
+
"NEWSLETTER",
|
|
31
|
+
"RECURRING_PAYMENT",
|
|
32
|
+
"ENROLLMENT",
|
|
33
|
+
"TRIAL"
|
|
34
|
+
], O = {
|
|
35
|
+
ACTIVE: "relationships.statuses.active",
|
|
36
|
+
TRIAL: "relationships.statuses.trial",
|
|
37
|
+
PAUSED: "relationships.statuses.paused",
|
|
38
|
+
PAST_DUE: "relationships.statuses.pastDue",
|
|
39
|
+
LAPSED: "relationships.statuses.lapsed",
|
|
40
|
+
EXPIRED: "relationships.statuses.expired",
|
|
41
|
+
CANCELLED: "relationships.statuses.cancelled"
|
|
42
|
+
}, k = {
|
|
43
|
+
SUBSCRIPTION: "relationships.kinds.subscription",
|
|
44
|
+
MEMBERSHIP: "relationships.kinds.membership",
|
|
45
|
+
LICENSE: "relationships.kinds.license",
|
|
46
|
+
CONTRACT: "relationships.kinds.contract",
|
|
47
|
+
NEWSLETTER: "relationships.kinds.newsletter",
|
|
48
|
+
RECURRING_PAYMENT: "relationships.kinds.recurringPayment",
|
|
49
|
+
ENROLLMENT: "relationships.kinds.enrollment",
|
|
50
|
+
TRIAL: "relationships.kinds.trial"
|
|
51
|
+
}, A = "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2.5 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]";
|
|
52
|
+
function j(e) {
|
|
88
53
|
return e == null ? "accent" : e >= 66 ? "positive" : e >= 33 ? "warning" : "negative";
|
|
89
54
|
}
|
|
90
|
-
function
|
|
91
|
-
let { navigateTo:
|
|
92
|
-
status:
|
|
93
|
-
kind:
|
|
94
|
-
search:
|
|
55
|
+
function M() {
|
|
56
|
+
let M = p(), { navigateTo: N } = e(), [P, F] = h(0), [I, L] = h(""), [R, z] = h(""), [B, V] = h(""), [H, U] = h(""), { data: W, isLoading: G, isError: K, refetch: q, isFetching: J } = f({
|
|
57
|
+
status: I || void 0,
|
|
58
|
+
kind: R || void 0,
|
|
59
|
+
search: B || void 0,
|
|
95
60
|
pagination: {
|
|
96
|
-
limit:
|
|
97
|
-
offset:
|
|
61
|
+
limit: T,
|
|
62
|
+
offset: P * T
|
|
98
63
|
}
|
|
99
|
-
}),
|
|
100
|
-
return /* @__PURE__ */
|
|
101
|
-
title: "Relationships",
|
|
102
|
-
description: "Every subscription, membership, license and contract you track",
|
|
103
|
-
actions: /* @__PURE__ */
|
|
64
|
+
}), Y = m(() => W?.items ?? [], [W]), X = W?.pageInfo.hasMore ?? !1;
|
|
65
|
+
return /* @__PURE__ */ S(n, {
|
|
66
|
+
title: M("relationships.title", "Relationships"),
|
|
67
|
+
description: M("relationships.description", "Every subscription, membership, license and contract you track"),
|
|
68
|
+
actions: /* @__PURE__ */ S("button", {
|
|
104
69
|
type: "button",
|
|
105
|
-
onClick: () =>
|
|
70
|
+
onClick: () => N("/relationships/new"),
|
|
106
71
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
107
|
-
children: [
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ x(_, { className: "h-4 w-4" }),
|
|
74
|
+
" ",
|
|
75
|
+
M("relationships.addRelationship", "Add relationship")
|
|
76
|
+
]
|
|
108
77
|
}),
|
|
109
78
|
children: [
|
|
110
|
-
/* @__PURE__ */
|
|
79
|
+
/* @__PURE__ */ x(w, {
|
|
111
80
|
className: "mb-4",
|
|
112
|
-
children: "This is the fundamental object of Subscriber Bot. Filter by lifecycle and kind, open any row to manage its plan, payment, entitlements, assets and renewals, or advance its lifecycle."
|
|
81
|
+
children: M("relationships.purpose", "This is the fundamental object of Subscriber Bot. Filter by lifecycle and kind, open any row to manage its plan, payment, entitlements, assets and renewals, or advance its lifecycle.")
|
|
113
82
|
}),
|
|
114
|
-
/* @__PURE__ */
|
|
83
|
+
/* @__PURE__ */ x(C, {
|
|
115
84
|
treatment: "plain",
|
|
116
85
|
className: "p-3 sm:p-4",
|
|
117
|
-
children: /* @__PURE__ */
|
|
86
|
+
children: /* @__PURE__ */ S("div", {
|
|
118
87
|
className: "flex flex-col gap-3 md:flex-row md:items-center",
|
|
119
|
-
children: [/* @__PURE__ */
|
|
88
|
+
children: [/* @__PURE__ */ S("form", {
|
|
120
89
|
onSubmit: (e) => {
|
|
121
|
-
e.preventDefault(),
|
|
90
|
+
e.preventDefault(), V(H.trim()), F(0);
|
|
122
91
|
},
|
|
123
92
|
className: "relative flex-1 min-w-0",
|
|
124
|
-
children: [/* @__PURE__ */
|
|
93
|
+
children: [/* @__PURE__ */ x(y, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ x("input", {
|
|
125
94
|
type: "text",
|
|
126
|
-
"aria-label": "Search relationships by label",
|
|
127
|
-
placeholder: "Search relationships by label…",
|
|
128
|
-
value:
|
|
129
|
-
onChange: (e) =>
|
|
95
|
+
"aria-label": M("relationships.searchPlaceholder", "Search relationships by label…"),
|
|
96
|
+
placeholder: M("relationships.searchPlaceholder", "Search relationships by label…"),
|
|
97
|
+
value: H,
|
|
98
|
+
onChange: (e) => U(e.target.value),
|
|
130
99
|
className: "w-full rounded-lg border border-border-subtle bg-bg-surface py-2.5 pl-10 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
131
100
|
})]
|
|
132
|
-
}), /* @__PURE__ */
|
|
101
|
+
}), /* @__PURE__ */ S("div", {
|
|
133
102
|
className: "flex flex-wrap gap-2 sm:flex-nowrap",
|
|
134
|
-
children: [/* @__PURE__ */
|
|
135
|
-
"aria-label": "Filter by kind",
|
|
136
|
-
value:
|
|
103
|
+
children: [/* @__PURE__ */ S("select", {
|
|
104
|
+
"aria-label": M("relationships.filters.kindLabel", "Filter by kind"),
|
|
105
|
+
value: R,
|
|
137
106
|
onChange: (e) => {
|
|
138
|
-
|
|
107
|
+
z(e.target.value), F(0);
|
|
139
108
|
},
|
|
140
|
-
className: t(
|
|
141
|
-
children:
|
|
142
|
-
value:
|
|
143
|
-
children:
|
|
144
|
-
}, e
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
109
|
+
className: t(A, "flex-1 sm:flex-none"),
|
|
110
|
+
children: [/* @__PURE__ */ x("option", {
|
|
111
|
+
value: "",
|
|
112
|
+
children: M("relationships.filters.allKinds", "All kinds")
|
|
113
|
+
}), D.map((e) => /* @__PURE__ */ x("option", {
|
|
114
|
+
value: e,
|
|
115
|
+
children: M(k[e], a(e))
|
|
116
|
+
}, e))]
|
|
117
|
+
}), /* @__PURE__ */ S("select", {
|
|
118
|
+
"aria-label": M("relationships.filters.statusLabel", "Filter by status"),
|
|
119
|
+
value: I,
|
|
148
120
|
onChange: (e) => {
|
|
149
|
-
|
|
121
|
+
L(e.target.value), F(0);
|
|
150
122
|
},
|
|
151
|
-
className: t(
|
|
152
|
-
children:
|
|
153
|
-
value:
|
|
154
|
-
children:
|
|
155
|
-
}, e
|
|
123
|
+
className: t(A, "flex-1 sm:flex-none"),
|
|
124
|
+
children: [/* @__PURE__ */ x("option", {
|
|
125
|
+
value: "",
|
|
126
|
+
children: M("relationships.filters.allStatuses", "All statuses")
|
|
127
|
+
}), E.map((e) => /* @__PURE__ */ x("option", {
|
|
128
|
+
value: e,
|
|
129
|
+
children: M(O[e], a(e))
|
|
130
|
+
}, e))]
|
|
156
131
|
})]
|
|
157
132
|
})]
|
|
158
133
|
})
|
|
159
134
|
}),
|
|
160
|
-
|
|
161
|
-
message: "Could not load relationships.",
|
|
162
|
-
onRetry: () => void
|
|
163
|
-
}) :
|
|
135
|
+
G ? /* @__PURE__ */ x(u, { label: M("relationships.loading", "Loading relationships…") }) : K ? /* @__PURE__ */ x(l, {
|
|
136
|
+
message: M("relationships.error", "Could not load relationships."),
|
|
137
|
+
onRetry: () => void q()
|
|
138
|
+
}) : Y.length === 0 ? /* @__PURE__ */ x("div", {
|
|
164
139
|
className: "mt-6",
|
|
165
|
-
children: /* @__PURE__ */
|
|
140
|
+
children: /* @__PURE__ */ x(c, {
|
|
166
141
|
illustration: "empty-data",
|
|
167
|
-
icon: /* @__PURE__ */
|
|
168
|
-
title: "No relationships found",
|
|
169
|
-
description:
|
|
170
|
-
action: /* @__PURE__ */
|
|
142
|
+
icon: /* @__PURE__ */ x(v, { className: "h-12 w-12" }),
|
|
143
|
+
title: M("relationships.emptyTitle", "No relationships found"),
|
|
144
|
+
description: B ? M("relationships.emptyDescriptionSearch", "No relationships match \"{{search}}\".", { search: B }) : M("relationships.emptyDescription", "Add your first recurring relationship to start tracking it."),
|
|
145
|
+
action: /* @__PURE__ */ x("button", {
|
|
171
146
|
type: "button",
|
|
172
|
-
onClick: () =>
|
|
147
|
+
onClick: () => N("/relationships/new"),
|
|
173
148
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
174
|
-
children: "Add relationship"
|
|
149
|
+
children: M("relationships.addRelationship", "Add relationship")
|
|
175
150
|
})
|
|
176
151
|
})
|
|
177
|
-
}) : /* @__PURE__ */
|
|
152
|
+
}) : /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(C, {
|
|
178
153
|
treatment: "plain",
|
|
179
154
|
className: "mt-4 overflow-hidden p-0",
|
|
180
|
-
children: /* @__PURE__ */
|
|
155
|
+
children: /* @__PURE__ */ x("div", {
|
|
181
156
|
className: "overflow-x-auto",
|
|
182
|
-
children: /* @__PURE__ */
|
|
157
|
+
children: /* @__PURE__ */ S("div", {
|
|
183
158
|
className: "min-w-[720px]",
|
|
184
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ S("div", {
|
|
185
160
|
className: "grid grid-cols-[minmax(0,2.4fr)_1fr_1fr_1fr_1fr_1.3fr] gap-4 border-b border-border-seam bg-bg-sunken/60 px-4 py-2.5 text-xs font-medium uppercase tracking-wide text-text-secondary",
|
|
186
161
|
children: [
|
|
187
|
-
/* @__PURE__ */
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
-
/* @__PURE__ */
|
|
190
|
-
/* @__PURE__ */
|
|
162
|
+
/* @__PURE__ */ x("span", { children: M("relationships.table.subscription", "Subscription") }),
|
|
163
|
+
/* @__PURE__ */ x("span", { children: M("relationships.table.kind", "Kind") }),
|
|
164
|
+
/* @__PURE__ */ x("span", { children: M("relationships.table.status", "Status") }),
|
|
165
|
+
/* @__PURE__ */ x("span", {
|
|
191
166
|
className: "text-right",
|
|
192
|
-
children: "Amount"
|
|
167
|
+
children: M("relationships.table.amount", "Amount")
|
|
193
168
|
}),
|
|
194
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ x("span", {
|
|
195
170
|
className: "text-right",
|
|
196
|
-
children: "Renews on"
|
|
171
|
+
children: M("relationships.table.renewsOn", "Renews on")
|
|
197
172
|
}),
|
|
198
|
-
/* @__PURE__ */
|
|
173
|
+
/* @__PURE__ */ x("span", { children: M("relationships.table.health", "Health") })
|
|
199
174
|
]
|
|
200
|
-
}), /* @__PURE__ */
|
|
175
|
+
}), /* @__PURE__ */ x("div", {
|
|
201
176
|
className: "divide-y divide-border-subtle",
|
|
202
|
-
children:
|
|
177
|
+
children: Y.map((e) => {
|
|
203
178
|
let t = e.healthScore;
|
|
204
|
-
return /* @__PURE__ */
|
|
179
|
+
return /* @__PURE__ */ S("button", {
|
|
205
180
|
type: "button",
|
|
206
|
-
onClick: () =>
|
|
181
|
+
onClick: () => N(`/relationships/${e.id}`),
|
|
207
182
|
className: "group grid w-full grid-cols-[minmax(0,2.4fr)_1fr_1fr_1fr_1fr_1.3fr] items-center gap-4 px-4 py-3.5 text-left transition-colors hover:bg-bg-sunken",
|
|
208
183
|
children: [
|
|
209
|
-
/* @__PURE__ */
|
|
184
|
+
/* @__PURE__ */ x("div", {
|
|
210
185
|
className: "flex min-w-0 items-center gap-2",
|
|
211
|
-
children: /* @__PURE__ */
|
|
186
|
+
children: /* @__PURE__ */ S("div", {
|
|
212
187
|
className: "min-w-0",
|
|
213
|
-
children: [/* @__PURE__ */
|
|
188
|
+
children: [/* @__PURE__ */ x("p", {
|
|
214
189
|
className: "truncate text-sm font-semibold text-text-primary",
|
|
215
190
|
children: e.label
|
|
216
|
-
}), e.provider?.name && /* @__PURE__ */
|
|
191
|
+
}), e.provider?.name && /* @__PURE__ */ x("p", {
|
|
217
192
|
className: "truncate text-xs text-text-secondary",
|
|
218
193
|
children: e.provider.name
|
|
219
194
|
})]
|
|
220
195
|
})
|
|
221
196
|
}),
|
|
222
|
-
/* @__PURE__ */
|
|
197
|
+
/* @__PURE__ */ x("span", { children: /* @__PURE__ */ x("span", {
|
|
223
198
|
className: "inline-flex items-center rounded-md bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
|
|
224
|
-
children: a(e.kind)
|
|
199
|
+
children: M(k[e.kind], a(e.kind))
|
|
225
200
|
}) }),
|
|
226
|
-
/* @__PURE__ */
|
|
227
|
-
/* @__PURE__ */
|
|
201
|
+
/* @__PURE__ */ x("span", { children: /* @__PURE__ */ x(s, { status: e.status }) }),
|
|
202
|
+
/* @__PURE__ */ S("span", {
|
|
228
203
|
className: "text-right text-sm font-semibold tabular-nums text-text-primary",
|
|
229
|
-
children: [r(e.amount, e.currency), /* @__PURE__ */
|
|
204
|
+
children: [r(e.amount, e.currency), /* @__PURE__ */ x("span", {
|
|
230
205
|
className: "text-xs font-normal text-text-secondary",
|
|
231
206
|
children: o(e.interval)
|
|
232
207
|
})]
|
|
233
208
|
}),
|
|
234
|
-
/* @__PURE__ */
|
|
209
|
+
/* @__PURE__ */ x("span", {
|
|
235
210
|
className: "text-right text-xs text-text-secondary",
|
|
236
211
|
children: e.renewsAt ? i(e.renewsAt) : "—"
|
|
237
212
|
}),
|
|
238
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ S("span", {
|
|
239
214
|
className: "flex items-center gap-2",
|
|
240
|
-
children: [t == null ? /* @__PURE__ */
|
|
215
|
+
children: [t == null ? /* @__PURE__ */ x("span", {
|
|
241
216
|
className: "text-xs text-text-secondary",
|
|
242
217
|
children: "—"
|
|
243
|
-
}) : /* @__PURE__ */
|
|
218
|
+
}) : /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(d, {
|
|
244
219
|
value: t,
|
|
245
220
|
max: 100,
|
|
246
|
-
tone:
|
|
221
|
+
tone: j(t),
|
|
247
222
|
className: "w-16 shrink-0 sm:w-20"
|
|
248
|
-
}), /* @__PURE__ */
|
|
223
|
+
}), /* @__PURE__ */ x("span", {
|
|
249
224
|
className: "text-xs tabular-nums text-text-secondary",
|
|
250
225
|
children: t
|
|
251
|
-
})] }), /* @__PURE__ */
|
|
226
|
+
})] }), /* @__PURE__ */ x(g, { className: "ml-auto h-4 w-4 shrink-0 text-text-secondary opacity-0 transition-opacity group-hover:opacity-100" })]
|
|
252
227
|
})
|
|
253
228
|
]
|
|
254
229
|
}, e.id);
|
|
@@ -256,29 +231,25 @@ function k() {
|
|
|
256
231
|
})]
|
|
257
232
|
})
|
|
258
233
|
})
|
|
259
|
-
}), /* @__PURE__ */
|
|
234
|
+
}), /* @__PURE__ */ S("div", {
|
|
260
235
|
className: "mt-4 flex items-center justify-between",
|
|
261
|
-
children: [/* @__PURE__ */
|
|
236
|
+
children: [/* @__PURE__ */ S("p", {
|
|
262
237
|
className: "text-xs text-text-secondary",
|
|
263
|
-
children: [
|
|
264
|
-
|
|
265
|
-
A + 1,
|
|
266
|
-
W ? " · refreshing…" : ""
|
|
267
|
-
]
|
|
268
|
-
}), /* @__PURE__ */ x("div", {
|
|
238
|
+
children: [M("relationships.pagination.page", "Page {{page}}", { page: P + 1 }), J ? ` · ${M("relationships.pagination.refreshing", "refreshing…")}` : ""]
|
|
239
|
+
}), /* @__PURE__ */ S("div", {
|
|
269
240
|
className: "flex gap-2",
|
|
270
|
-
children: [/* @__PURE__ */
|
|
241
|
+
children: [/* @__PURE__ */ x("button", {
|
|
271
242
|
type: "button",
|
|
272
|
-
disabled:
|
|
273
|
-
onClick: () =>
|
|
274
|
-
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium",
|
|
275
|
-
children: "Previous"
|
|
276
|
-
}), /* @__PURE__ */
|
|
243
|
+
disabled: P === 0,
|
|
244
|
+
onClick: () => F((e) => Math.max(0, e - 1)),
|
|
245
|
+
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", P === 0 ? "cursor-not-allowed text-text-secondary opacity-50" : "text-text-primary hover:bg-bg-sunken"),
|
|
246
|
+
children: M("relationships.pagination.previous", "Previous")
|
|
247
|
+
}), /* @__PURE__ */ x("button", {
|
|
277
248
|
type: "button",
|
|
278
|
-
disabled: !
|
|
279
|
-
onClick: () =>
|
|
280
|
-
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium",
|
|
281
|
-
children: "Next"
|
|
249
|
+
disabled: !X,
|
|
250
|
+
onClick: () => F((e) => e + 1),
|
|
251
|
+
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", X ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-50"),
|
|
252
|
+
children: M("relationships.pagination.next", "Next")
|
|
282
253
|
})]
|
|
283
254
|
})]
|
|
284
255
|
})] })
|
|
@@ -286,6 +257,6 @@ function k() {
|
|
|
286
257
|
});
|
|
287
258
|
}
|
|
288
259
|
//#endregion
|
|
289
|
-
export {
|
|
260
|
+
export { M as RelationshipsListPage };
|
|
290
261
|
|
|
291
262
|
//# sourceMappingURL=RelationshipsListPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RelationshipsListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/relationships/RelationshipsListPage.tsx"],"sourcesContent":["/**\n * Relationships list\n * @module subscriberbot/pages/relationships\n *\n * The core CRUDL list — every recurring relationship (subscription /\n * membership / license / contract) in one paginated, filterable view.\n * Data comes from `subListRelationships` (a SubRelationshipConnection).\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, Search, Repeat2, ChevronRight } from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { MiniProgress } from '../../components';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useRelationships } from '../../hooks/useSubscriberBotApi';\nimport { cn } from '../../utils/cn';\nimport {\n formatCurrency,\n formatDate,\n formatEnumLabel,\n formatIntervalSuffix,\n} from '../../utils/formatters';\nimport type { SubRelationshipKind, SubRelationshipStatus } from '../../types';\n\nconst PAGE_SIZE = 20;\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubRelationshipStatus; label: string }> = [\n { value: '', label: 'All statuses' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'TRIAL', label: 'Trial' },\n { value: 'PAUSED', label: 'Paused' },\n { value: 'PAST_DUE', label: 'Past due' },\n { value: 'LAPSED', label: 'Lapsed' },\n { value: 'EXPIRED', label: 'Expired' },\n { value: 'CANCELLED', label: 'Cancelled' },\n];\n\nconst KIND_OPTIONS: Array<{ value: '' | SubRelationshipKind; label: string }> = [\n { value: '', label: 'All kinds' },\n { value: 'SUBSCRIPTION', label: 'Subscription' },\n { value: 'MEMBERSHIP', label: 'Membership' },\n { value: 'LICENSE', label: 'License' },\n { value: 'CONTRACT', label: 'Contract' },\n { value: 'NEWSLETTER', label: 'Newsletter' },\n { value: 'RECURRING_PAYMENT', label: 'Recurring payment' },\n { value: 'ENROLLMENT', label: 'Enrollment' },\n { value: 'TRIAL', label: 'Trial' },\n];\n\nconst selectCls =\n 'rounded-lg border border-border-subtle bg-bg-surface px-3 py-2.5 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\n/** Map a 0–100 health score onto a MiniProgress tone. */\nfunction healthTone(\n score: number | null | undefined\n): 'positive' | 'accent' | 'warning' | 'negative' {\n if (score == null) return 'accent';\n if (score >= 66) return 'positive';\n if (score >= 33) return 'warning';\n return 'negative';\n}\n\nexport function RelationshipsListPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [statusFilter, setStatusFilter] = useState<'' | SubRelationshipStatus>('');\n const [kindFilter, setKindFilter] = useState<'' | SubRelationshipKind>('');\n const [search, setSearch] = useState('');\n const [searchInput, setSearchInput] = useState('');\n\n const { data, isLoading, isError, refetch, isFetching } = useRelationships({\n status: statusFilter || undefined,\n kind: kindFilter || undefined,\n search: search || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n\n const items = useMemo(() => data?.items ?? [], [data]);\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n const submitSearch = (e: React.FormEvent) => {\n e.preventDefault();\n setSearch(searchInput.trim());\n setPage(0);\n };\n\n return (\n <PageLayout\n title=\"Relationships\"\n description=\"Every subscription, membership, license and contract you track\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> Add relationship\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n This is the fundamental object of Subscriber Bot. Filter by lifecycle and kind, open any row\n to manage its plan, payment, entitlements, assets and renewals, or advance its lifecycle.\n </PagePurpose>\n\n {/* Toolbar — search + filters, responsive */}\n <GlassCard treatment=\"plain\" className=\"p-3 sm:p-4\">\n <div className=\"flex flex-col gap-3 md:flex-row md:items-center\">\n <form onSubmit={submitSearch} className=\"relative flex-1 min-w-0\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n aria-label=\"Search relationships by label\"\n placeholder=\"Search relationships by label…\"\n value={searchInput}\n onChange={(e) => setSearchInput(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2.5 pl-10 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </form>\n <div className=\"flex flex-wrap gap-2 sm:flex-nowrap\">\n <select\n aria-label=\"Filter by kind\"\n value={kindFilter}\n onChange={(e) => {\n setKindFilter(e.target.value as '' | SubRelationshipKind);\n setPage(0);\n }}\n className={cn(selectCls, 'flex-1 sm:flex-none')}\n >\n {KIND_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label=\"Filter by status\"\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as '' | SubRelationshipStatus);\n setPage(0);\n }}\n className={cn(selectCls, 'flex-1 sm:flex-none')}\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n </GlassCard>\n\n {isLoading ? (\n <LoadingView label=\"Loading relationships…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load relationships.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Repeat2 className=\"h-12 w-12\" />}\n title=\"No relationships found\"\n description={\n search\n ? `No relationships match \"${search}\".`\n : 'Add your first recurring relationship to start tracking it.'\n }\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Add relationship\n </button>\n }\n />\n </div>\n ) : (\n <>\n {/* Portfolio table — scrolls horizontally on small screens, never the page */}\n <GlassCard treatment=\"plain\" className=\"mt-4 overflow-hidden p-0\">\n <div className=\"overflow-x-auto\">\n <div className=\"min-w-[720px]\">\n <div className=\"grid grid-cols-[minmax(0,2.4fr)_1fr_1fr_1fr_1fr_1.3fr] gap-4 border-b border-border-seam bg-bg-sunken/60 px-4 py-2.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n <span>Subscription</span>\n <span>Kind</span>\n <span>Status</span>\n <span className=\"text-right\">Amount</span>\n <span className=\"text-right\">Renews on</span>\n <span>Health</span>\n </div>\n <div className=\"divide-y divide-border-subtle\">\n {items.map((r) => {\n const score = r.healthScore;\n return (\n <button\n key={r.id}\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${r.id}`)}\n className=\"group grid w-full grid-cols-[minmax(0,2.4fr)_1fr_1fr_1fr_1fr_1.3fr] items-center gap-4 px-4 py-3.5 text-left transition-colors hover:bg-bg-sunken\"\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {r.label}\n </p>\n {r.provider?.name && (\n <p className=\"truncate text-xs text-text-secondary\">\n {r.provider.name}\n </p>\n )}\n </div>\n </div>\n <span>\n <span className=\"inline-flex items-center rounded-md bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {formatEnumLabel(r.kind)}\n </span>\n </span>\n <span>\n <StatusBadge status={r.status} />\n </span>\n <span className=\"text-right text-sm font-semibold tabular-nums text-text-primary\">\n {formatCurrency(r.amount, r.currency)}\n <span className=\"text-xs font-normal text-text-secondary\">\n {formatIntervalSuffix(r.interval)}\n </span>\n </span>\n <span className=\"text-right text-xs text-text-secondary\">\n {r.renewsAt ? formatDate(r.renewsAt) : '—'}\n </span>\n <span className=\"flex items-center gap-2\">\n {score != null ? (\n <>\n <MiniProgress\n value={score}\n max={100}\n tone={healthTone(score)}\n className=\"w-16 shrink-0 sm:w-20\"\n />\n <span className=\"text-xs tabular-nums text-text-secondary\">\n {score}\n </span>\n </>\n ) : (\n <span className=\"text-xs text-text-secondary\">—</span>\n )}\n <ChevronRight className=\"ml-auto h-4 w-4 shrink-0 text-text-secondary opacity-0 transition-opacity group-hover:opacity-100\" />\n </span>\n </button>\n );\n })}\n </div>\n </div>\n </div>\n </GlassCard>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Page {page + 1}\n {isFetching ? ' · refreshing…' : ''}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n page === 0\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setPage((p) => p + 1)}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n !hasMore\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Next\n </button>\n </div>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RelationshipsListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/relationships/RelationshipsListPage.tsx"],"sourcesContent":["/**\n * Relationships list\n * @module subscriberbot/pages/relationships\n *\n * The core CRUDL list — every recurring relationship (subscription /\n * membership / license / contract) in one paginated, filterable view.\n * Data comes from `subListRelationships` (a SubRelationshipConnection).\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, Search, Repeat2, ChevronRight } from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { MiniProgress } from '../../components';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useRelationships } from '../../hooks/useSubscriberBotApi';\nimport { cn } from '../../utils/cn';\nimport {\n formatCurrency,\n formatDate,\n formatEnumLabel,\n formatIntervalSuffix,\n} from '../../utils/formatters';\nimport { useTr } from '../../hooks/useTr';\nimport type { SubRelationshipKind, SubRelationshipStatus } from '../../types';\n\nconst PAGE_SIZE = 20;\n\nconst STATUSES: SubRelationshipStatus[] = [\n 'ACTIVE',\n 'TRIAL',\n 'PAUSED',\n 'PAST_DUE',\n 'LAPSED',\n 'EXPIRED',\n 'CANCELLED',\n];\n\nconst KINDS: SubRelationshipKind[] = [\n 'SUBSCRIPTION',\n 'MEMBERSHIP',\n 'LICENSE',\n 'CONTRACT',\n 'NEWSLETTER',\n 'RECURRING_PAYMENT',\n 'ENROLLMENT',\n 'TRIAL',\n];\n\nconst STATUS_KEY: Record<SubRelationshipStatus, string> = {\n ACTIVE: 'relationships.statuses.active',\n TRIAL: 'relationships.statuses.trial',\n PAUSED: 'relationships.statuses.paused',\n PAST_DUE: 'relationships.statuses.pastDue',\n LAPSED: 'relationships.statuses.lapsed',\n EXPIRED: 'relationships.statuses.expired',\n CANCELLED: 'relationships.statuses.cancelled',\n};\n\nconst KIND_KEY: Record<SubRelationshipKind, string> = {\n SUBSCRIPTION: 'relationships.kinds.subscription',\n MEMBERSHIP: 'relationships.kinds.membership',\n LICENSE: 'relationships.kinds.license',\n CONTRACT: 'relationships.kinds.contract',\n NEWSLETTER: 'relationships.kinds.newsletter',\n RECURRING_PAYMENT: 'relationships.kinds.recurringPayment',\n ENROLLMENT: 'relationships.kinds.enrollment',\n TRIAL: 'relationships.kinds.trial',\n};\n\nconst selectCls =\n 'rounded-lg border border-border-subtle bg-bg-surface px-3 py-2.5 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\n/** Map a 0–100 health score onto a MiniProgress tone. */\nfunction healthTone(\n score: number | null | undefined\n): 'positive' | 'accent' | 'warning' | 'negative' {\n if (score == null) return 'accent';\n if (score >= 66) return 'positive';\n if (score >= 33) return 'warning';\n return 'negative';\n}\n\nexport function RelationshipsListPage() {\n const tr = useTr();\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [statusFilter, setStatusFilter] = useState<'' | SubRelationshipStatus>('');\n const [kindFilter, setKindFilter] = useState<'' | SubRelationshipKind>('');\n const [search, setSearch] = useState('');\n const [searchInput, setSearchInput] = useState('');\n\n const { data, isLoading, isError, refetch, isFetching } = useRelationships({\n status: statusFilter || undefined,\n kind: kindFilter || undefined,\n search: search || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n\n const items = useMemo(() => data?.items ?? [], [data]);\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n const submitSearch = (e: React.FormEvent) => {\n e.preventDefault();\n setSearch(searchInput.trim());\n setPage(0);\n };\n\n return (\n <PageLayout\n title={tr('relationships.title', 'Relationships')}\n description={tr(\n 'relationships.description',\n 'Every subscription, membership, license and contract you track'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> {tr('relationships.addRelationship', 'Add relationship')}\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n {tr(\n 'relationships.purpose',\n 'This is the fundamental object of Subscriber Bot. Filter by lifecycle and kind, open any row to manage its plan, payment, entitlements, assets and renewals, or advance its lifecycle.'\n )}\n </PagePurpose>\n\n {/* Toolbar — search + filters, responsive */}\n <GlassCard treatment=\"plain\" className=\"p-3 sm:p-4\">\n <div className=\"flex flex-col gap-3 md:flex-row md:items-center\">\n <form onSubmit={submitSearch} className=\"relative flex-1 min-w-0\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n aria-label={tr('relationships.searchPlaceholder', 'Search relationships by label…')}\n placeholder={tr('relationships.searchPlaceholder', 'Search relationships by label…')}\n value={searchInput}\n onChange={(e) => setSearchInput(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2.5 pl-10 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </form>\n <div className=\"flex flex-wrap gap-2 sm:flex-nowrap\">\n <select\n aria-label={tr('relationships.filters.kindLabel', 'Filter by kind')}\n value={kindFilter}\n onChange={(e) => {\n setKindFilter(e.target.value as '' | SubRelationshipKind);\n setPage(0);\n }}\n className={cn(selectCls, 'flex-1 sm:flex-none')}\n >\n <option value=\"\">{tr('relationships.filters.allKinds', 'All kinds')}</option>\n {KINDS.map((k) => (\n <option key={k} value={k}>\n {tr(KIND_KEY[k], formatEnumLabel(k))}\n </option>\n ))}\n </select>\n <select\n aria-label={tr('relationships.filters.statusLabel', 'Filter by status')}\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as '' | SubRelationshipStatus);\n setPage(0);\n }}\n className={cn(selectCls, 'flex-1 sm:flex-none')}\n >\n <option value=\"\">{tr('relationships.filters.allStatuses', 'All statuses')}</option>\n {STATUSES.map((s) => (\n <option key={s} value={s}>\n {tr(STATUS_KEY[s], formatEnumLabel(s))}\n </option>\n ))}\n </select>\n </div>\n </div>\n </GlassCard>\n\n {isLoading ? (\n <LoadingView label={tr('relationships.loading', 'Loading relationships…')} />\n ) : isError ? (\n <ErrorView\n message={tr('relationships.error', 'Could not load relationships.')}\n onRetry={() => void refetch()}\n />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Repeat2 className=\"h-12 w-12\" />}\n title={tr('relationships.emptyTitle', 'No relationships found')}\n description={\n search\n ? tr(\n 'relationships.emptyDescriptionSearch',\n 'No relationships match \"{{search}}\".',\n { search }\n )\n : tr(\n 'relationships.emptyDescription',\n 'Add your first recurring relationship to start tracking it.'\n )\n }\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/relationships/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n {tr('relationships.addRelationship', 'Add relationship')}\n </button>\n }\n />\n </div>\n ) : (\n <>\n {/* Portfolio table — scrolls horizontally on small screens, never the page */}\n <GlassCard treatment=\"plain\" className=\"mt-4 overflow-hidden p-0\">\n <div className=\"overflow-x-auto\">\n <div className=\"min-w-[720px]\">\n <div className=\"grid grid-cols-[minmax(0,2.4fr)_1fr_1fr_1fr_1fr_1.3fr] gap-4 border-b border-border-seam bg-bg-sunken/60 px-4 py-2.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n <span>{tr('relationships.table.subscription', 'Subscription')}</span>\n <span>{tr('relationships.table.kind', 'Kind')}</span>\n <span>{tr('relationships.table.status', 'Status')}</span>\n <span className=\"text-right\">{tr('relationships.table.amount', 'Amount')}</span>\n <span className=\"text-right\">\n {tr('relationships.table.renewsOn', 'Renews on')}\n </span>\n <span>{tr('relationships.table.health', 'Health')}</span>\n </div>\n <div className=\"divide-y divide-border-subtle\">\n {items.map((r) => {\n const score = r.healthScore;\n return (\n <button\n key={r.id}\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${r.id}`)}\n className=\"group grid w-full grid-cols-[minmax(0,2.4fr)_1fr_1fr_1fr_1fr_1.3fr] items-center gap-4 px-4 py-3.5 text-left transition-colors hover:bg-bg-sunken\"\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {r.label}\n </p>\n {r.provider?.name && (\n <p className=\"truncate text-xs text-text-secondary\">\n {r.provider.name}\n </p>\n )}\n </div>\n </div>\n <span>\n <span className=\"inline-flex items-center rounded-md bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {tr(KIND_KEY[r.kind], formatEnumLabel(r.kind))}\n </span>\n </span>\n <span>\n <StatusBadge status={r.status} />\n </span>\n <span className=\"text-right text-sm font-semibold tabular-nums text-text-primary\">\n {formatCurrency(r.amount, r.currency)}\n <span className=\"text-xs font-normal text-text-secondary\">\n {formatIntervalSuffix(r.interval)}\n </span>\n </span>\n <span className=\"text-right text-xs text-text-secondary\">\n {r.renewsAt ? formatDate(r.renewsAt) : '—'}\n </span>\n <span className=\"flex items-center gap-2\">\n {score != null ? (\n <>\n <MiniProgress\n value={score}\n max={100}\n tone={healthTone(score)}\n className=\"w-16 shrink-0 sm:w-20\"\n />\n <span className=\"text-xs tabular-nums text-text-secondary\">\n {score}\n </span>\n </>\n ) : (\n <span className=\"text-xs text-text-secondary\">—</span>\n )}\n <ChevronRight className=\"ml-auto h-4 w-4 shrink-0 text-text-secondary opacity-0 transition-opacity group-hover:opacity-100\" />\n </span>\n </button>\n );\n })}\n </div>\n </div>\n </div>\n </GlassCard>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n {tr('relationships.pagination.page', 'Page {{page}}', { page: page + 1 })}\n {isFetching ? ` · ${tr('relationships.pagination.refreshing', 'refreshing…')}` : ''}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n page === 0\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n {tr('relationships.pagination.previous', 'Previous')}\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setPage((p) => p + 1)}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n !hasMore\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n {tr('relationships.pagination.next', 'Next')}\n </button>\n </div>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|