Files

5.5 KiB
Raw Permalink Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

npm run dev       # Start dev server with HMR (Vite)
npm run build     # Production build to dist/
npm run preview   # Preview production build locally
npm run lint      # ESLint check

Tests exist in src/**/__tests__/ and src/test/ but are mostly placeholders — no test runner is configured.

Project Overview

Russian-language site for a ballistic window film company (Осколкам.Нет). Two completely separate apps live in the same repo:

  1. Public landing page — marketing site at /
  2. Admin panel — CRM at /admin/* (Supabase auth required)

There's also a public order tracking portal at /track/:token.

Architecture

Routing

App.jsx uses React Router with lazy-loaded routes. All admin routes are nested under ProtectedRouteAdminLayout. The public site has no routing — it's one scrollable page.

Public Landing Page

src/components/ — sections composed in App.jsx in this order: Navbar → Hero → PhysicsOfSafety → UseCases → TrustBar → Comparison → Estimator → LeadForm → Footer

  • All styling is inline style={{}} — no CSS modules, no Tailwind. Utility classes (.glass-panel, .btn-primary, .btn-outline, .container, .text-accent-blue/yellow/orange) are defined in src/index.css.
  • Animations use framer-motion (motion.* with whileInView, whileHover, whileTap).
  • Icons from lucide-react.
  • CSS custom properties in :root in index.css are the design tokens — always use var(--...) for colors and spacing.
  • Estimator.jsx reads prices from Supabase (usePricing hook) rather than hardcoding them. Fallback is 200µm=2000₽/m², 300µm=3000₽/m².
  • LeadForm.jsx POSTs to Telegram Bot API using VITE_TG_BOT_TOKEN / VITE_TG_CHAT_ID from .env.

Admin Panel

src/admin/ — scoped under .admin-panel CSS class (light theme tokens, see index.css). All admin styling is also inline style={{}} using var(--bg-*), var(--border-*), var(--text-*) tokens.

Layout: AdminLayout.jsx wraps everything in ToastProvider, renders the Sidebar, header (with push-bell, search, new-order button), and <Outlet>. On mobile, sidebar is a drawer toggled by hamburger.

Pages and their hooks:

Route Component Hook
/admin/kanban KanbanBoard useOrders
/admin/analytics AnalyticsPage useAnalyticsPage
/admin/calendar CalendarPage useCalendar
/admin/clients ClientsPage useClients
/admin/archive ArchivePage useArchive
/admin/settings SettingsPage useSettings
/admin/users UserManagementPage useAdminUsers

useOrders is the core hook — fetches all orders, subscribes to realtime changes via supabase.channel, exposes updateStatus, createOrder, updateOrder, refetch. Every mutation also writes to order_events table for the status timeline.

OrderModal handles both create and edit. Key patterns:

  • toPayload() converts form state to DB payload — empty strings become null for numeric fields (area, final_cost).
  • scheduled_at is stored as raw YYYY-MM-DDTHH:mm in form state and converted to ISO only in toPayload(). Never pass it through new Date() in onChange — this breaks datetime-local inputs.
  • Drag-and-drop in KanbanBoard uses @dnd-kit/core.

Overdue orders: isOverdue = scheduled_at < now && status !== 'закрыт' — highlighted in both OrderCard (red border) and CalendarPage chips.

Shared Hooks

  • usePricing — reads price_200/price_300 from settings table. Used by Estimator and OrderModal.
  • useIsMobilewindow.innerWidth < 768 with resize listener.
  • usePushNotifications — wraps Web Push API, stores subscription in push_subscriptions table via push-notify Edge Function.
  • useToast — from ToastContext, call addToast(message, type) where type is 'success' | 'error' | 'info'.

Supabase

src/lib/supabase.js — single client instance, reads VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY.

Database tables: orders, order_events, order_comments, order_photos (Storage bucket), push_subscriptions, settings, clients (view).

Edge Functions (supabase/functions/):

  • push-notify — sends Web Push to all subscribers. Called on status change.
  • send-reminders — scheduled every 15 min; pushes reminders for orders with scheduled_at in the next 5070 min. Uses reminder_sent_at guard to prevent duplicates.
  • invite-admin-user, list-admin-users, delete-admin-user — user management via Supabase Admin API (service role).

All hooks use an isMounted / active guard pattern to prevent state updates after unmount.

PWA

vite-plugin-pwa with injectManifest strategy (not generateSW) — required because src/sw.js has a custom push event handler alongside Workbox precaching. The built service worker is output as dist/sw.js.

Deployment

Docker multi-stage build: node:20-alpine builds the Vite app (VITE_ vars injected as ARG), caddy:2-alpine serves dist/. The Caddyfile uses {$DOMAIN:localhost} — setting DOMAIN to a real hostname enables automatic HTTPS via Let's Encrypt.

# On server
cd /opt/glass && git pull && docker compose up -d --build

Production server: 2.26.96.212, repo at /opt/glass. Gitea: gitea.houseassassin.keenetic.pro/houseassassin/glass.