Compare commits
19
Commits
8d35785a4c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fccc0115c4 | ||
|
|
189260bbb3 | ||
|
|
8e7070cbaf | ||
|
|
a8d7db562d | ||
|
|
e220f46d35 | ||
|
|
93460ece19 | ||
|
|
7b67897612 | ||
|
|
c5f79f1fd2 | ||
|
|
41abcaa7a4 | ||
|
|
966f3b23fa | ||
|
|
45b30918a0 | ||
|
|
7eeec7e781 | ||
|
|
527131a462 | ||
|
|
6b07b83583 | ||
|
|
c7e130ce4d | ||
|
|
5a2b68763f | ||
|
|
84624976a8 | ||
|
|
a3dd4b9ffa | ||
|
|
0121641163 |
@@ -1,11 +1,11 @@
|
||||
# Graph Report - 2e709500-79ab-403f-9ca0-3f027ac9f36e (2026-06-19)
|
||||
# Graph Report - 2e709500-79ab-403f-9ca0-3f027ac9f36e (2026-06-24)
|
||||
|
||||
## Corpus Check
|
||||
- 61 files · ~611,807 words
|
||||
- 64 files · ~615,668 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 596 nodes · 620 edges · 114 communities (87 shown, 27 thin omitted)
|
||||
- 619 nodes · 640 edges · 117 communities (90 shown, 27 thin omitted)
|
||||
- Extraction: 99% EXTRACTED · 1% INFERRED · 0% AMBIGUOUS · INFERRED: 4 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
@@ -50,9 +50,8 @@
|
||||
- [[_COMMUNITY_Community 34|Community 34]]
|
||||
- [[_COMMUNITY_Community 35|Community 35]]
|
||||
- [[_COMMUNITY_Community 36|Community 36]]
|
||||
- [[_COMMUNITY_Community 41|Community 41]]
|
||||
- [[_COMMUNITY_Community 42|Community 42]]
|
||||
- [[_COMMUNITY_Community 43|Community 43]]
|
||||
- [[_COMMUNITY_Community 37|Community 37]]
|
||||
- [[_COMMUNITY_Community 38|Community 38]]
|
||||
- [[_COMMUNITY_Community 44|Community 44]]
|
||||
- [[_COMMUNITY_Community 45|Community 45]]
|
||||
- [[_COMMUNITY_Community 46|Community 46]]
|
||||
@@ -71,6 +70,9 @@
|
||||
- [[_COMMUNITY_Community 59|Community 59]]
|
||||
- [[_COMMUNITY_Community 60|Community 60]]
|
||||
- [[_COMMUNITY_Community 61|Community 61]]
|
||||
- [[_COMMUNITY_Community 62|Community 62]]
|
||||
- [[_COMMUNITY_Community 63|Community 63]]
|
||||
- [[_COMMUNITY_Community 64|Community 64]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `Performance Testing Scripts` - 12 edges
|
||||
@@ -94,7 +96,7 @@
|
||||
- `POST()` --calls--> `logApiRequest()` [INFERRED]
|
||||
src/pages/api/newsletter.ts → src/utils/logger.ts
|
||||
|
||||
## Communities (114 total, 27 thin omitted)
|
||||
## Communities (117 total, 27 thin omitted)
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.05
|
||||
@@ -144,49 +146,49 @@ Nodes (11): Code Example, code:astro (---), code:javascript (// Traditional SPA:
|
||||
Cohesion: 0.18
|
||||
Nodes (10): code:hcl (# Define AWS EC2 instance), code:dockerfile (# Multi-stage build for a Go application), code:yaml (# AWS Cost allocation tags), Common Pitfalls, Conclusion, Containerization as a Migration Strategy, Cost Optimization Strategies, Infrastructure as Code (+2 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.43
|
||||
Nodes (6): trackCaseStudyView(), trackEvent(), trackExternalLink(), trackFormSubmit(), trackNewsletterSignup(), trackPortfolioFilter()
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.2
|
||||
Nodes (9): Catalogue Listing vs Bidding: How You Actually Win, Common Reasons Software Vendors Get Rejected or Delayed, Document Checklist, Fees, Caution Money, and Timelines, Frequently Asked Questions, MSME and Startup Benefits Worth Claiming, Step-by-Step Registration Process, Turning GeM Access Into Government Contracts (+1 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): extract_scores(), get_issues(), main(), Run Lighthouse audit on a URL, Extract key scores from Lighthouse data, Extract issues from Lighthouse audit, run_lighthouse()
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.22
|
||||
Nodes (8): 2026 Cost Ranges by Project Tier, A Simple Way to Estimate Your Own Cost, Build vs Buy: When Custom Actually Pays Off, Frequently Asked Questions, Getting an Accurate Number for Your Project, Hidden Costs Buyers Forget to Budget, The Biggest Cost Driver: The Team, What "Custom ERP" Actually Means for Pricing
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): captureScreenshot(), ensureDir(), generateIndex(), main(), screenshotPath(), setTheme(), waitForPageReady()
|
||||
Cohesion: 0.43
|
||||
Nodes (6): trackCaseStudyView(), trackEvent(), trackExternalLink(), trackFormSubmit(), trackNewsletterSignup(), trackPortfolioFilter()
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.36
|
||||
Nodes (4): deleteFromDB(), getAllFromDB(), openDB(), processSyncQueue()
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.43
|
||||
Nodes (6): trackCaseStudyView(), trackEvent(), trackExternalLink(), trackFormSubmit(), trackNewsletterSignup(), trackPortfolioFilter()
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): checkRateLimit(), corsHeaders(), escapeHtml(), OPTIONS(), POST(), sendContactEmail(), validateContactForm()
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): extract_scores(), get_issues(), main(), Run Lighthouse audit on a URL, Extract key scores from Lighthouse data, Extract issues from Lighthouse audit, run_lighthouse()
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): captureScreenshot(), ensureDir(), generateIndex(), main(), screenshotPath(), setTheme(), waitForPageReady()
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.36
|
||||
Nodes (4): deleteFromDB(), getAllFromDB(), openDB(), processSyncQueue()
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): checkRateLimit(), corsHeaders(), escapeHtml(), OPTIONS(), POST(), sendContactEmail(), validateContactForm()
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.43
|
||||
Nodes (6): trackCaseStudyView(), trackEvent(), trackExternalLink(), trackFormSubmit(), trackNewsletterSignup(), trackPortfolioFilter()
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): extract_scores(), get_issues(), main(), Run Lighthouse audit on a URL, Extract key scores from Lighthouse data, Extract issues from Lighthouse audit, run_lighthouse()
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.48
|
||||
Nodes (5): initAllAnimations(), initButtonRipple(), initCounters(), initProgressBars(), initScrollReveal()
|
||||
Cohesion: 0.46
|
||||
Nodes (7): captureScreenshot(), ensureDir(), generateIndex(), main(), screenshotPath(), setTheme(), waitForPageReady()
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.48
|
||||
Nodes (5): extractJsonLd(), main(), validateOpenGraph(), validatePage(), validateTwitterCard()
|
||||
Cohesion: 0.36
|
||||
Nodes (4): deleteFromDB(), getAllFromDB(), openDB(), processSyncQueue()
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.48
|
||||
@@ -197,23 +199,31 @@ Cohesion: 0.48
|
||||
Nodes (5): extractJsonLd(), main(), validateOpenGraph(), validatePage(), validateTwitterCard()
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.47
|
||||
Nodes (5): main(), measure_response_time(), print_stats(), Measure response time for a URL over multiple iterations, Print statistics for response times
|
||||
Cohesion: 0.48
|
||||
Nodes (5): initAllAnimations(), initButtonRipple(), initCounters(), initProgressBars(), initScrollReveal()
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.48
|
||||
Nodes (5): extractJsonLd(), main(), validateOpenGraph(), validatePage(), validateTwitterCard()
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.47
|
||||
Nodes (5): main(), measure_response_time(), print_stats(), Measure response time for a URL over multiple iterations, Print statistics for response times
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.47
|
||||
Nodes (5): main(), measure_response_time(), print_stats(), Measure response time for a URL over multiple iterations, Print statistics for response times
|
||||
|
||||
### Community 37 - "Community 37"
|
||||
Cohesion: 0.6
|
||||
Nodes (3): formatUptime(), GET(), percentile()
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
### Community 38 - "Community 38"
|
||||
Cohesion: 0.6
|
||||
Nodes (3): formatUptime(), GET(), percentile()
|
||||
|
||||
## Knowledge Gaps
|
||||
- **140 isolated node(s):** `Run Lighthouse audit on a URL`, `Extract key scores from Lighthouse data`, `Extract issues from Lighthouse audit`, `Measure response time for a URL over multiple iterations`, `Print statistics for response times` (+135 more)
|
||||
- **156 isolated node(s):** `Run Lighthouse audit on a URL`, `Extract key scores from Lighthouse data`, `Extract issues from Lighthouse audit`, `Measure response time for a URL over multiple iterations`, `Print statistics for response times` (+151 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **27 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
|
||||
@@ -223,7 +233,7 @@ _Questions this graph is uniquely positioned to answer:_
|
||||
- **Are the 2 inferred relationships involving `captureException()` (e.g. with `sendContactEmail()` and `subscribeToNewsletter()`) actually correct?**
|
||||
_`captureException()` has 2 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **What connects `Run Lighthouse audit on a URL`, `Extract key scores from Lighthouse data`, `Extract issues from Lighthouse audit` to the rest of the system?**
|
||||
_140 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
_156 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
- **Should `Community 0` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.05 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 1` be split into smaller, more focused modules?**
|
||||
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "label": "middleware.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L1"}, {"id": "src_middleware_validatecsrforigin", "label": "validateCsrfOrigin()", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L19"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "astro_middleware", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L1", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_utils_logger_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L2", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "utils_logger_logger", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L2", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_utils_sentry_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L3", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "utils_sentry_captureexception", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L3", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_middleware_ts", "target": "src_middleware_validatecsrforigin", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L19", "weight": 1.0}], "raw_calls": [{"caller_nid": "src_middleware_validatecsrforigin", "callee": "includes", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L24"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "startsWith", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L25"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "get", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L36"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "get", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L37"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "startsWith", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L52"}, {"caller_nid": "src_middleware_validatecsrforigin", "callee": "startsWith", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/middleware.ts", "source_location": "L52"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_xml_ts", "label": "sitemap.xml.ts", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L1"}, {"id": "pages_sitemap_xml_get", "label": "GET()", "file_type": "code", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L13"}], "edges": [{"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_xml_ts", "target": "astro", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L1", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_xml_ts", "target": "astro_content", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L2", "weight": 1.0}, {"source": "home_agent_ai_agent_backend_workspace_2e709500_79ab_403f_9ca0_3f027ac9f36e_src_pages_sitemap_xml_ts", "target": "pages_sitemap_xml_get", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L13", "weight": 1.0}], "raw_calls": [{"caller_nid": "pages_sitemap_xml_get", "callee": "getCollection", "is_member_call": false, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L15"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "getCollection", "is_member_call": false, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L20"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "map", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L43"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "map", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L52"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "join", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L69"}, {"caller_nid": "pages_sitemap_xml_get", "callee": "map", "is_member_call": true, "source_file": "/home/agent/ai-agent/backend/workspace/2e709500-79ab-403f-9ca0-3f027ac9f36e/src/pages/sitemap.xml.ts", "source_location": "L69"}]}
|
||||
File diff suppressed because one or more lines are too long
+697
-290
File diff suppressed because it is too large
Load Diff
@@ -58,6 +58,9 @@ Allow: /
|
||||
User-agent: GPTBot
|
||||
Allow: /
|
||||
|
||||
User-agent: OAI-SearchBot
|
||||
Allow: /
|
||||
|
||||
# Anthropic/Claude
|
||||
User-agent: anthropic-ai
|
||||
Allow: /
|
||||
@@ -65,10 +68,22 @@ Allow: /
|
||||
User-agent: Claude-Web
|
||||
Allow: /
|
||||
|
||||
User-agent: ClaudeBot
|
||||
Allow: /
|
||||
|
||||
User-agent: Claude-SearchBot
|
||||
Allow: /
|
||||
|
||||
User-agent: Claude-User
|
||||
Allow: /
|
||||
|
||||
# Perplexity AI
|
||||
User-agent: PerplexityBot
|
||||
Allow: /
|
||||
|
||||
User-agent: Perplexity-User
|
||||
Allow: /
|
||||
|
||||
# Google Gemini/Bard
|
||||
User-agent: Google-Extended
|
||||
Allow: /
|
||||
@@ -80,3 +95,40 @@ Allow: /
|
||||
# Meta AI
|
||||
User-agent: FacebookBot
|
||||
Allow: /
|
||||
|
||||
User-agent: Meta-ExternalAgent
|
||||
Allow: /
|
||||
|
||||
User-agent: meta-externalagent
|
||||
Allow: /
|
||||
|
||||
# Apple Intelligence
|
||||
User-agent: Applebot
|
||||
Allow: /
|
||||
|
||||
User-agent: Applebot-Extended
|
||||
Allow: /
|
||||
|
||||
# Amazon AI
|
||||
User-agent: Amazonbot
|
||||
Allow: /
|
||||
|
||||
# ByteDance / TikTok AI
|
||||
User-agent: Bytespider
|
||||
Allow: /
|
||||
|
||||
# Cohere AI
|
||||
User-agent: cohere-ai
|
||||
Allow: /
|
||||
|
||||
# DuckDuckGo AI (DuckAssist)
|
||||
User-agent: DuckAssistBot
|
||||
Allow: /
|
||||
|
||||
# You.com AI
|
||||
User-agent: YouBot
|
||||
Allow: /
|
||||
|
||||
# Diffbot (knowledge graph / AI training)
|
||||
User-agent: Diffbot
|
||||
Allow: /
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*!
|
||||
* WRNexus Web Push — service worker.
|
||||
*
|
||||
* ┌─────────────────────────────────────────────────────────────────────────┐
|
||||
* │ HOST THIS FILE ON YOUR OWN ORIGIN. │
|
||||
* │ │
|
||||
* │ A service worker can only control the origin it is served from, so the │
|
||||
* │ browser will NOT let wrnexus.com register a worker for your site. Copy │
|
||||
* │ this file to the ROOT of your site and serve it at: │
|
||||
* │ │
|
||||
* │ https://your-domain.example/wrnexus-push-sw.js │
|
||||
* │ │
|
||||
* │ It must be served same-origin, over HTTPS, with │
|
||||
* │ `Content-Type: text/javascript`. To control the whole site from a │
|
||||
* │ non-root path, also send `Service-Worker-Allowed: /`. │
|
||||
* └─────────────────────────────────────────────────────────────────────────┘
|
||||
*
|
||||
* Core behaviour needs NO configuration — the click/delivery tracking URLs are
|
||||
* carried inside each push payload built by the WRNexus API. The optional
|
||||
* CONFIG block below is only used to silently re-subscribe after the browser
|
||||
* rotates a subscription (`pushsubscriptionchange`); leave it blank to skip
|
||||
* that.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-restricted-globals */
|
||||
|
||||
// ── OPTIONAL: only needed for automatic re-subscription on key rotation ──────
|
||||
var WRNEXUS_PUSH_CONFIG = {
|
||||
// Your site's publicKey (the same value as data-site-id in the snippet).
|
||||
siteKey: '',
|
||||
// The WRNexus API origin (e.g. 'https://api.wrnexus.com').
|
||||
apiBase: '',
|
||||
// Your site's VAPID public key (shown in Push → Settings).
|
||||
vapidPublicKey: '',
|
||||
};
|
||||
|
||||
self.addEventListener('install', function () {
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener('activate', function (event) {
|
||||
event.waitUntil(self.clients.claim());
|
||||
});
|
||||
|
||||
// CORS-simple POST (text/plain, no custom headers, no credentials) so the
|
||||
// receipt reaches the WRNexus API cross-origin without a preflight.
|
||||
function postReceipt(url) {
|
||||
if (!url) return Promise.resolve();
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain;charset=UTF-8' },
|
||||
credentials: 'omit',
|
||||
keepalive: true,
|
||||
mode: 'cors',
|
||||
}).catch(function () {});
|
||||
}
|
||||
|
||||
function urlBase64ToUint8Array(base64String) {
|
||||
var padding = '='.repeat((4 - (base64String.length % 4)) % 4);
|
||||
var base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
|
||||
var raw = self.atob(base64);
|
||||
var out = new Uint8Array(raw.length);
|
||||
for (var i = 0; i < raw.length; ++i) out[i] = raw.charCodeAt(i);
|
||||
return out;
|
||||
}
|
||||
|
||||
function keyToB64(buf) {
|
||||
if (!buf) return '';
|
||||
var bytes = new Uint8Array(buf);
|
||||
var binary = '';
|
||||
for (var i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
||||
return self
|
||||
.btoa(binary)
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=+$/, '');
|
||||
}
|
||||
|
||||
// ── Incoming notification ─────────────────────────────────────────────────
|
||||
self.addEventListener('push', function (event) {
|
||||
var data = {};
|
||||
try {
|
||||
data = event.data ? event.data.json() : {};
|
||||
} catch (_e) {
|
||||
try {
|
||||
data = { title: event.data && event.data.text() };
|
||||
} catch (_e2) {
|
||||
data = {};
|
||||
}
|
||||
}
|
||||
|
||||
var title = data.title || 'Notification';
|
||||
var options = {
|
||||
body: data.body || '',
|
||||
icon: data.icon || undefined,
|
||||
image: data.image || undefined,
|
||||
badge: data.badge || undefined,
|
||||
tag: data.tag || data.campaignId || undefined,
|
||||
requireInteraction: !!data.requireInteraction,
|
||||
data: {
|
||||
clickUrl: data.clickUrl || data.url || '/',
|
||||
url: data.url || '/',
|
||||
openUrl: data.openUrl || '',
|
||||
campaignId: data.campaignId || '',
|
||||
subscriptionId: data.subscriptionId || '',
|
||||
},
|
||||
};
|
||||
|
||||
event.waitUntil(
|
||||
Promise.all([
|
||||
self.registration.showNotification(title, options),
|
||||
// Delivery receipt — records a `delivered` event for analytics.
|
||||
postReceipt(data.deliveredUrl),
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
// ── Click → record + open the destination ─────────────────────────────────
|
||||
self.addEventListener('notificationclick', function (event) {
|
||||
var d = (event.notification && event.notification.data) || {};
|
||||
event.notification.close();
|
||||
|
||||
// `clickUrl` is a signed WRNexus redirect: hitting it records the `clicked`
|
||||
// event and 302s to the real destination, so tracking fires from the open.
|
||||
var target = d.clickUrl || d.url || '/';
|
||||
|
||||
event.waitUntil(
|
||||
self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (list) {
|
||||
// Reuse an existing tab on the destination when possible.
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var client = list[i];
|
||||
if (d.url && client.url === d.url && 'focus' in client) return client.focus();
|
||||
}
|
||||
if (self.clients.openWindow) return self.clients.openWindow(target);
|
||||
return undefined;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// ── Optional: re-subscribe after the browser rotates the subscription ──────
|
||||
self.addEventListener('pushsubscriptionchange', function (event) {
|
||||
var cfg = WRNEXUS_PUSH_CONFIG || {};
|
||||
if (!cfg.siteKey || !cfg.apiBase || !cfg.vapidPublicKey) return;
|
||||
event.waitUntil(
|
||||
self.registration.pushManager
|
||||
.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(cfg.vapidPublicKey) })
|
||||
.then(function (sub) {
|
||||
return fetch(cfg.apiBase.replace(/\/+$/, '') + '/api/push/subscriptions', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain;charset=UTF-8' },
|
||||
credentials: 'omit',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify({
|
||||
key: cfg.siteKey,
|
||||
endpoint: sub.endpoint,
|
||||
keys: {
|
||||
p256dh: keyToB64(sub.getKey('p256dh')),
|
||||
auth: keyToB64(sub.getKey('auth')),
|
||||
},
|
||||
metadata: { source: 'pushsubscriptionchange' },
|
||||
}),
|
||||
}).catch(function () {});
|
||||
})
|
||||
.catch(function () {})
|
||||
);
|
||||
});
|
||||
@@ -166,6 +166,23 @@ const currentYear = new Date().getFullYear();
|
||||
By subscribing, you agree to our
|
||||
<a href="/privacy" class="text-primary hover:underline">Privacy Policy</a>.
|
||||
</p>
|
||||
|
||||
<!-- Web push opt-in (WRNexus). User-gesture subscribe control.
|
||||
Hidden by default; the client script below reveals it only when the
|
||||
WRNexus SDK + browser push support are available, and sets the
|
||||
correct label based on the visitor's current subscription state. -->
|
||||
<button
|
||||
id="wrnexus-subscribe"
|
||||
type="button"
|
||||
hidden
|
||||
aria-label="Enable notifications"
|
||||
class="mt-6 w-full px-4 py-3 bg-secondary-800 border border-secondary-700 text-white font-medium rounded-lg hover:bg-secondary-700 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-secondary-900 transition-all duration-200 text-sm flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed"
|
||||
>
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
<span id="wrnexus-subscribe-label">Enable notifications</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -193,6 +210,106 @@ const currentYear = new Date().getFullYear();
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- WRNexus push subscribe: state-aware control wired via addEventListener
|
||||
(Astro strips inline on* handlers). The button reflects whether the
|
||||
visitor is already subscribed and lets them toggle the subscription. -->
|
||||
<script is:inline>
|
||||
(function () {
|
||||
function init() {
|
||||
var btn = document.getElementById('wrnexus-subscribe');
|
||||
var label = document.getElementById('wrnexus-subscribe-label');
|
||||
if (!btn || !label) return;
|
||||
|
||||
var push = window.WRNexusPush;
|
||||
var supported =
|
||||
push &&
|
||||
'Notification' in window &&
|
||||
'serviceWorker' in navigator &&
|
||||
'PushManager' in window;
|
||||
|
||||
// No SDK or no browser push support: keep the button hidden entirely.
|
||||
if (!supported) {
|
||||
btn.hidden = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Permission permanently blocked by the user: the SDK can't re-prompt,
|
||||
// so surface a clear, non-actionable disabled state instead of a button
|
||||
// that silently does nothing.
|
||||
if (window.Notification && Notification.permission === 'denied') {
|
||||
label.textContent = 'Notifications blocked';
|
||||
btn.setAttribute('aria-label', 'Notifications are blocked in your browser settings');
|
||||
btn.disabled = true;
|
||||
btn.hidden = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Render the button for a given subscription state.
|
||||
// Default UX: subscribed users get an actionable "Unsubscribe" toggle
|
||||
// rather than a dead "Already subscribed" label.
|
||||
// (Alternative: render a disabled "Already subscribed" indicator instead
|
||||
// of the Unsubscribe toggle if product prefers a non-actionable state.)
|
||||
function render(subscribed) {
|
||||
if (subscribed) {
|
||||
label.textContent = 'Unsubscribe';
|
||||
btn.setAttribute('aria-label', 'Unsubscribe from notifications');
|
||||
} else {
|
||||
label.textContent = 'Enable notifications';
|
||||
btn.setAttribute('aria-label', 'Enable notifications');
|
||||
}
|
||||
btn.dataset.subscribed = subscribed ? 'true' : 'false';
|
||||
btn.disabled = false;
|
||||
btn.hidden = false;
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
var subscribed = await push.isSubscribed();
|
||||
render(!!subscribed);
|
||||
} catch (err) {
|
||||
// On error, fall back to the unsubscribed label so the control stays usable.
|
||||
console.error('[wrnexus-push] isSubscribed() failed:', err);
|
||||
render(false);
|
||||
}
|
||||
}
|
||||
|
||||
btn.addEventListener('click', async function () {
|
||||
if (btn.disabled) return;
|
||||
|
||||
var wasSubscribed = btn.dataset.subscribed === 'true';
|
||||
var busyLabel = wasSubscribed ? 'Unsubscribing…' : 'Enabling…';
|
||||
|
||||
// In-progress state to prevent double-clicks while the promise is pending.
|
||||
btn.disabled = true;
|
||||
label.textContent = busyLabel;
|
||||
|
||||
try {
|
||||
if (wasSubscribed) {
|
||||
await push.unsubscribe();
|
||||
} else {
|
||||
// prompt() must run from this real user gesture.
|
||||
await push.prompt();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[wrnexus-push] toggle failed:', err);
|
||||
} finally {
|
||||
// Re-read the authoritative state and re-render the label.
|
||||
await refresh();
|
||||
}
|
||||
});
|
||||
|
||||
// Determine and render the initial state.
|
||||
refresh();
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { trackNewsletterSignup } from '../utils/analytics';
|
||||
|
||||
|
||||
@@ -18,61 +18,68 @@
|
||||
// - Keyboard: native <button> handles Enter & Space; Tab moves between
|
||||
// items. No custom key handling needed.
|
||||
|
||||
// Each answer is written as a definitive, self-contained paragraph: it can be
|
||||
// read (or quoted) in isolation without the question for context, and the
|
||||
// buyer-stage answers bundle the WorkRoot name with the canonical numbers
|
||||
// (founded 2022 · 20+ projects · 11+ districts · 50,000+ records · 99.9%
|
||||
// uptime · GeM · IIT Patna). Keep every figure identical to <ProofNumbers />
|
||||
// and the homepage `companyFacts` set — the visible text below is also the
|
||||
// source for the FAQPage JSON-LD emitted at the bottom of this component.
|
||||
const moneyFaqs = [
|
||||
{
|
||||
question: 'How much does a project cost? Do you work with fixed budgets?',
|
||||
answer:
|
||||
'Projects start from ₹1,00,000, and most web, mobile, and ERP builds land between ₹2L and ₹15L depending on scope. And yes — we work with fixed budgets: tell us the number you have in mind and we will scope what we can deliver within it. Every quote is free, itemised, and no-obligation.',
|
||||
'WorkRoot IT Solutions projects start from ₹1,00,000, and most web, mobile, and ERP builds land between ₹2,00,000 and ₹15,00,000 depending on scope. Yes — we work with fixed budgets: tell us the number you have in mind and we will scope exactly what we can deliver within it. Every quote is free, itemised, and carries no obligation.',
|
||||
},
|
||||
{
|
||||
question: 'How much does a custom ERP cost?',
|
||||
answer:
|
||||
'Typical engagements run ₹2L–₹15L depending on scope. We offer fixed-price options for well-defined projects and time-and-materials for evolving ones.',
|
||||
'A custom ERP from WorkRoot IT Solutions typically runs ₹2,00,000–₹15,00,000, with a genuinely production-grade single-department system starting around ₹6,00,000. We offer fixed-price quotes for well-defined scopes and time-and-materials pricing for evolving ones, and every estimate is free and itemised.',
|
||||
},
|
||||
{
|
||||
question: 'What is your typical timeline?',
|
||||
answer:
|
||||
'4–16 weeks from kickoff to launch. MVPs ship in ~6 weeks on average.',
|
||||
'Most WorkRoot IT Solutions engagements run 4–16 weeks from kickoff to launch, and a typical MVP ships in about 6 weeks. We work in 2-week sprints with a live staging environment from day one, so you see and steer working software early rather than waiting for a single big-bang release.',
|
||||
},
|
||||
{
|
||||
question: 'Do you sign NDAs?',
|
||||
answer:
|
||||
'Yes — happily. We sign mutual NDAs before any detailed discussion.',
|
||||
'Yes. WorkRoot IT Solutions signs a mutual NDA before any detailed discussion of your idea, data, or codebase — at no cost and with no conditions attached. Your concept and commercial details stay confidential from the very first call.',
|
||||
},
|
||||
{
|
||||
question: 'Who owns the source code?',
|
||||
answer:
|
||||
'You do. Full IP transfer is included; we hand over repositories, deployment access, and documentation.',
|
||||
'You do. Every WorkRoot IT Solutions engagement includes full intellectual-property transfer on delivery: you receive the complete source repositories, deployment access, and documentation, with no per-seat licence fees and no vendor lock-in. You are free to host, extend, or hand the system to another team at any time.',
|
||||
},
|
||||
{
|
||||
question: 'Do you work with government tenders / GeM?',
|
||||
answer:
|
||||
"Yes. We're empanelled for relevant categories and have delivered platforms used across 11 districts in Bihar.",
|
||||
'Yes. WorkRoot IT Solutions is empanelled on the Government e-Marketplace (GeM) for relevant IT service categories and can respond to government tenders. We have delivered public-service platforms live across 11+ districts of Bihar at 99.9% uptime, including a Police Management System and a Smart Crematorium Management System for the Bihar Government.',
|
||||
},
|
||||
{
|
||||
question: 'What technologies do you work with?',
|
||||
answer:
|
||||
'Astro, Next.js, Angular, React Native, Spring Boot, Go, Node.js, PostgreSQL, and the full AWS/GCP stack.',
|
||||
'WorkRoot IT Solutions builds on Astro, Next.js, Angular, and React Native on the front end; Spring Boot, Go, and Node.js on the back end; and PostgreSQL or MySQL for data — the same stack behind the IIT Patna University Management System and our multi-district government deployments, all deployed and operated on the AWS and GCP cloud.',
|
||||
},
|
||||
{
|
||||
question: 'Do you offer ongoing support after launch?',
|
||||
answer:
|
||||
'Yes. We offer maintenance packages covering bug fixes, security patches, performance monitoring, and feature enhancements — with response SLAs to match your uptime needs.',
|
||||
'Yes. WorkRoot IT Solutions offers maintenance packages covering bug fixes, security patches, performance monitoring, and feature enhancements, each with a response SLA matched to your uptime needs — the same operational discipline that keeps our government systems running at 99.9% uptime.',
|
||||
},
|
||||
{
|
||||
question: 'What is your development process?',
|
||||
answer:
|
||||
'Agile with 2-week sprints, weekly check-ins, and a live staging environment from day one. You see working software early and steer the roadmap as it ships.',
|
||||
'WorkRoot IT Solutions works in Agile 2-week sprints with weekly check-ins and a live staging environment from day one. You see working software early and steer the roadmap as it ships, rather than waiting for a single final handover at the end of the project.',
|
||||
},
|
||||
{
|
||||
question: 'Can you work with our existing team?',
|
||||
answer:
|
||||
'Absolutely. We augment in-house teams as readily as we deliver fully managed builds — adapting to your repo, branch strategy, ticket system, and review process.',
|
||||
'Absolutely. WorkRoot IT Solutions augments in-house teams as readily as it delivers fully managed builds, adapting to your repository, branch strategy, ticketing system, and code-review process so we slot into the way your team already works.',
|
||||
},
|
||||
{
|
||||
question: 'How do you ensure project security?',
|
||||
answer:
|
||||
'Security is built into every phase: OWASP-aligned reviews, encrypted data at rest and in transit, role-based access control, regular dependency audits, and compliance with GDPR / SOC 2-style controls where the project demands them.',
|
||||
'Security is built into every phase at WorkRoot IT Solutions: OWASP-aligned code reviews, encrypted data at rest and in transit, role-based access control, regular dependency audits, and GDPR- and SOC 2-style controls where a project demands them. The same architecture secures a Bihar government police platform deployed across 11 districts.',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -40,12 +40,12 @@ interface Props {
|
||||
// credibility bug this set was created to fix.
|
||||
//
|
||||
// - "20+ Projects" — total delivered engagements
|
||||
// - "11 Districts Deployed" — matches hero trust badge copy
|
||||
// - "11+ Districts Deployed" — cumulative Bihar government district reach
|
||||
// - "50,000+ Records Managed" — records under management across deployments
|
||||
// - "8+ Years Avg Experience" — average team experience
|
||||
const defaultItems: ProofItem[] = [
|
||||
{ value: '20+', label: 'Projects' },
|
||||
{ value: '11', label: 'Districts Deployed' },
|
||||
{ value: '11+', label: 'Districts Deployed' },
|
||||
{ value: '50,000+', label: 'Records Managed' },
|
||||
{ value: '8+', label: 'Years Avg Experience' },
|
||||
];
|
||||
|
||||
@@ -54,7 +54,7 @@ const orgId = `${baseUrl}/#organization`;
|
||||
// src/pages/index.astro). All three are 5★, so the aggregate rating is 5.0
|
||||
// over reviewCount = 3.
|
||||
//
|
||||
// Headline proof numbers in the description (20+ projects · 11 districts ·
|
||||
// Headline proof numbers in the description (20+ projects · 11+ districts ·
|
||||
// 50,000+ records · 99.9% uptime) mirror the canonical set in
|
||||
// ProofNumbers.astro and the /services stats — keep them identical site-wide.
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -102,7 +102,7 @@ const organizationSchema = {
|
||||
"height": "512"
|
||||
},
|
||||
"image": `${baseUrl}/og-image.jpg`,
|
||||
"description": "WorkRoot IT Solutions LLP builds production-grade web, mobile, ERP, and government IT systems — 20+ projects delivered across 11 districts, 50,000+ records managed, and 99.9% uptime on mission-critical government deployments.",
|
||||
"description": "WorkRoot IT Solutions LLP builds production-grade web, mobile, ERP, and government IT systems — 20+ projects delivered across 11+ districts, 50,000+ records managed, and 99.9% uptime on mission-critical government deployments.",
|
||||
"foundingDate": "2022",
|
||||
"founder": {
|
||||
"@type": "Person",
|
||||
@@ -238,7 +238,7 @@ const blogPostingSchema = article && type === 'BlogPosting' ? {
|
||||
"dateModified": article.dateModified || article.datePublished,
|
||||
"publisher": {
|
||||
"@type": "Organization",
|
||||
"name": "WorkRoot IT Solutions",
|
||||
"name": "WorkRoot IT Solutions LLP",
|
||||
"url": baseUrl,
|
||||
"logo": {
|
||||
"@type": "ImageObject",
|
||||
@@ -298,7 +298,7 @@ const webPageSchema = type === 'WebPage' && Astro.url.pathname !== '/' ? {
|
||||
},
|
||||
"about": {
|
||||
"@type": "Organization",
|
||||
"name": "WorkRoot IT Solutions"
|
||||
"name": "WorkRoot IT Solutions LLP"
|
||||
},
|
||||
"primaryImageOfPage": {
|
||||
"@type": "ImageObject",
|
||||
@@ -330,7 +330,7 @@ const aboutPageSchema = type === 'AboutPage' ? {
|
||||
"inLanguage": "en-US",
|
||||
"mainEntity": {
|
||||
"@type": "Organization",
|
||||
"name": "WorkRoot IT Solutions",
|
||||
"name": "WorkRoot IT Solutions LLP",
|
||||
"url": baseUrl
|
||||
}
|
||||
} : null;
|
||||
@@ -345,8 +345,8 @@ const contactPageSchema = type === 'ContactPage' ? {
|
||||
"inLanguage": "en-US",
|
||||
"mainEntity": {
|
||||
"@type": "LocalBusiness",
|
||||
"name": "WorkRoot IT Solutions",
|
||||
"alternateName": "WorkRoot IT Solutions LLP",
|
||||
"name": "WorkRoot IT Solutions LLP",
|
||||
"alternateName": "WorkRoot IT Solutions",
|
||||
"url": baseUrl,
|
||||
"telephone": "+91-9561417403",
|
||||
"email": "admin@workroot.in",
|
||||
|
||||
@@ -181,6 +181,17 @@ const fullTitle = title === 'Home' ? homeTitle : `${title} | ${brand}`;
|
||||
<!-- Analytics (GA4 or Plausible) -->
|
||||
<Analytics />
|
||||
|
||||
<!-- WRNexus Web Push SDK — loaded once globally so it runs on every page
|
||||
that should collect push subscribers. is:inline keeps Astro's bundler
|
||||
from rewriting the external src and preserves data-site-id / defer. -->
|
||||
<script is:inline src="https://api.wrnexus.com/push/sdk.js" data-site-id="pk_rP51XN8tZnud5HdX9UqidjHk" defer></script>
|
||||
|
||||
<!-- WRNexus Bot Widget — loaded once globally here so the chat widget
|
||||
renders on every route (pages + blog posts) via this shared layout.
|
||||
is:inline keeps Astro's bundler from rewriting the external src and
|
||||
preserves data-bot-id / async. -->
|
||||
<script is:inline src="https://api.wrnexus.com/bots/widget.js" data-bot-id="d3998f1b-51d7-4f7c-a57e-3fa1af4394a0" async></script>
|
||||
|
||||
<!-- Additional head content slot -->
|
||||
<slot name="head" />
|
||||
</head>
|
||||
|
||||
+6
-4
@@ -155,11 +155,13 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
||||
// - inline scripts/styles: Required for Astro framework
|
||||
// - https://www.googletagmanager.com & https://www.google-analytics.com: GA4
|
||||
// - https://plausible.io: Plausible Analytics
|
||||
// - https://api.wrnexus.com: WRNexus web-push SDK (sdk.js), bot widget
|
||||
// (bots/widget.js) + runtime config/iframe served from the same origin
|
||||
// Note: unsafe-eval is removed in production (only needed during Astro HMR dev mode)
|
||||
const isProduction = import.meta.env.PROD;
|
||||
const scriptSrc = isProduction
|
||||
? "script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://plausible.io"
|
||||
: "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://www.google-analytics.com https://plausible.io";
|
||||
? "script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://plausible.io https://api.wrnexus.com"
|
||||
: "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://www.google-analytics.com https://plausible.io https://api.wrnexus.com";
|
||||
|
||||
const csp = [
|
||||
"default-src 'self'",
|
||||
@@ -167,11 +169,11 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
||||
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
||||
"img-src 'self' data: https://images.unsplash.com https://www.google-analytics.com https://www.googletagmanager.com https://maps.googleapis.com https://maps.gstatic.com",
|
||||
"font-src 'self' data: https://fonts.gstatic.com",
|
||||
"connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://stats.g.doubleclick.net https://plausible.io",
|
||||
"connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://stats.g.doubleclick.net https://plausible.io https://api.wrnexus.com",
|
||||
"worker-src 'self'",
|
||||
"manifest-src 'self'",
|
||||
"object-src 'none'", // Block Flash/plugins (defense-in-depth)
|
||||
"frame-src https://www.google.com", // Allow Google Maps iframe embed
|
||||
"frame-src https://www.google.com https://api.wrnexus.com", // Google Maps iframe + WRNexus bot widget iframe
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
|
||||
@@ -49,7 +49,7 @@ const teamMembers = [
|
||||
// stats counters) so the numbers never contradict across pages.
|
||||
const stats = [
|
||||
{ value: '20+', label: 'Projects' },
|
||||
{ value: '11', label: 'Districts Deployed' },
|
||||
{ value: '11+', label: 'Districts Deployed' },
|
||||
{ value: '50,000+', label: 'Records Managed' },
|
||||
{ value: '8+', label: 'Years Avg Experience' },
|
||||
];
|
||||
|
||||
+108
-3
@@ -124,7 +124,7 @@ const testimonials = [
|
||||
];
|
||||
|
||||
// NOTE: The homepage stat band is rendered once by <ProofNumbers />, which
|
||||
// holds the canonical site-wide proof set (20+ Projects · 11 Districts ·
|
||||
// holds the canonical site-wide proof set (20+ Projects · 11+ Districts ·
|
||||
// 50,000+ Records · 8+ Years). The previously-duplicated "Numbers That
|
||||
// Matter" stats section (and its local `stats` array + count-up script) was
|
||||
// removed so a visitor never sees the same four numbers twice on one page.
|
||||
@@ -171,11 +171,42 @@ const techStack = [
|
||||
// "Common Questions" block that used to live above the tech-stack section
|
||||
// was removed; its unique questions were merged into HomepageFaq's
|
||||
// `moneyFaqs` array so no buyer-facing content was dropped.
|
||||
|
||||
// Quotable, self-contained company facts. Each sentence deliberately bundles
|
||||
// the full company name + a concrete claim + a number so the line stays
|
||||
// accurate and citable when lifted out of context (search snippets, press
|
||||
// quotes, AI assistants). Every figure here is the canonical site-wide value
|
||||
// and MUST stay identical to <ProofNumbers />, the hero trust badge, /about,
|
||||
// and the /services pages. Do not change a number here without updating it
|
||||
// everywhere it appears — mismatched stats reintroduce the credibility bug
|
||||
// this canonical set was created to fix.
|
||||
const companyFacts = [
|
||||
'WorkRoot IT Solutions was founded in 2022 and has delivered 20+ production software systems for startups, enterprises, and government bodies.',
|
||||
'WorkRoot IT Solutions runs government platforms across 11+ districts of Bihar, India, sustaining 99.9% uptime on mission-critical public services.',
|
||||
'WorkRoot IT Solutions manages 50,000+ live records across its government and enterprise deployments.',
|
||||
'WorkRoot IT Solutions built the University Management System for IIT Patna on a Next.js and Spring Boot stack.',
|
||||
'WorkRoot IT Solutions is empanelled on the Government e-Marketplace (GeM), making it eligible to bid on Indian government IT tenders.',
|
||||
'WorkRoot IT Solutions delivered a Police Management System that digitised FIRs and case tracking for 15,000+ officers across 11 districts of Bihar.',
|
||||
'WorkRoot IT Solutions is a senior in-house engineering team averaging 8+ years of experience per engineer.',
|
||||
];
|
||||
|
||||
// Comparison table — custom software (WorkRoot) vs off-the-shelf ERP / SaaS.
|
||||
// Mirrors the deeper table on /services/custom-erp-development, surfaced on the
|
||||
// homepage so the core buying trade-off is framed before a late-stage visitor
|
||||
// reaches the inline lead form. The final row carries the canonical
|
||||
// "11+ districts · 99.9% uptime" proof point.
|
||||
const comparison = [
|
||||
{ dimension: 'Fit to your workflow', offShelf: 'You reshape your process to fit the software', custom: 'The software is built around your exact process' },
|
||||
{ dimension: 'Source-code ownership', offShelf: 'Vendor-locked, closed source', custom: 'Full IP transfer on delivery — you own the code' },
|
||||
{ dimension: 'Licensing cost', offShelf: 'Recurring per-seat fees that grow with headcount', custom: 'One-time build — no per-seat licence tax' },
|
||||
{ dimension: 'Data residency', offShelf: 'The vendor’s cloud and region', custom: 'Your servers, your region, your control' },
|
||||
{ dimension: 'Proven at scale', offShelf: 'Generic, one-size-fits-all', custom: 'Live across 11+ districts at 99.9% uptime' },
|
||||
];
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Home"
|
||||
description="WorkRoot IT Solutions builds production-grade web, mobile, ERP, and government IT systems. 20+ projects delivered across 11 districts with 99.9% uptime."
|
||||
description="WorkRoot IT Solutions builds production-grade web, mobile, ERP, and government IT systems. 20+ projects delivered across 11+ districts with 99.9% uptime."
|
||||
>
|
||||
<SEO
|
||||
slot="head"
|
||||
@@ -208,7 +239,7 @@ const techStack = [
|
||||
<svg class="w-5 h-5 text-accent-400" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-white">Trusted by 20+ teams · Deployed across 11 districts</span>
|
||||
<span class="text-sm font-medium text-white">Trusted by 20+ teams · Deployed across 11+ districts</span>
|
||||
</div>
|
||||
|
||||
<!-- Main heading — benefit-led: outcome + audience -->
|
||||
@@ -681,6 +712,80 @@ const techStack = [
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Company Facts + Comparison — extractable, self-contained statements
|
||||
that bundle the WorkRoot name with a concrete claim and a number so each
|
||||
line stays accurate when quoted out of context (search snippets, press,
|
||||
AI assistants). Followed by a custom-vs-off-the-shelf comparison table so
|
||||
the buying trade-off is framed in one glance. Every figure matches the
|
||||
canonical set in <ProofNumbers />. -->
|
||||
<section
|
||||
id="company-facts"
|
||||
class="py-24 bg-white dark:bg-secondary-900 border-t border-secondary-100 dark:border-secondary-800"
|
||||
aria-labelledby="company-facts-heading"
|
||||
>
|
||||
<div class="container-wrapper">
|
||||
<div class="text-center max-w-3xl mx-auto mb-16" data-animate="fade-up">
|
||||
<span class="inline-block px-4 py-2 rounded-full bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-sm font-bold uppercase tracking-wider mb-4">The facts</span>
|
||||
<h2 id="company-facts-heading" class="text-4xl sm:text-5xl font-bold text-secondary-900 dark:text-secondary-100 mb-6">WorkRoot at a glance</h2>
|
||||
<p class="text-xl text-secondary-600 dark:text-secondary-400">
|
||||
The record behind the work, stated plainly — so you know exactly who you're hiring.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul class="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-5 max-w-5xl mx-auto mb-20" role="list">
|
||||
{companyFacts.map((fact, index) => (
|
||||
<li
|
||||
class="flex items-start gap-4 bg-secondary-50 dark:bg-secondary-800/60 rounded-2xl border border-secondary-100 dark:border-secondary-700 p-5 sm:p-6"
|
||||
data-animate="fade-up"
|
||||
data-delay={String((index % 2) * 100)}
|
||||
>
|
||||
<span class="flex-shrink-0 w-10 h-10 rounded-xl bg-primary-100 dark:bg-primary-900/40 flex items-center justify-center" aria-hidden="true">
|
||||
<svg class="w-6 h-6 text-primary-600 dark:text-primary-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</span>
|
||||
<p class="text-base sm:text-lg text-secondary-800 dark:text-secondary-200 leading-relaxed">{fact}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<!-- Comparison table: custom build vs off-the-shelf. Reuses the markup
|
||||
pattern from /services/custom-erp-development for visual consistency. -->
|
||||
<div class="max-w-4xl mx-auto" data-animate="fade-up">
|
||||
<div class="text-center mb-8">
|
||||
<h3 class="text-2xl sm:text-3xl font-bold text-secondary-900 dark:text-secondary-100 mb-3">Custom software vs off-the-shelf ERP</h3>
|
||||
<p class="text-secondary-600 dark:text-secondary-400 text-base sm:text-lg">Where a bespoke WorkRoot build pays you back over a packaged SaaS tool.</p>
|
||||
</div>
|
||||
<div class="overflow-x-auto rounded-2xl border border-secondary-200 dark:border-secondary-700 shadow-sm">
|
||||
<table class="w-full text-left border-collapse min-w-[640px]">
|
||||
<caption class="sr-only">Comparison of off-the-shelf ERP software versus custom software built by WorkRoot IT Solutions</caption>
|
||||
<thead>
|
||||
<tr class="bg-secondary-50 dark:bg-secondary-800">
|
||||
<th scope="col" class="px-5 py-4 text-sm font-bold text-secondary-700 dark:text-secondary-200">Dimension</th>
|
||||
<th scope="col" class="px-5 py-4 text-sm font-bold text-secondary-500 dark:text-secondary-400">Off-the-shelf ERP</th>
|
||||
<th scope="col" class="px-5 py-4 text-sm font-bold text-primary-700 dark:text-primary-300">Custom software (WorkRoot)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{comparison.map((row, i) => (
|
||||
<tr class={i % 2 === 0 ? 'bg-white dark:bg-secondary-900' : 'bg-secondary-50/50 dark:bg-secondary-800/40'}>
|
||||
<th scope="row" class="px-5 py-4 text-sm font-semibold text-secondary-900 dark:text-secondary-100 align-top">{row.dimension}</th>
|
||||
<td class="px-5 py-4 text-sm text-secondary-600 dark:text-secondary-400 align-top">{row.offShelf}</td>
|
||||
<td class="px-5 py-4 text-sm text-secondary-700 dark:text-secondary-200 align-top font-medium">
|
||||
<span class="inline-flex items-start gap-2">
|
||||
<svg class="w-4 h-4 mt-0.5 flex-shrink-0 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>
|
||||
{row.custom}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Technologies We Work With — relocated from the hero so social proof
|
||||
leads above the fold. Kept intentionally subdued (border-only cards,
|
||||
neutral grayscale icons) so it reads as supporting context rather than
|
||||
|
||||
@@ -230,7 +230,7 @@ const featuredProjects = projects.filter(p => p.featured);
|
||||
<div class="mt-14 grid grid-cols-2 sm:grid-cols-4 gap-4 lg:gap-6 reveal-on-scroll reveal-delay-2">
|
||||
{[
|
||||
{ value: '20+', label: 'Projects' },
|
||||
{ value: '11', label: 'Districts Deployed' },
|
||||
{ value: '11+', label: 'Districts Deployed' },
|
||||
{ value: '50,000+', label: 'Records Managed' },
|
||||
{ value: '8+', label: 'Years Avg Experience' },
|
||||
].map((stat) => (
|
||||
|
||||
@@ -294,7 +294,7 @@ const processSteps = [
|
||||
|
||||
<BaseLayout
|
||||
title="ERP, Web & Mobile App Development Services"
|
||||
description="Custom web development, mobile apps, ERP platforms, and government IT solutions from WorkRoot. 20+ projects delivered across 11 districts — get a free quote."
|
||||
description="Custom web development, mobile apps, ERP platforms, and government IT solutions from WorkRoot. 20+ projects delivered across 11+ districts — get a free quote."
|
||||
>
|
||||
<SEO
|
||||
slot="head"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// ServiceFaq) and feeds Service + FAQ schema into SEO.astro.
|
||||
//
|
||||
// All company stats mirror the canonical site-wide set used in
|
||||
// ProofNumbers.astro, /portfolio, and SEO.astro — 20+ projects · 11 districts ·
|
||||
// ProofNumbers.astro, /portfolio, and SEO.astro — 20+ projects · 11+ districts ·
|
||||
// 50,000+ records · 99.9% uptime · 8+ years. Case-study numbers are taken
|
||||
// verbatim from the IIT Patna and Bihar government case studies on /portfolio.
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
@@ -44,7 +44,7 @@ const comparison = [
|
||||
const stats = [
|
||||
{ value: '5+', label: 'ERP & enterprise deployments' },
|
||||
{ value: '20,000+', label: 'Users across systems' },
|
||||
{ value: '11', label: 'Districts running our platforms' },
|
||||
{ value: '11+', label: 'Districts running our platforms' },
|
||||
{ value: '99.9%', label: 'Uptime on critical systems' },
|
||||
];
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
// schema into SEO.astro.
|
||||
//
|
||||
// Canonical stats mirror ProofNumbers.astro / /portfolio / SEO.astro:
|
||||
// 20+ projects · 11 districts · 50,000+ records · 99.9% uptime · 8+ years.
|
||||
// 20+ projects · 11+ districts · 50,000+ records · 99.9% uptime · 8+ years.
|
||||
// (This page's own hero stat shows "11" — the specific Bihar deployment
|
||||
// underpinning the company-wide "11+" figure; do not inflate it.)
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import SEO from '../../components/SEO.astro';
|
||||
import GuaranteeBand from '../../components/GuaranteeBand.astro';
|
||||
|
||||
@@ -35,7 +35,7 @@ const sitemapSections = [
|
||||
links: [
|
||||
{ name: 'Custom ERP Development', url: '/services/custom-erp-development', description: 'Bespoke enterprise systems on Spring Boot & Angular' },
|
||||
{ name: 'University ERP', url: '/services/university-erp', description: 'Admissions, exams, grading — built for IIT Patna' },
|
||||
{ name: 'Government IT & GeM Vendor', url: '/services/government-it-solutions-gem-vendor', description: 'GeM-empanelled, live across 11 districts' },
|
||||
{ name: 'Government IT & GeM Vendor', url: '/services/government-it-solutions-gem-vendor', description: 'GeM-empanelled, live across 11+ districts' },
|
||||
{ name: 'Police Management System', url: '/services/police-management-system', description: 'FIR, case tracking & personnel for 15,000+ officers' },
|
||||
{ name: 'React Native App Development', url: '/services/react-native-app-development', description: 'One codebase for iOS and Android' },
|
||||
],
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ export function generateArticleSchema(
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'WorkRoot IT Solutions',
|
||||
name: 'WorkRoot IT Solutions LLP',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: 'https://workroot.in/logo.png',
|
||||
@@ -132,7 +132,7 @@ export function generateServiceSchema(
|
||||
description: description,
|
||||
provider: {
|
||||
'@type': 'Organization',
|
||||
name: 'WorkRoot IT Solutions',
|
||||
name: 'WorkRoot IT Solutions LLP',
|
||||
url: siteUrl,
|
||||
},
|
||||
areaServed: {
|
||||
|
||||
Reference in New Issue
Block a user