diff --git a/.env.example b/.env.example index 8633ee7..01acd1f 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,7 @@ APP_KEY= APP_DEBUG=true APP_URL=http://localhost -APP_LOCALE=en +APP_LOCALE=el APP_FALLBACK_LOCALE=en APP_FAKER_LOCALE=en_US @@ -59,6 +59,10 @@ MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_FROM_ADDRESS="hello@example.com" MAIL_FROM_NAME="${APP_NAME}" +CONTACT_EMAIL= + +HCAPTCHA_SITEKEY= +HCAPTCHA_SECRET= AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= diff --git a/.gitignore b/.gitignore index aacff36..3bffc96 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ /public/sitemap.xml /public/logos/core /public/storage +/public/css/filament +/public/js/filament +/public/fonts/filament /storage/*.key /storage/framework/migrated /storage/pail diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bb4cced --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,215 @@ +# Front-end Guidelines + +This is a Laravel 12 e-commerce project using Lunar PHP (headless). These guidelines apply to all front-end work. + +--- + +## Project Language Settings + + +- **Greek register:** `singular` — use the informal second person (εσύ/σου/σε). e.g. "Η κριτική σου", "Το όνομά σου", "Το email σου" + + +All UI text written for this project must follow the register above. + +--- + +## Localization + +Uses classic Laravel localization (https://laravel.com/docs/13.x/localization) — no third-party i18n routing package. + +**Locales:** Greek (`el`) is primary/default, English (`en`) is secondary. Set in `config('app.available_locales')` (`config/app.php`). + +**URL structure:** every route is prefixed with `{locale}`, including the default — `3dealer.gr/el/...` and `3dealer.gr/en/...`. Bare root (`/`) 301-redirects to `/el` (see `routes/web.php`). This avoids the ambiguity of an unprefixed default locale (see the reasoning in project memory / past conversation — prefixing every locale keeps hreflang symmetrical and scales cleanly to a third language later). + +**URL slugs are the same across both locales, permanently** — e.g. `/el/products/{x}` and `/en/products/{x}`, not `/el/proionta/{x}` vs `/en/products/{x}`. This is a deliberate, standing decision for this project, not a placeholder to revisit later. Only the visible content is translated, not the slug. Do not introduce per-locale translated slugs (e.g. a `lang/{locale}/routes.php` segment map) unless the user explicitly asks for that to change. + +**How it's wired:** +- All routes live inside `Route::prefix('{locale}')->where('locale', ...)->middleware('setlocale')` in `routes/web.php`. +- `App\Http\Middleware\SetLocale` validates the `{locale}` segment (404s if not in `available_locales`), calls `App::setLocale()`, and shares `$currentLocale`, `$altLocale`, and `$altLocaleUrl` (the same page in the other locale) to every view — this is what powers both the header's language switcher and the `` hreflang tags in `resources/views/layouts/app.blade.php`. Don't recompute this logic elsewhere; read those shared variables instead. +- When adding a new route, put it inside that `{locale}` group and give it a route `name()` — the alt-locale URL generation in the middleware depends on the current route being named (falls back to the locale root if unnamed). +- When linking to a page in Blade, prefer `route('name', [...])` (locale is a normal route param, defaults to `app()->getLocale()` implicitly via the shared route group) over hand-built path strings, except for pages that don't have a controller/route yet (e.g. `/products`, `/contact`, `/cart` placeholders in the header currently use `url('/'.app()->getLocale().'/products')` — replace with `route()` once those pages exist). +- **Every controller action for a route inside the `{locale}` group must declare `$locale` as its first parameter, even if unused** — e.g. `show(string $locale, Product $product)`. Laravel's `ControllerDispatcher` ultimately calls the controller with `...array_values($parameters)`, i.e. **positionally**. If a route-bound model parameter (like `$product`) isn't preceded by a matching `$locale` parameter in the method signature, the resolved values shift out of position and the wrong value (the locale string) gets passed where the model was expected — a `TypeError` that's easy to misread as a binding failure. Closures with zero declared parameters are unaffected (PHP just ignores the extra positional arg), so this only bites real controller methods. + +**Where translatable strings go:** +- Most UI copy is editable by the client/marketing team via **Stoic** (the headless CMS) — don't hardcode it here. +- Small strings that don't need client editing (nav labels, aria-labels, tab labels, pluralized counts) go in Laravel's own lang files at **`lang/{locale}/*.php`** (project root, per Laravel 12+ convention — not `resources/lang`). Current file: `lang/el/general.php` / `lang/en/general.php`. Use `__('general.key')` or `trans_choice('general.key', $count, [...])` for pluralized strings. + +--- + +## Tech Stack + +- **Laravel 12** + **Lunar PHP 1.3** (headless e-commerce) +- **Blade** for templating (`@extends`, `@section`, `@yield`, components) +- **Tailwind CSS v4** via `@tailwindcss/vite` — zero config, `@import 'tailwindcss'` only +- **Stimulus JS** for interactivity — controllers registered in `resources/js/stimulus/index.js` +- **Popover API** (native browser) for overlays — no JS overlay libraries +- SQLite database, `npm run dev` for local asset compilation + +--- + +## Key File Locations + +| What | Where | +|---|---| +| CSS entry point | `resources/css/app.css` | +| Font definitions | `resources/css/fonts.css` | +| JS entry point | `resources/js/app.js` | +| Stimulus controllers | `resources/js/stimulus/` | +| JS utilities | `resources/js/utils/` | +| Layout | `resources/views/layouts/app.blade.php` | +| UI components (atomic) | `resources/views/components/ui/` | +| Section components | `resources/views/components/` | +| Page views | `resources/views/{page}/` | +| Public assets | `public/images/` | + +--- + +## Design Reference + +The Bluebeard template (`resources/css/bluebeard.css`) is used as a **reference only** for colors, spacing, typography, and effects. It is never imported or used directly. Everything is translated to Tailwind utilities or custom CSS where unavoidable. + +The boboko project at `/Users/farenoubi/Projects/boboko-test` can be referenced for CSS architecture patterns. + +--- + +## Tailwind vs. Custom CSS + +**Prefer Tailwind utilities by default.** Write custom CSS only when something genuinely cannot be expressed as a utility: + +- Pseudo-elements (`::before`, `::after`) with dynamic transforms or transitions +- Complex descendant/sibling selectors (e.g. `.group:hover .nav-dropdown`) +- Keyframe animations +- The underline-slide animation on nav links (background-size trick) + +**Never** write inline CSS (`style="..."`). **Never** write custom CSS for something Tailwind already covers. + +When a component requires a CSS class (e.g. as a hook for a pseudo-element), still put all properties that can be Tailwind utilities as classes on the element in the component file. The CSS rule should contain only what genuinely cannot be a utility — pseudo-elements, complex selectors, keyframes. Do not default to putting all styles in the CSS class just because the class exists. + +When custom CSS is needed, add it to `resources/css/app.css` inside `@layer components`. Keep the rule minimal — only what can't be a utility. + +--- + +## Design Tokens + +Defined in `@theme {}` in `app.css`: + +- `--font-sans`: Manrope (body text) +- `--font-display`: Manrope (headings, nav, buttons) — kept separate from `--font-sans` in case they diverge later + +Reuse existing colors (`neutral-200`, `black`), font sizes, spacing, and border styles as components are built. Do not invent new values — check what's already in use first. + +--- + +## Component Structure + +### `resources/views/components/ui/` — atomic, reusable UI elements + +Single-purpose, stateless or minimally stateful elements used across many contexts: + +- `button.blade.php` ✓ +- `input.blade.php` +- `input-group.blade.php` +- `icon.blade.php` +- `image.blade.php` +- `tabs.blade.php` / `tab.blade.php` +- `accordion.blade.php` +- `tooltip.blade.php` +- `badge.blade.php` +- etc. + +### `resources/views/components/` — section-level or composite components + +Larger reusable blocks made of several elements, often with real content: + +- `header.blade.php` ✓ +- `footer.blade.php` +- `cta.blade.php` +- `product-card.blade.php` +- `contact-section.blade.php` +- etc. + +--- + +## Interactivity + +### Popover API for overlays + +For **tooltips, modals, dropdowns, and any overlay**: use the native [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) with CSS — no JS, no libraries. + +```html + +
...
+``` + +Style open/closed states with `[popover]` and `:popover-open` selectors in CSS. + +### Stimulus JS for behavior + +Use Stimulus when JS is genuinely needed (form handling, cart updates, dynamic state, etc.). + +- Keep controllers lean: manage state and coordinate the DOM, don't build UI inside them +- Use Stimulus values, targets, and outlets — avoid reading from the DOM imperatively +- **Never inject HTML strings or classes from inside a controller.** Instead, drive appearance via data attributes or CSS classes toggled on existing elements, and let CSS handle the visual result +- Never write inline JS (`onclick="..."`, etc.) + +### Pure CSS for simple interactions + +Hover dropdowns, focus states, active states — handle with CSS (`:hover`, `:focus-within`, `.group:hover`) before reaching for Stimulus. + +--- + +## Accessibility (ARIA) + +Always add ARIA attributes. This is not optional: + +- Interactive elements: `aria-label`, `aria-expanded`, `aria-controls`, `aria-haspopup` as appropriate +- Images: meaningful `alt` text, or `alt=""` + `aria-hidden="true"` for decorative images +- Icons used as buttons: `aria-label` on the button, `aria-hidden="true"` on the SVG +- Form fields: always associated `