Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/phase1-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Phase 1 checks
on:
pull_request:
branches: [main]
push:
branches: [feature/phase1-working-forms]
jobs:
syntax-and-wiring:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Check JavaScript syntax
run: |
node --check app.js
node --check forms.js
node --check part2.js
node --check src/index.js
- name: Check Phase 1 wiring
run: |
grep -q 'src="forms.js" defer' index.html
grep -q 'src="forms.js" defer' service.html
grep -q 'id="enquiry-form"' index.html
grep -q 'id="service-enquiry-form"' service.html
grep -q 'POST /api/enquiry' README.md
grep -q 'CREATE TABLE IF NOT EXISTS Leads' migrations/0001_phase1_enquiries.sql
40 changes: 23 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
# GOAXIS CONSULTANCY — Cloudflare Pages static deployment
# GOAXIS CONSULTANCY — Phase 1 Working Forms

This repository is configured as a **static frontend** for Cloudflare Pages. It does not include a production backend, secure authentication, enquiry storage, admin API, or verified payment processing.
Repository: `arpit-ae/axis`. The public site is served as static assets by a Cloudflare Worker; Phase 1 adds a Worker API route at `POST /api/enquiry` and a D1 `Leads` table.

## Cloudflare Pages settings
- Connect GitHub repository: `arpit-ae/axis`
- Production branch: `main`
- Framework preset: `None`
- Build command: leave blank (or `exit 0` if the dashboard requires a command)
- Build output directory: `/` (repository root; in Cloudflare's UI use `.` if accepted)
- Root directory: leave blank
## Files added
- `src/index.js`: Worker API plus static asset fallback.
- `forms.js`: sends the homepage and service quotation forms to the API.
- `migrations/0001_phase1_enquiries.sql`: creates the enquiry table and indexes.
- `wrangler.toml`: Worker asset and D1 binding configuration.

Cloudflare Pages will rebuild automatically when commits land on `main`. Do **not** use `npx wrangler deploy` for this static Pages site; that command targets Workers. If deploying from CLI, use `npx wrangler pages deploy . --project-name=goaxis-consultancy --branch=main` after authenticating Wrangler and confirming the Pages project name.
## Safety before production
- Confirm this Wrangler config targets the intended Cloudflare account and existing `goaxis` Worker.
- Confirm the D1 database ID in Cloudflare before applying migrations.
- Apply the D1 migration only after checking existing migration/table state.
- Preview-test the homepage and service forms, and verify their records in D1.
- Review and complete the Privacy Policy and Terms before collecting real customer information.
- The customer login/signup and admin pages remain prototypes; this phase does not implement secure authentication, admin access, payments or a customer dashboard.
- Do not run a production deploy until the binding and migration have been verified.

## Before accepting real customers/payments
- Replace placeholder contact details and review draft Terms/Privacy pages.
- Build and configure a server-side API for enquiry submissions, customer accounts and admin access.
- Configure verified payment-gateway webhooks; never trust a browser return URL as proof of payment.
- Add Cloudflare Pages Functions/Workers and configure D1/R2 bindings and secrets only when backend code is ready.
- Do not treat the demo login/signup pages as secure authentication.
## Suggested local checks
```sh
node --check app.js
node --check forms.js
node --check part2.js
node --check src/index.js
```

The enquiry forms are preview-only and do not transmit or save personal information. The admin page is only a shell and must not be used for real operations.
Production uses Cloudflare Worker static assets. The API handles `/api/*`; other requests are passed to the existing static asset service.
2 changes: 1 addition & 1 deletion app.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions forms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
document.addEventListener("DOMContentLoaded", () => {
const forms = ["enquiry-form", "service-enquiry-form"]
.map(id => document.getElementById(id))
.filter(Boolean);

for (const form of forms) {
const feedback = form.querySelector("#form-feedback, #service-feedback, .form-feedback");
const submit = form.querySelector('button[type="submit"]');

// Bot honeypot; real users never see or fill this field.
if (!form.querySelector('[name="website"]')) {
const trap = document.createElement("div");
trap.setAttribute("aria-hidden", "true");
trap.style.cssText = "position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden";
trap.innerHTML = '<label>Leave this field empty<input name="website" type="text" tabindex="-1" autocomplete="off"></label>';
form.appendChild(trap);
}

form.addEventListener("submit", async event => {
event.preventDefault();
if (!form.reportValidity()) return;

const data = new FormData(form);
const requiredConsent = form.querySelector('input[type="checkbox"][required]');
const payload = {
name: String(data.get("name") || ""),
email: String(data.get("email") || ""),
phone: String(data.get("phone") || ""),
service: String(data.get("service") || ""),
businessName: String(data.get("businessName") || ""),
businessType: String(data.get("businessType") || ""),
message: String(data.get("message") || data.get("details") || ""),
consent: Boolean(requiredConsent && requiredConsent.checked),
website: String(data.get("website") || "")
};

if (feedback) {
feedback.textContent = "Sending your enquiry securely…";
feedback.setAttribute("data-state", "loading");
}
if (submit) {
submit.disabled = true;
submit.setAttribute("aria-busy", "true");
}

try {
const response = await fetch("/api/enquiry", {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
credentials: "same-origin",
body: JSON.stringify(payload)
});
const result = await response.json().catch(() => ({}));
if (!response.ok || !result.ok) {
throw new Error(result.error || "We could not send your enquiry. Please try again.");
}
if (feedback) {
feedback.textContent = "Thank you. Your enquiry was received. Reference: " + result.reference;
feedback.setAttribute("data-state", "success");
}
form.reset();
} catch (error) {
if (feedback) {
feedback.textContent = error instanceof Error ? error.message : "Network error. Please try again.";
feedback.setAttribute("data-state", "error");
}
} finally {
if (submit) {
submit.disabled = false;
submit.removeAttribute("aria-busy");
}
}
});
}
});
5 changes: 3 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<link rel="icon" href="assets/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="styles.css">
<script src="app.js" defer></script>
<script src="forms.js" defer></script>
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
Expand Down Expand Up @@ -83,13 +84,13 @@ <h1>Build your business.<br><span>We'll help you</span><br>move forward.</h1>
</div></div></section>
<section class="cta"><div class="container cta-inner"><div><p class="eyebrow light">START WITH A CONVERSATION</p><h2>Your next business step<br>can start <em>today.</em></h2></div><a class="button button-white" href="#contact">Tell us what you need →</a></div></section>
<section class="section-pad contact" id="contact"><div class="container contact-grid">
<div><p class="eyebrow">CONTACT GOAXIS</p><h2>Tell us a little<br>about <span>your plans.</span></h2><p>Share what you are working on and what kind of support you’re looking for.</p><div class="info"><strong>Email &amp; phone</strong><small>Contact details will be added before launch.</small></div><div class="info"><strong>Service area</strong><small>Online support; availability will be confirmed for each request.</small></div><div class="disclaimer-box">Preview form only. It does not send or save your information yet. Online submission will be connected in a later part.</div></div>
<div><p class="eyebrow">CONTACT GOAXIS</p><h2>Tell us a little<br>about <span>your plans.</span></h2><p>Share what you are working on and what kind of support you’re looking for.</p><div class="info"><strong>Email &amp; phone</strong><small>Contact details will be added before launch.</small></div><div class="info"><strong>Service area</strong><small>Online support; availability will be confirmed for each request.</small></div><div class="disclaimer-box">Your details will be used to respond to this enquiry. Please do not include passwords, bank details or sensitive identity numbers.</div></div>
<div class="form-box"><p class="eyebrow">YOUR ENQUIRY</p><h3>How can we help?</h3><form id="enquiry-form">
<div class="form-row"><label for="name">Full name *<input id="name" name="name" autocomplete="name" required maxlength="100" placeholder="Your name"></label><label for="email">Email address *<input id="email" name="email" type="email" autocomplete="email" required maxlength="150" placeholder="you@example.com"></label></div>
<div class="form-row"><label for="phone">Phone (optional)<input id="phone" name="phone" type="tel" autocomplete="tel" placeholder="+91"></label><label for="service">Service of interest *<select id="service" name="service" required><option value="">Choose a service</option><option>Website Development</option><option>GST / Udyam Assistance</option><option>FSSAI Assistance</option><option>IEC / APEDA Guidance</option><option>Business Documentation</option><option>Other / Not sure yet</option></select></label></div>
<label for="message">Tell us about your requirement *<textarea id="message" name="message" required maxlength="2000" rows="4" placeholder="A few details about your goal..."></textarea></label>
<label class="consent"><input type="checkbox" name="consent" required><span>I agree that GOAXIS may use these details to respond to my enquiry.</span></label>
<button class="button form-submit" type="submit">Prepare enquiry →</button><p class="feedback" id="form-feedback" role="status" aria-live="polite">Preview only — no data is sent.</p>
<button class="button form-submit" type="submit">Send enquiry →</button><p class="feedback" id="form-feedback" role="status" aria-live="polite">Your enquiry will be submitted securely.</p>
</form></div>
</div></section>
<section class="final"><div class="container"><strong>G.</strong><span>Make your next move with clarity.</span><a href="#home">Back to top ↑</a></div></section>
Expand Down
17 changes: 17 additions & 0 deletions migrations/0001_phase1_enquiries.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS Leads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
reference TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT,
service TEXT NOT NULL,
business_name TEXT,
business_type TEXT,
message TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'new',
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_leads_created_at ON Leads(created_at);
CREATE INDEX IF NOT EXISTS idx_leads_status ON Leads(status);
CREATE INDEX IF NOT EXISTS idx_leads_email ON Leads(email);
2 changes: 1 addition & 1 deletion part2.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading