jet-2007 commited on
Commit
59f8f47
·
verified ·
1 Parent(s): ce77cc0

Build a Cross‑Platform Time Clock App (Multi‑Company, Employees, Geotagging, Reports)

Browse files

You are Cursor, acting as a senior full‑stack engineer. Generate a production‑ready application with clean, modular code, strong typing, and tests. Use modern, boring tech that deploys easily.

Tech Stack (preferred)

Frontend: Next.js 14 (App Router), TypeScript, React Server Components where appropriate

UI: Tailwind CSS + shadcn/ui + lucide-react icons

State: React Query (TanStack Query) for data fetching; Zod for validation

Auth: NextAuth (Auth.js) or Supabase Auth; support email+password first (can add OAuth later)

Backend: Next.js API routes or Route Handlers; if using Supabase, leverage Database + RLS

DB: PostgreSQL (Supabase). Use Prisma or Supabase SQL directly. Provide both options in code where feasible.

Background jobs: Next.js Route Handlers / Edge Functions as needed

Storage: Supabase storage for avatars/files

Reports/Export: server-side CSV + PDF (e.g., pdf-lib or @react-pdf/renderer)

Maps/Geotag: HTML5 Geolocation API in browser; store lat/long/accuracy; optional reverse geocode later

PWA: Enable PWA behavior for phone/tablet/desktop (installable, offline cache for clocking while offline)

Testing: Vitest + Playwright (critical flows). ESLint + Prettier

Deployment: Vercel for web + Supabase for DB

Core Requirements

Multi‑tenant: Create multiple Companies. Each company has many Employees. Users may belong to one or multiple companies with roles.

Two roles:

Admin: manage companies, employees, timesheets, run reports, export CSV/PDF, view profiles.

Employee (user-level): clock in/out, view/edit own times (if allowed), download own timesheet report.

Profiles:

Company profile: name, legal name, time zone, pay period (weekly/biweekly/semimonthly/monthly), rounding rules (e.g., 5/10/15 min), break policies, overtime rules, geofence (optional radius), business address.

Employee profile: first/last, email, phone, status (active/inactive), hire date, employee ID, role title, base location, pay type (hourly/salary just as label), optional avatar.

User account links to employees (so a user can be an employee in multiple companies via a join table). Admins are users with company-scoped admin role.

Time Tracking:

Clock In/Out with one tap; capture timestamp, device info, and geotag (latitude, longitude, accuracy in meters, optional address if available later), note (optional), method (web/pwa)

GPS Prompting: ask permission; if declined, still allow clocking but label entry as no_location with reason

Offline mode: queue entries locally when offline; sync on reconnect

Prevent double punches; warn if already clocked in

Breaks: start/stop break, or deduct auto-break per company policy

Manual adjustments: Admins can edit entries with an audit log (who/when/what changed)

Timezone correctness: store timestamps in UTC, display in company timezone

Geotagging / Geofencing:

Store lat/long/accuracy per punch

If company has a geofence (center+radius meters), flag entries outside_geofence

Show map preview pin on entry detail (use a simple static map tile or library; keep vendor-agnostic)

Reports:

Employee Self‑Report: date range filter; totals by day/week; export CSV/PDF

Admin Reports: by company, by employee, by date range; include raw entries and rollups (regular hours, overtime, breaks, flags for outside_geofence or missing punches). Export CSV/PDF

Profiles export: company-wise employee profile export when needed

Access Control:

RBAC with company scope. Employees only see their own data. Admins see their company’s data. A Platform SuperAdmin (env‑gated) can see all for support.

If using Supabase: enforce RLS by company_id and user_id joins

Auditing & Compliance:

Every create/update/delete on time entries recorded in audit_log with before/after JSON

Keep immutable punch history; allow corrections via new revisions tied to original entry

UX:

Clock screen with big buttons (Clock In / Start Break / End Break / Clock Out)

Clear status: “You are clocked in since HH:MM” with running duration

Responsive, mobile-first; tablet/kiosk friendly (PIN or QR login optional later)

Data Model (Postgres)

Use snake_case. Provide Prisma schema and raw SQL migrations.

Tables

users (id, email, password_hash or external, name, created_at)

companies (id, name, legal_name, timezone, pay_period, rounding_rule_minutes, auto_break_minutes, overtime_policy_json, geofence_center_lat, geofence_center_lng, geofence_radius_m, address_json, created_at)

company_users (id, company_id, user_id, role ENUM('ADMIN','EMPLOYEE'), created_at)

employees (id, company_id, user_id NULLABLE, employee_code, first_name, last_name, email, phone, status ENUM('ACTIVE','INACTIVE'), hire_date, role_title, base_location_json, pay_type ENUM('HOURLY','SALARY'), avatar_url, created_at)

time_entries (id, company_id, employee_id, type ENUM('CLOCK_IN','CLOCK_OUT','BREAK_START','BREAK_END'), occurred_at_utc timestamptz, client_tz, lat NUMERIC(9,6) NULL, lng NUMERIC(9,6) NULL, accuracy_m NUMERIC NULL, location_status ENUM('ok','no_location','outside_geofence') DEFAULT 'ok', note TEXT NULL, device_fingerprint TEXT NULL, source ENUM('web','pwa'), linked_session_id UUID NULL)

time_sessions (id, company_id, employee_id, started_at_utc, ended_at_utc NULL, total_seconds INT NULL, flags JSONB)

audit_log (id, actor_user_id, company_id, entity_type, entity_id, action ENUM('CREATE','UPDATE','DELETE'), before JSONB NULL, after JSONB NULL, created_at)

invites (id, company_id, email, role, token, expires_at, accepted_at)

Notes

Sessions are optional but useful to compute totals efficiently. Each CLOCK_IN…CLOCK_OUT pair forms a session; breaks are sub-intervals.

Keep all timestamps UTC, display in company tz via server utilities.

API Contract (Route Handlers)

Create typed handlers with Zod validation. Return JSON: { data, error }. All endpoints require auth; RBAC enforced.

POST /api/auth/register
POST /api/auth/login
POST /api/companies // create company (admin or superadmin)
GET /api/companies // list for current user
GET /api/companies/:id // details
PATCH/DELETE /api/companies/:id


POST /api/companies/:id/employees
GET /api/companies/:id/employees
GET /api/employees/:id
PATCH/DELETE /api/employees/:id


POST /api/time/punch // { employeeId, type, note, geolocation: {lat,lng,accuracy}, clientTz }
POST /api/time/sync-offline // batch punch payload from PWA queue
GET /api/time/entries?companyId&employeeId&from&to
GET /api/time/sessions?companyId&employeeId&from&to


GET /api/reports/employee?employeeId&from&to&format=csv|pdf
GET /api/reports/company?companyId&from&to&format=csv|pdf


GET /api/export/employees?companyId&format=csv

Include pagination where lists can be large. For reports, stream CSV when possible.

Geotagging Details

Use navigator.geolocation.getCurrentPosition with { enableHighAccuracy: true, timeout: 8000, maximumAge: 0 }

Store lat, lng, accuracy_m and a location_status:

ok if coords present and (if geofence set) inside radius

outside_geofence if coords present but outside

no_location if user denied or timeout

Geofence check: haversine distance vs. geofence_radius_m

Show a tiny map snapshot on punch detail (defer provider selection; keep abstraction)

Timesheet Logic

Rounding (configurable): apply to session boundaries (in/out) per company rule: 5, 10, or 15 min to nearest (with half-up behavior). Preserve raw times in DB; compute rounded on report

Overtime: support daily (8+), weekly (40+), configurable in overtime_policy_json

Breaks: subtract explicit break intervals; optional auto-deduct; flag if missing break per policy

Edge cases: unpaired punches -> flagged, surfaced in admin review

Pages / Screens

Auth: login/register, accept invite

Employee:

Clock (primary): big buttons, current status, last punch, geotag prompt, offline queue status

My Timesheet: date range, list of sessions, totals, export CSV/PDF

Profile: edit personal info (limited)

Admin:

Dashboard: company selector, KPIs (active employees today, outside-geofence flags, missing punches)

Companies: create/edit company settings

Employees: CRUD, invite flow, import CSV

Timesheets: filters (company, employee, date range), review & approve edits, bulk export

Reports: employee report and company summary with exports

Audit Log: searchable table

Settings: roles, pay periods, rounding, geofence, overtime, break policies

File Structure (example)
app/
(auth)/login/page.tsx
(auth)/register/page.tsx
dashboard/page.tsx
companies/[companyId]/settings/page.tsx
companies/[companyId]/employees/page.tsx
employees/[employeeId]/page.tsx
clock/page.tsx
timesheets/page.tsx
reports/page.tsx
api/
auth/register/route.ts
auth/login/route.ts
companies/route.ts
companies/[id]/route.ts
companies/[id]/employees/route.ts
employees/[id]/route.ts
time/punch/route.ts
time/sync-offline/route.ts
time/entries/route.ts
time/sessions/route.ts
reports/employee/route.ts
reports/company/route.ts
export/employees/route.ts
components/
ClockWidget.tsx
GeoPrompt.tsx
MapPreview.tsx
DataTable.tsx
DateRangePicker.tsx
RoleGate.tsx
lib/
auth.ts
db.ts (Prisma) or supabase.ts
rls.ts (helpers for RLS policies)
geofence.ts (haversine)
timecalc.ts (rounding, overtime)
csv.ts, pdf.ts
zod-schemas.ts
prisma/
schema.prisma
migrations/
Prisma Schema (excerpt)

Provide a full schema.prisma covering all tables above, relations, and enums. Generate types and migrations.

SQL (Supabase) — Tables & RLS (excerpt)

SQL to create tables, indexes, and enums

RLS policies enforcing:

users can read their own user row

company_users controls access; employee rows restricted by company membership

time_entries restricted to employees in same company; employees can read their own entries; admins can read company entries

Validation & Error States

Zod schemas for all payloads

Graceful fallbacks when geolocation fails (surface reason on UI)

Prevent duplicate punches within N minutes

PWA & Offline

Service worker caches core routes

Local queue for punches when offline;

Files changed (6) hide show
  1. README.md +8 -5
  2. components/footer.js +142 -0
  3. components/navbar.js +124 -0
  4. index.html +108 -19
  5. script.js +17 -0
  6. style.css +35 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Timekeeper Pro
3
- emoji: 💻
4
- colorFrom: pink
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: TimeKeeper Pro ⏱️
3
+ colorFrom: gray
4
+ colorTo: yellow
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/footer.js ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomFooter extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ footer {
7
+ background-color: var(--secondary-700);
8
+ color: white;
9
+ padding: 3rem 0;
10
+ }
11
+ .container {
12
+ max-width: 1200px;
13
+ margin: 0 auto;
14
+ padding: 0 1rem;
15
+ display: grid;
16
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
17
+ gap: 2rem;
18
+ }
19
+ .footer-logo {
20
+ font-size: 1.5rem;
21
+ font-weight: 700;
22
+ margin-bottom: 1rem;
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 0.5rem;
26
+ }
27
+ .footer-description {
28
+ color: var(--secondary-200);
29
+ margin-bottom: 1.5rem;
30
+ }
31
+ .social-links {
32
+ display: flex;
33
+ gap: 1rem;
34
+ }
35
+ .social-link {
36
+ color: white;
37
+ width: 36px;
38
+ height: 36px;
39
+ border-radius: 50%;
40
+ background-color: rgba(255,255,255,0.1);
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ transition: background-color 0.2s;
45
+ }
46
+ .social-link:hover {
47
+ background-color: var(--primary-500);
48
+ }
49
+ .footer-heading {
50
+ font-weight: 600;
51
+ margin-bottom: 1.25rem;
52
+ font-size: 1.125rem;
53
+ }
54
+ .footer-links {
55
+ display: flex;
56
+ flex-direction: column;
57
+ gap: 0.75rem;
58
+ }
59
+ .footer-link {
60
+ color: var(--secondary-200);
61
+ text-decoration: none;
62
+ transition: color 0.2s;
63
+ }
64
+ .footer-link:hover {
65
+ color: white;
66
+ }
67
+ .copyright {
68
+ margin-top: 3rem;
69
+ padding-top: 1.5rem;
70
+ border-top: 1px solid rgba(255,255,255,0.1);
71
+ text-align: center;
72
+ color: var(--secondary-300);
73
+ }
74
+ @media (max-width: 768px) {
75
+ .container {
76
+ grid-template-columns: 1fr 1fr;
77
+ }
78
+ }
79
+ </style>
80
+ <footer>
81
+ <div class="container">
82
+ <div class="footer-col">
83
+ <div class="footer-logo">
84
+ <i data-feather="clock"></i>
85
+ TimeKeeper Pro
86
+ </div>
87
+ <p class="footer-description">
88
+ Modern time tracking solution for businesses of all sizes with geotagging and reporting.
89
+ </p>
90
+ <div class="social-links">
91
+ <a href="#" class="social-link"><i data-feather="twitter"></i></a>
92
+ <a href="#" class="social-link"><i data-feather="facebook"></i></a>
93
+ <a href="#" class="social-link"><i data-feather="linkedin"></i></a>
94
+ <a href="#" class="social-link"><i data-feather="github"></i></a>
95
+ </div>
96
+ </div>
97
+ <div class="footer-col">
98
+ <h4 class="footer-heading">Product</h4>
99
+ <div class="footer-links">
100
+ <a href="/features" class="footer-link">Features</a>
101
+ <a href="/pricing" class="footer-link">Pricing</a>
102
+ <a href="/solutions" class="footer-link">Solutions</a>
103
+ <a href="/updates" class="footer-link">Updates</a>
104
+ </div>
105
+ </div>
106
+ <div class="footer-col">
107
+ <h4 class="footer-heading">Resources</h4>
108
+ <div class="footer-links">
109
+ <a href="/blog" class="footer-link">Blog</a>
110
+ <a href="/guides" class="footer-link">Guides</a>
111
+ <a href="/support" class="footer-link">Support</a>
112
+ <a href="/api" class="footer-link">API</a>
113
+ </div>
114
+ </div>
115
+ <div class="footer-col">
116
+ <h4 class="footer-heading">Company</h4>
117
+ <div class="footer-links">
118
+ <a href="/about" class="footer-link">About Us</a>
119
+ <a href="/careers" class="footer-link">Careers</a>
120
+ <a href="/contact" class="footer-link">Contact</a>
121
+ <a href="/legal" class="footer-link">Legal</a>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ <div class="container">
126
+ <div class="copyright">
127
+ © ${new Date().getFullYear()} TimeKeeper Pro. All rights reserved.
128
+ </div>
129
+ </div>
130
+ </footer>
131
+ `;
132
+
133
+ // Replace Feather icons
134
+ setTimeout(() => {
135
+ if (window.feather) {
136
+ window.feather.replace({ class: 'feather-inline' });
137
+ }
138
+ }, 100);
139
+ }
140
+ }
141
+
142
+ customElements.define('custom-footer', CustomFooter);
components/navbar.js ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ .navbar {
7
+ background-color: white;
8
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
9
+ padding: 1rem 0;
10
+ }
11
+ .container {
12
+ max-width: 1200px;
13
+ margin: 0 auto;
14
+ padding: 0 1rem;
15
+ display: flex;
16
+ justify-content: space-between;
17
+ align-items: center;
18
+ }
19
+ .logo {
20
+ font-size: 1.5rem;
21
+ font-weight: 700;
22
+ color: var(--primary-600);
23
+ text-decoration: none;
24
+ display: flex;
25
+ align-items: center;
26
+ gap: 0.5rem;
27
+ }
28
+ .nav-links {
29
+ display: flex;
30
+ gap: 1.5rem;
31
+ align-items: center;
32
+ }
33
+ .nav-link {
34
+ color: var(--secondary-600);
35
+ text-decoration: none;
36
+ font-weight: 500;
37
+ transition: color 0.2s;
38
+ }
39
+ .nav-link:hover {
40
+ color: var(--primary-600);
41
+ }
42
+ .nav-link.active {
43
+ color: var(--primary-600);
44
+ font-weight: 600;
45
+ }
46
+ .btn-primary {
47
+ background-color: var(--primary-600);
48
+ color: white;
49
+ padding: 0.5rem 1rem;
50
+ border-radius: 0.375rem;
51
+ font-weight: 500;
52
+ transition: background-color 0.2s;
53
+ }
54
+ .btn-primary:hover {
55
+ background-color: var(--primary-700);
56
+ }
57
+ .mobile-menu-btn {
58
+ display: none;
59
+ background: none;
60
+ border: none;
61
+ color: var(--secondary-600);
62
+ cursor: pointer;
63
+ }
64
+ @media (max-width: 768px) {
65
+ .nav-links {
66
+ display: none;
67
+ flex-direction: column;
68
+ position: absolute;
69
+ top: 70px;
70
+ left: 0;
71
+ right: 0;
72
+ background-color: white;
73
+ padding: 1rem;
74
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
75
+ }
76
+ .nav-links.open {
77
+ display: flex;
78
+ }
79
+ .mobile-menu-btn {
80
+ display: block;
81
+ }
82
+ }
83
+ </style>
84
+ <nav class="navbar">
85
+ <div class="container">
86
+ <a href="/" class="logo">
87
+ <i data-feather="clock"></i>
88
+ TimeKeeper Pro
89
+ </a>
90
+
91
+ <button class="mobile-menu-btn" id="mobileMenuBtn">
92
+ <i data-feather="menu"></i>
93
+ </button>
94
+
95
+ <div class="nav-links" id="navLinks">
96
+ <a href="/features" class="nav-link">Features</a>
97
+ <a href="/pricing" class="nav-link">Pricing</a>
98
+ <a href="/about" class="nav-link">About</a>
99
+ <a href="/contact" class="nav-link">Contact</a>
100
+ <a href="/login" class="nav-link">Login</a>
101
+ <a href="/register" class="btn-primary">Get Started</a>
102
+ </div>
103
+ </div>
104
+ </nav>
105
+ `;
106
+
107
+ // Initialize mobile menu toggle
108
+ const mobileMenuBtn = this.shadowRoot.getElementById('mobileMenuBtn');
109
+ const navLinks = this.shadowRoot.getElementById('navLinks');
110
+
111
+ mobileMenuBtn.addEventListener('click', () => {
112
+ navLinks.classList.toggle('open');
113
+ });
114
+
115
+ // Replace Feather icons
116
+ setTimeout(() => {
117
+ if (window.feather) {
118
+ window.feather.replace({ class: 'feather-inline' });
119
+ }
120
+ }, 100);
121
+ }
122
+ }
123
+
124
+ customElements.define('custom-navbar', CustomNavbar);
index.html CHANGED
@@ -1,19 +1,108 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>TimeKeeper Pro | Modern Time Tracking</title>
7
+ <meta name="description" content="Cross-platform time clock app with geotagging and reporting">
8
+ <link rel="icon" type="image/x-icon" href="/static/favicon.ico">
9
+ <link rel="stylesheet" href="style.css">
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
12
+ <script src="https://unpkg.com/feather-icons"></script>
13
+ <script>
14
+ tailwind.config = {
15
+ theme: {
16
+ extend: {
17
+ colors: {
18
+ primary: {
19
+ 50: '#f0f9ff',
20
+ 100: '#e0f2fe',
21
+ 500: '#3b82f6',
22
+ 600: '#2563eb',
23
+ 700: '#1d4ed8',
24
+ },
25
+ secondary: {
26
+ 50: '#f8fafc',
27
+ 100: '#f1f5f9',
28
+ 500: '#64748b',
29
+ 600: '#475569',
30
+ 700: '#334155',
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ </script>
37
+ </head>
38
+ <body class="bg-gray-50 min-h-screen">
39
+ <custom-navbar></custom-navbar>
40
+
41
+ <main class="container mx-auto px-4 py-8">
42
+ <section class="hero bg-primary-50 rounded-xl p-8 mb-12">
43
+ <div class="flex flex-col md:flex-row items-center gap-8">
44
+ <div class="md:w-1/2">
45
+ <h1 class="text-4xl md:text-5xl font-bold text-primary-700 mb-4">Time Tracking Made Simple</h1>
46
+ <p class="text-lg text-secondary-600 mb-6">Geotagged clock-in/out, multi-company support, and powerful reporting for businesses of all sizes.</p>
47
+ <div class="flex gap-4">
48
+ <a href="/login" class="bg-primary-600 hover:bg-primary-700 text-white px-6 py-3 rounded-lg font-medium shadow-md transition-colors">
49
+ Get Started
50
+ </a>
51
+ <a href="/features" class="border border-primary-600 text-primary-600 hover:bg-primary-50 px-6 py-3 rounded-lg font-medium transition-colors">
52
+ Learn More
53
+ </a>
54
+ </div>
55
+ </div>
56
+ <div class="md:w-1/2">
57
+ <img src="http://static.photos/technology/1024x576/42" alt="Time tracking dashboard" class="rounded-lg shadow-xl">
58
+ </div>
59
+ </div>
60
+ </section>
61
+
62
+ <section class="features mb-16">
63
+ <h2 class="text-3xl font-bold text-center mb-12 text-secondary-700">Key Features</h2>
64
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
65
+ <div class="feature-card bg-white p-6 rounded-xl shadow-md hover:shadow-lg transition-shadow">
66
+ <div class="bg-primary-100 w-12 h-12 rounded-full flex items-center justify-center mb-4">
67
+ <i data-feather="clock" class="text-primary-600"></i>
68
+ </div>
69
+ <h3 class="text-xl font-semibold mb-2">One-Tap Clocking</h3>
70
+ <p class="text-secondary-600">Simple in/out interface with geotagging and offline support.</p>
71
+ </div>
72
+ <div class="feature-card bg-white p-6 rounded-xl shadow-md hover:shadow-lg transition-shadow">
73
+ <div class="bg-primary-100 w-12 h-12 rounded-full flex items-center justify-center mb-4">
74
+ <i data-feather="users" class="text-primary-600"></i>
75
+ </div>
76
+ <h3 class="text-xl font-semibold mb-2">Multi-Company</h3>
77
+ <p class="text-secondary-600">Manage multiple businesses with role-based access control.</p>
78
+ </div>
79
+ <div class="feature-card bg-white p-6 rounded-xl shadow-md hover:shadow-lg transition-shadow">
80
+ <div class="bg-primary-100 w-12 h-12 rounded-full flex items-center justify-center mb-4">
81
+ <i data-feather="bar-chart-2" class="text-primary-600"></i>
82
+ </div>
83
+ <h3 class="text-xl font-semibold mb-2">Advanced Reporting</h3>
84
+ <p class="text-secondary-600">Export CSV/PDF reports with overtime, breaks, and geofencing.</p>
85
+ </div>
86
+ </div>
87
+ </section>
88
+
89
+ <section class="cta bg-primary-600 text-white rounded-xl p-8 text-center">
90
+ <h2 class="text-3xl font-bold mb-4">Ready to Simplify Your Time Tracking?</h2>
91
+ <p class="text-primary-100 mb-6 max-w-2xl mx-auto">Join thousands of businesses using TimeKeeper Pro to manage their workforce efficiently.</p>
92
+ <a href="/register" class="bg-white text-primary-600 hover:bg-gray-100 px-8 py-3 rounded-lg font-semibold inline-block shadow-md transition-colors">
93
+ Start Free Trial
94
+ </a>
95
+ </section>
96
+ </main>
97
+
98
+ <custom-footer></custom-footer>
99
+
100
+ <script src="components/navbar.js"></script>
101
+ <script src="components/footer.js"></script>
102
+ <script src="script.js"></script>
103
+ <script>
104
+ feather.replace();
105
+ </script>
106
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
107
+ </body>
108
+ </html>
script.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ // Initialize any global functionality here
3
+
4
+ // Example: Mobile menu toggle would be handled in the navbar component
5
+
6
+ // Smooth scrolling for anchor links
7
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
8
+ anchor.addEventListener('click', function(e) {
9
+ e.preventDefault();
10
+ document.querySelector(this.getAttribute('href')).scrollIntoView({
11
+ behavior: 'smooth'
12
+ });
13
+ });
14
+ });
15
+
16
+ // Initialize any global event listeners
17
+ });
style.css CHANGED
@@ -1,28 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
28
  }
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
2
+
3
+ :root {
4
+ --primary-50: #f0f9ff;
5
+ --primary-100: #e0f2fe;
6
+ --primary-500: #3b82f6;
7
+ --primary-600: #2563eb;
8
+ --primary-700: #1d4ed8;
9
+ --secondary-50: #f8fafc;
10
+ --secondary-100: #f1f5f9;
11
+ --secondary-500: #64748b;
12
+ --secondary-600: #475569;
13
+ --secondary-700: #334155;
14
+ }
15
+
16
  body {
17
+ font-family: 'Inter', sans-serif;
18
+ line-height: 1.6;
19
  }
20
 
21
+ .hero {
22
+ background: linear-gradient(135deg, var(--primary-50) 0%, rgba(255,255,255,1) 100%);
 
23
  }
24
 
25
+ .feature-card:hover {
26
+ transform: translateY(-4px);
27
+ transition: transform 0.2s ease;
 
 
28
  }
29
 
30
+ .cta {
31
+ background: linear-gradient(135deg, var(--primary-500) 0%, var(--primary-700) 100%);
 
 
 
 
32
  }
33
 
34
+ /* Responsive typography */
35
+ @media (min-width: 768px) {
36
+ html {
37
+ font-size: 16px;
38
+ }
39
  }
40
+
41
+ @media (max-width: 767px) {
42
+ html {
43
+ font-size: 14px;
44
+ }
45
+ }