@microcosmmoney/portal-react 3.5.1 → 3.6.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.
@@ -0,0 +1,5 @@
1
+ export interface MicrocosmManagerIncomePageProps {
2
+ basePath?: string;
3
+ onNavigate?: (path: string) => void;
4
+ }
5
+ export declare function MicrocosmManagerIncomePage({}?: MicrocosmManagerIncomePageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.MicrocosmManagerIncomePage = MicrocosmManagerIncomePage;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const auth_react_1 = require("@microcosmmoney/auth-react");
8
+ const terminal_1 = require("../terminal");
9
+ const SHARE_RATIOS = { station: 0.16, matrix: 0.12, sector: 0.08, system: 0.04 };
10
+ const LEVEL_LABELS = { station: 'Station', matrix: 'Matrix', sector: 'Sector', system: 'System' };
11
+ const LEVEL_ROLES = { station: 'Commander', matrix: 'Pioneer', sector: 'Warden', system: 'Admiral' };
12
+ function MicrocosmManagerIncomePage({} = {}) {
13
+ const api = (0, auth_react_1.useMicrocosmApi)();
14
+ const [income, setIncome] = (0, react_1.useState)(null);
15
+ const [loading, setLoading] = (0, react_1.useState)(true);
16
+ const [refreshing, setRefreshing] = (0, react_1.useState)(false);
17
+ const [dateRange, setDateRange] = (0, react_1.useState)('30d');
18
+ const [startDate, setStartDate] = (0, react_1.useState)('');
19
+ const [endDate, setEndDate] = (0, react_1.useState)('');
20
+ const loadData = (0, react_1.useCallback)(async () => {
21
+ setLoading(true);
22
+ try {
23
+ const params = new URLSearchParams();
24
+ const now = new Date();
25
+ let start;
26
+ if (dateRange === '7d') {
27
+ const d = new Date(now);
28
+ d.setDate(d.getDate() - 7);
29
+ start = d.toISOString().split('T')[0];
30
+ }
31
+ else if (dateRange === '30d') {
32
+ const d = new Date(now);
33
+ d.setDate(d.getDate() - 30);
34
+ start = d.toISOString().split('T')[0];
35
+ }
36
+ else if (dateRange === '90d') {
37
+ const d = new Date(now);
38
+ d.setDate(d.getDate() - 90);
39
+ start = d.toISOString().split('T')[0];
40
+ }
41
+ if (startDate)
42
+ start = startDate;
43
+ if (start)
44
+ params.set('start', start);
45
+ if (endDate)
46
+ params.set('end', endDate);
47
+ const qs = params.toString();
48
+ const res = await api.get(`/territories/manager/income${qs ? '?' + qs : ''}`);
49
+ setIncome(res?.data ?? res);
50
+ }
51
+ catch {
52
+ setIncome(null);
53
+ }
54
+ finally {
55
+ setLoading(false);
56
+ }
57
+ }, [api, dateRange, startDate, endDate]);
58
+ (0, react_1.useEffect)(() => { loadData(); }, [dateRange]);
59
+ const handleRefresh = async () => {
60
+ setRefreshing(true);
61
+ await loadData();
62
+ setRefreshing(false);
63
+ };
64
+ const totalIncome = income?.total_income ? parseFloat(income.total_income) : 0;
65
+ const levels = ['station', 'matrix', 'sector', 'system'];
66
+ const levelIncomes = levels.map(level => ({
67
+ level,
68
+ data: income?.income_by_level?.[level],
69
+ share: SHARE_RATIOS[level],
70
+ role: LEVEL_ROLES[level],
71
+ }));
72
+ const maxIncome = Math.max(...levelIncomes.map(l => l.data?.total_income ? parseFloat(l.data.total_income) : 0)) || 1;
73
+ if (loading) {
74
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto px-3 py-4 space-y-3 xs:px-4 xs:space-y-4 sm:px-6 sm:py-6 sm:space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-lg sm:text-2xl font-bold text-white tracking-wider", children: "Manager Income" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs sm:text-sm text-neutral-400", children: "Magistrate reward summary" })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center py-20", children: (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Loading..." }) })] }));
75
+ }
76
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto px-3 py-4 space-y-3 xs:px-4 xs:space-y-4 sm:px-6 sm:py-6 sm:space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-lg sm:text-2xl font-bold text-white tracking-wider", children: "Manager Income" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs sm:text-sm text-neutral-400", children: "Magistrate reward summary (Companion Yield)" })] }), (0, jsx_runtime_1.jsx)("button", { onClick: handleRefresh, disabled: refreshing, className: "px-3 py-1.5 text-xs border border-neutral-700 text-neutral-400 hover:bg-neutral-800 hover:text-white rounded transition-colors disabled:opacity-50", children: refreshing ? 'Refreshing...' : 'Refresh' })] }), (0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { title: "Distribution Ratio (Companion Yield per Mining Event)", children: (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-6 gap-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-center p-3 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-white", children: "16%" }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Commander" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center p-3 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-cyan-400", children: "12%" }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Pioneer" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center p-3 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-cyan-400", children: "8%" }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Warden" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center p-3 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-cyan-400", children: "4%" }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Admiral" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center p-3 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-cyan-400", children: "30%" }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "LP Reserve" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center p-3 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-cyan-400", children: "30%" }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Vault MCD" })] })] }) }), (0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col md:flex-row gap-4 items-end", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex gap-2 flex-wrap", children: ['7d', '30d', '90d', 'all'].map(range => ((0, jsx_runtime_1.jsx)("button", { onClick: () => setDateRange(range), className: `px-3 py-1.5 text-sm rounded transition-colors ${dateRange === range ? 'bg-cyan-700 text-white' : 'bg-neutral-800 text-neutral-400 hover:bg-neutral-700 hover:text-white'}`, children: range === '7d' ? '7 days' : range === '30d' ? '30 days' : range === '90d' ? '90 days' : 'All' }, range))) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2 items-end flex-1", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "text-neutral-400 text-xs tracking-wider block mb-1", children: "Start" }), (0, jsx_runtime_1.jsx)("input", { type: "date", value: startDate, onChange: (e) => setStartDate(e.target.value), className: "bg-neutral-800 border border-neutral-600 text-white w-36 h-8 text-sm rounded px-2" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "text-neutral-400 text-xs tracking-wider block mb-1", children: "End" }), (0, jsx_runtime_1.jsx)("input", { type: "date", value: endDate, onChange: (e) => setEndDate(e.target.value), className: "bg-neutral-800 border border-neutral-600 text-white w-36 h-8 text-sm rounded px-2" })] }), (0, jsx_runtime_1.jsx)("button", { onClick: () => loadData(), className: "px-3 py-1.5 text-xs border border-neutral-700 text-neutral-400 hover:bg-neutral-800 hover:text-white rounded transition-colors h-8", children: "Query" })] })] }) }), (0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { children: (0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-between", children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "TOTAL MANAGER INCOME" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-4xl font-bold text-white", children: [totalIncome.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }), (0, jsx_runtime_1.jsx)("span", { className: "text-lg text-neutral-500 ml-2", children: "MCC" })] })] }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: levelIncomes.map(({ level, data, share, role }) => {
77
+ const incomeAmount = data?.total_income ? parseFloat(data.total_income) : 0;
78
+ const recordCount = data?.record_count || 0;
79
+ const percentage = maxIncome > 0 ? (incomeAmount / maxIncome) * 100 : 0;
80
+ return ((0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-white font-medium", children: LEVEL_LABELS[level] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-neutral-500", children: [role, " \u00B7 ", (share * 100).toFixed(0), "%"] })] }), (0, jsx_runtime_1.jsx)("span", { className: `px-2 py-1 rounded text-xs ${incomeAmount > 0 ? 'bg-white/20 text-white' : 'bg-neutral-800 text-neutral-500'}`, children: incomeAmount > 0 ? 'Active' : 'None' })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-sm mb-1", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Cumulative" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white", children: [incomeAmount.toLocaleString(undefined, { minimumFractionDigits: 2 }), " MCC"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-800 rounded-full h-2", children: (0, jsx_runtime_1.jsx)("div", { className: "bg-cyan-400 h-2 rounded-full transition-all", style: { width: `${percentage}%` } }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-4 pt-3 border-t border-neutral-700", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xl font-bold text-white", children: recordCount }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Records" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xl font-bold text-white", children: recordCount > 0 ? (incomeAmount / recordCount).toFixed(2) : '0.00' }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Avg/record" })] })] })] }, level));
81
+ }) })] }));
82
+ }
@@ -0,0 +1,6 @@
1
+ export interface MicrocosmProfilePageProps {
2
+ basePath?: string;
3
+ onNavigate?: (path: string) => void;
4
+ mainPortalSettingsUrl?: string;
5
+ }
6
+ export declare function MicrocosmProfilePage({ mainPortalSettingsUrl, }?: MicrocosmProfilePageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.MicrocosmProfilePage = MicrocosmProfilePage;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const auth_react_1 = require("@microcosmmoney/auth-react");
8
+ const terminal_1 = require("../terminal");
9
+ const API_BASE = 'https://api.microcosm.money/v1';
10
+ const LEVEL_INFO = {
11
+ miner: { label: 'Miner', description: 'Registered user', color: 'text-white' },
12
+ commander: { label: 'Commander', description: 'Owns ≥1 Station NFT', color: 'text-cyan-400' },
13
+ pioneer: { label: 'Pioneer', description: 'Auto-minted Matrix NFT (≥10 Station)', color: 'text-cyan-400' },
14
+ warden: { label: 'Warden', description: 'Auto-minted Sector NFT (≥10 Matrix)', color: 'text-cyan-400' },
15
+ admiral: { label: 'Admiral', description: 'Auto-minted System NFT (≥10 Sector)', color: 'text-red-400' },
16
+ };
17
+ function MicrocosmProfilePage({ mainPortalSettingsUrl = '/user-system/profile', } = {}) {
18
+ const api = (0, auth_react_1.useMicrocosmApi)();
19
+ const { getAccessToken } = (0, auth_react_1.useMicrocosmContext)();
20
+ const authState = (0, auth_react_1.useAuth)();
21
+ const userInfo = authState?.user;
22
+ const [loading, setLoading] = (0, react_1.useState)(true);
23
+ const [profile, setProfile] = (0, react_1.useState)(null);
24
+ const [levelData, setLevelData] = (0, react_1.useState)(null);
25
+ const [editing, setEditing] = (0, react_1.useState)(false);
26
+ const [editName, setEditName] = (0, react_1.useState)('');
27
+ const [saving, setSaving] = (0, react_1.useState)(false);
28
+ const [uploadingAvatar, setUploadingAvatar] = (0, react_1.useState)(false);
29
+ const [error, setError] = (0, react_1.useState)(null);
30
+ const fileInputRef = (0, react_1.useRef)(null);
31
+ const loadProfile = (0, react_1.useCallback)(async () => {
32
+ setLoading(true);
33
+ setError(null);
34
+ try {
35
+ const res = await api.get('/users/me/profile');
36
+ const data = res?.data ?? res?.user ?? res;
37
+ if (data) {
38
+ setProfile(data);
39
+ setEditName(data.display_name || data.email?.split('@')[0] || 'user');
40
+ }
41
+ }
42
+ catch (e) {
43
+ setError(e instanceof Error ? e.message : 'Failed to load profile');
44
+ }
45
+ finally {
46
+ setLoading(false);
47
+ }
48
+ try {
49
+ const res = await api.get('/users/me/level');
50
+ setLevelData(res?.data ?? res);
51
+ }
52
+ catch { }
53
+ }, [api]);
54
+ (0, react_1.useEffect)(() => { loadProfile(); }, [loadProfile]);
55
+ const handleSave = async () => {
56
+ if (!editName.trim())
57
+ return;
58
+ setSaving(true);
59
+ setError(null);
60
+ try {
61
+ await api.patch('/users/me/profile', { display_name: editName });
62
+ setProfile(prev => (prev ? { ...prev, display_name: editName } : prev));
63
+ setEditing(false);
64
+ }
65
+ catch (e) {
66
+ setError(e instanceof Error ? e.message : 'Save failed');
67
+ }
68
+ finally {
69
+ setSaving(false);
70
+ }
71
+ };
72
+ const handleAvatarChange = async (e) => {
73
+ const file = e.target.files?.[0];
74
+ if (!file)
75
+ return;
76
+ if (!file.type.startsWith('image/')) {
77
+ setError('Please select an image file');
78
+ return;
79
+ }
80
+ if (file.size > 2 * 1024 * 1024) {
81
+ setError('Image must be under 2MB');
82
+ return;
83
+ }
84
+ setUploadingAvatar(true);
85
+ setError(null);
86
+ try {
87
+ const token = await getAccessToken();
88
+ if (!token)
89
+ throw new Error('Not authenticated');
90
+ const formData = new FormData();
91
+ formData.append('avatar', file);
92
+ const response = await fetch(`${API_BASE}/users/me/avatar`, {
93
+ method: 'POST',
94
+ headers: { Authorization: `Bearer ${token}` },
95
+ body: formData,
96
+ });
97
+ if (!response.ok)
98
+ throw new Error(`Avatar upload failed: ${response.status}`);
99
+ const result = await response.json();
100
+ const newUrl = result?.data?.avatar_url || result?.avatar_url;
101
+ if (newUrl) {
102
+ setProfile(prev => (prev ? { ...prev, avatar_url: newUrl } : prev));
103
+ }
104
+ }
105
+ catch (e) {
106
+ setError(e instanceof Error ? e.message : 'Avatar upload failed');
107
+ }
108
+ finally {
109
+ setUploadingAvatar(false);
110
+ if (fileInputRef.current)
111
+ fileInputRef.current.value = '';
112
+ }
113
+ };
114
+ const formatDate = (s) => {
115
+ if (!s)
116
+ return '-';
117
+ try {
118
+ return new Date(s).toLocaleDateString();
119
+ }
120
+ catch {
121
+ return '-';
122
+ }
123
+ };
124
+ if (loading) {
125
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto px-3 py-4 space-y-3 xs:px-4 xs:space-y-4 sm:px-6 sm:py-6 sm:space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-lg sm:text-2xl font-bold text-white tracking-wider", children: "Profile" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs sm:text-sm text-neutral-400", children: "Your account information" })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center py-20", children: (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Loading profile..." }) })] }));
126
+ }
127
+ const displayName = profile?.display_name || profile?.email?.split('@')[0] || 'user';
128
+ const lvl = (levelData?.current_rank || levelData?.database_level || 'miner').toLowerCase();
129
+ const lvlInfo = LEVEL_INFO[lvl] || LEVEL_INFO.miner;
130
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto px-3 py-4 space-y-3 xs:px-4 xs:space-y-4 sm:px-6 sm:py-6 sm:space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-lg sm:text-2xl font-bold text-white tracking-wider", children: "Profile" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs sm:text-sm text-neutral-400", children: "Your account information" })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-red-900/20 border border-red-800 rounded text-sm text-red-300", children: error })), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-4 mb-6 pb-6 border-b border-neutral-700", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative group", children: [profile?.avatar_url ? ((0, jsx_runtime_1.jsx)("img", { src: profile.avatar_url, alt: "", className: "h-20 w-20 rounded bg-neutral-800 border border-neutral-700 object-cover" })) : ((0, jsx_runtime_1.jsx)("div", { className: "h-20 w-20 rounded bg-cyan-400/20 border border-cyan-400/30 flex items-center justify-center text-cyan-400 text-3xl font-bold", children: displayName.charAt(0).toUpperCase() })), (0, jsx_runtime_1.jsx)("button", { onClick: () => fileInputRef.current?.click(), disabled: uploadingAvatar, className: "absolute inset-0 bg-black/50 rounded opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center text-white text-xs disabled:opacity-30", children: uploadingAvatar ? '...' : 'edit' }), (0, jsx_runtime_1.jsx)("input", { ref: fileInputRef, type: "file", accept: "image/jpeg,image/png,image/gif,image/webp", onChange: handleAvatarChange, className: "hidden" })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1", children: editing ? ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-3", children: [(0, jsx_runtime_1.jsx)("input", { type: "text", value: editName, onChange: (e) => setEditName(e.target.value), className: "w-full bg-neutral-800 border border-neutral-600 rounded px-3 py-2 text-white text-sm focus:outline-none focus:border-cyan-400", placeholder: "Display name" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: handleSave, disabled: saving, className: "px-3 py-1.5 bg-cyan-700 hover:bg-cyan-600 text-white rounded text-sm disabled:opacity-50", children: saving ? 'Saving...' : 'Save' }), (0, jsx_runtime_1.jsx)("button", { onClick: () => {
131
+ setEditName(profile?.display_name || '');
132
+ setEditing(false);
133
+ }, disabled: saving, className: "px-3 py-1.5 border border-neutral-700 text-neutral-400 hover:bg-neutral-800 rounded text-sm", children: "Cancel" })] })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-lg text-white", children: displayName }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setEditing(true), className: "text-xs text-neutral-500 hover:text-cyan-400", children: "[edit]" })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-sm text-neutral-400 mt-1", children: profile?.email || '(no email)' }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2 mt-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "px-2 py-0.5 bg-neutral-800 text-neutral-300 rounded text-xs border border-neutral-700", children: profile?.role || 'user' }), profile?.email_verified ? ((0, jsx_runtime_1.jsx)("span", { className: "px-2 py-0.5 bg-green-900/30 text-green-400 rounded text-xs border border-green-800", children: "verified" })) : ((0, jsx_runtime_1.jsx)("span", { className: "px-2 py-0.5 bg-red-900/30 text-red-400 rounded text-xs border border-red-800", children: "unverified" }))] })] })) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-800 rounded p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "UID" }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-white font-mono break-all", children: profile?.uid || userInfo?.uid || '-' })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-800 rounded p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "Short ID" }), (0, jsx_runtime_1.jsx)("div", { className: "text-sm text-white font-mono", children: profile?.short_id || '-' })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-800 rounded p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "Created" }), (0, jsx_runtime_1.jsx)("div", { className: "text-sm text-white", children: formatDate(profile?.created_at) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-800 rounded p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "Last Login" }), (0, jsx_runtime_1.jsx)("div", { className: "text-sm text-white", children: formatDate(profile?.last_login_at) })] })] })] }), levelData && ((0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { title: "Level Status", children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "CURRENT LEVEL" }), (0, jsx_runtime_1.jsx)("div", { className: `text-xl font-bold ${lvlInfo.color}`, children: lvlInfo.label }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: lvlInfo.description })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "HOLDINGS" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-white font-mono", children: ["S:", levelData.holdings?.station ?? 0, " M:", levelData.holdings?.matrix ?? 0, " Se:", levelData.holdings?.sector ?? 0, " Sy:", levelData.holdings?.system ?? 0] })] })] }), levelData.next_level_requirement && ((0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "PROGRESS" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white text-sm font-mono", children: [levelData.progress_percent ?? 0, "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "w-full bg-neutral-900 rounded-full h-2", children: (0, jsx_runtime_1.jsx)("div", { className: "h-2 rounded-full bg-cyan-400 transition-all", style: { width: `${Math.min(levelData.progress_percent ?? 0, 100)}%` } }) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-2", children: levelData.next_level_requirement.description || '' }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-neutral-400 mt-1", children: [levelData.next_level_requirement.have, "/", levelData.next_level_requirement.need, ' ', levelData.next_level_requirement.tier, " \u2192 ", ' ', (0, jsx_runtime_1.jsx)("span", { className: "text-white", children: levelData.next_rank })] })] }))] }) })), (0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { title: "Advanced Settings", children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-3 text-sm text-neutral-400", children: [(0, jsx_runtime_1.jsx)("p", { children: "Email verification, email change, two-factor authentication, and wallet management are available on the main portal:" }), (0, jsx_runtime_1.jsx)("a", { href: mainPortalSettingsUrl, className: "inline-block px-3 py-1.5 border border-cyan-800 text-cyan-400 hover:bg-cyan-950 rounded text-sm", children: "Open main portal settings \u2192" })] }) }), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-neutral-400 text-sm mb-3", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-cyan-400", children: "!" }), " Security"] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-1 text-sm text-neutral-400", children: [(0, jsx_runtime_1.jsx)("p", { children: "- Keep your account credentials secure" }), (0, jsx_runtime_1.jsx)("p", { children: "- Change password periodically via main portal" }), (0, jsx_runtime_1.jsx)("p", { children: "- Report suspicious activity immediately" }), (0, jsx_runtime_1.jsx)("p", { children: "- Verify your email to receive important notifications" })] })] })] }));
134
+ }
@@ -0,0 +1,5 @@
1
+ export interface MicrocosmQueueStatusPageProps {
2
+ basePath?: string;
3
+ onNavigate?: (path: string) => void;
4
+ }
5
+ export declare function MicrocosmQueueStatusPage({}?: MicrocosmQueueStatusPageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.MicrocosmQueueStatusPage = MicrocosmQueueStatusPage;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const auth_react_1 = require("@microcosmmoney/auth-react");
8
+ const terminal_1 = require("../terminal");
9
+ const LEVEL_LABELS = {
10
+ miner: 'Miner',
11
+ commander: 'Commander',
12
+ pioneer: 'Pioneer',
13
+ warden: 'Warden',
14
+ admiral: 'Admiral',
15
+ };
16
+ function MicrocosmQueueStatusPage({} = {}) {
17
+ const api = (0, auth_react_1.useMicrocosmApi)();
18
+ const [userQueue, setUserQueue] = (0, react_1.useState)(null);
19
+ const [loading, setLoading] = (0, react_1.useState)(true);
20
+ const [submitting, setSubmitting] = (0, react_1.useState)(false);
21
+ const [showJoinConfirm, setShowJoinConfirm] = (0, react_1.useState)(false);
22
+ const [showCancelConfirm, setShowCancelConfirm] = (0, react_1.useState)(false);
23
+ const [error, setError] = (0, react_1.useState)(null);
24
+ const loadStatus = (0, react_1.useCallback)(async () => {
25
+ setLoading(true);
26
+ setError(null);
27
+ try {
28
+ const res = await api.get('/territories/queue');
29
+ setUserQueue(res?.data ?? res);
30
+ }
31
+ catch (e) {
32
+ setError(e instanceof Error ? e.message : 'Failed to load queue status');
33
+ }
34
+ finally {
35
+ setLoading(false);
36
+ }
37
+ }, [api]);
38
+ (0, react_1.useEffect)(() => { loadStatus(); }, [loadStatus]);
39
+ const handleJoin = async () => {
40
+ setSubmitting(true);
41
+ try {
42
+ await api.post('/territories/queue', {});
43
+ setShowJoinConfirm(false);
44
+ await loadStatus();
45
+ }
46
+ catch (e) {
47
+ setError(e instanceof Error ? e.message : 'Failed to join queue');
48
+ }
49
+ finally {
50
+ setSubmitting(false);
51
+ }
52
+ };
53
+ const handleCancel = async () => {
54
+ setSubmitting(true);
55
+ try {
56
+ await api.delete('/territories/queue');
57
+ setShowCancelConfirm(false);
58
+ await loadStatus();
59
+ }
60
+ catch (e) {
61
+ setError(e instanceof Error ? e.message : 'Failed to cancel queue');
62
+ }
63
+ finally {
64
+ setSubmitting(false);
65
+ }
66
+ };
67
+ if (loading) {
68
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto px-3 py-4 space-y-3 xs:px-4 xs:space-y-4 sm:px-6 sm:py-6 sm:space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-lg sm:text-2xl font-bold text-white tracking-wider", children: "Station Queue" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs sm:text-sm text-neutral-400", children: "Your onboarding status" })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center py-20", children: (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Loading..." }) })] }));
69
+ }
70
+ const level = userQueue?.user_type?.toLowerCase() || 'miner';
71
+ const levelLabel = LEVEL_LABELS[level] || 'Miner';
72
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto px-3 py-4 space-y-3 xs:px-4 xs:space-y-4 sm:px-6 sm:py-6 sm:space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-lg sm:text-2xl font-bold text-white tracking-wider", children: "Station Queue" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs sm:text-sm text-neutral-400", children: "Your onboarding status" })] }), (0, jsx_runtime_1.jsx)("button", { onClick: loadStatus, className: "px-3 py-1.5 text-xs border border-neutral-700 text-neutral-400 hover:bg-neutral-800 hover:text-white rounded transition-colors", children: "Refresh" })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-red-900/20 border border-red-800 rounded text-sm text-red-300", children: error })), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-neutral-800 rounded mb-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "CURRENT LEVEL" }), (0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-cyan-300", children: levelLabel })] }), (0, jsx_runtime_1.jsxs)("span", { className: "px-3 py-1 bg-cyan-900/30 text-cyan-300 rounded border border-cyan-700 text-xs", children: ["Lv.", Object.keys(LEVEL_LABELS).indexOf(level) + 3] })] }) }), userQueue?.is_onboarded ? ((0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-950 rounded-lg p-6 border border-neutral-700", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-semibold text-white", children: "Onboarded" }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-neutral-400", children: ["Assigned to ", userQueue.station_name || userQueue.territory_id || ''] })] }), (0, jsx_runtime_1.jsx)("span", { className: "px-2 py-1 bg-white/20 text-white rounded text-xs", children: "Active" })] }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 gap-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 rounded-lg p-4 text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-xl font-bold text-white", children: userQueue.territory_id }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "Territory ID" })] }) })] })) : userQueue?.in_queue ? ((0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-950 rounded-lg p-6 border border-neutral-800", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-semibold text-white", children: "In Queue" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "Waiting for station assignment" })] }), userQueue.status && ((0, jsx_runtime_1.jsx)("span", { className: "px-2 py-1 bg-cyan-900/30 text-cyan-300 rounded text-xs", children: userQueue.status }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-3xl font-bold text-white", children: userQueue.position || '-' }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-400", children: "Position" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-3xl font-bold text-white", children: userQueue.estimated_wait_minutes || '-' }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-400", children: "Est. wait (min)" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-semibold text-white truncate", children: userQueue.preferred_territory_id || 'Auto' }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-400", children: "Preferred" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-semibold text-white", children: userQueue.joined_at ? new Date(userQueue.joined_at).toLocaleString() : '-' }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-neutral-400", children: "Joined at" })] })] }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setShowCancelConfirm(true), disabled: submitting, className: "w-full py-2 bg-neutral-800 hover:bg-neutral-700 text-white rounded transition-colors disabled:opacity-50", children: "Cancel Queue" })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-950 rounded-lg p-6 border border-neutral-800", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-semibold text-white", children: "Not Onboarded" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "Join the queue to be assigned a station" })] }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setShowJoinConfirm(true), disabled: submitting, className: "w-full py-2 bg-white/20 hover:bg-neutral-800 text-white border border-neutral-700 rounded transition-colors disabled:opacity-50", children: "Join Queue" })] }))] }), showJoinConfirm && ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4", onClick: () => setShowJoinConfirm(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg w-full max-w-md p-6 space-y-4", onClick: e => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-white font-medium", children: "Confirm Join Queue" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "You will be added to the station queue. Auto-assignment happens when a slot opens." }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setShowJoinConfirm(false), className: "flex-1 px-3 py-2 border border-neutral-700 text-neutral-400 hover:bg-neutral-800 rounded text-sm", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleJoin, disabled: submitting, className: "flex-1 px-3 py-2 bg-cyan-700 hover:bg-cyan-600 text-white rounded text-sm disabled:opacity-50", children: submitting ? 'Joining...' : 'Confirm' })] })] }) })), showCancelConfirm && ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4", onClick: () => setShowCancelConfirm(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg w-full max-w-md p-6 space-y-4", onClick: e => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-white font-medium", children: "Cancel Queue" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "Are you sure? You will lose your current queue position." }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setShowCancelConfirm(false), className: "flex-1 px-3 py-2 border border-neutral-700 text-neutral-400 hover:bg-neutral-800 rounded text-sm", children: "Keep Waiting" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleCancel, disabled: submitting, className: "flex-1 px-3 py-2 bg-red-700 hover:bg-red-600 text-white rounded text-sm disabled:opacity-50", children: submitting ? 'Cancelling...' : 'Confirm Cancel' })] })] }) }))] }));
73
+ }
@@ -0,0 +1,5 @@
1
+ export interface MicrocosmRewardsPageProps {
2
+ basePath?: string;
3
+ onNavigate?: (path: string) => void;
4
+ }
5
+ export declare function MicrocosmRewardsPage({}?: MicrocosmRewardsPageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.MicrocosmRewardsPage = MicrocosmRewardsPage;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const auth_react_1 = require("@microcosmmoney/auth-react");
8
+ const terminal_1 = require("../terminal");
9
+ function MicrocosmRewardsPage({} = {}) {
10
+ const api = (0, auth_react_1.useMicrocosmApi)();
11
+ const [history, setHistory] = (0, react_1.useState)([]);
12
+ const [stats, setStats] = (0, react_1.useState)(null);
13
+ const [topRecipients, setTopRecipients] = (0, react_1.useState)([]);
14
+ const [topSenders, setTopSenders] = (0, react_1.useState)([]);
15
+ const [loading, setLoading] = (0, react_1.useState)(true);
16
+ const [tab, setTab] = (0, react_1.useState)('history');
17
+ const [showSendDialog, setShowSendDialog] = (0, react_1.useState)(false);
18
+ const [showBatchDialog, setShowBatchDialog] = (0, react_1.useState)(false);
19
+ const [error, setError] = (0, react_1.useState)(null);
20
+ const [submitting, setSubmitting] = (0, react_1.useState)(false);
21
+ const [singleReward, setSingleReward] = (0, react_1.useState)({ recipient_id: '', amount: '', reason: '' });
22
+ const [batchRewards, setBatchRewards] = (0, react_1.useState)([{ user_id: '', amount: '', reason: '' }]);
23
+ const loadData = (0, react_1.useCallback)(async () => {
24
+ setLoading(true);
25
+ setError(null);
26
+ try {
27
+ try {
28
+ const historyRes = await api.get('/territories/rewards/history?limit=50');
29
+ const data = historyRes?.data ?? historyRes;
30
+ setHistory(Array.isArray(data?.rewards) ? data.rewards : Array.isArray(data) ? data : []);
31
+ setStats(data?.stats || historyRes?.stats || null);
32
+ }
33
+ catch {
34
+ setHistory([]);
35
+ }
36
+ try {
37
+ const lbRes = await api.get('/territories/rewards/leaderboard?period=month&limit=10');
38
+ const data = lbRes?.data ?? lbRes;
39
+ setTopRecipients(Array.isArray(data?.top_recipients) ? data.top_recipients : []);
40
+ setTopSenders(Array.isArray(data?.top_senders) ? data.top_senders : []);
41
+ }
42
+ catch { }
43
+ }
44
+ finally {
45
+ setLoading(false);
46
+ }
47
+ }, [api]);
48
+ (0, react_1.useEffect)(() => { loadData(); }, [loadData]);
49
+ const handleSendReward = async () => {
50
+ if (!singleReward.recipient_id || !singleReward.amount) {
51
+ setError('Fill recipient and amount');
52
+ return;
53
+ }
54
+ setSubmitting(true);
55
+ setError(null);
56
+ try {
57
+ await api.post('/territories/rewards/send', {
58
+ recipient_id: singleReward.recipient_id,
59
+ amount: parseFloat(singleReward.amount),
60
+ reason: singleReward.reason,
61
+ });
62
+ setShowSendDialog(false);
63
+ setSingleReward({ recipient_id: '', amount: '', reason: '' });
64
+ await loadData();
65
+ }
66
+ catch (e) {
67
+ setError(e instanceof Error ? e.message : 'Send failed');
68
+ }
69
+ finally {
70
+ setSubmitting(false);
71
+ }
72
+ };
73
+ const handleBatchReward = async () => {
74
+ const valid = batchRewards.filter(r => r.user_id && r.amount);
75
+ if (valid.length === 0) {
76
+ setError('Add at least one valid reward');
77
+ return;
78
+ }
79
+ setSubmitting(true);
80
+ setError(null);
81
+ try {
82
+ await api.post('/territories/rewards/batch', {
83
+ rewards: valid.map(r => ({
84
+ user_id: r.user_id,
85
+ amount: parseFloat(r.amount),
86
+ reason: r.reason,
87
+ })),
88
+ });
89
+ setShowBatchDialog(false);
90
+ setBatchRewards([{ user_id: '', amount: '', reason: '' }]);
91
+ await loadData();
92
+ }
93
+ catch (e) {
94
+ setError(e instanceof Error ? e.message : 'Batch failed');
95
+ }
96
+ finally {
97
+ setSubmitting(false);
98
+ }
99
+ };
100
+ const formatDate = (dateStr) => {
101
+ try {
102
+ return new Date(dateStr).toLocaleString();
103
+ }
104
+ catch {
105
+ return '-';
106
+ }
107
+ };
108
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto px-3 py-4 space-y-3 xs:px-4 xs:space-y-4 sm:px-6 sm:py-6 sm:space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between flex-wrap gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-lg sm:text-2xl font-bold text-white tracking-wider", children: "Rewards" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs sm:text-sm text-neutral-400", children: "Send MCC rewards to your territory members" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setShowSendDialog(true), className: "px-3 py-1.5 bg-cyan-700 hover:bg-cyan-600 text-white rounded text-sm", children: "Send Reward" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setShowBatchDialog(true), className: "px-3 py-1.5 border border-neutral-700 text-neutral-400 hover:bg-neutral-800 hover:text-white rounded text-sm", children: "Batch Send" })] })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-red-900/20 border border-red-800 rounded text-sm text-red-300", children: error })), stats && ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [(0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "MCC RECEIVED" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white", children: Number(stats.total_received || 0).toFixed(2) })] }), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "MCC SENT" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-cyan-400", children: Number(stats.total_sent || 0).toFixed(2) })] }), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "RECEIVE COUNT" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white", children: stats.receive_count })] }), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "SEND COUNT" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white", children: stats.send_count })] })] })), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2 border-b border-neutral-800", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setTab('history'), className: `px-4 py-2 text-sm ${tab === 'history' ? 'text-white border-b-2 border-cyan-400' : 'text-neutral-400 hover:text-white'}`, children: "History" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setTab('leaderboard'), className: `px-4 py-2 text-sm ${tab === 'leaderboard' ? 'text-white border-b-2 border-cyan-400' : 'text-neutral-400 hover:text-white'}`, children: "Leaderboard" })] }), tab === 'history' && ((0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { title: "Reward History", children: loading ? ((0, jsx_runtime_1.jsx)("div", { className: "text-center py-8 text-neutral-500", children: "Loading..." })) : history.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "text-center py-8 text-neutral-500", children: "No records" })) : ((0, jsx_runtime_1.jsx)("div", { className: "overflow-x-auto", children: (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: "text-xs text-neutral-400 border-b border-neutral-800", children: [(0, jsx_runtime_1.jsx)("th", { className: "text-left py-2", children: "Time" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left py-2", children: "Sender" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left py-2", children: "Recipient" }), (0, jsx_runtime_1.jsx)("th", { className: "text-right py-2", children: "Amount" }), (0, jsx_runtime_1.jsx)("th", { className: "text-left py-2", children: "Reason" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: history.map(record => ((0, jsx_runtime_1.jsxs)("tr", { className: "border-b border-neutral-800", children: [(0, jsx_runtime_1.jsx)("td", { className: "py-2 text-xs text-neutral-500", children: formatDate(record.created_at) }), (0, jsx_runtime_1.jsx)("td", { className: "py-2 text-neutral-300", children: record.manager_email || record.manager_id }), (0, jsx_runtime_1.jsx)("td", { className: "py-2 text-neutral-300", children: record.recipient_email || record.recipient_id }), (0, jsx_runtime_1.jsxs)("td", { className: "py-2 text-right text-white", children: ["+", Number(record.amount || 0).toFixed(2), " MCC"] }), (0, jsx_runtime_1.jsx)("td", { className: "py-2 text-xs text-neutral-500", children: record.reason || '-' })] }, record.id))) })] }) })) })), tab === 'leaderboard' && ((0, jsx_runtime_1.jsxs)("div", { className: "grid md:grid-cols-2 gap-6", children: [(0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { title: "Top Recipients", children: topRecipients.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "text-center py-4 text-neutral-500", children: "No data" })) : ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: topRecipients.map((entry, idx) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("span", { className: `w-6 h-6 flex items-center justify-center rounded text-xs ${idx < 3 ? 'bg-cyan-400/20 text-cyan-300' : 'bg-neutral-800 text-neutral-400'}`, children: idx + 1 }), (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-300", children: entry.email || entry.user_id })] }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white", children: [Number(entry.total_received || 0).toFixed(2), " MCC"] })] }, entry.user_id))) })) }), (0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { title: "Top Senders", children: topSenders.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "text-center py-4 text-neutral-500", children: "No data" })) : ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: topSenders.map((entry, idx) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("span", { className: `w-6 h-6 flex items-center justify-center rounded text-xs ${idx < 3 ? 'bg-cyan-400/20 text-cyan-300' : 'bg-neutral-800 text-neutral-400'}`, children: idx + 1 }), (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-300", children: entry.email || entry.user_id })] }), (0, jsx_runtime_1.jsxs)("span", { className: "text-cyan-400", children: [Number(entry.total_sent || 0).toFixed(2), " MCC"] })] }, entry.user_id))) })) })] })), showSendDialog && ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4", onClick: () => setShowSendDialog(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg w-full max-w-md p-6 space-y-4", onClick: e => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-white font-medium", children: "Send Single Reward" }), (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: "Recipient UID" }), (0, jsx_runtime_1.jsx)("input", { type: "text", value: singleReward.recipient_id, onChange: (e) => setSingleReward({ ...singleReward, recipient_id: e.target.value }), className: "w-full bg-neutral-800 border border-neutral-600 text-white rounded px-3 py-2 text-sm", placeholder: "uid" })] }), (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: "Amount (MCC)" }), (0, jsx_runtime_1.jsx)("input", { type: "number", min: 1, max: 10000, value: singleReward.amount, onChange: (e) => setSingleReward({ ...singleReward, amount: e.target.value }), className: "w-full bg-neutral-800 border border-neutral-600 text-white rounded px-3 py-2 text-sm", placeholder: "1-10000" })] }), (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: "Reason" }), (0, jsx_runtime_1.jsx)("textarea", { value: singleReward.reason, onChange: (e) => setSingleReward({ ...singleReward, reason: e.target.value }), rows: 2, className: "w-full bg-neutral-800 border border-neutral-600 text-white rounded px-3 py-2 text-sm" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setShowSendDialog(false), className: "flex-1 px-3 py-2 border border-neutral-700 text-neutral-400 hover:bg-neutral-800 rounded text-sm", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleSendReward, disabled: submitting, className: "flex-1 px-3 py-2 bg-cyan-700 hover:bg-cyan-600 text-white rounded text-sm disabled:opacity-50", children: submitting ? 'Sending...' : 'Send' })] })] }) })), showBatchDialog && ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4", onClick: () => setShowBatchDialog(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg w-full max-w-2xl p-6 space-y-4", onClick: e => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-white font-medium", children: "Batch Reward Send" }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2 max-h-96 overflow-y-auto", children: [batchRewards.map((r, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2 items-start", children: [(0, jsx_runtime_1.jsx)("input", { value: r.user_id, onChange: (e) => {
109
+ const next = [...batchRewards];
110
+ next[i] = { ...next[i], user_id: e.target.value };
111
+ setBatchRewards(next);
112
+ }, placeholder: "User UID", className: "flex-1 bg-neutral-800 border border-neutral-600 text-white rounded px-2 py-1.5 text-sm" }), (0, jsx_runtime_1.jsx)("input", { type: "number", value: r.amount, onChange: (e) => {
113
+ const next = [...batchRewards];
114
+ next[i] = { ...next[i], amount: e.target.value };
115
+ setBatchRewards(next);
116
+ }, placeholder: "MCC", className: "w-24 bg-neutral-800 border border-neutral-600 text-white rounded px-2 py-1.5 text-sm" }), (0, jsx_runtime_1.jsx)("input", { value: r.reason, onChange: (e) => {
117
+ const next = [...batchRewards];
118
+ next[i] = { ...next[i], reason: e.target.value };
119
+ setBatchRewards(next);
120
+ }, placeholder: "reason", className: "flex-1 bg-neutral-800 border border-neutral-600 text-white rounded px-2 py-1.5 text-sm" }), batchRewards.length > 1 && ((0, jsx_runtime_1.jsx)("button", { onClick: () => setBatchRewards(batchRewards.filter((_, idx) => idx !== i)), className: "px-2 py-1 text-red-400 hover:bg-red-900/20 rounded text-sm", children: "\u00D7" }))] }, i))), (0, jsx_runtime_1.jsx)("button", { onClick: () => setBatchRewards([...batchRewards, { user_id: '', amount: '', reason: '' }]), className: "w-full px-3 py-2 border border-dashed border-neutral-700 text-neutral-400 hover:bg-neutral-800 rounded text-sm", children: "+ Add row" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setShowBatchDialog(false), className: "flex-1 px-3 py-2 border border-neutral-700 text-neutral-400 hover:bg-neutral-800 rounded text-sm", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleBatchReward, disabled: submitting, className: "flex-1 px-3 py-2 bg-cyan-700 hover:bg-cyan-600 text-white rounded text-sm disabled:opacity-50", children: submitting ? 'Sending...' : 'Batch Send' })] })] }) }))] }));
121
+ }
@@ -0,0 +1,6 @@
1
+ export interface MicrocosmStationListPageProps {
2
+ basePath?: string;
3
+ onNavigate?: (path: string) => void;
4
+ currentUid?: string;
5
+ }
6
+ export declare function MicrocosmStationListPage({ currentUid }?: MicrocosmStationListPageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.MicrocosmStationListPage = MicrocosmStationListPage;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const auth_react_1 = require("@microcosmmoney/auth-react");
8
+ const terminal_1 = require("../terminal");
9
+ const UNIT_LABELS = { station: 'Station', matrix: 'Matrix', sector: 'Sector', system: 'System' };
10
+ const MAGISTRATE_TITLES = { station: 'Commander', matrix: 'Pioneer', sector: 'Warden', system: 'Admiral' };
11
+ function MicrocosmStationListPage({ currentUid } = {}) {
12
+ const api = (0, auth_react_1.useMicrocosmApi)();
13
+ const [units, setUnits] = (0, react_1.useState)([]);
14
+ const [summary, setSummary] = (0, react_1.useState)(null);
15
+ const [loading, setLoading] = (0, react_1.useState)(true);
16
+ const [filter, setFilter] = (0, react_1.useState)('all');
17
+ const [searchTerm, setSearchTerm] = (0, react_1.useState)('');
18
+ const [unitDataCache, setUnitDataCache] = (0, react_1.useState)({});
19
+ const [editingUnit, setEditingUnit] = (0, react_1.useState)(null);
20
+ const [editFormData, setEditFormData] = (0, react_1.useState)({ unit_name: '', description: '' });
21
+ const [submitting, setSubmitting] = (0, react_1.useState)(false);
22
+ const [error, setError] = (0, react_1.useState)(null);
23
+ const loadSummary = (0, react_1.useCallback)(async () => {
24
+ try {
25
+ const res = await api.get('/territories/summary');
26
+ setSummary(res?.data ?? res);
27
+ }
28
+ catch { }
29
+ }, [api]);
30
+ const loadUnits = (0, react_1.useCallback)(async () => {
31
+ setLoading(true);
32
+ setError(null);
33
+ try {
34
+ const params = filter !== 'all' ? `?unit_type=${filter}` : '';
35
+ const res = await api.get(`/territories${params}`);
36
+ const list = res?.data ?? res ?? [];
37
+ setUnits(Array.isArray(list) ? list : []);
38
+ for (const unit of list) {
39
+ loadUnitData(unit.unit_id);
40
+ }
41
+ }
42
+ catch (e) {
43
+ setError(e instanceof Error ? e.message : 'Failed to load units');
44
+ }
45
+ finally {
46
+ setLoading(false);
47
+ }
48
+ }, [api, filter]);
49
+ const loadUnitData = async (unitId) => {
50
+ try {
51
+ const res = await api.get(`/territories/${unitId}/detailed-stats`);
52
+ const metrics = res?.data?.metrics;
53
+ if (metrics) {
54
+ setUnitDataCache(prev => ({ ...prev, [unitId]: metrics }));
55
+ }
56
+ }
57
+ catch { }
58
+ };
59
+ (0, react_1.useEffect)(() => { loadUnits(); loadSummary(); }, [loadUnits, loadSummary]);
60
+ const filteredUnits = units.filter(unit => {
61
+ const q = searchTerm.toLowerCase();
62
+ if (!q)
63
+ return true;
64
+ return (unit.unit_name?.toLowerCase().includes(q) ||
65
+ unit.description?.toLowerCase().includes(q) ||
66
+ unit.location?.toLowerCase().includes(q) ||
67
+ unit.short_id?.toLowerCase().includes(q) ||
68
+ unit.full_path?.toLowerCase().includes(q));
69
+ });
70
+ const canEditUnit = (unit) => {
71
+ if (!currentUid)
72
+ return false;
73
+ return unit.manager_uid === currentUid || unit.manager_id === currentUid;
74
+ };
75
+ const openEditDialog = (unit) => {
76
+ setEditingUnit(unit);
77
+ setEditFormData({ unit_name: unit.unit_name, description: unit.description || '' });
78
+ };
79
+ const handleEdit = async () => {
80
+ if (!editingUnit || !editFormData.unit_name.trim())
81
+ return;
82
+ setSubmitting(true);
83
+ try {
84
+ await api.put(`/territories/${editingUnit.unit_id}`, {
85
+ unit_name: editFormData.unit_name,
86
+ description: editFormData.description,
87
+ });
88
+ setEditingUnit(null);
89
+ await loadUnits();
90
+ }
91
+ catch (e) {
92
+ setError(e instanceof Error ? e.message : 'Failed to update unit');
93
+ }
94
+ finally {
95
+ setSubmitting(false);
96
+ }
97
+ };
98
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto px-3 py-4 space-y-3 xs:px-4 xs:space-y-4 sm:px-6 sm:py-6 sm:space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-lg sm:text-2xl font-bold text-white tracking-wider", children: "Station List" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs sm:text-sm text-neutral-400", children: "All territories (Station / Matrix / Sector / System)" })] }), error && ((0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-red-900/20 border border-red-800 rounded text-sm text-red-300", children: error })), summary && ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [(0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "TOTAL STATIONS" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white", children: summary.total_stations ?? 0 })] }), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "TOTAL MEMBERS" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-cyan-400", children: summary.total_members ?? 0 })] }), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "VAULT MCD" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-cyan-400", children: (summary.total_vault_mcd ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 }) })] }), (0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "AVG KPI" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white", children: (summary.avg_kpi_score ?? 0).toFixed(1) })] })] })), (0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { children: (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col md:flex-row gap-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex gap-2 flex-wrap", children: ['all', 'station', 'matrix', 'sector', 'system'].map(f => ((0, jsx_runtime_1.jsx)("button", { onClick: () => setFilter(f), className: `px-3 py-1.5 text-sm rounded transition-colors ${filter === f ? 'bg-cyan-700 text-white' : 'bg-neutral-800 text-neutral-400 hover:bg-neutral-700 hover:text-white'}`, children: f === 'all' ? 'All' : UNIT_LABELS[f] }, f))) }), (0, jsx_runtime_1.jsx)("input", { type: "text", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), placeholder: "Search by name / location / short_id...", className: "flex-1 bg-neutral-800 border border-neutral-600 text-white rounded px-3 py-1.5 text-sm focus:outline-none focus:border-cyan-400" })] }) }), loading ? ((0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center py-20", children: (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Loading units..." }) })) : filteredUnits.length === 0 ? ((0, jsx_runtime_1.jsx)(terminal_1.TerminalCard, { children: (0, jsx_runtime_1.jsx)("div", { className: "text-center py-8 text-neutral-500", children: "No units match your filter" }) })) : ((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: filteredUnits.map(unit => {
99
+ const metrics = unitDataCache[unit.unit_id];
100
+ return ((0, jsx_runtime_1.jsxs)(terminal_1.TerminalCard, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between mb-3", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-white font-semibold", children: unit.unit_name }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-neutral-500", children: [UNIT_LABELS[unit.unit_type], " \u00B7 ", MAGISTRATE_TITLES[unit.unit_type]] }), unit.short_id && ((0, jsx_runtime_1.jsx)("div", { className: "text-xs font-mono text-cyan-400 mt-1", children: unit.short_id }))] }), canEditUnit(unit) && ((0, jsx_runtime_1.jsx)("button", { onClick: () => openEditDialog(unit), className: "text-xs px-2 py-1 border border-neutral-700 text-neutral-400 hover:text-white hover:bg-neutral-800 rounded", children: "Edit" }))] }), unit.description && ((0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 mb-3 line-clamp-2", children: unit.description })), metrics && ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-2 pt-3 border-t border-neutral-700", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Members" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-sm text-white", children: [metrics.member_count, "/", metrics.max_capacity] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Occupancy" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-sm text-cyan-400", children: [(metrics.occupancy_rate * 100).toFixed(0), "%"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "col-span-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Vault MCD" }), (0, jsx_runtime_1.jsx)("div", { className: "text-sm text-white", children: (metrics.vault_mcd ?? 0).toLocaleString(undefined, { maximumFractionDigits: 0 }) })] })] })), unit.image_status === 'pending' && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2 text-xs text-yellow-400", children: "Image pending review" }))] }, unit.unit_id));
101
+ }) })), editingUnit && ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4", onClick: () => setEditingUnit(null), children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg w-full max-w-lg p-6 space-y-4", onClick: e => e.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("h3", { className: "text-white font-medium", children: ["Edit ", editingUnit.unit_name] }), (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: "Name" }), (0, jsx_runtime_1.jsx)("input", { type: "text", value: editFormData.unit_name, onChange: (e) => setEditFormData({ ...editFormData, unit_name: e.target.value }), className: "w-full bg-neutral-800 border border-neutral-600 text-white rounded px-3 py-2 text-sm" })] }), (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: editFormData.description, onChange: (e) => setEditFormData({ ...editFormData, description: e.target.value }), rows: 4, className: "w-full bg-neutral-800 border border-neutral-600 text-white rounded px-3 py-2 text-sm" })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500", children: "Note: image upload available on main portal only" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setEditingUnit(null), className: "flex-1 px-3 py-2 border border-neutral-700 text-neutral-400 hover:bg-neutral-800 rounded text-sm", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleEdit, disabled: submitting || !editFormData.unit_name.trim(), className: "flex-1 px-3 py-2 bg-cyan-700 hover:bg-cyan-600 text-white rounded text-sm disabled:opacity-50", children: submitting ? 'Saving...' : 'Save' })] })] }) }))] }));
102
+ }
package/dist/index.d.ts CHANGED
@@ -69,3 +69,13 @@ export { MicrocosmVotingPage } from './components/voting/voting-page';
69
69
  export type { MicrocosmVotingPageProps } from './components/voting/voting-page';
70
70
  export { MicrocosmReincarnationPage } from './components/reincarnation/reincarnation-page';
71
71
  export type { MicrocosmReincarnationPageProps } from './components/reincarnation/reincarnation-page';
72
+ export { MicrocosmManagerIncomePage } from './components/income/manager-income-page';
73
+ export type { MicrocosmManagerIncomePageProps } from './components/income/manager-income-page';
74
+ export { MicrocosmQueueStatusPage } from './components/queue/queue-status-page';
75
+ export type { MicrocosmQueueStatusPageProps } from './components/queue/queue-status-page';
76
+ export { MicrocosmStationListPage } from './components/stations/station-list-page';
77
+ export type { MicrocosmStationListPageProps } from './components/stations/station-list-page';
78
+ export { MicrocosmRewardsPage } from './components/rewards/rewards-page';
79
+ export type { MicrocosmRewardsPageProps } from './components/rewards/rewards-page';
80
+ export { MicrocosmProfilePage } from './components/profile/profile-page';
81
+ export type { MicrocosmProfilePageProps } from './components/profile/profile-page';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MicrocosmTerritoryPage = exports.MicrocosmAuctionPage = exports.MicrocosmMCDPage = exports.MicrocosmMiningPage = exports.MicrocosmWalletPage = exports.MicrocosmLendingPage = exports.MicrocosmFragmentPage = exports.MicrocosmLockPeriods = exports.MicrocosmMCDStats = exports.MicrocosmMCCTokenStats = exports.MicrocosmEcosystemStats = exports.MicrocosmMyMining = exports.MicrocosmMiningWeight = exports.MicrocosmMintingStats = exports.MicrocosmPriceChart = exports.MicrocosmAssetsSummary = exports.MicrocosmQuickActions = exports.MicrocosmMarketBar = exports.MicrocosmDashboardOverview = exports.KPIRadialChart = exports.VoteResultBar = exports.MiningProgressBar = exports.TerritoryCard = exports.TerminalTooltip = exports.TerminalInput = exports.TerminalCountdown = exports.TerminalDialog = exports.TerminalTabs = exports.TerminalTable = exports.TerminalProgress = exports.TerminalDataRow = exports.TerminalBadge = exports.TerminalEmpty = exports.TerminalError = exports.TerminalLoading = exports.TerminalPageHeader = exports.TerminalCommand = exports.StatBox = exports.TerminalCard = exports.getMenuDescription = exports.getMenuTitle = exports.resolveMenuPath = exports.getAllMenuItems = exports.microcosmMenuGroups = exports.dashboardMenu = exports.web3OsMenu = exports.blockchainMenu = exports.useLinkComponent = exports.LinkProvider = exports.MicrocosmMenuSection = void 0;
4
- exports.MicrocosmReincarnationPage = exports.MicrocosmVotingPage = exports.MicrocosmOrganizationPage = void 0;
4
+ exports.MicrocosmProfilePage = exports.MicrocosmRewardsPage = exports.MicrocosmStationListPage = exports.MicrocosmQueueStatusPage = exports.MicrocosmManagerIncomePage = exports.MicrocosmReincarnationPage = exports.MicrocosmVotingPage = exports.MicrocosmOrganizationPage = void 0;
5
5
  var menu_section_1 = require("./components/menu-section");
6
6
  Object.defineProperty(exports, "MicrocosmMenuSection", { enumerable: true, get: function () { return menu_section_1.MicrocosmMenuSection; } });
7
7
  var link_context_1 = require("./link-context");
@@ -91,3 +91,13 @@ var voting_page_1 = require("./components/voting/voting-page");
91
91
  Object.defineProperty(exports, "MicrocosmVotingPage", { enumerable: true, get: function () { return voting_page_1.MicrocosmVotingPage; } });
92
92
  var reincarnation_page_1 = require("./components/reincarnation/reincarnation-page");
93
93
  Object.defineProperty(exports, "MicrocosmReincarnationPage", { enumerable: true, get: function () { return reincarnation_page_1.MicrocosmReincarnationPage; } });
94
+ var manager_income_page_1 = require("./components/income/manager-income-page");
95
+ Object.defineProperty(exports, "MicrocosmManagerIncomePage", { enumerable: true, get: function () { return manager_income_page_1.MicrocosmManagerIncomePage; } });
96
+ var queue_status_page_1 = require("./components/queue/queue-status-page");
97
+ Object.defineProperty(exports, "MicrocosmQueueStatusPage", { enumerable: true, get: function () { return queue_status_page_1.MicrocosmQueueStatusPage; } });
98
+ var station_list_page_1 = require("./components/stations/station-list-page");
99
+ Object.defineProperty(exports, "MicrocosmStationListPage", { enumerable: true, get: function () { return station_list_page_1.MicrocosmStationListPage; } });
100
+ var rewards_page_1 = require("./components/rewards/rewards-page");
101
+ Object.defineProperty(exports, "MicrocosmRewardsPage", { enumerable: true, get: function () { return rewards_page_1.MicrocosmRewardsPage; } });
102
+ var profile_page_1 = require("./components/profile/profile-page");
103
+ Object.defineProperty(exports, "MicrocosmProfilePage", { enumerable: true, get: function () { return profile_page_1.MicrocosmProfilePage; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microcosmmoney/portal-react",
3
- "version": "3.5.1",
3
+ "version": "3.6.0",
4
4
  "description": "Microcosm Portal UI components for React/Next.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@microcosmmoney/auth-core": "^2.3.2",
22
- "@microcosmmoney/auth-react": "^2.3.2"
22
+ "@microcosmmoney/auth-react": "^2.4.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/react": "^18.0.0",