@microcosmmoney/portal-react 3.1.0 → 3.2.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.
|
@@ -6,6 +6,7 @@ exports.MicrocosmTerritoryPage = MicrocosmTerritoryPage;
|
|
|
6
6
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
7
|
const react_1 = require("react");
|
|
8
8
|
const auth_react_1 = require("@microcosmmoney/auth-react");
|
|
9
|
+
const recharts_1 = require("recharts");
|
|
9
10
|
/* ------------------------------------------------------------------ */
|
|
10
11
|
/* Inline SVG Icons (24x24 viewBox, stroke 2, round caps) */
|
|
11
12
|
/* ------------------------------------------------------------------ */
|
|
@@ -81,20 +82,118 @@ function TerritoryCard({ unit, onClick }) {
|
|
|
81
82
|
/* ------------------------------------------------------------------ */
|
|
82
83
|
/* Detail View */
|
|
83
84
|
/* ------------------------------------------------------------------ */
|
|
85
|
+
/* ------------------------------------------------------------------ */
|
|
86
|
+
/* Income Chart (recharts LineChart) */
|
|
87
|
+
/* ------------------------------------------------------------------ */
|
|
88
|
+
function IncomeChart({ territoryId }) {
|
|
89
|
+
const { data: raw, loading } = (0, auth_react_1.useTerritoryIncome)(territoryId, '30d');
|
|
90
|
+
const uid = (0, react_1.useId)();
|
|
91
|
+
const chartData = (0, react_1.useMemo)(() => {
|
|
92
|
+
if (!raw)
|
|
93
|
+
return [];
|
|
94
|
+
// API may return { labels, datasets: { income, cumulative } } or array
|
|
95
|
+
if (Array.isArray(raw))
|
|
96
|
+
return raw;
|
|
97
|
+
if (raw.labels && raw.datasets) {
|
|
98
|
+
return raw.labels.map((label, i) => ({
|
|
99
|
+
date: label,
|
|
100
|
+
income: raw.datasets.income?.[i] ?? 0,
|
|
101
|
+
cumulative: raw.datasets.cumulative?.[i] ?? 0,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
return raw.data ?? [];
|
|
105
|
+
}, [raw]);
|
|
106
|
+
if (loading)
|
|
107
|
+
return (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg p-6", children: (0, jsx_runtime_1.jsx)("div", { className: "h-64 flex items-center justify-center", children: (0, jsx_runtime_1.jsx)(Spinner, {}) }) });
|
|
108
|
+
if (chartData.length === 0)
|
|
109
|
+
return null;
|
|
110
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold mb-4 flex items-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)(IconZap, { className: "w-4 h-4 text-cyan-400" }), "Income Trend (30D)"] }), (0, jsx_runtime_1.jsx)("div", { className: "h-64", children: (0, jsx_runtime_1.jsx)(recharts_1.ResponsiveContainer, { width: "100%", height: "100%", children: (0, jsx_runtime_1.jsxs)(recharts_1.LineChart, { data: chartData, margin: { top: 5, right: 10, left: 0, bottom: 5 }, children: [(0, jsx_runtime_1.jsx)(recharts_1.CartesianGrid, { strokeDasharray: "3 3", stroke: "#404040" }), (0, jsx_runtime_1.jsx)(recharts_1.XAxis, { dataKey: "date", tick: { fill: '#a3a3a3', fontSize: 11 }, axisLine: { stroke: '#525252' } }), (0, jsx_runtime_1.jsx)(recharts_1.YAxis, { tick: { fill: '#a3a3a3', fontSize: 11 }, axisLine: { stroke: '#525252' } }), (0, jsx_runtime_1.jsx)(recharts_1.Tooltip, { contentStyle: { backgroundColor: '#171717', border: '1px solid #404040', borderRadius: '8px' }, labelStyle: { color: '#a3a3a3' }, itemStyle: { color: '#fff' } }), (0, jsx_runtime_1.jsx)(recharts_1.Line, { type: "monotone", dataKey: "income", name: "Daily Income", stroke: "#22d3ee", strokeWidth: 2, dot: false, activeDot: { r: 4 } }), (0, jsx_runtime_1.jsx)(recharts_1.Line, { type: "monotone", dataKey: "cumulative", name: "Cumulative", stroke: "#ffffff", strokeWidth: 2, dot: false, activeDot: { r: 4 } })] }) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-center gap-6 mt-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-3 h-0.5 bg-cyan-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400", children: "Daily Income" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-3 h-0.5 bg-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400", children: "Cumulative" })] })] })] }));
|
|
111
|
+
}
|
|
112
|
+
/* ------------------------------------------------------------------ */
|
|
113
|
+
/* Member Ranking */
|
|
114
|
+
/* ------------------------------------------------------------------ */
|
|
115
|
+
const RANK_BADGE = { 1: 'bg-cyan-400/20 text-cyan-400', 2: 'bg-white/20 text-white', 3: 'bg-cyan-300/20 text-cyan-300' };
|
|
116
|
+
function MemberRanking({ territoryId }) {
|
|
117
|
+
const { data: raw, loading } = (0, auth_react_1.useTerritoryRanking)(territoryId, { page_size: 10 });
|
|
118
|
+
const rankList = (0, react_1.useMemo)(() => {
|
|
119
|
+
if (!raw)
|
|
120
|
+
return [];
|
|
121
|
+
return Array.isArray(raw) ? raw : raw.data ?? [];
|
|
122
|
+
}, [raw]);
|
|
123
|
+
if (loading)
|
|
124
|
+
return (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg p-6", children: (0, jsx_runtime_1.jsx)("div", { className: "h-64 flex items-center justify-center", children: (0, jsx_runtime_1.jsx)(Spinner, {}) }) });
|
|
125
|
+
if (rankList.length === 0)
|
|
126
|
+
return null;
|
|
127
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold mb-4 flex items-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)(IconShield, { className: "w-4 h-4 text-cyan-400" }), "Member Contribution Ranking"] }), (0, jsx_runtime_1.jsx)("div", { className: "space-y-2", children: rankList.map((m, idx) => {
|
|
128
|
+
const rank = m.rank ?? idx + 1;
|
|
129
|
+
const name = m.nickname || m.display_name || 'Unknown';
|
|
130
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 p-2 bg-neutral-800 rounded hover:bg-neutral-700 transition-colors", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-8 text-center", children: rank <= 3 ? ((0, jsx_runtime_1.jsxs)("span", { className: `inline-block px-1.5 py-0.5 rounded text-xs font-bold ${RANK_BADGE[rank] ?? ''}`, children: ["#", rank] })) : ((0, jsx_runtime_1.jsxs)("span", { className: "text-neutral-500 font-mono text-sm", children: ["#", rank] })) }), (0, jsx_runtime_1.jsx)("div", { className: "w-8 h-8 rounded-full bg-neutral-700 flex items-center justify-center text-xs text-white font-bold flex-shrink-0", children: name.slice(0, 2).toUpperCase() }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-white text-sm truncate", children: name }), m.email && (0, jsx_runtime_1.jsx)("div", { className: "text-neutral-500 text-xs truncate", children: m.email })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right flex-shrink-0", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-white font-mono font-medium text-sm", children: (m.contribution ?? 0).toLocaleString() }), (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-500 text-xs ml-1", children: "MCD" })] })] }, m.user_id || idx));
|
|
131
|
+
}) })] }));
|
|
132
|
+
}
|
|
133
|
+
/* ------------------------------------------------------------------ */
|
|
134
|
+
/* KPI History Chart (recharts AreaChart) */
|
|
135
|
+
/* ------------------------------------------------------------------ */
|
|
136
|
+
function KPIHistoryChart({ territoryId }) {
|
|
137
|
+
const { data: raw, loading } = (0, auth_react_1.useTerritoryKPI)(territoryId);
|
|
138
|
+
const uid = (0, react_1.useId)();
|
|
139
|
+
const chartData = (0, react_1.useMemo)(() => {
|
|
140
|
+
if (!raw)
|
|
141
|
+
return [];
|
|
142
|
+
if (Array.isArray(raw))
|
|
143
|
+
return raw;
|
|
144
|
+
return raw.data ?? [];
|
|
145
|
+
}, [raw]);
|
|
146
|
+
if (loading)
|
|
147
|
+
return (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg p-6", children: (0, jsx_runtime_1.jsx)("div", { className: "h-64 flex items-center justify-center", children: (0, jsx_runtime_1.jsx)(Spinner, {}) }) });
|
|
148
|
+
if (chartData.length === 0)
|
|
149
|
+
return null;
|
|
150
|
+
const gradMember = `kpi-member-${uid}`;
|
|
151
|
+
const gradVolume = `kpi-volume-${uid}`;
|
|
152
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold mb-4 flex items-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)(IconEdit3, { className: "w-4 h-4 text-cyan-400" }), "KPI History"] }), (0, jsx_runtime_1.jsx)("div", { className: "h-64", children: (0, jsx_runtime_1.jsx)(recharts_1.ResponsiveContainer, { width: "100%", height: "100%", children: (0, jsx_runtime_1.jsxs)(recharts_1.AreaChart, { data: chartData, margin: { top: 5, right: 10, left: 0, bottom: 5 }, children: [(0, jsx_runtime_1.jsxs)("defs", { children: [(0, jsx_runtime_1.jsxs)("linearGradient", { id: gradMember, x1: "0", y1: "0", x2: "0", y2: "1", children: [(0, jsx_runtime_1.jsx)("stop", { offset: "5%", stopColor: "#22d3ee", stopOpacity: 0.3 }), (0, jsx_runtime_1.jsx)("stop", { offset: "95%", stopColor: "#22d3ee", stopOpacity: 0 })] }), (0, jsx_runtime_1.jsxs)("linearGradient", { id: gradVolume, x1: "0", y1: "0", x2: "0", y2: "1", children: [(0, jsx_runtime_1.jsx)("stop", { offset: "5%", stopColor: "#ffffff", stopOpacity: 0.3 }), (0, jsx_runtime_1.jsx)("stop", { offset: "95%", stopColor: "#ffffff", stopOpacity: 0 })] })] }), (0, jsx_runtime_1.jsx)(recharts_1.CartesianGrid, { strokeDasharray: "3 3", stroke: "#404040" }), (0, jsx_runtime_1.jsx)(recharts_1.XAxis, { dataKey: "date", tick: { fill: '#a3a3a3', fontSize: 11 }, axisLine: { stroke: '#525252' } }), (0, jsx_runtime_1.jsx)(recharts_1.YAxis, { domain: [0, 100], tick: { fill: '#a3a3a3', fontSize: 11 }, axisLine: { stroke: '#525252' }, tickFormatter: (v) => `${v}%` }), (0, jsx_runtime_1.jsx)(recharts_1.Tooltip, { contentStyle: { backgroundColor: '#171717', border: '1px solid #404040', borderRadius: '8px' }, labelStyle: { color: '#a3a3a3' }, formatter: (value) => [`${Number(value).toFixed(1)}%`, ''] }), (0, jsx_runtime_1.jsx)(recharts_1.Area, { type: "monotone", dataKey: "member_progress", name: "Member Progress", stroke: "#22d3ee", strokeWidth: 2, fill: `url(#${gradMember})` }), (0, jsx_runtime_1.jsx)(recharts_1.Area, { type: "monotone", dataKey: "volume_progress", name: "Volume Progress", stroke: "#ffffff", strokeWidth: 2, fill: `url(#${gradVolume})` })] }) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-center gap-6 mt-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-3 h-3 rounded-full bg-cyan-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400", children: "Member Progress" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-3 h-3 rounded-full bg-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400", children: "Volume Progress" })] })] })] }));
|
|
153
|
+
}
|
|
154
|
+
/* ------------------------------------------------------------------ */
|
|
155
|
+
/* Edit Territory Dialog */
|
|
156
|
+
/* ------------------------------------------------------------------ */
|
|
157
|
+
function EditTerritoryDialog({ territory, open, onClose, onSaved }) {
|
|
158
|
+
const { update, loading: updating } = (0, auth_react_1.useTerritoryUpdate)();
|
|
159
|
+
const [name, setName] = (0, react_1.useState)(territory.unit_name);
|
|
160
|
+
const [desc, setDesc] = (0, react_1.useState)(territory.description || '');
|
|
161
|
+
const [error, setError] = (0, react_1.useState)('');
|
|
162
|
+
const handleSave = (0, react_1.useCallback)(async () => {
|
|
163
|
+
if (!name.trim()) {
|
|
164
|
+
setError('Name is required');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
setError('');
|
|
169
|
+
await update(territory.unit_id, { unit_name: name, description: desc });
|
|
170
|
+
onSaved();
|
|
171
|
+
onClose();
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
setError(e.message || 'Update failed');
|
|
175
|
+
}
|
|
176
|
+
}, [update, territory.unit_id, name, desc, onSaved, onClose]);
|
|
177
|
+
if (!open)
|
|
178
|
+
return null;
|
|
179
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", onClick: onClose, children: [(0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 bg-black/60" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative bg-neutral-900 border border-neutral-700 rounded-lg w-full max-w-lg p-6 space-y-4 mx-4", onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-white font-bold text-lg", children: "Edit Territory" }), (0, jsx_runtime_1.jsxs)("p", { className: "text-neutral-500 text-xs font-mono mt-1", children: [territory.unit_type, " \u00B7 ", territory.full_path || territory.short_id || territory.unit_id] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-3", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-neutral-400 tracking-wider block mb-1", children: "unit_name *" }), (0, jsx_runtime_1.jsx)("input", { value: name, onChange: (e) => setName(e.target.value), className: "w-full bg-neutral-800 border border-neutral-700 text-white px-3 py-2 rounded text-sm focus:border-cyan-400/50 outline-none" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-neutral-400 tracking-wider block mb-1", children: "description" }), (0, jsx_runtime_1.jsx)("textarea", { value: desc, onChange: (e) => setDesc(e.target.value), rows: 3, className: "w-full bg-neutral-800 border border-neutral-700 text-white px-3 py-2 rounded text-sm focus:border-cyan-400/50 outline-none resize-none" })] })] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-red-400 text-xs", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3 pt-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: onClose, disabled: updating, className: "px-4 py-2 text-sm text-neutral-400 border border-neutral-700 rounded hover:bg-neutral-800 transition-colors", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleSave, disabled: updating, className: "px-4 py-2 text-sm text-white bg-cyan-700 hover:bg-cyan-600 rounded transition-colors disabled:opacity-50", children: updating ? 'Saving...' : 'Save' })] })] })] }));
|
|
180
|
+
}
|
|
181
|
+
/* ------------------------------------------------------------------ */
|
|
182
|
+
/* Detail View (full) */
|
|
183
|
+
/* ------------------------------------------------------------------ */
|
|
84
184
|
function TerritoryDetailView({ territoryId, territory, onBack }) {
|
|
85
185
|
const { data: detailedStats, loading: statsLoading } = (0, auth_react_1.useTerritoryDetailedStats)(territoryId);
|
|
86
186
|
const { data: members, loading: membersLoading } = (0, auth_react_1.useTerritoryMembers)(territoryId);
|
|
187
|
+
const [editOpen, setEditOpen] = (0, react_1.useState)(false);
|
|
87
188
|
const stats = detailedStats;
|
|
88
189
|
const memberList = Array.isArray(members) ? members : [];
|
|
89
190
|
const unitType = (territory?.unit_type || 'station');
|
|
90
191
|
const techBonus = TECH_BONUS[unitType.toLowerCase()] ?? 0;
|
|
91
|
-
const memberCount = territory?.member_count ?? stats?.stats?.member_count ?? 0;
|
|
92
|
-
const maxCapacity = territory?.max_capacity ?? stats?.stats?.max_capacity ?? 0;
|
|
93
|
-
const vaultBalance = territory?.vault_balance ?? stats?.stats?.vault_balance ?? 0;
|
|
192
|
+
const memberCount = territory?.member_count ?? stats?.stats?.member_count ?? stats?.metrics?.member_count ?? 0;
|
|
193
|
+
const maxCapacity = territory?.max_capacity ?? stats?.stats?.max_capacity ?? stats?.metrics?.max_capacity ?? 0;
|
|
194
|
+
const vaultBalance = territory?.vault_balance ?? stats?.stats?.vault_balance ?? stats?.metrics?.vault_mcd ?? 0;
|
|
94
195
|
const occupancy = maxCapacity > 0 ? Math.round((memberCount / maxCapacity) * 100) : 0;
|
|
95
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: onBack, className: "flex items-center gap-2 text-sm text-neutral-400 hover:text-white transition-colors bg-transparent border-none cursor-pointer", children: [(0, jsx_runtime_1.jsx)(IconArrowLeft, { className: "w-4 h-4" }), "Back to list"] }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-5", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-24 h-24 bg-neutral-800 rounded-lg flex items-center justify-center overflow-hidden flex-shrink-0", children: territory?.image_url ? ((0, jsx_runtime_1.jsx)("img", { src: territory.image_url, alt: territory.unit_name, className: "w-full h-full object-cover" })) : ((0, jsx_runtime_1.jsx)("div", { className: "text-neutral-600", children: getTypeIcon(unitType, 'w-10 h-10') })) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 flex-wrap", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-xl font-bold text-white", children: territory?.unit_name ?? territoryId }), territory?.short_id && ((0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-500 font-mono", children: territory.short_id })), (0, jsx_runtime_1.jsxs)("span", { className: `border px-1.5 py-0.5 rounded text-xs capitalize ${getTypeBadgeColor(unitType)}`, children: [getTypeIcon(unitType, 'w-3 h-3 inline-block -mt-0.5 mr-1'), unitType] }), territory?.manager_display_name && ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-1 text-xs text-emerald-400 border border-emerald-400/30 px-1.5 py-0.5 rounded", children: [(0, jsx_runtime_1.jsx)(IconShield, { className: "w-3 h-3" }), "Magistrate: ", territory.manager_display_name] }))] }), territory?.full_path && ((0, jsx_runtime_1.jsx)("p", { className: "text-neutral-500 text-sm font-mono mt-1", children: territory.full_path })), territory?.description && ((0, jsx_runtime_1.jsx)("p", { className: "text-neutral-400 text-sm mt-2", children: territory.description }))] })] }) }), statsLoading ? ((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [...Array(4)].map((_, i) => (0, jsx_runtime_1.jsx)(Skeleton, { className: "h-24" }, i)) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconUsers, { className: "w-4 h-4 text-neutral-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400", children: "Members" })] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-2xl font-bold text-white font-mono", children: [fmt(memberCount), (0, jsx_runtime_1.jsxs)("span", { className: "text-sm text-neutral-500 font-normal", children: ["/", fmt(maxCapacity)] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconVault, { className: "w-4 h-4 text-neutral-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400", children: "Vault MCD" })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-2xl font-bold text-white font-mono", children: fmt(Math.round(vaultBalance)) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconZap, { className: "w-4 h-4 text-yellow-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400", children: "Tech Bonus" })] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-2xl font-bold text-yellow-400 font-mono", children: ["+", stats?.tech_bonus ?? techBonus, "%"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconSettings, { className: "w-4 h-4 text-neutral-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400", children: "Occupancy" })] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-2xl font-bold text-white font-mono", children: [occupancy, "%"] })] })] })), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold mb-4 flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(IconEdit3, { className: "w-4 h-4 text-cyan-400" }), "KPI Progress"] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400", children: "Occupancy Rate" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-sm text-white font-mono", children: [occupancy, "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-2 bg-neutral-800 rounded-full overflow-hidden", children: (0, jsx_runtime_1.jsx)("div", { className: "h-full bg-cyan-400 rounded-full transition-all duration-500", style: { width: `${Math.min(occupancy, 100)}%` } }) })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400", children: "Vault Balance" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-sm text-white font-mono", children: [fmt(Math.round(vaultBalance)), " MCD"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-2 bg-neutral-800 rounded-full overflow-hidden", children: (0, jsx_runtime_1.jsx)("div", { className: "h-full bg-cyan-400 rounded-full transition-all duration-500", style: { width: `${Math.min(vaultBalance > 0 ? Math.max(Math.log10(vaultBalance) * 10, 5) : 0, 100)}%` } }) })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold mb-3 flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(IconVault, { className: "w-4 h-4 text-cyan-400" }), "Distribution Mechanism"] }), (0, jsx_runtime_1.jsx)("p", { className: "text-neutral-400 text-sm leading-relaxed", children: "Each territory vault distributes 1% of its MCD balance daily to all active miners within the territory. The distribution is proportional to each miner's mining activity. Vault balances are replenished through companion yield from mining operations (30% of companion output as MCD) and monthly recycling cycles." })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: [(0, jsx_runtime_1.jsx)("div", { className: "p-4 border-b border-neutral-700/50", children: (0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(IconUsers, { className: "w-4 h-4 text-cyan-400" }), "Members (", memberList.length, ")"] }) }), (0, jsx_runtime_1.jsx)("div", { className: "overflow-x-auto", children: membersLoading ? ((0, jsx_runtime_1.jsx)("div", { className: "p-6 flex justify-center", children: (0, jsx_runtime_1.jsx)(Spinner, {}) })) : memberList.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "p-8 text-center text-neutral-500 text-sm", children: "No members found" })) : ((0, jsx_runtime_1.jsxs)("table", { className: "w-full text-sm", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { className: "border-b border-neutral-700/50", children: [(0, jsx_runtime_1.jsx)("th", { className: "text-left px-4 py-3 text-neutral-400 font-medium", children: "Name / UID" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left px-4 py-3 text-neutral-400 font-medium", children: "Role" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left px-4 py-3 text-neutral-400 font-medium", children: "Email" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left px-4 py-3 text-neutral-400 font-medium", children: "Joined" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: memberList.map((member, idx) => ((0, jsx_runtime_1.jsxs)("tr", { className: "border-b border-neutral-800/50 hover:bg-neutral-800/30", children: [(0, jsx_runtime_1.jsxs)("td", { className: "px-4 py-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-white", children: member.display_name || member.username || 'Unknown' }), (0, jsx_runtime_1.jsx)("div", { className: "text-neutral-500 text-xs font-mono", children: member.uid ? `${member.uid.slice(0, 12)}...` : '-' })] }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-300 capitalize", children: member.role || member.level_name || 'Miner' }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400 font-mono text-xs", children: member.email ? (member.email.length > 20 ? member.email.slice(0, 20) + '...' : member.email) : '-' }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsxs)("span", { className: "text-neutral-400 flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(IconCalendar, { className: "w-3 h-3" }), member.joined_at || member.created_at
|
|
96
|
-
? new Date(member.joined_at || member.created_at).toLocaleDateString()
|
|
97
|
-
: '-'] }) })] }, member.uid || idx))) })] })) })] })] }));
|
|
196
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: onBack, className: "flex items-center gap-2 text-sm text-neutral-400 hover:text-white transition-colors bg-transparent border-none cursor-pointer", children: [(0, jsx_runtime_1.jsx)(IconArrowLeft, { className: "w-4 h-4" }), "Back to list"] }), territory && ((0, jsx_runtime_1.jsxs)("button", { onClick: () => setEditOpen(true), className: "flex items-center gap-1.5 px-3 py-1.5 text-sm text-white bg-cyan-700 hover:bg-cyan-600 rounded transition-colors", children: [(0, jsx_runtime_1.jsx)(IconEdit3, { className: "w-3.5 h-3.5" }), "Edit"] }))] }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-5", children: [(0, jsx_runtime_1.jsx)("div", { className: "w-28 h-28 bg-neutral-800 rounded-lg flex items-center justify-center overflow-hidden flex-shrink-0", children: territory?.image_url ? ((0, jsx_runtime_1.jsx)("img", { src: territory.image_url, alt: territory.unit_name, className: "w-full h-full object-cover" })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center text-neutral-600", children: [getTypeIcon(unitType, 'w-10 h-10'), (0, jsx_runtime_1.jsx)("span", { className: "text-[10px] mt-1 capitalize", children: unitType })] })) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 flex-wrap", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-xl font-bold text-white", children: territory?.unit_name ?? territoryId }), territory?.short_id && ((0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400 font-mono bg-neutral-800 px-2 py-0.5 rounded", children: territory.short_id })), (0, jsx_runtime_1.jsx)("span", { className: `border px-1.5 py-0.5 rounded text-xs capitalize ${getTypeBadgeColor(unitType)}`, children: unitType }), occupancy >= 90 ? ((0, jsx_runtime_1.jsx)("span", { className: "text-xs bg-white/20 text-white px-1.5 py-0.5 rounded", children: "Qualified" })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-xs bg-neutral-500/20 text-neutral-400 px-1.5 py-0.5 rounded", children: "Not Qualified" }))] }), territory?.manager_display_name && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1.5 mt-2 text-xs text-emerald-400", children: [(0, jsx_runtime_1.jsx)(IconShield, { className: "w-3.5 h-3.5" }), "Magistrate: ", territory.manager_display_name] })), territory?.full_path && ((0, jsx_runtime_1.jsx)("p", { className: "text-neutral-500 text-sm font-mono mt-1", children: territory.full_path })), territory?.description && ((0, jsx_runtime_1.jsx)("p", { className: "text-neutral-400 text-sm mt-2", children: territory.description }))] })] }) }), statsLoading ? ((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [...Array(4)].map((_, i) => (0, jsx_runtime_1.jsx)(Skeleton, { className: "h-24" }, i)) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconUsers, { className: "w-5 h-5 text-neutral-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400", children: "Members" })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-2xl font-bold text-white font-mono", children: fmt(memberCount) }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-neutral-500 mt-1", children: ["Capacity: ", (0, jsx_runtime_1.jsx)("span", { className: "font-mono", children: fmt(maxCapacity) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconVault, { className: "w-5 h-5 text-neutral-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400", children: "Vault Balance" })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-2xl font-bold text-white font-mono", children: fmt(Math.round(vaultBalance)) }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-500 mt-1", children: "MCD" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconZap, { className: "w-5 h-5 text-yellow-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400", children: "Tech Bonus" })] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-2xl font-bold text-white font-mono", children: ["+", stats?.tech_bonus ?? techBonus, "%"] }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-500 mt-1", children: "Mining output bonus" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconSettings, { className: "w-5 h-5 text-neutral-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400", children: "Occupancy" })] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-2xl font-bold text-white font-mono", children: [occupancy, "%"] }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-500 mt-1", children: "Members / Capacity" })] })] })), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold mb-4 flex items-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)(IconEdit3, { className: "w-4 h-4 text-cyan-400" }), "KPI Progress"] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between text-sm mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Members" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white font-mono", children: [fmt(memberCount), " / ", fmt(maxCapacity)] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-2 bg-neutral-800 rounded-full overflow-hidden", children: (0, jsx_runtime_1.jsx)("div", { className: "h-full bg-cyan-400 rounded-full transition-all duration-500", style: { width: `${Math.min(occupancy, 100)}%` } }) }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-neutral-500 font-mono mt-1", children: [occupancy, "% filled"] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between text-sm mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Vault Balance" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white font-mono", children: [fmt(Math.round(vaultBalance)), " MCD"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-2 bg-neutral-800 rounded-full overflow-hidden", children: (0, jsx_runtime_1.jsx)("div", { className: "h-full bg-cyan-400 rounded-full transition-all duration-500", style: { width: `${Math.min(vaultBalance > 0 ? Math.max(Math.log10(vaultBalance) * 10, 5) : 0, 100)}%` } }) }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-500 mt-1", children: "Current vault funds" })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors p-6", children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold mb-3 flex items-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)(IconSettings, { className: "w-4 h-4 text-cyan-400" }), "Distribution Mechanism"] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-800 rounded p-4", children: [(0, jsx_runtime_1.jsx)("h4", { className: "text-white font-semibold text-sm mb-2", children: "Auto Distribution by Contribution" }), (0, jsx_runtime_1.jsx)("p", { className: "text-neutral-400 text-sm", children: "Vault MCD is distributed daily at 1% of balance, allocated to miners proportional to their mining activity." }), (0, jsx_runtime_1.jsx)("p", { className: "text-neutral-500 text-xs mt-2", children: "Distribution ratio: Miners 100% (by contribution)" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: [(0, jsx_runtime_1.jsx)(IncomeChart, { territoryId: territoryId }), (0, jsx_runtime_1.jsx)(MemberRanking, { territoryId: territoryId })] }), (0, jsx_runtime_1.jsx)(KPIHistoryChart, { territoryId: territoryId }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: [(0, jsx_runtime_1.jsx)("div", { className: "p-4 border-b border-neutral-700/50", children: (0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-semibold flex items-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)(IconUsers, { className: "w-4 h-4 text-cyan-400" }), "Members (", memberList.length, ")"] }) }), (0, jsx_runtime_1.jsx)("div", { className: "overflow-x-auto", children: membersLoading ? ((0, jsx_runtime_1.jsx)("div", { className: "p-6 flex justify-center", children: (0, jsx_runtime_1.jsx)(Spinner, {}) })) : memberList.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "p-8 text-center text-neutral-500 text-sm", children: "No members found" })) : ((0, jsx_runtime_1.jsxs)("table", { className: "w-full text-sm", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { className: "border-b border-neutral-700/50", children: [(0, jsx_runtime_1.jsx)("th", { className: "text-left px-4 py-3 text-neutral-400 font-medium", children: "Name / UID" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left px-4 py-3 text-neutral-400 font-medium", children: "Role" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left px-4 py-3 text-neutral-400 font-medium", children: "Email" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left px-4 py-3 text-neutral-400 font-medium", children: "Joined" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: memberList.map((member, idx) => ((0, jsx_runtime_1.jsxs)("tr", { className: "border-b border-neutral-800/50 hover:bg-neutral-800/30", children: [(0, jsx_runtime_1.jsxs)("td", { className: "px-4 py-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-white", children: member.display_name || member.username || 'Unknown' }), (0, jsx_runtime_1.jsx)("div", { className: "text-neutral-500 text-xs font-mono", children: member.uid ? `${member.uid.slice(0, 12)}...` : '-' })] }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-300 capitalize", children: member.role || member.level_name || 'Miner' }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400 font-mono text-xs", children: member.email ? (member.email.length > 20 ? member.email.slice(0, 20) + '...' : member.email) : '-' }) }), (0, jsx_runtime_1.jsx)("td", { className: "px-4 py-3", children: (0, jsx_runtime_1.jsxs)("span", { className: "text-neutral-400 flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(IconCalendar, { className: "w-3 h-3" }), member.joined_at || member.created_at ? new Date(member.joined_at || member.created_at).toLocaleDateString() : '-'] }) })] }, member.uid || idx))) })] })) })] }), territory && ((0, jsx_runtime_1.jsx)(EditTerritoryDialog, { territory: territory, open: editOpen, onClose: () => setEditOpen(false), onSaved: () => { } }))] }));
|
|
98
197
|
}
|
|
99
198
|
/* ------------------------------------------------------------------ */
|
|
100
199
|
/* Main Component */
|