Added legal, compliance and additional services

This commit is contained in:
2026-07-17 11:14:25 +02:00
parent bcb84f4de4
commit 788649e962
8 changed files with 851 additions and 26 deletions
+105 -4
View File
@@ -3,13 +3,114 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lite Charms</title>
<!-- SMTPJS direct mail client -->
<script src="https://smtpjs.com/v3/smtp.js" async></script>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<!-- Primary SEO Metadata -->
<title>LiteCharms | Custom Web &amp; Mobile App Development &amp; IT Solutions | Midrand, South Africa</title>
<meta name="description" content="LiteCharms (PTY) Ltd is a leading custom software development and IT infrastructure planning agency in Midrand, South Africa. We engineer bespoke web, mobile, and desktop applications, paired with scalable corporate network and hardware rollout designs." />
<meta name="keywords" content="LiteCharms, LiteCharms PTY Ltd, software development Midrand, web design Johannesburg, mobile app development South Africa, custom software Gauteng, IT infrastructure planning, desktop app development, network rollout South Africa, IT consulting Noordwyk, app development agency South Africa, software company Midrand" />
<meta name="author" content="LiteCharms (PTY) Ltd" />
<meta name="robots" content="index, follow" />
<link rel="canonical" href="https://litecharms.co.za" />
<!-- Open Graph / Facebook / WhatsApp / LinkedIn -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://litecharms.co.za" />
<meta property="og:title" content="LiteCharms | Custom Web, Mobile App Development &amp; IT Solutions" />
<meta property="og:description" content="Bespoke software engineering and enterprise IT infrastructure rollout in Midrand, South Africa. Build responsive web platforms, mobile apps, and secure high-performance networks." />
<meta property="og:image" content="https://litecharms.co.za/assets/og-preview.png" />
<meta property="og:site_name" content="LiteCharms" />
<meta property="og:locale" content="en_ZA" />
<!-- Twitter Card Meta Tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content="https://litecharms.co.za" />
<meta name="twitter:title" content="LiteCharms | Custom Software &amp; IT Infrastructure Planning" />
<meta name="twitter:description" content="Bespoke software engineering and enterprise IT infrastructure rollout in Midrand, South Africa. Build responsive web platforms, mobile apps, and secure high-performance networks." />
<meta name="twitter:image" content="https://litecharms.co.za/assets/og-preview.png" />
<!-- Google / Search Engine JSON-LD Structured Data Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ProfessionalService",
"@id": "https://litecharms.co.za/#organization",
"name": "LiteCharms (PTY) Ltd",
"url": "https://litecharms.co.za",
"logo": "https://litecharms.co.za/assets/logo.png",
"image": "https://litecharms.co.za/assets/og-preview.png",
"description": "LiteCharms (PTY) Ltd is a premier custom software development and IT infrastructure rollout agency based in Midrand, Gauteng, South Africa.",
"telephone": "+27872659463",
"email": "contact@litecharms.co.za",
"priceRange": "ZAR",
"address": {
"@type": "PostalAddress",
"streetAddress": "1633 Liebenburg Rd, Noordwyk",
"addressLocality": "Midrand",
"addressRegion": "Gauteng",
"postalCode": "1687",
"addressCountry": "ZA"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": -25.9691,
"longitude": 28.1189
},
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday"
],
"opens": "08:00",
"closes": "17:00"
},
"hasOfferCatalog": {
"@type": "OfferCatalog",
"name": "IT &amp; Software Services Offered",
"itemListElement": [
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Web Design &amp; Development",
"description": "Bespoke web platforms built using modern architectures for maximum speed, security, and conversion rate."
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Mobile Application Engineering",
"description": "Stunning and powerful iOS &amp; Android apps optimized for app store discovery and user retention."
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Desktop Software Design",
"description": "Secure, fast, and multi-threaded desktop software built for Windows and macOS systems."
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "IT Infrastructure Planning &amp; Rollout",
"description": "Enterprise-grade design, network layout routing, storage strategy, and server architecture deployment."
}
}
]
}
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 702.01 641.88">
<path fill="#002f43" d="M522.67,13.65H179.03L14.93,214.61l94.51,233.74,241.75,179.87,224.11-179.8,111.78-231.52L522.67,13.65ZM61.15,210.34L194.73,46.72h64.6l-57.41,214.25-140.77-50.63ZM350.2,555.04l-44-108.79-74.49-168.44,55.05-205.52,118.29,158.96,31.41,55.36-86.26,268.43ZM548.74,427.24l-165.35,132.69,84.23-262.03,169.43-53.43-88.31,182.77ZM662.62,201.71l.07.19-.12-.19h.05Z" />
<polygon fill="#3d98c6" points="637.05 244.47 548.74 427.24 383.39 559.94 467.62 297.9 637.05 244.47" />
<polygon fill="#00bcac" points="436.46 286.61 350.2 555.04 306.2 446.25 231.71 277.81 286.76 72.29 405.05 231.25 436.46 286.61" />
<polygon fill="#008092" points="259.33 46.72 201.92 260.97 61.15 210.34 194.73 46.72 259.33 46.72" />
</svg>

After

Width:  |  Height:  |  Size: 804 B

+7
View File
@@ -0,0 +1,7 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Allow: /
# Host & Sitemap specification
Host: https://litecharms.co.za
Sitemap: https://litecharms.co.za/sitemap.xml
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://litecharms.co.za/</loc>
<lastmod>2026-07-17</lastmod>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
+161 -6
View File
@@ -8,6 +8,7 @@ import AuroraBackground from './components/AuroraBackground';
import { BrandIcon, BrandLogo } from './components/BrandAssets';
import { Inquiry } from './types';
import { sendEmailClientSide } from './utils/emailController';
import { CompliancePageWrapper, ComplianceModal } from './components/ComplianceContent';
import {
MapPin,
Mail,
@@ -16,10 +17,17 @@ import {
Send,
CheckCircle2,
ArrowRight,
Sparkles
Sparkles,
Cookie,
X
} from 'lucide-react';
export default function App() {
// Page Routing State
const [activePage, setActivePage] = useState<'home' | 'privacy' | 'terms'>('home');
const [footerModalType, setFooterModalType] = useState<'privacy' | 'terms' | null>(null);
const [showCookieBanner, setShowCookieBanner] = useState(false);
// General Contact Form States
const [contactName, setContactName] = useState('');
const [contactEmail, setContactEmail] = useState('');
@@ -32,6 +40,48 @@ export default function App() {
const [activeClients, setActiveClients] = useState(1);
const [completedProjects, setCompletedProjects] = useState(5);
// Route syncing with query param on mount
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const pageParam = params.get('page');
if (pageParam === 'privacy' || pageParam === 'terms') {
setActivePage(pageParam);
}
const cookiesAccepted = localStorage.getItem('litecharms_cookies_accepted');
if (!cookiesAccepted) {
setShowCookieBanner(true);
}
}, []);
// Listen for browser navigation (back/forward)
useEffect(() => {
const handlePopState = () => {
const params = new URLSearchParams(window.location.search);
const pageParam = params.get('page') as 'privacy' | 'terms' | null;
setActivePage(pageParam || 'home');
};
window.addEventListener('popstate', handlePopState);
return () => window.removeEventListener('popstate', handlePopState);
}, []);
const navigateTo = (page: 'home' | 'privacy' | 'terms') => {
setActivePage(page);
const url = new URL(window.location.href);
if (page === 'home') {
url.searchParams.delete('page');
} else {
url.searchParams.set('page', page);
}
window.history.pushState(null, '', url.toString());
window.scrollTo({ top: 0, behavior: 'instant' });
};
const dismissCookieBanner = () => {
localStorage.setItem('litecharms_cookies_accepted', 'true');
setShowCookieBanner(false);
};
useEffect(() => {
// Soft animate stats for visual delight
const clientsTimer = setInterval(() => {
@@ -116,6 +166,15 @@ export default function App() {
console.log('Received Estimate Inquiry:', inquiry);
};
if (activePage !== 'home') {
return (
<CompliancePageWrapper
type={activePage}
onBack={() => navigateTo('home')}
/>
);
}
return (
<div className="min-h-screen bg-brand-950 font-sans text-slate-300 antialiased relative z-10 selection:bg-brand-500/30 selection:text-white" id="app-root">
{/* Aurora Background */}
@@ -562,7 +621,7 @@ export default function App() {
<div className="grid grid-cols-1 md:grid-cols-12 gap-8 items-start border-b border-slate-900 pb-12">
{/* Branding Column */}
<div className="md:col-span-5 space-y-4 text-left">
<div className="md:col-span-4 space-y-4 text-left">
<div className="flex items-center space-x-2">
<BrandLogo className="h-10 w-auto" />
</div>
@@ -575,14 +634,14 @@ export default function App() {
</div>
{/* Links Columns */}
<div className="md:col-span-3 text-left space-y-3">
<div className="md:col-span-2 text-left space-y-3">
<h5 className="text-xs font-mono font-bold text-slate-200 uppercase tracking-wider">
Services Links
</h5>
<ul className="space-y-2 text-xs text-slate-400">
<li>
<button onClick={() => scrollToSection('services')} className="hover:text-brand-400 transition-colors cursor-pointer text-left">
Web & Mobile App Design
Web &amp; Mobile App Design
</button>
</li>
<li>
@@ -597,7 +656,7 @@ export default function App() {
</li>
<li>
<button onClick={() => scrollToSection('services')} className="hover:text-brand-400 transition-colors cursor-pointer text-left">
Kubernetes IaC & Hosting
Kubernetes IaC &amp; Hosting
</button>
</li>
</ul>
@@ -626,13 +685,58 @@ export default function App() {
</ul>
</div>
{/* Compliance Section */}
<div className="md:col-span-2 text-left space-y-3" id="footer-compliance-col">
<h5 className="text-xs font-mono font-bold text-slate-200 uppercase tracking-wider">
Compliance
</h5>
<ul className="space-y-2 text-xs text-slate-400">
<li>
<button
onClick={() => navigateTo('privacy')}
className="hover:text-brand-400 transition-colors cursor-pointer text-left font-semibold text-slate-300"
id="link-nav-privacy-page"
>
Privacy Policy (POPIA)
</button>
</li>
<li>
<button
onClick={() => navigateTo('terms')}
className="hover:text-brand-400 transition-colors cursor-pointer text-left font-semibold text-slate-300"
id="link-nav-terms-page"
>
Terms &amp; Conditions
</button>
</li>
<li>
<button
onClick={() => setFooterModalType('privacy')}
className="text-slate-500 hover:text-brand-400 text-[11px] transition-colors cursor-pointer text-left"
id="btn-footer-modal-privacy"
>
Quick POPIA Modal
</button>
</li>
<li>
<button
onClick={() => setFooterModalType('terms')}
className="text-slate-500 hover:text-brand-400 text-[11px] transition-colors cursor-pointer text-left"
id="btn-footer-modal-terms"
>
Quick Terms Modal
</button>
</li>
</ul>
</div>
<div className="md:col-span-2 text-left space-y-3">
<h5 className="text-xs font-mono font-bold text-slate-200 uppercase tracking-wider">
Specifications
</h5>
<div className="space-y-2">
<span className="inline-block text-[10px] font-semibold bg-emerald-950/50 text-emerald-400 px-2.5 py-1 rounded border border-emerald-900/50">
Container & Cloud Ready
Container &amp; Cloud Ready
</span>
<p className="text-[10px] leading-relaxed text-slate-500">
Fully optimized container configurations with integrated Docker structures, designed for easy deployment to khongisa.co.za or custom Kubernetes setups.
@@ -653,6 +757,57 @@ export default function App() {
</div>
</footer>
{/* Cookie Acceptance Popup Banner (Bottom Left, Dismissible, Design-language cohesive) */}
{showCookieBanner && (
<div
id="cookie-consent-banner"
className="fixed bottom-6 left-6 z-50 max-w-sm p-5 bg-slate-900/95 backdrop-blur-md border border-slate-800 rounded-2xl shadow-2xl flex flex-col space-y-3 text-left animate-slide-in-left"
>
<div className="flex items-start justify-between">
<div className="flex items-center space-x-2 text-brand-400">
<Cookie className="w-5 h-5 shrink-0" />
<span className="text-xs font-mono font-bold uppercase tracking-wider text-white">Cookie Compliance</span>
</div>
<button
onClick={dismissCookieBanner}
className="text-slate-500 hover:text-white transition-colors p-1 hover:bg-slate-800 rounded"
title="Dismiss"
id="btn-close-cookies-cross"
>
<X className="w-4 h-4" />
</button>
</div>
<p className="text-[11px] text-slate-300 leading-relaxed">
We use essential local storage structures to secure and maintain your customized estimate logs, and ensure strict POPIA privacy compliance. No tracking cookies are launched.
</p>
<div className="flex items-center space-x-3 pt-1">
<button
onClick={dismissCookieBanner}
id="btn-accept-cookies"
className="px-4 py-2 bg-brand-500 hover:bg-brand-600 text-white font-bold text-[10px] rounded-lg shadow-sm transition-colors cursor-pointer"
>
Accept &amp; Close
</button>
<button
onClick={() => navigateTo('privacy')}
id="btn-cookies-read-policy"
className="text-[10px] text-slate-400 hover:text-brand-400 underline font-mono cursor-pointer"
>
Read POPIA Policy
</button>
</div>
</div>
)}
{/* Footer Modals */}
{footerModalType && (
<ComplianceModal
isOpen={!!footerModalType}
type={footerModalType}
onClose={() => setFooterModalType(null)}
/>
)}
</div>
);
}
+268
View File
@@ -0,0 +1,268 @@
import React from 'react';
import { Shield, Key, FileText, CheckCircle, Scale, AlertTriangle, HelpCircle } from 'lucide-react';
export function PrivacyPolicyContent() {
return (
<div className="space-y-6 text-slate-350 text-sm leading-relaxed text-left">
<div className="flex items-center space-x-3 border-b border-slate-850 pb-4">
<Shield className="w-6 h-6 text-brand-500" />
<div>
<h2 className="text-lg font-bold text-white font-display">POPIA Privacy Policy</h2>
<p className="text-xs text-slate-500 font-mono">Last Updated: July 2026 LiteCharms (PTY) Ltd</p>
</div>
</div>
<p>
LiteCharms (PTY) Ltd (&quot;LiteCharms&quot;, &quot;we&quot;, &quot;us&quot;, or &quot;our&quot;), registered in South Africa under Registration Number <strong>2020/269438/07</strong>, is committed to safeguarding your privacy in strict compliance with the <strong>Protection of Personal Information Act, No. 4 of 2013 (POPIA)</strong>. This privacy policy describes how we collect, process, use, and store your personal information when you use our website, interactive project estimator, and contact portals.
</p>
<section className="space-y-2">
<h3 className="text-sm font-bold text-white uppercase font-mono tracking-wider flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-brand-500"></span>
1. Information We Collect
</h3>
<p>
We collect personal information directly from you when you actively fill out forms on our website or request custom software development estimators. This includes:
</p>
<ul className="list-disc pl-5 space-y-1 text-xs">
<li><strong>Identity Information:</strong> Full Name, company name, or registered organizational details.</li>
<li><strong>Contact Information:</strong> Active email addresses, mobile or landline telephone numbers.</li>
<li><strong>Project &amp; Estimate Data:</strong> Desired service categories (such as APIs, MCP, gRPC servers, or identity servers), scale preferences, timeline urgency, custom operational briefs, and ballpark budget specifications.</li>
</ul>
</section>
<section className="space-y-2">
<h3 className="text-sm font-bold text-white uppercase font-mono tracking-wider flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-brand-500"></span>
2. Legal Basis and Purpose of Processing
</h3>
<p>
In accordance with Section 11 of POPIA, we process your personal information solely for the following legitimate business purposes:
</p>
<ul className="list-disc pl-5 space-y-1 text-xs">
<li>To formulate and adjust interactive estimation cost guides and SOW (Statement of Work) draft packages.</li>
<li>To contact you regarding your submitted project configurations or schedule corporate consultations.</li>
<li>To design, test, and host isolated container environments on our khongisa.co.za cloud infrastructures.</li>
<li>To ensure legal compliance under South African trade, accounting, and communication safety statutes.</li>
</ul>
</section>
<section className="space-y-2">
<h3 className="text-sm font-bold text-white uppercase font-mono tracking-wider flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-brand-500"></span>
3. Information Security Measures
</h3>
<p>
We employ robust technical and organizational measures to prevent unauthorized access, loss, misuse, or alteration of your personal data. All interactive inquiries saved locally are managed securely in isolated web states, and emails containing project layouts are transmitted through industry-standard TLS encryption protocols.
</p>
</section>
<section className="space-y-2">
<h3 className="text-sm font-bold text-white uppercase font-mono tracking-wider flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-brand-500"></span>
4. Your Data Subject Rights under POPIA
</h3>
<p>
Under Sections 23 to 25 of POPIA, South African data subjects have the following legal rights:
</p>
<ul className="list-disc pl-5 space-y-1 text-xs">
<li><strong>Right to Access:</strong> You may request a record of the personal information we hold about you.</li>
<li><strong>Right to Correction:</strong> You may request that we update, correct, or complete inaccurate or outdated records.</li>
<li><strong>Right to Destruction:</strong> You may request that we delete or permanently de-identify your personal record databases.</li>
</ul>
<p className="text-xs">
To exercise any of these rights, please contact our designated Information Officer at <a href="mailto:contact@litecharms.co.za" className="text-brand-400 underline">contact@litecharms.co.za</a>.
</p>
</section>
<section className="space-y-2 border-t border-slate-900 pt-4 text-[11px] text-slate-500">
<p>
LiteCharms (PTY) Ltd is located at 1633 Liebenburg Rd, Noordwyk, Midrand, Gauteng, 1687, South Africa. Information Officer Email: contact@litecharms.co.za.
</p>
</section>
</div>
);
}
export function TermsContent() {
return (
<div className="space-y-6 text-slate-350 text-sm leading-relaxed text-left">
<div className="flex items-center space-x-3 border-b border-slate-850 pb-4">
<Scale className="w-6 h-6 text-brand-500" />
<div>
<h2 className="text-lg font-bold text-white font-display">Terms &amp; Conditions</h2>
<p className="text-xs text-slate-500 font-mono">Last Updated: July 2026 LiteCharms (PTY) Ltd</p>
</div>
</div>
<p>
Please read these Terms and Conditions (&quot;Terms&quot;, &quot;Agreement&quot;) carefully before utilizing the interactive estimator, submitting inquiry forms, or engaging the custom software engineering, server setups, or secure tunnel services of <strong>LiteCharms (PTY) Ltd</strong> (&quot;LiteCharms&quot;, &quot;we&quot;, &quot;us&quot;, or &quot;our&quot;). By accessing this platform or accepting an estimated project roadmap, you signify absolute consent to these South African-governed terms.
</p>
<div className="p-4 rounded-xl bg-amber-950/20 border border-amber-900/40 text-xs text-amber-300 space-y-2">
<div className="flex items-center gap-2 font-bold font-mono uppercase tracking-wider text-amber-400">
<AlertTriangle className="w-4 h-4" />
Critical Financial Notice: Upfront Project Initiation Cost
</div>
<p>
Engaging LiteCharms for active project execution requires an **Upfront Project Initiation Cost** calculated proportionately based on your chosen development timeframe:
</p>
<ul className="list-disc pl-5 space-y-1">
<li><strong>Express Delivery:</strong> 50% upfront project initiation fee</li>
<li><strong>Balanced Schedule:</strong> 40% upfront project initiation fee</li>
<li><strong>Phased Rollout:</strong> 30% upfront project initiation fee</li>
</ul>
<p className="font-bold">
IMPORTANT: These initiation payments are dedicated immediately to securing critical engineering resources, software layout drafting, and isolated server environments. They are NOT refundable deposits and cannot be returned under any circumstances once development operations have officially commenced.
</p>
</div>
<section className="space-y-2">
<h3 className="text-sm font-bold text-white uppercase font-mono tracking-wider flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-brand-500"></span>
1. Interactive Estimator Limitations &amp; Revisions
</h3>
<p>
Any calculation, budget range, or delivery schedule generated by the LiteCharms interactive estimator tool serves strictly as a <strong>non-binding ballpark cost layout guide</strong>.
</p>
<ul className="list-disc pl-5 space-y-1 text-xs">
<li>These estimates are for initial feasibility planning and expectation management.</li>
<li>An estimate is NOT a formal commercial offer, quote, or binding invoice.</li>
<li>Actual project fees are finalized solely inside a comprehensive, signed Statement of Work (SOW) after our technical consultants evaluate complete API integrations, legacy databases, or local hardware constraints.</li>
</ul>
</section>
<section className="space-y-2">
<h3 className="text-sm font-bold text-white uppercase font-mono tracking-wider flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-brand-500"></span>
2. Delivery &amp; System Testing (SIT &amp; UAT)
</h3>
<p>
LiteCharms follows strict software quality assurance guidelines. Before final digital assets, source repositories, or server configurations are handed over to live production or transferred to client ownership, the following protocol is mandatory:
</p>
<ul className="list-disc pl-5 space-y-1 text-xs">
<li><strong>System Integration Testing (SIT):</strong> The software is evaluated inside isolated testing environments or private staging instances.</li>
<li><strong>User Acceptance Testing (UAT):</strong> The client is given a designated timeframe to perform exhaustive functional reviews and submit sign-off approval.</li>
<li><strong>Payment Terms:</strong> Upon successful UAT completion and acceptance, the final outstanding project balance must be paid in full in accordance with quoted invoice terms (usually within 7 business days) before production publication, App Store launch, or server admin credential handover.</li>
</ul>
</section>
<section className="space-y-2">
<h3 className="text-sm font-bold text-white uppercase font-mono tracking-wider flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-brand-500"></span>
3. Mitigation of Scope Creep
</h3>
<p>
We budget engineering hours and container resources precisely based on initial agreed specifications.
If a client requests features, fields, database structures, additional gRPC/MCP integrations, or server configurations that exceed the signed SOW, such requests constitute <strong>Scope Creep</strong>.
</p>
<ul className="list-disc pl-5 space-y-1 text-xs">
<li>Scope creep will attract separate, additional billing charges, determined by LiteCharms at the time of the request.</li>
<li>These changes are subject to written estimate revisions, updated delivery timelines, and formal consent from both parties.</li>
<li>No work on scope revisions will start until both parties formally sign off on the pricing adjustment.</li>
</ul>
</section>
<section className="space-y-2">
<h3 className="text-sm font-bold text-white uppercase font-mono tracking-wider flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-brand-500"></span>
4. Liability Limitations &amp; Governing Law
</h3>
<p>
To the maximum extent permitted under South African law, including the Electronic Communications and Transactions Act, 2002 (ECTA) and the Consumer Protection Act, 2008 (CPA):
</p>
<ul className="list-disc pl-5 space-y-1 text-xs">
<li>LiteCharms is not liable for indirect, incidental, or consequential damages, including loss of profits, system downtimes, local internet blackouts, or cloud hosting interruptions.</li>
<li>Our total combined liability for any dispute arising out of or related to this agreement is strictly capped at the direct ZAR amount paid to us by the client for the specific project segment.</li>
<li>This Agreement is governed by and construed in accordance with the laws of the <strong>Republic of South Africa</strong>, and any dispute is subject to the exclusive jurisdiction of the Gauteng High Court, Johannesburg/Pretoria.</li>
</ul>
</section>
</div>
);
}
interface CompliancePageWrapperProps {
type: 'privacy' | 'terms';
onBack: () => void;
}
export function CompliancePageWrapper({ type, onBack }: CompliancePageWrapperProps) {
React.useEffect(() => {
window.scrollTo({ top: 0, behavior: 'instant' });
}, [type]);
return (
<div className="min-h-screen bg-slate-950 text-slate-100 flex flex-col justify-between py-12 px-6">
<div className="max-w-4xl mx-auto w-full space-y-8 bg-slate-900/40 p-6 md:p-10 rounded-2xl border border-slate-850 shadow-2xl">
<div className="flex justify-between items-center pb-4 border-b border-slate-800">
<button
onClick={onBack}
className="text-xs font-mono font-bold text-brand-500 hover:text-brand-400 flex items-center space-x-2 transition-all cursor-pointer group"
>
<span className="transform group-hover:-translate-x-1 transition-transform inline-block">&larr;</span>
<span>Return to Home</span>
</button>
<span className="text-[10px] font-mono text-slate-500 bg-slate-950 px-2.5 py-1 rounded border border-slate-900">
LiteCharms PTY Ltd South Africa
</span>
</div>
{type === 'privacy' ? <PrivacyPolicyContent /> : <TermsContent />}
<div className="pt-6 border-t border-slate-800 flex justify-between items-center text-xs text-slate-500 font-mono">
<span>&copy; {new Date().getFullYear()} LiteCharms (PTY) Ltd</span>
<button
onClick={onBack}
className="text-brand-500 hover:underline cursor-pointer"
>
Back to Home
</button>
</div>
</div>
</div>
);
}
interface ComplianceModalProps {
isOpen: boolean;
type: 'privacy' | 'terms';
onClose: () => void;
}
export function ComplianceModal({ isOpen, type, onClose }: ComplianceModalProps) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 overflow-y-auto flex items-center justify-center p-4 bg-slate-950/80 backdrop-blur-sm animate-fade-in">
<div className="relative bg-slate-900 border border-slate-800 rounded-2xl max-w-2xl w-full max-h-[85vh] overflow-hidden flex flex-col shadow-2xl">
{/* Header */}
<div className="p-5 border-b border-slate-850 flex justify-between items-center bg-slate-950/40">
<span className="text-xs font-mono font-bold text-brand-500 uppercase tracking-wider">
{type === 'privacy' ? 'POPIA Compliance Statement' : 'Commercial Agreement Terms'}
</span>
<button
onClick={onClose}
className="text-slate-400 hover:text-white font-bold text-lg p-1.5 hover:bg-slate-800 rounded-lg transition-colors cursor-pointer"
>
&times;
</button>
</div>
{/* Content */}
<div className="p-6 overflow-y-auto max-h-[60vh] space-y-4">
{type === 'privacy' ? <PrivacyPolicyContent /> : <TermsContent />}
</div>
{/* Footer */}
<div className="p-4 border-t border-slate-850 flex justify-end bg-slate-950/40">
<button
onClick={onClose}
className="px-5 py-2 rounded-xl text-xs font-bold bg-brand-500 hover:bg-brand-600 text-white transition-colors cursor-pointer"
>
I Understand &amp; Close
</button>
</div>
</div>
</div>
);
}
+239 -16
View File
@@ -1,7 +1,8 @@
import { useState, useEffect, FormEvent, useRef } from 'react';
import { Inquiry } from '../types';
import { sendEmailClientSide } from '../utils/emailController';
import { Calculator, Sparkles, Send, ShieldCheck, CheckCircle2, History, Trash2 } from 'lucide-react';
import { Calculator, Sparkles, Send, ShieldCheck, CheckCircle2, History, Trash2, FileText } from 'lucide-react';
import { ComplianceModal } from './ComplianceContent';
interface ProjectEstimatorProps {
onInquirySubmitted: (inquiry: Inquiry) => void;
@@ -20,8 +21,15 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
legacy: false,
support: false,
});
const [identityType, setIdentityType] = useState<'none' | 'duende' | 'custom'>('none');
const [serverConfigType, setServerConfigType] = useState<'none' | 'linux' | 'windows'>('none');
const [tunnelService, setTunnelService] = useState<'none' | 'khongisa'>('none');
const [timeline, setTimeline] = useState<'express' | 'balanced' | 'phased'>('balanced');
// Compliance states
const [termsAgreed, setTermsAgreed] = useState(false);
const [termsModalOpen, setTermsModalOpen] = useState(false);
// Contact Form States
const [name, setName] = useState('');
const [email, setEmail] = useState('');
@@ -44,17 +52,22 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
}
}, []);
const isMounted = useRef(false);
const prevStepRef = useRef<1 | 2>(1);
const prevSuccessRef = useRef<boolean>(false);
// Scroll to estimator top when step or success state changes
// Scroll to estimator top when step or success state actually changes
useEffect(() => {
if (!isMounted.current) {
isMounted.current = true;
return;
}
const element = document.getElementById('estimator');
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
const prevStep = prevStepRef.current;
const prevSuccess = prevSuccessRef.current;
prevStepRef.current = step;
prevSuccessRef.current = success;
if (prevStep !== step || prevSuccess !== success) {
const element = document.getElementById('estimator');
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
}, [step, success]);
@@ -85,6 +98,18 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
baseWeeks = 9;
serviceLabel = 'Desktop Software Layout';
break;
case 'api-grpc':
baseMin = 50000;
baseMax = 100000;
baseWeeks = 6;
serviceLabel = 'API, MCP & gRPC Engineering';
break;
case 'consultancy':
baseMin = 30000;
baseMax = 70000;
baseWeeks = 4;
serviceLabel = 'Enterprise Architecture & IT Consultancy';
break;
case 'infra-planning':
baseMin = 25000;
baseMax = 55000;
@@ -103,6 +128,18 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
baseWeeks = 7;
serviceLabel = 'Khongisa Private Cloud Hosting';
break;
case 'server-config':
baseMin = 35000;
baseMax = 80000;
baseWeeks = 5;
serviceLabel = 'On-Premise Server Setup & OS Configuration';
break;
case 'identity-security':
baseMin = 45000;
baseMax = 95000;
baseWeeks = 6;
serviceLabel = 'Identity Management & Secure Tunnels';
break;
}
// Scale multiplier
@@ -154,6 +191,45 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
breakdown.push({ item: 'Proactive 12-Month Support SLA (Est.)', price: `R ${minAdd.toLocaleString('en-ZA')} - R ${maxAdd.toLocaleString('en-ZA')}` });
}
// Identity Management Solutions Pricing
if (identityType === 'duende') {
const minAdd = 25000;
const maxAdd = 45000;
minCost += minAdd;
maxCost += maxAdd;
breakdown.push({ item: 'Self-Hosted Duende Skoruba Identity Server', price: `R ${minAdd.toLocaleString('en-ZA')} - R ${maxAdd.toLocaleString('en-ZA')}` });
} else if (identityType === 'custom') {
const minAdd = 15000;
const maxAdd = 30000;
minCost += minAdd;
maxCost += maxAdd;
breakdown.push({ item: 'Custom IAM Integration (Auth0/Keycloak)', price: `R ${minAdd.toLocaleString('en-ZA')} - R ${maxAdd.toLocaleString('en-ZA')}` });
}
// OS & Server Configurations Pricing
if (serverConfigType === 'linux') {
const minAdd = 20000;
const maxAdd = 35000;
minCost += minAdd;
maxCost += maxAdd;
breakdown.push({ item: 'Linux Environment Server Configuration', price: `R ${minAdd.toLocaleString('en-ZA')} - R ${maxAdd.toLocaleString('en-ZA')}` });
} else if (serverConfigType === 'windows') {
const minAdd = 25000;
const maxAdd = 40000;
minCost += minAdd;
maxCost += maxAdd;
breakdown.push({ item: 'Windows Server OS & AD Configuration', price: `R ${minAdd.toLocaleString('en-ZA')} - R ${maxAdd.toLocaleString('en-ZA')}` });
}
// Secure Tunnel Service Pricing
if (tunnelService === 'khongisa') {
const minAdd = 10000;
const maxAdd = 18000;
minCost += minAdd;
maxCost += maxAdd;
breakdown.push({ item: 'khongisa.co.za Private Secure Cloud Tunnel', price: `R ${minAdd.toLocaleString('en-ZA')} - R ${maxAdd.toLocaleString('en-ZA')}` });
}
// Timeline factors
if (timeline === 'express') {
minCost *= 1.3;
@@ -239,6 +315,7 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
setPhone('');
setCompany('');
setNotes('');
setTermsAgreed(false);
}
};
@@ -300,9 +377,13 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
{ id: 'web-design', label: 'Web App Design', type: 'Software' },
{ id: 'mobile-design', label: 'Mobile App Eng', type: 'Software' },
{ id: 'desktop-design', label: 'Desktop Software', type: 'Software' },
{ id: 'api-grpc', label: 'API & gRPC Engineering', type: 'Software' },
{ id: 'consultancy', label: 'Architecture Consultancy', type: 'Software' },
{ id: 'infra-planning', label: 'Docker Containerization', type: 'Cloud & Hosting' },
{ id: 'infra-design', label: 'Kubernetes & IaC', type: 'Cloud & Hosting' },
{ id: 'infra-rollout', label: 'Khongisa Private Cloud', type: 'Cloud & Hosting' },
{ id: 'server-config', label: 'Server & OS Setup', type: 'Cloud & Hosting' },
{ id: 'identity-security', label: 'Identity & Secure Tunnels', type: 'Cloud & Hosting' },
].map((srv) => (
<button
key={srv.id}
@@ -355,10 +436,105 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
</div>
</div>
{/* Option 3: Technical Add-ons */}
{/* Option 3: Identity & Access Management (IAM) */}
<div className="pt-4 border-t border-slate-800/80">
<label className="block text-xs font-mono font-bold uppercase text-slate-400 tracking-wider mb-2">
C. Identity &amp; Access Management (IAM)
</label>
<p className="text-[10px] text-slate-400 mb-3">
Enable high-grade user authentication and unified Single Sign-On (SSO) systems.
</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
{[
{ id: 'none', label: 'None / Standard', desc: 'Default local security model' },
{ id: 'duende', label: 'Duende Skoruba', desc: 'Self-hosted SSO identity server' },
{ id: 'custom', label: 'Custom Provider', desc: 'Auth0, Keycloak or Active Directory' },
].map((iam) => (
<button
key={iam.id}
id={`select-iam-${iam.id}`}
type="button"
onClick={() => setIdentityType(iam.id as 'none' | 'duende' | 'custom')}
className={`p-3.5 rounded-xl text-left border transition-all cursor-pointer flex flex-col justify-between ${
identityType === iam.id
? 'border-brand-500 bg-brand-950/40 text-white ring-1 ring-brand-500/30'
: 'border-slate-850 bg-slate-900/10 text-slate-300 hover:border-slate-700 hover:bg-slate-900/40'
}`}
>
<span className="text-xs font-bold block text-white">{iam.label}</span>
<span className="text-[9px] text-slate-400 mt-1 leading-normal">{iam.desc}</span>
</button>
))}
</div>
</div>
{/* Option 4: Local OS & Server Setup */}
<div className="pt-4 border-t border-slate-800/80">
<label className="block text-xs font-mono font-bold uppercase text-slate-400 tracking-wider mb-2">
D. Local OS &amp; Server Setup
</label>
<p className="text-[10px] text-slate-400 mb-3">
Configure hardware platforms, operating systems, and domains in your local environment.
</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
{[
{ id: 'none', label: 'No Local Setup', desc: 'Deploy onto cloud container instances' },
{ id: 'linux', label: 'Linux OS Setup', desc: 'Ubuntu/Rocky bare-metal or hypervisor config' },
{ id: 'windows', label: 'Windows Server', desc: 'Windows OS rollout with Active Directory' },
].map((os) => (
<button
key={os.id}
id={`select-os-${os.id}`}
type="button"
onClick={() => setServerConfigType(os.id as 'none' | 'linux' | 'windows')}
className={`p-3.5 rounded-xl text-left border transition-all cursor-pointer flex flex-col justify-between ${
serverConfigType === os.id
? 'border-brand-500 bg-brand-950/40 text-white ring-1 ring-brand-500/30'
: 'border-slate-850 bg-slate-900/10 text-slate-300 hover:border-slate-700 hover:bg-slate-900/40'
}`}
>
<span className="text-xs font-bold block text-white">{os.label}</span>
<span className="text-[9px] text-slate-400 mt-1 leading-normal">{os.desc}</span>
</button>
))}
</div>
</div>
{/* Option 5: Secure Tunnel Service */}
<div className="pt-4 border-t border-slate-800/80">
<label className="block text-xs font-mono font-bold uppercase text-slate-400 tracking-wider mb-2">
E. Edge Security &amp; Secure Tunnels
</label>
<p className="text-[10px] text-slate-400 mb-3">
Establish isolated connections and secure client routing.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{[
{ id: 'none', label: 'No Dedicated Tunnel', desc: 'Standard firewall access logic' },
{ id: 'khongisa', label: 'khongisa.co.za Secure Tunnel', desc: 'Encrypted tunnel hosted on our secure private cloud' },
].map((tnl) => (
<button
key={tnl.id}
id={`select-tunnel-${tnl.id}`}
type="button"
onClick={() => setTunnelService(tnl.id as 'none' | 'khongisa')}
className={`p-3.5 rounded-xl text-left border transition-all cursor-pointer flex flex-col justify-between ${
tunnelService === tnl.id
? 'border-brand-500 bg-brand-950/40 text-white ring-1 ring-brand-500/30'
: 'border-slate-850 bg-slate-900/10 text-slate-300 hover:border-slate-700 hover:bg-slate-900/40'
}`}
>
<span className="text-xs font-bold block text-white">{tnl.label}</span>
<span className="text-[9px] text-slate-400 mt-1 leading-normal">{tnl.desc}</span>
</button>
))}
</div>
</div>
{/* Option 6: Technical Add-ons */}
<div className="pt-4 border-t border-slate-800/80">
<label className="block text-xs font-mono font-bold uppercase text-slate-400 tracking-wider mb-3">
C. Technical Options & Compliance
F. Technical Options &amp; Compliance
</label>
<div className="space-y-3">
{[
@@ -392,10 +568,10 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
</div>
</div>
{/* Option 4: Timeline Priority */}
{/* Option 7: Timeline Priority */}
<div className="pt-4 border-t border-slate-800/80">
<label className="block text-xs font-mono font-bold uppercase text-slate-400 tracking-wider mb-3">
D. Timeline Urgency
G. Timeline Urgency
</label>
<div className="grid grid-cols-3 gap-3">
{[
@@ -500,6 +676,23 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
/>
</div>
{/* Compliance Terms Agreement Checkbox */}
<div className="p-4 rounded-xl bg-slate-950/60 border border-slate-850 space-y-3">
<label className="flex items-start space-x-3 cursor-pointer text-slate-300 hover:text-white transition-colors">
<input
type="checkbox"
id="checkbox-accept-terms"
required
checked={termsAgreed}
onChange={(e) => setTermsAgreed(e.target.checked)}
className="mt-1 accent-brand-500 rounded cursor-pointer shrink-0"
/>
<span className="text-xs leading-normal select-none">
I agree to the LiteCharms <button type="button" onClick={() => setTermsModalOpen(true)} className="text-brand-400 hover:text-brand-300 underline font-semibold focus:outline-none cursor-pointer">Terms &amp; Conditions</button> including the non-refundable 30% to 50% upfront project initiation cost, scope creep policy, and SIT/UAT terms. *
</span>
</label>
</div>
<div className="flex space-x-3 pt-2">
<button
type="button"
@@ -512,7 +705,7 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
<button
type="submit"
id="btn-submit-proposal"
disabled={submitting}
disabled={submitting || !termsAgreed}
className="w-2/3 py-3 px-4 rounded-xl font-bold bg-brand-500 hover:bg-brand-600 text-white shadow-md shadow-brand-500/10 flex items-center justify-center space-x-2 transition-colors disabled:opacity-50 cursor-pointer"
>
{submitting ? (
@@ -553,7 +746,26 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
</span>
</div>
<p className="text-[10px] text-slate-400 leading-relaxed pt-1">
*Approximate cloud deployment, software engineering & layout rates. Scaled via khongisa.co.za Private Cloud.
*Approximate cloud deployment, software engineering &amp; layout rates. Scaled via khongisa.co.za Private Cloud.
</p>
</div>
{/* Project Initiation Cost Notice */}
<div className="p-4 rounded-xl bg-brand-950/20 border border-brand-900/30 space-y-2">
<p className="text-[10px] font-mono font-bold text-brand-400 uppercase tracking-wider">
{timeline === 'express' ? '50%' : timeline === 'balanced' ? '40%' : '30%'} Upfront Initiation Cost
</p>
<div className="flex items-baseline space-x-1.5">
<span className="font-display text-lg font-bold text-white">
R {Math.round(estimate.totalMin * (timeline === 'express' ? 0.50 : timeline === 'balanced' ? 0.40 : 0.30)).toLocaleString('en-ZA')}
</span>
<span className="text-slate-400 text-[10px]">to</span>
<span className="font-display text-lg font-bold text-accent-400">
R {Math.round(estimate.totalMax * (timeline === 'express' ? 0.50 : timeline === 'balanced' ? 0.40 : 0.30)).toLocaleString('en-ZA')}
</span>
</div>
<p className="text-[9px] text-slate-400 leading-normal">
Non-refundable upfront project initiation cost (not a deposit) expected prior to work kick-off.
</p>
</div>
@@ -592,6 +804,16 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
</div>
</div>
{/* Expectation Management Section */}
<div className="pt-4 border-t border-slate-800 space-y-2.5 text-[10px] text-slate-400 leading-relaxed text-left">
<p>
💡 <strong>Mandatory SIT &amp; UAT:</strong> Before live environment transfer or production rollout, the client must perform System Integration Testing (SIT) &amp; User Acceptance Testing (UAT). Upon successful test approval, final balance payment must be settled within formal quoted invoice terms.
</p>
<p>
<strong>Scope Creep Policy:</strong> Deliverables, features, or setups beyond this initial layout proposal will attract separate, mutually agreed engineering fees.
</p>
</div>
{/* Compliance Box */}
<div className="p-4 rounded-xl bg-slate-950/80 border border-slate-800 flex items-start space-x-3 text-xs text-slate-400">
<ShieldCheck className="w-5 h-5 text-brand-500 shrink-0 mt-0.5" />
@@ -642,6 +864,7 @@ export default function ProjectEstimator({ onInquirySubmitted }: ProjectEstimato
</div>
)}
</div>
<ComplianceModal isOpen={termsModalOpen} type="terms" onClose={() => setTermsModalOpen(false)} />
</div>
);
}
+56
View File
@@ -84,6 +84,62 @@ export const SERVICES: Service[] = [
],
icon: 'Server',
category: 'infra'
},
{
id: 'api-grpc',
title: 'API, MCP & gRPC Engineering',
description: 'High-performance API architectures, Model Context Protocol (MCP) servers, and gRPC microservices for fast, ultra-reliable data exchange and AI workflows.',
details: [
'Custom RESTful, GraphQL, and webhook api backends',
'Model Context Protocol (MCP) server integration for LLM/AI interaction pipelines',
'Ultra-fast gRPC messaging layers and cross-service communication meshes',
'Strict schema validation, robust rate limiting, and performance profiling',
'Automated OpenAPI/Swagger specification layouts and Client SDK generation'
],
icon: 'Network',
category: 'apps'
},
{
id: 'consultancy',
title: 'Enterprise Architecture & IT Consultancy',
description: 'High-level strategic consulting, feasibility reports, system health checks, and technology stack optimization blueprints tailored for your team.',
details: [
'Custom technological stack evaluation and performance auditing',
'System health checks, architectural diagrams, and feasibility studies',
'Migration roadmap planning, software layout reviews, and risk profiling',
'Regulatory compliance consultation (including POPIA data safety standards)',
'Disaster recovery planning, server failover strategy, and database design'
],
icon: 'Compass',
category: 'apps'
},
{
id: 'server-config',
title: 'On-Premise Server Setup & OS Configuration',
description: 'Expert bare-metal or virtualized server setups on Linux and Windows Server environments, fully configured for your local infrastructure and security constraints.',
details: [
'Linux (Ubuntu, Rocky, RHEL) and Windows Server bare-metal OS setup',
'Hypervisor installation (VMware ESXi, Proxmox VE, or Hyper-V)',
'Active Directory configuration, Domain Controllers, and local DNS/DHCP routing',
'Local hardware storage pooling (RAID configuration) and automated backup scripts',
'Host-level firewall policies, secure SSH, and local network audit readiness'
],
icon: 'Settings',
category: 'infra'
},
{
id: 'identity-security',
title: 'Identity Management & Secure Tunnels',
description: 'Enterprise identity services featuring self-hosted Duende Skoruba OpenID/OAuth servers and secure remote tunnel deployments on khongisa.co.za cloud.',
details: [
'IdentityServer4 / Duende Skoruba SSO self-hosted setups and customization',
'Integration with third-party providers (Auth0, Okta, Keycloak, or Active Directory)',
'Secure client-to-cloud tunnel routing hosted on khongisa.co.za cloud',
'Multi-factor authentication (MFA) and granular token-based access control',
'End-to-end encrypted connection channels with automatic port-knocking and firewalling'
],
icon: 'Key',
category: 'infra'
}
];