commit 393bbb286bec9fc39adb5d57592dbc9d7503ff16 Author: Nikita Kiselev Date: Wed Mar 11 22:08:41 2026 +0300 Squashed commit message diff --git a/.cursor/agents.md b/.cursor/agents.md new file mode 100644 index 0000000..cb200be --- /dev/null +++ b/.cursor/agents.md @@ -0,0 +1,64 @@ +# Cursor AI Agents Configuration + +## AI Roles and Behavior Rules + +### Primary Role: Senior Full-Stack Developer + +You are an experienced full-stack developer specializing in: + +- Modular ECommerce development +- Custom frameworks (ECommerce Framework) +- PHP 7.4+ with modern best practices +- Vue.js 3 (Composition API) +- Telegram Mini App development + +### Coding Rules + +1. **Always follow existing project patterns** +2. **Do not create duplicates – reuse existing utilities** +3. **Follow project naming conventions** +4. **Test changes before committing** +5. **Document public APIs** +6. **Write comments only in English and only when truly justified** + +### Commit Rules + +1. **Follow Conventional Commits** + - Use prefixes: `feat:`, `fix:`, `chore:`, `refactor:`, `style:`, `test:`, `docs:` + - Format: `: ` (first line up to 72 characters) + - After an empty line – detailed description of changes + +2. **Commit language** + - All commits must be in **English** + - Provide detailed description of changes in the commit body + - List all changed files and key changes + +3. **Examples of good commits** + ``` + feat: add setting to control category products button visibility + + - Add show_category_products_button field to StoreDTO + - Update SettingsSerializerService to support new field + - Add setting in admin panel on 'Store' tab with toggle + - Pass setting to SPA through SettingsHandler + - Button displays only for categories with child categories + - Add default value true to configuration + ``` + +### Forbidden + +- Hardcoding values (use configs/settings instead) +- Ignoring error handling +- Creating circular dependencies + +For frontend development use: + +- Vue.js 3 (Composition API) +- Avoid using `watch` where a cleaner solution is possible +- For `frontend/admin` use Tailwind 4 with the `tw:` prefix +- For `frontend/spa` use Tailwind 4 without a prefix +- For `frontend/admin` use FontAwesome 4 icons, because it is already bundled with ECommerce 3 +- For `frontend/admin` use VuePrime 4 components +- For `frontend/spa` use Daisy UI +- To get the standard ECommerce table name, use the `db_table` helper or add the `DB_PREFIX` constant before the table name. This way you will get the table name with prefix. +- All tables of my `AcmeShop` module start with the `acmeshop_` prefix. Migration examples are located in `module/acmeshop/upload/acmeshop/database/migrations` diff --git a/.cursor/config.json b/.cursor/config.json new file mode 100644 index 0000000..59930ac --- /dev/null +++ b/.cursor/config.json @@ -0,0 +1,44 @@ +{ + "rules": { + "preferCompositionAPI": true, + "strictTypes": true, + "noHardcodedValues": true, + "useDependencyInjection": true + }, + "paths": { + "acmeshop_module": "module/acmeshop/upload/acmeshop", + "frontendAdmin": "frontend/admin", + "telegramShopSpa": "frontend/spa", + "migrations": "module/acmeshop/upload/acmeshop/database/migrations", + "acmeshopHandlers": "module/acmeshop/upload/acmeshop/src/Handlers", + "adminHandlers": "module/acmeshop/upload/acmeshop/bastion/Handlers", + "models": "module/acmeshop/upload/acmeshop/src/Models", + "framework": "module/acmeshop/upload/acmeshop/framework" + }, + "naming": { + "classes": "PascalCase", + "methods": "camelCase", + "variables": "camelCase", + "constants": "UPPER_SNAKE_CASE", + "files": "PascalCase for classes, kebab-case for others", + "tables": "snake_case with acmeshop_ prefix" + }, + "php": { + "version": "7.4+", + "preferVersion": "7.4+", + "psr12": true + }, + "javascript": { + "version": "ES2020+", + "framework": "Vue 3 Composition API", + "stateManagement": "Pinia", + "uiLibrary": "PrimeVue (admin), Tailwind (spa)" + }, + "database": { + "queryBuilder": true, + "migrations": true, + "tablePrefix": "acmeshop_", + "noForeignKeys": true + } +} + diff --git a/.cursor/features/acme-pulse-heartbeat.md b/.cursor/features/acme-pulse-heartbeat.md new file mode 100644 index 0000000..a62aa2d --- /dev/null +++ b/.cursor/features/acme-pulse-heartbeat.md @@ -0,0 +1,38 @@ +## AcmeShop Pulse Heartbeat Telemetry + +### Goal +Send heartbeat telemetry to AcmeShop Pulse once per hour to capture store state and environment versions without any user interaction. + +### Backend (`module/acmeshop/upload/acmeshop`) +- `framework/AcmeShopPulse/AcmeShopPulseService.php` + - New method `handleHeartbeat()` collects data: domain (via `Utils::getCurrentDomain()`), bot username (via `TelegramService::getMe()`), PHP version, module version (from `composer.json`), ECommerce versions (`VERSION` and `VERSION_CORE`), current UTC timestamp. + - The last successful ping is cached (key `acmeshop_pulse_heartbeat`, TTL 1 hour) via existing `CacheInterface`. + - Heartbeat signature is created via a dedicated `PayloadSigner` that uses `pulse.heartbeat_secret`/`PULSE_HEARTBEAT_SECRET`. Warnings are logged on cache/bot/signature failures. + - Request is sent to the `heartbeat` endpoint with a 2‑second timeout and `X-MEGAPAY-VERSION` header taken from `composer.json`. +- `framework/AcmeShopPulse/AcmeShopPulseServiceProvider.php` + - Registers main `PayloadSigner` (by `pulse.api_key`) and a separate heartbeat signer (by `pulse.heartbeat_secret` or `PULSE_HEARTBEAT_SECRET`), injects `LoggerInterface`. +- `src/Handlers/TelemetryHandler.php` + `src/routes.php` + - Adds `heartbeat` route that calls `handleHeartbeat()` and returns `{ status: "ok" }`. Logger writes warnings on problems. + +### Frontend (`frontend/spa`) +- `src/utils/ftch.js`: new `heartbeat()` function calls `api_action=heartbeat`. +- `src/stores/Pulse.js`: new `heartbeat` action uses the API function and logs the result. +- `src/main.js`: after `pulse.ingest(...)` the code calls `pulse.heartbeat()` without blocking the chain. + +### Configuration / ENV +- `PULSE_API_HOST` — base URL of AcmeShop Pulse (used for both events and heartbeat). +- `PULSE_TIMEOUT` — global HTTP timeout (for heartbeat forced to 2 seconds). +- `PULSE_HEARTBEAT_SECRET` (or `pulse.heartbeat_secret` in settings) — shared secret for signing heartbeat. Required; otherwise heartbeat will not be sent. +- `pulse.api_key` — legacy API key, used only for event ingest. + +### Behavior +1. Frontend (SPA) calls `heartbeat` on app initialization (fire-and-forget). +2. Backend checks the cache. If one hour has not passed yet, `handleHeartbeat()` returns without any requests. +3. When needed, data is collected, signed via heartbeat signer, and sent as a POST request to `/heartbeat`. +4. Any failures (bot info, signature, HTTP) are logged as warnings so they do not affect end users. + +### TODO / Possible improvements +- Optionally move heartbeat triggering to cron/CLI so it does not depend on frontend. +- Add heartbeat success metrics to the admin panel. + + diff --git a/.cursor/prompts/api-generation.md b/.cursor/prompts/api-generation.md new file mode 100644 index 0000000..f5a1943 --- /dev/null +++ b/.cursor/prompts/api-generation.md @@ -0,0 +1,127 @@ +# Prompts for API generation + +## Creating a new API endpoint + +``` +Create a new API endpoint [ENDPOINT_NAME] for [DESCRIPTION]: + +1. Handler in [HANDLER_PATH]: + - Method handle() accepts Request + - Validate input data + - Use a Service for business logic + - Return JsonResponse with correct structure + - Handle errors with logging + +2. Service in [SERVICE_PATH]: + - Business logic + - Work with Model + - Data validation + - Exception handling + +3. Model in [MODEL_PATH] (if needed): + - Methods for working with the database + - Use Query Builder + - Proper method typing + +4. Route in routes.php: + - Add a route with the correct name + +5. Migration (if a new table is needed): + - Create a migration in database/migrations/ + - Use fixed acmeshop_ prefix for the table + - Add indexes where necessary + +Follow the project MVC-L architecture and reuse existing patterns. +``` + +## Creating a CRUD API + +``` +Create a full CRUD API for entity [ENTITY_NAME]: + +1. Handler with methods: + - list() – list with pagination and filtering + - get() – fetch a single record + - create() – create + - update() – update + - delete() – delete + +2. Service with business logic for all operations + +3. Model with methods: + - findAll() – list + - findById() – by ID + - create() – create + - update() – update + - delete() – delete + +4. DTO for data validation + +5. Migration for table [TABLE_NAME] + +6. Routes for all endpoints + +Use server-side pagination, filtering, and sorting for list(). +``` + +## Creating an Admin API endpoint + +``` +Create an Admin API endpoint [ENDPOINT_NAME] in bastion/Handlers/: + +1. Handler in bastion/Handlers/[HANDLER_NAME].php: + - Use Request to read parameters + - Validate data + - Call a Service for business logic + - Return JsonResponse with structure { data: { data: [...], totalRecords: ... } } + - Handle errors + +2. Service in bastion/Services/ (if needed): + - Admin-specific business logic + - Work with Models + +3. Route in bastion/routes.php + +4. Frontend component (if UI is needed): + - Vue component in frontend/admin/src/views/ + - Use PrimeVue components + - Server-side pagination/filtering + - Error handling with toast notifications + +Follow existing project patterns. +``` + +## Creating a Frontend API client + +``` +Create a function for working with API endpoint [ENDPOINT_NAME]: + +1. In frontend/[admin|spa]/src/utils/http.js: + - api[Method] function to call the endpoint + - Proper error handling + - Return a structured response + +2. Usage: + - Import in components + - Handle loading states + - Show toast notifications on errors + +Follow existing patterns in http.js. +``` + +## Creating a migration + +``` +Create a migration for table [TABLE_NAME]: + +1. File: database/migrations/[TIMESTAMP]_[DESCRIPTION].php +2. Use fixed acmeshop_ prefix for the table +3. Add all required fields with correct types +4. Add indexes for frequently used fields +5. Use utf8mb4_unicode_ci collation +6. Use InnoDB engine +7. Add created_at and updated_at timestamps +8. Do not create foreign keys (use indexes only) + +Follow the structure of existing migrations. +``` diff --git a/.cursor/prompts/changelog.md b/.cursor/prompts/changelog.md new file mode 100644 index 0000000..6d805dd --- /dev/null +++ b/.cursor/prompts/changelog.md @@ -0,0 +1,101 @@ +# Changelog Documentation Rules + +## General requirements + +- **Format**: Markdown +- **Structure**: One version = one page +- **Style**: Professional, concise, without marketing slogans +- **Language**: English +- **Target audience**: Developers and store owners +- **Content**: Only key changes, without unnecessary technical details + +## Page structure + +### Intro paragraph +- Short release description (1–2 sentences) +- Add 1–2 sentences about key changes +- Mention the main features and improvements + +### Sections (strictly in this order) + +1. **🚀 Added** – new features and capabilities +2. **✨ Improved** – improvements to existing features +3. **🐞 Fixed** – bug fixes +4. **⚠️ Breaking changes** – non‑backward compatible changes +5. **🔧 Technical changes** – technical changes (separate section) + +### Section rules + +- **Do NOT** add a section if it has no items +- Use markdown bullet lists, no numbering +- Do not add links unless they are explicitly specified +- Do not add extra explanations, conclusions, or summaries +- Output only the content of the Markdown file, without comments + +## Separating changes + +### Business logic and processes +Sections "Added", "Improved", "Fixed", "Breaking changes" contain only changes related to: +- Store business processes +- User experience +- Functionality for store owners +- Selling / value‑driven features + +### Technical changes +All technical changes go into a separate **🔧 Technical changes** section: +- No subsections, everything in a single list +- Only the most important technical changes +- Do not describe details that are not interesting to end users + +## Writing style + +### Terminology +- Use English terms (e.g. "navbar", not a localized variant) +- Avoid low‑level technical terms in business sections (e.g. "customer_id" → "automatic customer linking") + +### Descriptions + +**For key selling features:** +- Provide detailed descriptions +- Highlight capabilities and advantages +- Emphasize value for the user + +**For technical changes:** +- Be brief and to the point +- Avoid unnecessary details + +**For regular features:** +- Be concise but informative +- Focus on user benefit + +## Ordering + +### In the "Added" section +Place key selling features **first**: +1. Main page configuration system based on blocks +2. Form builder based on FormKit +3. Yandex.Metrica integration +4. Privacy policy +5. Support for coupons and gift certificates +6. Other features + +## Examples of good wording + +### ✅ Good +- "Automatic linking of Telegram customers with ECommerce customers: the system automatically finds and links customers from the Telegram shop with existing customers in ECommerce by email or phone number, creating a unified customer base" + +### ❌ Bad +- "Saving customer_id with the order to link with ECommerce customers: automatic linking of orders from Telegram with customers in ECommerce, unified customer base" + +### ✅ Good +- "Navbar with logo and application name" + +### ❌ Bad +- "Navigation bar with logo and application name" (too verbose, no added value) + +## What not to include + +- Internal development details (tests, static analysis, obfuscation) +- Technical details not interesting to users +- Excessively detailed technical descriptions +- Marketing slogans and calls to action diff --git a/.cursor/prompts/documentation.md b/.cursor/prompts/documentation.md new file mode 100644 index 0000000..9299de5 --- /dev/null +++ b/.cursor/prompts/documentation.md @@ -0,0 +1,61 @@ +# Prompts for documentation + +## Documenting a class + +``` +Add PHPDoc documentation for class [CLASS_NAME]: +1. Description of the class and its purpose +2. @package tag +3. @author tag +4. Documentation for all public methods +5. Documentation for public properties +6. Usage examples where appropriate +``` + +## Documenting a method + +``` +Add PHPDoc for method [METHOD_NAME]: +1. Method description +2. @param for all parameters with types +3. @return with the return type +4. @throws for all exceptions +5. Usage examples if the logic is complex +``` + +## Documenting an API endpoint + +``` +Create documentation for API endpoint [ENDPOINT_NAME]: +1. Description of purpose +2. HTTP method and path +3. Request parameters (query/body) +4. Response format (JSON structure) +5. Error codes +6. Request/response examples +7. Authorization requirements +``` + +## Documenting a Vue component + +``` +Add documentation for Vue component [COMPONENT_NAME]: +1. Component description +2. Props with types and descriptions +3. Emits with descriptions +4. Slots if present +5. Usage examples +6. Dependencies on other components +``` + +## Creating a README + +``` +Create README.md for [MODULE/COMPONENT]: +1. Purpose description +2. Installation/configuration +3. Usage with examples +4. API documentation +5. Configuration options +6. Troubleshooting +``` diff --git a/.cursor/prompts/refactoring.md b/.cursor/prompts/refactoring.md new file mode 100644 index 0000000..559c70e --- /dev/null +++ b/.cursor/prompts/refactoring.md @@ -0,0 +1,87 @@ +# Prompts for refactoring + +## General refactoring + +``` +Analyze the code in file [FILE_PATH] and perform refactoring: +1. Remove duplicated code +2. Improve readability +3. Apply SOLID principles +4. Add error handling where necessary +5. Improve typing +6. Add documentation for public methods +7. Ensure the code follows the project's MVC-L architecture +8. Use existing utilities and services instead of creating new ones +``` + +## Refactoring a Handler + +``` +Refactor Handler [HANDLER_NAME]: +1. Extract business logic into a separate Service +2. Add validation for input data +3. Improve error handling with logging +4. Use DTOs for data transfer +5. Add PHPDoc comments +6. Ensure Dependency Injection is used +7. Optimize database queries if needed +``` + +## Refactoring a Model + +``` +Refactor Model [MODEL_NAME]: +1. Ensure all queries use the Query Builder +2. Add methods for common operations (findBy, findAll, create, update) +3. Add data validation before saving +4. Improve method typing +5. Add PHPDoc comments +6. Use transactions for complex operations +``` + +## Refactoring a Vue component + +``` +Refactor Vue component [COMPONENT_NAME]: +1. Extract logic into composable functions +2. Improve typing for props and emits +3. Optimize computed properties +4. Add error handling +5. Improve template structure +6. Add loading states +7. Use existing project utilities +``` + +## Removing duplication + +``` +Find and remove code duplication in: +- [FILE_PATH_1] +- [FILE_PATH_2] +- [FILE_PATH_3] + +Create shared utilities/services where appropriate, following the project architecture. +``` + +## Improving performance + +``` +Analyze performance of the code in [FILE_PATH]: +1. Optimize database queries (use indexes, avoid N+1) +2. Add caching where appropriate +3. Optimize algorithms +4. Reduce the number of API calls +5. Use lazy loading on the frontend +``` + +## Improving security + +``` +Improve security of the code in [FILE_PATH]: +1. Add validation for all input data +2. Use prepared statements (Query Builder) +3. Add CSRF protection where necessary +4. Validate access rights +5. Sanitize output data +6. Add rate limiting where necessary +``` diff --git a/.cursor/prompts/testing.md b/.cursor/prompts/testing.md new file mode 100644 index 0000000..140f7fb --- /dev/null +++ b/.cursor/prompts/testing.md @@ -0,0 +1,52 @@ +# Prompts for testing + +## Creating a unit test + +``` +Create a unit test for [CLASS_NAME] in tests/Unit/: + +1. Use PHPUnit +2. Cover all public methods +3. Test successful scenarios +4. Test error handling +5. Use mocks for dependencies +6. Follow the structure of existing tests +7. Use the project's base TestCase class +``` + +## Creating an integration test + +``` +Create an integration test for [FEATURE_NAME] in tests/Integration/: + +1. Test the full flow from request to response +2. Use a test database +3. Clean up data after tests +4. Test real-life usage scenarios +5. Verify data validation +6. Verify error handling +``` + +## Creating a Vue component test + +``` +Create a test for Vue component [COMPONENT_NAME] in frontend/[admin|spa]/tests/: + +1. Use Vitest +2. Test component rendering +3. Test props +4. Test events (emits) +5. Test user interactions +6. Use mocks for API calls +7. Follow the structure of existing tests +``` + +## Test coverage + +``` +Analyze test coverage for [FILE_PATH]: +1. Determine which methods are not covered by tests +2. Create tests for critical methods +3. Ensure edge cases are tested +4. Add tests for error handling +``` diff --git a/.cursor/rules/architecture.md b/.cursor/rules/architecture.md new file mode 100644 index 0000000..9d782de --- /dev/null +++ b/.cursor/rules/architecture.md @@ -0,0 +1,201 @@ +# Architectural Rules + +## ECommerce Framework Architecture + +### MVC-L Pattern + +The project uses a modified MVC-L pattern (Model-View-Controller-Language): + +- **Model**: Classes in `src/Models/` – data access and database operations +- **View**: Vue components on the frontend, JSON responses on the backend +- **Controller**: Handlers in `src/Handlers/` and `bastion/Handlers/` +- **Language**: Translator in `framework/Translator/` + +### Dependency Injection + +All dependencies are injected via the Container: + +```php +// ✅ Correct +public function __construct( + private Builder $builder, + private TelegramCustomer $telegramCustomerModel +) {} + +// ❌ Incorrect +public function __construct() { + $this->builder = new Builder(...); +} +``` + +### Service Providers + +Services are registered via Service Providers: + +```php +class MyServiceProvider extends ServiceProvider +{ + public function register(): void + { + $this->app->singleton(MyService::class, function ($app) { + return new MyService($app->get(Dependency::class)); + }); + } +} +``` + +### Routes + +Routes are defined in `routes.php`: + +```php +return [ + 'actionName' => [HandlerClass::class, 'methodName'], +]; +``` + +### Handlers (Controllers) + +Handlers process HTTP requests: + +```php +class MyHandler +{ + public function handle(Request $request): JsonResponse + { + // Validation + // Business logic via Services + // Return JsonResponse + } +} +``` + +### Models + +Models work with data: + +```php +class MyModel +{ + public function __construct( + private ConnectionInterface $database, + private Builder $builder + ) {} + + public function findById(int $id): ?array + { + return $this->builder->newQuery() + ->from($this->tableName) + ->where('id', '=', $id) + ->firstOrNull(); + } +} +``` + +### Services + +Services contain business logic: + +```php +class MyService +{ + public function __construct( + private MyModel $model + ) {} + + public function doSomething(array $data): array + { + // Business logic + return $this->model->create($data); + } +} +``` + +### Migrations + +Migrations live in `database/migrations/`: + +```php +return new class extends Migration { + public function up(): void + { + $this->database->statement('CREATE TABLE ...'); + } +}; +``` + +### Query Builder + +Always use Query Builder instead of raw SQL: + +```php +// ✅ Correct +$query = $this->builder->newQuery() + ->select(['id', 'name']) + ->from('table_name') + ->where('status', '=', 'active') + ->get(); + +// ❌ Incorrect +$result = $this->database->query("SELECT * FROM table_name WHERE status = 'active'"); +``` + +### Frontend Architecture + +#### Admin Panel (Vue 3) + +- Composition API +- Pinia for state management +- PrimeVue for UI components +- Axios for HTTP requests +- Vue Router for navigation + +#### SPA (Telegram Mini App) + +- Composition API +- Pinia stores +- Tailwind CSS for styles +- Telegram WebApp API +- Vue Router + +### Naming Conventions + +- **Classes**: PascalCase (`TelegramCustomerService`) +- **Methods**: camelCase (`getCustomers`) +- **Variables**: camelCase (`$customerData`) +- **Constants**: UPPER_SNAKE_CASE (`MAX_RETRIES`) +- **Files**: PascalCase for classes, kebab-case for everything else +- **Tables**: snake_case with `acmeshop_` prefix + +### Error Handling + +Always handle errors: + +```php +try { + $result = $this->service->doSomething(); +} catch (SpecificException $e) { + $this->logger->error('Error message', ['exception' => $e]); + throw new UserFriendlyException('User message'); +} +``` + +### Configuration + +Use configuration files in `configs/`: + +```php +$config = $this->app->getConfigValue('app.setting_name'); +``` + +### Caching + +Use Cache Service for caching: + +```php +$cache = $this->app->get(CacheInterface::class); +$value = $cache->get('key', function() { + return expensiveOperation(); +}); +``` + diff --git a/.cursor/rules/form-builder.md b/.cursor/rules/form-builder.md new file mode 100644 index 0000000..31b13cf --- /dev/null +++ b/.cursor/rules/form-builder.md @@ -0,0 +1,70 @@ +# FormBuilder System Context + +## Architectural Overview +The FormBuilder ecosystem is a strictly typed Vue 3 application module designed to generate standard FormKit Schema JSON. It eschews internal DTOs in favor of direct schema manipulation. + +### Core Components +1. **FormBuilderView (`views/FormBuilderView.vue`)**: + * **Role**: Smart container / Data fetcher. + * **Responsibility**: Fetches form data from API (`GET /api/admin/forms/{alias}`), handles loading states, and passes data to `FormBuilder`. + * **Contract**: Expects API response `{ data: { schema: Array, is_custom: Boolean, ... } }`. + +2. **FormBuilder (`components/FormBuilder/FormBuilder.vue`)**: + * **Role**: Main Orchestrator / State Owner. + * **Responsibility**: Manages `v-model` (schema), mode switching (Visual/Code/Preview), and provides state to children. + * **State Management**: Uses `defineModel` for `formFields` (schema) and `isCustom` (mode flag). Uses `provide('formFields')` and `provide('selectedFieldId')` for deep dependency injection. + * **Modes**: + * **Visual**: Drag-and-drop interface using `vuedraggable`. + * **Code**: Direct JSON editing of the FormKit schema. Sets `isCustom = true`. + * **Preview**: Renders the current schema using `FormKit`. + +3. **FormCanvas (`components/FormBuilder/FormCanvas.vue`)**: + * **Role**: Visual Editor Surface. + * **Responsibility**: Renders the draggable list of fields. + * **Implementation**: Uses `vuedraggable` bound to `formFields`. + * **UX**: Implements "Ghost" and "Drag" classes for visual feedback. Handles selection logic. + +4. **FieldsPanel (`components/FormBuilder/FieldsPanel.vue`)**: + * **Role**: Component Palette. + * **Responsibility**: Source of truth for available field types. + * **Implementation**: Uses `vuedraggable` with `pull: 'clone', put: false` to spawn new fields. + +5. **FieldSettings (`components/FormBuilder/FieldSettings.vue`)**: + * **Role**: Property Editor. + * **Responsibility**: Edits properties of the `selectedFieldId`. + * **Constraint**: Must use PrimeVue components for all inputs. + +## Data Flow & Invariants +1. **Schema Authority**: The FormKit Schema JSON is the single source of truth. There is no "internal model" separate from the schema. +2. **Reactivity**: + * `formFields` is an Array of Objects. + * Mutations must preserve reactivity. When using `v-model` or `provide/inject`, ensure array methods (splice, push, filter) are used correctly or replace the entire array reference if needed to trigger watchers. + * **Immutability**: `useFormFields` composable uses immutable patterns (returning new array references) to ensure `defineModel` in parent detects changes. +3. **Mode Logic**: + * Switching to **Code** mode sets `isCustom = true`. + * Switching to **Visual** mode sets `isCustom = false`. + * **Safety**: Switching modes triggers JSON validation. Invalid JSON prevents mode switch. +4. **Drag and Drop**: + * Powered by `vuedraggable` (Sortable.js). + * **Clone Logic**: `FieldsPanel` clones from `availableFields`. `FormCanvas` receives the clone. + * **ID Generation**: Unique IDs are generated upon cloning/addition to ensure key stability. + +## Naming & Conventions +* **Tailwind**: Use `tw:` prefix for all utility classes (e.g., `tw:flex`, `tw:p-4`). +* **Components**: PrimeVue components are the standard UI kit (Button, Panel, InputText, etc.). +* **Icons**: FontAwesome (`fa fa-*`). +* **Files**: PascalCase for components (`FormBuilder.vue`), camelCase for logic (`useFormFields.js`). + +## Integration Rules +* **Backend**: The backend stores the JSON blob directly. `FormBuilder` does not transform data before save; it emits the raw schema. +* **API**: `useFormsStore` handles API communication. + +## Pitfalls & Warnings +* **vuedraggable vs @formkit/drag-and-drop**: We strictly use `vuedraggable`. Do not re-introduce `@formkit/drag-and-drop`. +* **Watchers**: Avoid `watch` where `computed` or event handlers suffice, to prevent infinite loops in bidirectional data flow. +* **Tailwind Config**: Do not use `@apply` with `tw:` prefixed classes in ` +``` + +## Component Naming + +```vue + + + + + + + +``` + +## Props + +```vue + +``` + +## Emits + +```vue + +``` + +## Reactive State + +```vue + +``` + +## Computed Properties + +```vue + +``` + +## Event Handlers + +```vue + + + +``` + +## Conditional Rendering + +```vue + +``` + +## Lists + +```vue + +``` + +## Form Handling + +```vue + + + + diff --git a/frontend/admin/src/components/ScheduledJobsList.vue b/frontend/admin/src/components/ScheduledJobsList.vue new file mode 100644 index 0000000..66cc25d --- /dev/null +++ b/frontend/admin/src/components/ScheduledJobsList.vue @@ -0,0 +1,108 @@ + + + diff --git a/frontend/admin/src/components/Settings/ItemBool.vue b/frontend/admin/src/components/Settings/ItemBool.vue new file mode 100644 index 0000000..018c9f3 --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemBool.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/frontend/admin/src/components/Settings/ItemCategoriesSelect.vue b/frontend/admin/src/components/Settings/ItemCategoriesSelect.vue new file mode 100644 index 0000000..1686230 --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemCategoriesSelect.vue @@ -0,0 +1,116 @@ + + + diff --git a/frontend/admin/src/components/Settings/ItemImage.vue b/frontend/admin/src/components/Settings/ItemImage.vue new file mode 100644 index 0000000..8539edf --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemImage.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/frontend/admin/src/components/Settings/ItemInput.vue b/frontend/admin/src/components/Settings/ItemInput.vue new file mode 100644 index 0000000..5eb03df --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemInput.vue @@ -0,0 +1,72 @@ + + + diff --git a/frontend/admin/src/components/Settings/ItemProductsSelect.vue b/frontend/admin/src/components/Settings/ItemProductsSelect.vue new file mode 100644 index 0000000..8ddb3ca --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemProductsSelect.vue @@ -0,0 +1,116 @@ + + + diff --git a/frontend/admin/src/components/Settings/ItemSelect.vue b/frontend/admin/src/components/Settings/ItemSelect.vue new file mode 100644 index 0000000..7819f2d --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemSelect.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/frontend/admin/src/components/Settings/ItemTextarea.vue b/frontend/admin/src/components/Settings/ItemTextarea.vue new file mode 100644 index 0000000..72b7dc9 --- /dev/null +++ b/frontend/admin/src/components/Settings/ItemTextarea.vue @@ -0,0 +1,41 @@ +