Release 0.2.22: capital chart, Investor View, GP stakes, doc folders
Snapshot commit bringing the uncommitted phase-2 work into version control
together with four new features and the 0.2.22 version bump.
New features:
- Investor capital-over-time chart (value, paid-in, distributions per
quarter), rendered from existing capital-account history.
- Admin Investor View: read-only reconstruction of an investor's portal
(GET /api/users/{id}/investor-view), reusing the investor portal UI.
- Document upload scoped to the selected fund's own investors, with an
explicit upload-target confirmation to prevent mis-attaching.
- GP/mgmt entities gain an Assets tab listing their stakes in the funds
they manage (new entity_stakes table + /api/entities/{id}/stakes).
- Edit-entity form (change type/status/etc.), so GP entities can be
categorized correctly.
Verified: 11/11 backend tests pass; alembic upgrades to head b8c9d0e1f2a3;
frontend tsc + vite build clean; s9pk packs at 0.2.22:0 (x86_64).
Also: ignore .DS_Store and *.s9pk artifacts.
This commit is contained in:
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Ten31 Portal",
|
||||
"short_name": "Ten31",
|
||||
"description": "Ten31 fund portal — entities, valuations, and investor capital accounts.",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait-primary",
|
||||
"background_color": "#16243A",
|
||||
"theme_color": "#16243A",
|
||||
"icons": [
|
||||
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
|
||||
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
|
||||
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Ten31 Portal service worker. Deliberately conservative so it never serves a stale app:
|
||||
// - navigations are network-first (always get the latest index.html), cache only as offline fallback
|
||||
// - content-hashed /assets/* are cache-first (immutable, safe forever)
|
||||
// - /api/* is never cached
|
||||
// Bump CACHE on each release so old entries are purged.
|
||||
const CACHE = 'ten31-portal-0.2.22'
|
||||
|
||||
self.addEventListener('install', () => self.skipWaiting())
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
(async () => {
|
||||
const keys = await caches.keys()
|
||||
await Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
|
||||
await self.clients.claim()
|
||||
})(),
|
||||
)
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const req = event.request
|
||||
if (req.method !== 'GET') return
|
||||
const url = new URL(req.url)
|
||||
if (url.origin !== self.location.origin) return
|
||||
if (url.pathname.startsWith('/api/')) return
|
||||
|
||||
if (req.mode === 'navigate') {
|
||||
event.respondWith(
|
||||
(async () => {
|
||||
try {
|
||||
const fresh = await fetch(req)
|
||||
const cache = await caches.open(CACHE)
|
||||
cache.put('/', fresh.clone())
|
||||
return fresh
|
||||
} catch {
|
||||
const cache = await caches.open(CACHE)
|
||||
return (await cache.match('/')) || Response.error()
|
||||
}
|
||||
})(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (url.pathname.startsWith('/assets/')) {
|
||||
event.respondWith(
|
||||
(async () => {
|
||||
const cache = await caches.open(CACHE)
|
||||
const hit = await cache.match(req)
|
||||
if (hit) return hit
|
||||
const res = await fetch(req)
|
||||
if (res.ok) cache.put(req, res.clone())
|
||||
return res
|
||||
})(),
|
||||
)
|
||||
}
|
||||
})
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user