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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_SUPABASE_URL=https://your-project-ref.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-public-key
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
lint-and-build:
runs-on: ubuntu-latest
env:
VITE_SUPABASE_URL: https://example.supabase.co
VITE_SUPABASE_ANON_KEY: public-anon-placeholder
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run lint
- run: npm run build
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ pnpm-debug.log*
*.sln
*.sw?

# Vercel
.vercel
# Playwright
/playwright-report
/test-results
/blob-report
135 changes: 89 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,119 @@
# HM Coding

Official website and showcase platform for HM Coding — a modern software studio focused on building high-quality digital products.
Official website for HM Coding, a software studio that builds websites, web apps, mobile apps, AI integrations, and business systems.

## About Us
Live site: https://hm-coding.vercel.app
Canonical domain: https://hmcoding.com

HM Coding is a technology-driven firm specializing in:
## Tech stack

- Modern Websites
- Web Applications
- Mobile Applications
- AI Integrations
- CRM & Business Systems
- UI/UX Focused Digital Products
- React 19
- TypeScript
- Vite 7
- React Router
- Tailwind CSS 3
- Framer Motion
- Supabase Auth + PostgreSQL
- Vercel (static SPA)

We build scalable, visually polished, and performance-focused solutions for startups, businesses, and growing brands.
There is no Express, MongoDB, or Render backend in this repository.

---
## Local setup

## Tech Stack
```bash
npm install
cp .env.example .env
```

Frontend:
Fill `.env` with your Supabase project URL and anon key, then:

- React
- TypeScript
- Tailwind CSS
- Framer Motion
- Vite
```bash
npm run dev
```

Backend:
Other commands:

- Node.js
- Express
- TypeScript
```bash
npm run lint
npm run build
npm run preview
npm run test:e2e
```

## Environment variables

Deployment:
Frontend variables (Vite, must be prefixed with `VITE_`):

- Vercel
- Render
| Variable | Required | Purpose |
| --- | --- | --- |
| `VITE_SUPABASE_URL` | Yes | Supabase project URL |
| `VITE_SUPABASE_ANON_KEY` | Yes | Supabase anon/public key |

---
Never put a Supabase service-role key in frontend code or Vercel `VITE_*` settings.

## Features
For production, set the same variables in the Vercel project.

- Responsive modern UI
- Dark / Light mode support
- Animated sections
- Showcase project pages
- Careers section
- Contact integration
- SEO-friendly structure
- Reusable component architecture
## Data and admin

---
Supabase tables used by the app:

## Live Website
- `jobs` — public careers listings
- `reviews` — testimonials, shown only when approved
- `contact_messages` — contact, internship, startup, and demo inquiries
- `profiles` — admin authorization (`role = admin` or `user`)

https://hm-coding.vercel.app
Apply SQL from `supabase/migrations/20260814_production_security.sql`, then promote an Auth user:

---
```sql
update public.profiles
set role = 'admin', updated_at = now()
where id = '<auth-user-uuid>';
```

## Development
See `supabase/README.md` for the access model.

Install dependencies:
Admin portal: `/hm-portal-admin-dashboard`
Shortcut on the public site: `Ctrl+Shift+H`

```bash
npm install
Route guards are UX only. Row Level Security is authoritative.

Job applications use each listing's external `apply_url`. Internship and startup inquiries are stored as contact messages.

## Public routes

- `/` Home
- `/about`
- `/services`
- `/projects`
- `/projects/:slug` product demos or project pages
- `/careers`
- `/terms`
- `/privacy`

Unknown routes render a branded 404 page.

## Project structure

```
src/ React application
src/admin/ Admin portal
src/components/ Shared UI
src/pages/ Public routes
src/lib/ Supabase client, auth, validation
supabase/migrations Versioned SQL
public/ Static assets, robots.txt, sitemap.xml
```

Vision
## Deployment

Vercel builds with `npm run build` (`tsc -b && vite build`) and publishes `dist`.

`vercel.json` rewrites all paths to `index.html` so React Router deep links work.

## Design

We aim to create modern software experiences that combine clean design, strong engineering, and future-ready technologies.
The site is a dark premium interface. There is no light-mode toggle.

License
## License

Private project — HM Coding
36 changes: 36 additions & 0 deletions e2e/smoke.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { expect, test } from "@playwright/test";

const publicRoutes = [
"/",
"/about",
"/services",
"/projects",
"/careers",
"/terms",
"/privacy",
];

for (const path of publicRoutes) {
test(`loads ${path}`, async ({ page }) => {
const response = await page.goto(path);
expect(response?.ok()).toBeTruthy();
await expect(page.locator("header, h1").first()).toBeVisible();
});
}

test("unknown route shows branded 404", async ({ page }) => {
await page.goto("/this-page-does-not-exist");
await expect(page.getByRole("heading", { name: /does not exist/i })).toBeVisible();
await expect(page.getByRole("link", { name: /return home/i })).toBeVisible();
});

test("admin login route is reachable", async ({ page }) => {
await page.goto("/hm-portal-admin-dashboard/login");
await expect(page.getByRole("heading", { name: /admin login/i })).toBeVisible();
await expect(page.getByLabel(/email/i)).toBeVisible();
});

test("protected admin route redirects unauthenticated users", async ({ page }) => {
await page.goto("/hm-portal-admin-dashboard");
await expect(page).toHaveURL(/hm-portal-admin-dashboard\/login/);
});
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
globalIgnores(['dist']),
globalIgnores(['dist', 'e2e', 'playwright.config.ts']),
{
files: ['**/*.{ts,tsx}'],
extends: [
Expand Down
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
property="og:description"
content="Empowering businesses through custom-built websites, web apps, and mobile apps — with future-ready AI integration."
/>
<meta property="og:image" content="https://hmcoding.com/og-image.jpg" />
<meta property="og:image" content="https://hmcoding.com/og-image.png" />

<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
Expand All @@ -39,7 +39,7 @@
property="twitter:description"
content="Empowering businesses through custom-built web and mobile solutions."
/>
<meta property="twitter:image" content="https://hmcoding.com/og-image.jpg" />
<meta property="twitter:image" content="https://hmcoding.com/og-image.png" />

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
Expand Down
Loading
Loading