chore: initial commit — UdyamLinc UI, phases A-E

Vite/React 19 console and enterprise PWA. Officer console screens, offline
ledger and sync, data feeds and trip-wire rules screens with freshness chip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
LincPay
2026-08-06 13:30:24 +05:30
co-authored by Claude Opus 5
commit f430f85202
55 changed files with 8691 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
{
"name": "UdyamLinc — उद्यम हिसाब",
"short_name": "UdyamLinc",
"description": "Cash-flow forecast and early warning for microenterprises. Works offline.",
"start_url": "/app",
"scope": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#06301f",
"theme_color": "#0b5d3b",
"lang": "hi",
"icons": [
{ "src": "/udyamlinc-tile.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable" }
]
}
+54
View File
@@ -0,0 +1,54 @@
/*
* UdyamLinc service worker — app-shell precache + stale-while-revalidate.
*
* Scope of the offline promise (deck feature 7): the enterprise PWA must open,
* render and accept ledger entries with zero connectivity. Ledger writes are
* queued in localStorage by the app itself (see lib/ledgerStore.ts); this worker
* only guarantees the shell and its assets are reachable.
*/
const CACHE = 'udyamlinc-v1'
const SHELL = ['/', '/app', '/index.html', '/manifest.webmanifest', '/udyamlinc-tile.svg']
self.addEventListener('install', (event) => {
event.waitUntil(caches.open(CACHE).then((c) => c.addAll(SHELL)).then(() => self.skipWaiting()))
})
self.addEventListener('activate', (event) => {
event.waitUntil(
caches
.keys()
.then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
.then(() => self.clients.claim()),
)
})
self.addEventListener('fetch', (event) => {
const req = event.request
if (req.method !== 'GET') return
const url = new URL(req.url)
// API calls are never served stale — the app falls back to its local store instead.
if (url.pathname.includes('/api/')) return
// Navigations: network first, fall back to the cached shell when offline.
if (req.mode === 'navigate') {
event.respondWith(fetch(req).catch(() => caches.match('/index.html')))
return
}
// Assets: cache first, refresh in the background.
event.respondWith(
caches.match(req).then((hit) => {
const network = fetch(req)
.then((res) => {
if (res.ok) {
const copy = res.clone()
caches.open(CACHE).then((c) => c.put(req, copy))
}
return res
})
.catch(() => hit)
return hit || network
}),
)
})
+10
View File
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
<defs>
<linearGradient id="ul" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#1e7a46"/>
<stop offset="100%" stop-color="#0b5d3b"/>
</linearGradient>
</defs>
<path d="M32 3 58 17.5v29L32 61 6 46.5v-29z" fill="url(#ul)"/>
<path d="M34.5 15 20 36h10l-2.5 13L44 27H33.5z" fill="#f2b544"/>
</svg>

After

Width:  |  Height:  |  Size: 417 B