mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-11 03:36:37 +08:00
feat: node management enhancements and UX improvements (#297)
* feat(nodes): add renewal cycle and auto-advance scheduling - Add renewal_cycle field to nodes for tracking paid vs free cycles - Implement auto-advance scheduling when renewal is processed - Add migration test for renewal_cycle column - Update dashboard to show renewal cycle count - Add renewal status display on node detail page * fix(frontend): show rule count in compact view mode for forward pages * style(frontend): format code and improve node card layout Entire-Checkpoint: 08b760b76538 * fix: restore renewal.ts from main to resolve build errors
This commit is contained in:
@@ -231,14 +231,20 @@ const normalizeNodeExpiryReminders = (items: NodeApiItem[]) => {
|
||||
}))
|
||||
.filter((item) => {
|
||||
if (!item.expiryTime || !item.renewalCycle) return false;
|
||||
const snapshot = getNodeRenewalSnapshot(item.expiryTime, item.renewalCycle);
|
||||
const snapshot = getNodeRenewalSnapshot(
|
||||
item.expiryTime,
|
||||
item.renewalCycle,
|
||||
);
|
||||
|
||||
if (!snapshot.nextDueTime) return false;
|
||||
|
||||
return snapshot.nextDueTime <= now + warningWindowMs;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const aDue = getNodeRenewalSnapshot(a.expiryTime, a.renewalCycle).nextDueTime || 0;
|
||||
const bDue = getNodeRenewalSnapshot(b.expiryTime, b.renewalCycle).nextDueTime || 0;
|
||||
const aDue =
|
||||
getNodeRenewalSnapshot(a.expiryTime, a.renewalCycle).nextDueTime || 0;
|
||||
const bDue =
|
||||
getNodeRenewalSnapshot(b.expiryTime, b.renewalCycle).nextDueTime || 0;
|
||||
|
||||
return aDue - bDue;
|
||||
});
|
||||
@@ -265,31 +271,34 @@ export const useDashboardData = (): DashboardDataState => {
|
||||
const packageRequestInFlightRef = useRef(false);
|
||||
const nodeExpiryRequestInFlightRef = useRef(false);
|
||||
|
||||
const applyPackageData = useCallback((data: {
|
||||
userInfo?: DashboardUserInfo;
|
||||
tunnelPermissions?: DashboardUserTunnel[];
|
||||
forwards?: ForwardApiItem[];
|
||||
statisticsFlows?: DashboardStatisticsFlow[];
|
||||
}) => {
|
||||
const normalizedTunnelPermissions = normalizeTunnelPermissions(
|
||||
data.tunnelPermissions || [],
|
||||
);
|
||||
const normalizedForwards = normalizeForwards(data.forwards || []);
|
||||
const applyPackageData = useCallback(
|
||||
(data: {
|
||||
userInfo?: DashboardUserInfo;
|
||||
tunnelPermissions?: DashboardUserTunnel[];
|
||||
forwards?: ForwardApiItem[];
|
||||
statisticsFlows?: DashboardStatisticsFlow[];
|
||||
}) => {
|
||||
const normalizedTunnelPermissions = normalizeTunnelPermissions(
|
||||
data.tunnelPermissions || [],
|
||||
);
|
||||
const normalizedForwards = normalizeForwards(data.forwards || []);
|
||||
|
||||
if (!isMountedRef.current) {
|
||||
return;
|
||||
}
|
||||
if (!isMountedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setUserInfo(data.userInfo || ({} as DashboardUserInfo));
|
||||
setUserTunnels(normalizedTunnelPermissions);
|
||||
setForwardList(normalizedForwards);
|
||||
setStatisticsFlows(data.statisticsFlows || []);
|
||||
setUserInfo(data.userInfo || ({} as DashboardUserInfo));
|
||||
setUserTunnels(normalizedTunnelPermissions);
|
||||
setForwardList(normalizedForwards);
|
||||
setStatisticsFlows(data.statisticsFlows || []);
|
||||
|
||||
checkExpirationNotifications(
|
||||
data.userInfo || ({} as DashboardUserInfo),
|
||||
normalizedTunnelPermissions,
|
||||
);
|
||||
}, []);
|
||||
checkExpirationNotifications(
|
||||
data.userInfo || ({} as DashboardUserInfo),
|
||||
normalizedTunnelPermissions,
|
||||
);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const loadPackageData = useCallback(
|
||||
async ({ silent = false, notifyOnError = false } = {}) => {
|
||||
|
||||
Reference in New Issue
Block a user