mirror of
https://github.com/hansjone/netx.git
synced 2026-10-15 12:13:21 +08:00
Refactor web UI: feature-first structure, tokenized styles, modern blue theme.
Structure
- pages/ -> features/<domain>/{Page, components, hooks, lib}; 95 files moved,
333 imports rewritten, verified with tsc.
- services/api.ts -> services/api/<domain>.ts (250 exports preserved),
types.ts -> types/<domain>.ts, i18n zh/en -> locales/<lang>/<namespace>.ts
(2696 messages deep-equal before/after).
- ESLint rule: shared layers must not import features/.
Styles
- index.css (16k lines) split losslessly into styles/{base,layout,components,
pages,legacy,theme}; compiled CSS was byte-identical before tokenizing.
- ~930 hard-coded colors replaced by --nx-* tokens (theme/tokens.css); WebCRT
off-palette colors unified; fixed dark-on-dark text introduced by mapping.
- theme/theme.css: modern blue design language (rounded shapes, gradients,
unified buttons/fields/tables/modals), form-grid alignment fix, flat nav list
without tree indent, soft selected state instead of left accent bar.
Docs: WEB.md documents code structure and style rules.
Not touched: the large Biz*/Webcrt/Topology page components (to be split later).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3b45d6abe3
commit
0d894efe5e
196 changed files with 26947 additions and 28091 deletions
|
|
@ -1,70 +0,0 @@
|
|||
/**
|
||||
* Short-lived cache for cutover list APIs.
|
||||
* Switching biz-state / compare / migration / templates remounts each page;
|
||||
* without cache every click re-waits on Promise.all network round-trips.
|
||||
*/
|
||||
|
||||
type Entry = { at: number; data: unknown };
|
||||
|
||||
const store = new Map<string, Entry>();
|
||||
const inFlight = new Map<string, Promise<unknown>>();
|
||||
const DEFAULT_TTL_MS = 45_000;
|
||||
|
||||
export async function cutoverCachedGet<T>(
|
||||
key: string,
|
||||
fetcher: () => Promise<T>,
|
||||
opts?: { force?: boolean; ttlMs?: number },
|
||||
): Promise<T> {
|
||||
const ttl = opts?.ttlMs ?? DEFAULT_TTL_MS;
|
||||
if (!opts?.force) {
|
||||
const hit = store.get(key);
|
||||
if (hit && Date.now() - hit.at < ttl) {
|
||||
return hit.data as T;
|
||||
}
|
||||
}
|
||||
const existing = inFlight.get(key);
|
||||
if (!opts?.force && existing) return existing as Promise<T>;
|
||||
const request = Promise.resolve().then(fetcher).then((data) => {
|
||||
// An invalidation or forced refresh may supersede this request.
|
||||
if (inFlight.get(key) === request) store.set(key, { at: Date.now(), data });
|
||||
return data;
|
||||
}).finally(() => {
|
||||
if (inFlight.get(key) === request) inFlight.delete(key);
|
||||
});
|
||||
inFlight.set(key, request);
|
||||
return request;
|
||||
}
|
||||
|
||||
/** Return stale immediately (if any), refresh in background and notify. */
|
||||
export async function cutoverCachedGetSWR<T>(
|
||||
key: string,
|
||||
fetcher: () => Promise<T>,
|
||||
onFresh?: (data: T) => void,
|
||||
): Promise<T> {
|
||||
const hit = store.get(key);
|
||||
if (hit) {
|
||||
void cutoverCachedGet(key, fetcher, { ttlMs: 0 })
|
||||
.then((data) => {
|
||||
if (store.get(key)?.data === data) onFresh?.(data);
|
||||
})
|
||||
.catch(() => {
|
||||
/* keep stale */
|
||||
});
|
||||
return hit.data as T;
|
||||
}
|
||||
return cutoverCachedGet(key, fetcher);
|
||||
}
|
||||
|
||||
export function invalidateCutoverCache(prefix = ""): void {
|
||||
if (!prefix) {
|
||||
store.clear();
|
||||
inFlight.clear();
|
||||
return;
|
||||
}
|
||||
for (const k of [...store.keys()]) {
|
||||
if (k.startsWith(prefix)) store.delete(k);
|
||||
}
|
||||
for (const k of [...inFlight.keys()]) {
|
||||
if (k.startsWith(prefix)) inFlight.delete(k);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue