- */
- public function definition(): array
- {
- return [
- 'name' => fake()->name(),
- 'email' => fake()->unique()->safeEmail(),
- 'email_verified_at' => now(),
- 'password' => static::$password ??= Hash::make('password'),
- 'remember_token' => Str::random(10),
- ];
- }
-
- /**
- * Indicate that the model's email address should be unverified.
- */
- public function unverified(): static
- {
- return $this->state(fn (array $attributes) => [
- 'email_verified_at' => null,
- ]);
- }
-}
diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php
deleted file mode 100644
index 05fb5d9e..00000000
--- a/database/migrations/0001_01_01_000000_create_users_table.php
+++ /dev/null
@@ -1,49 +0,0 @@
-id();
- $table->string('name');
- $table->string('email')->unique();
- $table->timestamp('email_verified_at')->nullable();
- $table->string('password');
- $table->rememberToken();
- $table->timestamps();
- });
-
- Schema::create('password_reset_tokens', function (Blueprint $table) {
- $table->string('email')->primary();
- $table->string('token');
- $table->timestamp('created_at')->nullable();
- });
-
- Schema::create('sessions', function (Blueprint $table) {
- $table->string('id')->primary();
- $table->foreignId('user_id')->nullable()->index();
- $table->string('ip_address', 45)->nullable();
- $table->text('user_agent')->nullable();
- $table->longText('payload');
- $table->integer('last_activity')->index();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('users');
- Schema::dropIfExists('password_reset_tokens');
- Schema::dropIfExists('sessions');
- }
-};
diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/database/migrations/0001_01_01_000001_create_cache_table.php
deleted file mode 100644
index b9c106be..00000000
--- a/database/migrations/0001_01_01_000001_create_cache_table.php
+++ /dev/null
@@ -1,35 +0,0 @@
-string('key')->primary();
- $table->mediumText('value');
- $table->integer('expiration');
- });
-
- Schema::create('cache_locks', function (Blueprint $table) {
- $table->string('key')->primary();
- $table->string('owner');
- $table->integer('expiration');
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('cache');
- Schema::dropIfExists('cache_locks');
- }
-};
diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php
deleted file mode 100644
index 425e7058..00000000
--- a/database/migrations/0001_01_01_000002_create_jobs_table.php
+++ /dev/null
@@ -1,57 +0,0 @@
-id();
- $table->string('queue')->index();
- $table->longText('payload');
- $table->unsignedTinyInteger('attempts');
- $table->unsignedInteger('reserved_at')->nullable();
- $table->unsignedInteger('available_at');
- $table->unsignedInteger('created_at');
- });
-
- Schema::create('job_batches', function (Blueprint $table) {
- $table->string('id')->primary();
- $table->string('name');
- $table->integer('total_jobs');
- $table->integer('pending_jobs');
- $table->integer('failed_jobs');
- $table->longText('failed_job_ids');
- $table->mediumText('options')->nullable();
- $table->integer('cancelled_at')->nullable();
- $table->integer('created_at');
- $table->integer('finished_at')->nullable();
- });
-
- Schema::create('failed_jobs', function (Blueprint $table) {
- $table->id();
- $table->string('uuid')->unique();
- $table->text('connection');
- $table->text('queue');
- $table->longText('payload');
- $table->longText('exception');
- $table->timestamp('failed_at')->useCurrent();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('jobs');
- Schema::dropIfExists('job_batches');
- Schema::dropIfExists('failed_jobs');
- }
-};
diff --git a/database/migrations/2024_01_01_000001_create_currencies_table.php b/database/migrations/2024_01_01_000001_create_currencies_table.php
new file mode 100644
index 00000000..a66741d8
--- /dev/null
+++ b/database/migrations/2024_01_01_000001_create_currencies_table.php
@@ -0,0 +1,27 @@
+id();
+ $table->string('code', 3)->unique();
+ $table->string('name', 50);
+ $table->string('symbol', 10);
+ $table->tinyInteger('decimal_places')->default(2);
+ $table->decimal('exchange_rate_to_usd', 15, 6)->nullable();
+ $table->boolean('is_active')->default(true);
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('currencies');
+ }
+};
diff --git a/database/migrations/2024_01_01_000002_create_tenants_table.php b/database/migrations/2024_01_01_000002_create_tenants_table.php
new file mode 100644
index 00000000..514fa413
--- /dev/null
+++ b/database/migrations/2024_01_01_000002_create_tenants_table.php
@@ -0,0 +1,32 @@
+id();
+ $table->string('name');
+ $table->string('slug')->unique();
+ $table->string('domain')->nullable();
+ $table->boolean('is_active')->default(true);
+ $table->string('default_locale', 10)->default('fa');
+ $table->enum('default_calendar', ['gregorian', 'jalali', 'hijri'])->default('jalali');
+ $table->foreignId('base_currency_id')->nullable()->constrained('currencies')->nullOnDelete();
+ $table->boolean('allow_multi_currency')->default(false);
+ $table->string('logo_path')->nullable();
+ $table->json('settings')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('tenants');
+ }
+};
diff --git a/database/migrations/2024_01_01_000003_create_users_table.php b/database/migrations/2024_01_01_000003_create_users_table.php
new file mode 100644
index 00000000..efaf137f
--- /dev/null
+++ b/database/migrations/2024_01_01_000003_create_users_table.php
@@ -0,0 +1,32 @@
+id();
+ $table->foreignId('tenant_id')->nullable()->constrained()->nullOnDelete();
+ $table->string('name');
+ $table->string('email')->unique();
+ $table->timestamp('email_verified_at')->nullable();
+ $table->string('password');
+ $table->string('locale', 10)->nullable();
+ $table->enum('preferred_calendar', ['gregorian', 'jalali', 'hijri'])->nullable();
+ $table->boolean('is_active')->default(true);
+ $table->rememberToken();
+ $table->timestamps();
+ $table->softDeletes();
+ $table->index('tenant_id');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('users');
+ }
+};
diff --git a/database/migrations/2024_01_01_000004_create_roles_permissions_tables.php b/database/migrations/2024_01_01_000004_create_roles_permissions_tables.php
new file mode 100644
index 00000000..61801c21
--- /dev/null
+++ b/database/migrations/2024_01_01_000004_create_roles_permissions_tables.php
@@ -0,0 +1,57 @@
+id();
+ $table->foreignId('tenant_id')->nullable()->constrained()->nullOnDelete();
+ $table->string('name');
+ $table->string('guard_name')->default('web');
+ $table->timestamps();
+ });
+
+ Schema::create('permissions', function (Blueprint $table) {
+ $table->id();
+ $table->string('name');
+ $table->string('guard_name')->default('web');
+ $table->timestamps();
+ });
+
+ Schema::create('model_has_roles', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('role_id')->constrained()->cascadeOnDelete();
+ $table->string('model_type');
+ $table->unsignedBigInteger('model_id');
+ $table->index(['model_type', 'model_id']);
+ });
+
+ Schema::create('model_has_permissions', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('permission_id')->constrained()->cascadeOnDelete();
+ $table->string('model_type');
+ $table->unsignedBigInteger('model_id');
+ $table->index(['model_type', 'model_id']);
+ });
+
+ Schema::create('role_has_permissions', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('permission_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('role_id')->constrained()->cascadeOnDelete();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('role_has_permissions');
+ Schema::dropIfExists('model_has_permissions');
+ Schema::dropIfExists('model_has_roles');
+ Schema::dropIfExists('permissions');
+ Schema::dropIfExists('roles');
+ }
+};
diff --git a/database/migrations/2024_01_01_000005_create_projects_table.php b/database/migrations/2024_01_01_000005_create_projects_table.php
new file mode 100644
index 00000000..c5333165
--- /dev/null
+++ b/database/migrations/2024_01_01_000005_create_projects_table.php
@@ -0,0 +1,36 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->string('name');
+ $table->string('code', 20);
+ $table->text('description')->nullable();
+ $table->enum('status', ['planning', 'in_progress', 'on_hold', 'completed', 'cancelled'])->default('planning');
+ $table->date('start_date');
+ $table->date('end_date');
+ $table->tinyInteger('progress')->default(0);
+ $table->decimal('budget', 15, 0)->default(0);
+ $table->foreignId('default_currency_id')->nullable()->constrained('currencies')->nullOnDelete();
+ $table->decimal('budget_usd', 15, 2)->default(0);
+ $table->foreignId('manager_id')->nullable()->constrained('users')->nullOnDelete();
+ $table->timestamps();
+ $table->softDeletes();
+ $table->index(['tenant_id', 'code']);
+ $table->index('status');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('projects');
+ }
+};
diff --git a/database/migrations/2024_01_01_000006_create_project_users_table.php b/database/migrations/2024_01_01_000006_create_project_users_table.php
new file mode 100644
index 00000000..cc98c12a
--- /dev/null
+++ b/database/migrations/2024_01_01_000006_create_project_users_table.php
@@ -0,0 +1,26 @@
+id();
+ $table->foreignId('project_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('user_id')->constrained()->cascadeOnDelete();
+ $table->string('role');
+ $table->timestamps();
+
+ $table->unique(['project_id', 'user_id']);
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('project_users');
+ }
+};
diff --git a/database/migrations/2024_01_01_000007_create_employees_table.php b/database/migrations/2024_01_01_000007_create_employees_table.php
new file mode 100644
index 00000000..9dd4101d
--- /dev/null
+++ b/database/migrations/2024_01_01_000007_create_employees_table.php
@@ -0,0 +1,41 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->nullable()->constrained()->nullOnDelete();
+ $table->string('national_code');
+ $table->string('first_name');
+ $table->string('last_name');
+ $table->string('phone')->nullable();
+ $table->string('job_title');
+ $table->enum('contract_type', ['full_time', 'part_time', 'contractor', 'intern'])->default('full_time');
+ $table->date('hire_date');
+ $table->date('termination_date')->nullable();
+ $table->enum('status', ['active', 'inactive', 'terminated'])->default('active');
+ $table->decimal('base_salary', 15, 0)->default(0);
+ $table->foreignId('currency_id')->nullable()->constrained('currencies')->nullOnDelete();
+ $table->string('bank_account')->nullable();
+ $table->text('notes')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->index('tenant_id');
+ $table->index('project_id');
+ $table->index('status');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('employees');
+ }
+};
diff --git a/database/migrations/2024_01_01_000008_create_work_logs_table.php b/database/migrations/2024_01_01_000008_create_work_logs_table.php
new file mode 100644
index 00000000..ad362426
--- /dev/null
+++ b/database/migrations/2024_01_01_000008_create_work_logs_table.php
@@ -0,0 +1,33 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('employee_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->nullable()->constrained()->nullOnDelete();
+ $table->date('date');
+ $table->decimal('hours_worked', 5, 2);
+ $table->text('description')->nullable();
+ $table->boolean('is_overtime')->default(false);
+ $table->timestamps();
+
+ $table->index('tenant_id');
+ $table->index('employee_id');
+ $table->index('project_id');
+ $table->index('date');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('work_logs');
+ }
+};
diff --git a/database/migrations/2024_01_01_000009_create_petty_cashes_table.php b/database/migrations/2024_01_01_000009_create_petty_cashes_table.php
new file mode 100644
index 00000000..cbbd8dd1
--- /dev/null
+++ b/database/migrations/2024_01_01_000009_create_petty_cashes_table.php
@@ -0,0 +1,35 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->constrained()->cascadeOnDelete();
+ $table->decimal('amount', 15, 0);
+ $table->foreignId('currency_id')->nullable()->constrained('currencies')->nullOnDelete();
+ $table->decimal('original_amount', 15, 0)->nullable();
+ $table->text('description')->nullable();
+ $table->date('date');
+ $table->string('recipient')->nullable();
+ $table->enum('status', ['pending', 'approved', 'rejected', 'settled'])->default('pending');
+ $table->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
+ $table->timestamps();
+
+ $table->index('tenant_id');
+ $table->index('project_id');
+ $table->index('status');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('petty_cashes');
+ }
+};
diff --git a/database/migrations/2024_01_01_000010_create_expenses_table.php b/database/migrations/2024_01_01_000010_create_expenses_table.php
new file mode 100644
index 00000000..877cb287
--- /dev/null
+++ b/database/migrations/2024_01_01_000010_create_expenses_table.php
@@ -0,0 +1,40 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('petty_cash_id')->nullable()->constrained()->nullOnDelete();
+ $table->foreignId('employee_id')->nullable()->constrained()->nullOnDelete();
+ $table->decimal('amount', 15, 0);
+ $table->foreignId('currency_id')->constrained('currencies')->cascadeOnDelete();
+ $table->decimal('original_amount', 15, 0)->nullable();
+ $table->text('description')->nullable();
+ $table->date('expense_date');
+ $table->string('category')->nullable();
+ $table->string('receipt_path')->nullable();
+ $table->enum('status', ['pending', 'approved', 'rejected'])->default('pending');
+ $table->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->index('tenant_id');
+ $table->index('project_id');
+ $table->index('status');
+ $table->index('expense_date');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('expenses');
+ }
+};
diff --git a/database/migrations/2024_01_01_000011_create_employee_financials_table.php b/database/migrations/2024_01_01_000011_create_employee_financials_table.php
new file mode 100644
index 00000000..3b0e5a4b
--- /dev/null
+++ b/database/migrations/2024_01_01_000011_create_employee_financials_table.php
@@ -0,0 +1,38 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('employee_id')->constrained()->cascadeOnDelete();
+ $table->enum('type', ['salary', 'bonus', 'deduction', 'advance', 'overtime']);
+ $table->decimal('amount', 15, 0);
+ $table->foreignId('currency_id')->constrained('currencies')->cascadeOnDelete();
+ $table->decimal('original_amount', 15, 0)->nullable();
+ $table->text('description')->nullable();
+ $table->date('date');
+ $table->tinyInteger('month');
+ $table->unsignedInteger('year');
+ $table->boolean('is_paid')->default(false);
+ $table->timestamp('paid_at')->nullable();
+ $table->timestamps();
+
+ $table->index('tenant_id');
+ $table->index('employee_id');
+ $table->index(['month', 'year']);
+ $table->index('type');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('employee_financials');
+ }
+};
diff --git a/database/migrations/2024_01_01_000012_create_items_table.php b/database/migrations/2024_01_01_000012_create_items_table.php
new file mode 100644
index 00000000..f8fa02be
--- /dev/null
+++ b/database/migrations/2024_01_01_000012_create_items_table.php
@@ -0,0 +1,30 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->string('name');
+ $table->string('code')->nullable();
+ $table->string('unit', 30);
+ $table->string('category')->nullable();
+ $table->decimal('min_stock', 15, 2)->default(0);
+ $table->timestamps();
+
+ $table->index('tenant_id');
+ $table->index('code');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('items');
+ }
+};
diff --git a/database/migrations/2024_01_01_000013_create_warehouses_table.php b/database/migrations/2024_01_01_000013_create_warehouses_table.php
new file mode 100644
index 00000000..c8ca78c5
--- /dev/null
+++ b/database/migrations/2024_01_01_000013_create_warehouses_table.php
@@ -0,0 +1,29 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->nullable()->constrained()->nullOnDelete();
+ $table->string('name');
+ $table->string('location')->nullable();
+ $table->boolean('is_active')->default(true);
+ $table->timestamps();
+
+ $table->index('tenant_id');
+ $table->index('project_id');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('warehouses');
+ }
+};
diff --git a/database/migrations/2024_01_01_000014_create_inventory_transactions_table.php b/database/migrations/2024_01_01_000014_create_inventory_transactions_table.php
new file mode 100644
index 00000000..3e698ad4
--- /dev/null
+++ b/database/migrations/2024_01_01_000014_create_inventory_transactions_table.php
@@ -0,0 +1,39 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('item_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('warehouse_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->nullable()->constrained()->nullOnDelete();
+ $table->enum('type', ['in', 'out', 'transfer']);
+ $table->decimal('quantity', 15, 2);
+ $table->decimal('unit_price', 15, 0)->nullable();
+ $table->foreignId('currency_id')->nullable()->constrained('currencies')->nullOnDelete();
+ $table->string('reference')->nullable();
+ $table->text('notes')->nullable();
+ $table->date('date');
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->index('tenant_id');
+ $table->index('item_id');
+ $table->index('warehouse_id');
+ $table->index('project_id');
+ $table->index('type');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('inventory_transactions');
+ }
+};
diff --git a/database/migrations/2024_01_01_000015_create_tasks_table.php b/database/migrations/2024_01_01_000015_create_tasks_table.php
new file mode 100644
index 00000000..dcea68a1
--- /dev/null
+++ b/database/migrations/2024_01_01_000015_create_tasks_table.php
@@ -0,0 +1,40 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('parent_id')->nullable()->constrained('tasks')->nullOnDelete();
+ $table->string('title');
+ $table->text('description')->nullable();
+ $table->enum('status', ['todo', 'in_progress', 'review', 'done', 'cancelled'])->default('todo');
+ $table->enum('priority', ['low', 'medium', 'high', 'urgent'])->default('medium');
+ $table->foreignId('assignee_id')->nullable()->constrained('users')->nullOnDelete();
+ $table->date('due_date')->nullable();
+ $table->tinyInteger('progress')->default(0);
+ $table->decimal('estimated_hours', 5, 2)->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->index('tenant_id');
+ $table->index('project_id');
+ $table->index('parent_id');
+ $table->index('status');
+ $table->index('priority');
+ $table->index('assignee_id');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('tasks');
+ }
+};
diff --git a/database/migrations/2024_01_01_000016_create_task_logs_table.php b/database/migrations/2024_01_01_000016_create_task_logs_table.php
new file mode 100644
index 00000000..eb5518e4
--- /dev/null
+++ b/database/migrations/2024_01_01_000016_create_task_logs_table.php
@@ -0,0 +1,30 @@
+id();
+ $table->foreignId('task_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('user_id')->constrained()->cascadeOnDelete();
+ $table->string('action');
+ $table->text('description')->nullable();
+ $table->json('old_value')->nullable();
+ $table->json('new_value')->nullable();
+ $table->timestamp('created_at');
+
+ $table->index('task_id');
+ $table->index('user_id');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('task_logs');
+ }
+};
diff --git a/database/migrations/2024_01_01_000017_create_letters_table.php b/database/migrations/2024_01_01_000017_create_letters_table.php
new file mode 100644
index 00000000..36e93419
--- /dev/null
+++ b/database/migrations/2024_01_01_000017_create_letters_table.php
@@ -0,0 +1,38 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->nullable()->constrained()->nullOnDelete();
+ $table->enum('type', ['incoming', 'outgoing']);
+ $table->string('subject');
+ $table->text('content')->nullable();
+ $table->string('sender');
+ $table->string('recipient');
+ $table->date('letter_date');
+ $table->string('reference_number')->nullable();
+ $table->foreignId('parent_letter_id')->nullable()->constrained('letters')->nullOnDelete();
+ $table->enum('status', ['draft', 'sent', 'received', 'archived'])->default('draft');
+ $table->timestamps();
+
+ $table->index('tenant_id');
+ $table->index('project_id');
+ $table->index('type');
+ $table->index('status');
+ $table->index('letter_date');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('letters');
+ }
+};
diff --git a/database/migrations/2024_01_01_000018_create_letter_attachments_table.php b/database/migrations/2024_01_01_000018_create_letter_attachments_table.php
new file mode 100644
index 00000000..a80c5141
--- /dev/null
+++ b/database/migrations/2024_01_01_000018_create_letter_attachments_table.php
@@ -0,0 +1,28 @@
+id();
+ $table->foreignId('letter_id')->constrained()->cascadeOnDelete();
+ $table->string('file_path');
+ $table->string('file_name');
+ $table->unsignedInteger('file_size');
+ $table->string('mime_type');
+ $table->timestamp('created_at');
+
+ $table->index('letter_id');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('letter_attachments');
+ }
+};
diff --git a/database/migrations/2024_01_01_000019_create_documents_table.php b/database/migrations/2024_01_01_000019_create_documents_table.php
new file mode 100644
index 00000000..0f17a49c
--- /dev/null
+++ b/database/migrations/2024_01_01_000019_create_documents_table.php
@@ -0,0 +1,32 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('project_id')->nullable()->constrained()->nullOnDelete();
+ $table->string('title');
+ $table->text('description')->nullable();
+ $table->string('file_path');
+ $table->string('file_type', 50);
+ $table->foreignId('uploaded_by')->constrained('users')->cascadeOnDelete();
+ $table->timestamps();
+
+ $table->index('tenant_id');
+ $table->index('project_id');
+ $table->index('uploaded_by');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('documents');
+ }
+};
diff --git a/database/migrations/2024_01_01_000020_create_audit_logs_table.php b/database/migrations/2024_01_01_000020_create_audit_logs_table.php
new file mode 100644
index 00000000..f73ace1a
--- /dev/null
+++ b/database/migrations/2024_01_01_000020_create_audit_logs_table.php
@@ -0,0 +1,34 @@
+id();
+ $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
+ $table->string('model_type');
+ $table->unsignedBigInteger('model_id');
+ $table->string('action');
+ $table->json('old_values')->nullable();
+ $table->json('new_values')->nullable();
+ $table->string('ip_address', 45)->nullable();
+ $table->text('user_agent')->nullable();
+ $table->timestamp('created_at');
+
+ $table->index('tenant_id');
+ $table->index('user_id');
+ $table->index(['model_type', 'model_id']);
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('audit_logs');
+ }
+};
diff --git a/database/migrations/2024_01_01_000021_add_missing_columns_to_tables.php b/database/migrations/2024_01_01_000021_add_missing_columns_to_tables.php
new file mode 100644
index 00000000..9407ef4c
--- /dev/null
+++ b/database/migrations/2024_01_01_000021_add_missing_columns_to_tables.php
@@ -0,0 +1,148 @@
+string('title')->nullable()->after('project_id');
+ }
+ if (!Schema::hasColumn('petty_cashes', 'remaining')) {
+ $table->decimal('remaining', 15, 0)->nullable()->after('original_amount');
+ }
+ if (!Schema::hasColumn('petty_cashes', 'amount_usd')) {
+ $table->decimal('amount_usd', 15, 2)->nullable()->after('amount');
+ }
+ if (!Schema::hasColumn('petty_cashes', 'approval_date')) {
+ $table->timestamp('approval_date')->nullable()->after('approved_by');
+ }
+ if (!Schema::hasColumn('petty_cashes', 'requested_by')) {
+ $table->foreignId('requested_by')->nullable()->after('approval_date')->constrained('users')->nullOnDelete();
+ }
+ if (!Schema::hasColumn('petty_cashes', 'approved_at')) {
+ $table->timestamp('approved_at')->nullable()->after('requested_by');
+ }
+ if (!Schema::hasColumn('petty_cashes', 'deleted_at')) {
+ $table->softDeletes();
+ }
+ });
+
+ // Rename petty_cashes.date → request_date
+ if (Schema::hasColumn('petty_cashes', 'date') && !Schema::hasColumn('petty_cashes', 'request_date')) {
+ Schema::table('petty_cashes', function (Blueprint $table) {
+ $table->renameColumn('date', 'request_date');
+ });
+ }
+
+ // ============================================================
+ // 2. WORK LOGS — add missing columns
+ // ============================================================
+ Schema::table('work_logs', function (Blueprint $table) {
+ if (!Schema::hasColumn('work_logs', 'overtime_hours')) {
+ $table->decimal('overtime_hours', 5, 2)->default(0)->after('hours_worked');
+ }
+ if (!Schema::hasColumn('work_logs', 'task_id')) {
+ $table->foreignId('task_id')->nullable()->after('project_id')->constrained('tasks')->nullOnDelete();
+ }
+ if (!Schema::hasColumn('work_logs', 'deleted_at')) {
+ $table->softDeletes();
+ }
+ });
+
+ // Drop old is_overtime (replaced by overtime_hours)
+ if (Schema::hasColumn('work_logs', 'is_overtime') && Schema::hasColumn('work_logs', 'overtime_hours')) {
+ Schema::table('work_logs', function (Blueprint $table) {
+ $table->dropColumn('is_overtime');
+ });
+ }
+
+ // ============================================================
+ // 3. EMPLOYEE FINANCIALS — rename date → effective_date
+ // ============================================================
+ if (Schema::hasColumn('employee_financials', 'date') && !Schema::hasColumn('employee_financials', 'effective_date')) {
+ Schema::table('employee_financials', function (Blueprint $table) {
+ $table->renameColumn('date', 'effective_date');
+ });
+ }
+
+ // ============================================================
+ // 4. INVENTORY TRANSACTIONS — add missing columns
+ // ============================================================
+ Schema::table('inventory_transactions', function (Blueprint $table) {
+ if (!Schema::hasColumn('inventory_transactions', 'created_by')) {
+ $table->foreignId('created_by')->nullable()->after('currency_id')->constrained('users')->nullOnDelete();
+ }
+ if (!Schema::hasColumn('inventory_transactions', 'notes')) {
+ $table->text('notes')->nullable()->after('description');
+ }
+ if (!Schema::hasColumn('inventory_transactions', 'reference_number')) {
+ $table->string('reference_number', 100)->nullable()->after('reference');
+ }
+ });
+ }
+
+ public function down(): void
+ {
+ // 4. Inventory Transactions
+ Schema::table('inventory_transactions', function (Blueprint $table) {
+ if (Schema::hasColumn('inventory_transactions', 'created_by')) {
+ $table->dropConstrainedForeignId('created_by');
+ }
+ if (Schema::hasColumn('inventory_transactions', 'notes')) {
+ $table->dropColumn('notes');
+ }
+ if (Schema::hasColumn('inventory_transactions', 'reference_number')) {
+ $table->dropColumn('reference_number');
+ }
+ });
+
+ // 3. Employee Financials
+ if (Schema::hasColumn('employee_financials', 'effective_date') && !Schema::hasColumn('employee_financials', 'date')) {
+ Schema::table('employee_financials', function (Blueprint $table) {
+ $table->renameColumn('effective_date', 'date');
+ });
+ }
+
+ // 2. Work Logs
+ Schema::table('work_logs', function (Blueprint $table) {
+ if (!Schema::hasColumn('work_logs', 'is_overtime')) {
+ $table->boolean('is_overtime')->default(false);
+ }
+ if (Schema::hasColumn('work_logs', 'task_id')) {
+ $table->dropConstrainedForeignId('task_id');
+ }
+ if (Schema::hasColumn('work_logs', 'overtime_hours')) {
+ $table->dropColumn('overtime_hours');
+ }
+ if (Schema::hasColumn('work_logs', 'deleted_at')) {
+ $table->dropSoftDeletes();
+ }
+ });
+
+ // 1. Petty Cashes
+ if (Schema::hasColumn('petty_cashes', 'request_date') && !Schema::hasColumn('petty_cashes', 'date')) {
+ Schema::table('petty_cashes', function (Blueprint $table) {
+ $table->renameColumn('request_date', 'date');
+ });
+ }
+ Schema::table('petty_cashes', function (Blueprint $table) {
+ if (Schema::hasColumn('petty_cashes', 'requested_by')) {
+ $table->dropConstrainedForeignId('requested_by');
+ }
+ $cols = array_filter(['title', 'remaining', 'amount_usd', 'approval_date', 'approved_at', 'deleted_at'], function($col) {
+ return Schema::hasColumn('petty_cashes', $col);
+ });
+ if (!empty($cols)) {
+ $table->dropColumn($cols);
+ }
+ });
+ }
+};
diff --git a/database/migrations/2024_01_01_000022_add_expense_and_pettycash_missing_columns.php b/database/migrations/2024_01_01_000022_add_expense_and_pettycash_missing_columns.php
new file mode 100644
index 00000000..2c4f61ae
--- /dev/null
+++ b/database/migrations/2024_01_01_000022_add_expense_and_pettycash_missing_columns.php
@@ -0,0 +1,94 @@
+foreignId('requested_by')->nullable()->after('receipt_path')->constrained('users')->nullOnDelete();
+ }
+ if (!Schema::hasColumn('expenses', 'approval_date')) {
+ $table->timestamp('approval_date')->nullable()->after('approved_by');
+ }
+ if (!Schema::hasColumn('expenses', 'amount_usd')) {
+ $table->decimal('amount_usd', 15, 2)->nullable()->after('amount');
+ }
+ if (!Schema::hasColumn('expenses', 'original_amount')) {
+ $table->decimal('original_amount', 15, 0)->nullable()->after('amount_usd');
+ }
+ });
+
+ // Add 'paid' to expenses status enum if needed
+ // Note: MySQL requires altering the enum column directly
+ if (Schema::hasColumn('expenses', 'status')) {
+ \DB::statement("ALTER TABLE expenses MODIFY COLUMN status ENUM('pending','approved','rejected','paid') DEFAULT 'pending'");
+ }
+
+ // ============================================================
+ // 2. PETTY CASHES — add receipt_path column
+ // ============================================================
+ Schema::table('petty_cashes', function (Blueprint $table) {
+ if (!Schema::hasColumn('petty_cashes', 'receipt_path')) {
+ $table->string('receipt_path')->nullable()->after('request_date');
+ }
+ });
+
+ // ============================================================
+ // 3. AUDIT LOGS — add url and method columns (for middleware)
+ // ============================================================
+ Schema::table('audit_logs', function (Blueprint $table) {
+ if (!Schema::hasColumn('audit_logs', 'url')) {
+ $table->string('url')->nullable()->after('user_agent');
+ }
+ if (!Schema::hasColumn('audit_logs', 'method')) {
+ $table->string('method', 10)->nullable()->after('url');
+ }
+ });
+ }
+
+ public function down(): void
+ {
+ // 3. Audit Logs
+ Schema::table('audit_logs', function (Blueprint $table) {
+ if (Schema::hasColumn('audit_logs', 'method')) {
+ $table->dropColumn('method');
+ }
+ if (Schema::hasColumn('audit_logs', 'url')) {
+ $table->dropColumn('url');
+ }
+ });
+
+ // 2. Petty Cashes
+ Schema::table('petty_cashes', function (Blueprint $table) {
+ if (Schema::hasColumn('petty_cashes', 'receipt_path')) {
+ $table->dropColumn('receipt_path');
+ }
+ });
+
+ // 1. Expenses — revert status enum
+ \DB::statement("ALTER TABLE expenses MODIFY COLUMN status ENUM('pending','approved','rejected') DEFAULT 'pending'");
+
+ Schema::table('expenses', function (Blueprint $table) {
+ if (Schema::hasColumn('expenses', 'original_amount')) {
+ $table->dropColumn('original_amount');
+ }
+ if (Schema::hasColumn('expenses', 'amount_usd')) {
+ $table->dropColumn('amount_usd');
+ }
+ if (Schema::hasColumn('expenses', 'approval_date')) {
+ $table->dropColumn('approval_date');
+ }
+ if (Schema::hasColumn('expenses', 'requested_by')) {
+ $table->dropConstrainedForeignId('requested_by');
+ }
+ });
+ }
+};
diff --git a/database/migrations/2024_01_01_000023_create_attachments_table.php b/database/migrations/2024_01_01_000023_create_attachments_table.php
new file mode 100644
index 00000000..7b0958d3
--- /dev/null
+++ b/database/migrations/2024_01_01_000023_create_attachments_table.php
@@ -0,0 +1,33 @@
+id();
+ $table->foreignId('tenant_id')->nullable()->constrained()->nullOnDelete();
+ $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
+ $table->morphs('attachable');
+ $table->string('file_name');
+ $table->string('file_path');
+ $table->string('mime_type')->nullable();
+ $table->unsignedBigInteger('file_size')->default(0);
+ $table->string('category')->default('receipt'); // receipt, document, photo, other
+ $table->text('description')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->index(['tenant_id', 'attachable_type', 'attachable_id']);
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('attachments');
+ }
+};
diff --git a/database/migrations/2024_01_01_000024_create_task_dependencies_table.php b/database/migrations/2024_01_01_000024_create_task_dependencies_table.php
new file mode 100644
index 00000000..88593bd5
--- /dev/null
+++ b/database/migrations/2024_01_01_000024_create_task_dependencies_table.php
@@ -0,0 +1,35 @@
+id();
+ $table->foreignId('task_id')->constrained('tasks')->cascadeOnDelete();
+ $table->foreignId('depends_on_task_id')->constrained('tasks')->cascadeOnDelete();
+ $table->enum('dependency_type', ['finish_to_start', 'start_to_start', 'finish_to_finish', 'start_to_finish'])->default('finish_to_start');
+ $table->timestamps();
+
+ // Prevent duplicate dependencies
+ $table->unique(['task_id', 'depends_on_task_id']);
+
+ // Prevent self-dependency (handled at app level too)
+ $table->index('task_id');
+ $table->index('depends_on_task_id');
+ });
+
+ // Note: Adding columns to tasks table is now handled by the dedicated migration:
+ // 2024_01_01_000025_add_missing_columns_to_tasks_table.php
+ // This avoids the issue of ->after('completed_at') when completed_at doesn't exist yet
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('task_dependencies');
+ }
+};
diff --git a/database/migrations/2024_01_01_000025_add_missing_columns_to_tasks_table.php b/database/migrations/2024_01_01_000025_add_missing_columns_to_tasks_table.php
new file mode 100644
index 00000000..4290f933
--- /dev/null
+++ b/database/migrations/2024_01_01_000025_add_missing_columns_to_tasks_table.php
@@ -0,0 +1,66 @@
+date('start_date')->nullable()->after('due_date');
+ }
+ });
+
+ Schema::table('tasks', function (Blueprint $table) {
+ if (!Schema::hasColumn('tasks', 'reporter_id')) {
+ $table->foreignId('reporter_id')->nullable()->after('assignee_id')->constrained('users')->nullOnDelete();
+ }
+ });
+
+ Schema::table('tasks', function (Blueprint $table) {
+ if (!Schema::hasColumn('tasks', 'completed_at')) {
+ $table->timestamp('completed_at')->nullable()->after('estimated_hours');
+ }
+ });
+
+ Schema::table('tasks', function (Blueprint $table) {
+ if (!Schema::hasColumn('tasks', 'order')) {
+ $table->integer('order')->default(0)->after('completed_at');
+ }
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('tasks', function (Blueprint $table) {
+ if (Schema::hasColumn('tasks', 'order')) {
+ $table->dropColumn('order');
+ }
+ });
+
+ Schema::table('tasks', function (Blueprint $table) {
+ if (Schema::hasColumn('tasks', 'completed_at')) {
+ $table->dropColumn('completed_at');
+ }
+ });
+
+ Schema::table('tasks', function (Blueprint $table) {
+ if (Schema::hasColumn('tasks', 'reporter_id')) {
+ $table->dropConstrainedForeignId('reporter_id');
+ }
+ });
+
+ Schema::table('tasks', function (Blueprint $table) {
+ if (Schema::hasColumn('tasks', 'start_date')) {
+ $table->dropColumn('start_date');
+ }
+ });
+ }
+};
diff --git a/database/migrations/2026_05_30_225223_add_created_by_to_tasks_table.php b/database/migrations/2026_05_30_225223_add_created_by_to_tasks_table.php
new file mode 100644
index 00000000..29808b7e
--- /dev/null
+++ b/database/migrations/2026_05_30_225223_add_created_by_to_tasks_table.php
@@ -0,0 +1,28 @@
+foreignId('requested_by')->nullable()->after('employee_id')->constrained('users')->nullOnDelete();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('expenses', function (Blueprint $table) {
+ $table->dropForeign(['requested_by']);
+ $table->dropColumn('requested_by');
+ });
+ }
+};
diff --git a/database/migrations/2026_06_04_143839_add_requested_by_and_request_date_to_petty_cashes_table.php b/database/migrations/2026_06_04_143839_add_requested_by_and_request_date_to_petty_cashes_table.php
new file mode 100644
index 00000000..e6e85040
--- /dev/null
+++ b/database/migrations/2026_06_04_143839_add_requested_by_and_request_date_to_petty_cashes_table.php
@@ -0,0 +1,24 @@
+foreignId('requested_by')->nullable()->after('tenant_id')->constrained('users')->nullOnDelete();
+ $table->date('request_date')->nullable()->after('description');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('petty_cashes', function (Blueprint $table) {
+ $table->dropForeign(['requested_by']);
+ $table->dropColumn(['requested_by', 'request_date']);
+ });
+ }
+};
\ No newline at end of file
diff --git a/database/seeders/CurrencySeeder.php b/database/seeders/CurrencySeeder.php
new file mode 100644
index 00000000..f36bfc62
--- /dev/null
+++ b/database/seeders/CurrencySeeder.php
@@ -0,0 +1,66 @@
+ 'IRR',
+ 'name' => 'Iranian Rial',
+ 'symbol' => '﷼',
+ 'decimal_places' => 0,
+ 'exchange_rate_to_usd' => 42000.000000,
+ 'is_active' => true,
+ ],
+ [
+ 'code' => 'USD',
+ 'name' => 'US Dollar',
+ 'symbol' => '$',
+ 'decimal_places' => 2,
+ 'exchange_rate_to_usd' => 1.000000,
+ 'is_active' => true,
+ ],
+ [
+ 'code' => 'EUR',
+ 'name' => 'Euro',
+ 'symbol' => '€',
+ 'decimal_places' => 2,
+ 'exchange_rate_to_usd' => 0.920000,
+ 'is_active' => true,
+ ],
+ [
+ 'code' => 'AED',
+ 'name' => 'UAE Dirham',
+ 'symbol' => 'د.إ',
+ 'decimal_places' => 2,
+ 'exchange_rate_to_usd' => 3.670000,
+ 'is_active' => true,
+ ],
+ ];
+
+ foreach ($currencies as $currency) {
+ Currency::create($currency);
+ }
+
+ $this->command->info('Currencies seeded: ' . count($currencies) . ' records created.');
+ }
+}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index d01a0ef2..0730fd87 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -2,22 +2,34 @@
namespace Database\Seeders;
-use App\Models\User;
-// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
+ *
+ * Execution order is critical due to foreign key dependencies:
+ * 1. Currencies — no dependencies (platform-level data)
+ * 2. Roles & Perms — no dependencies (Spatie is self-contained)
+ * 3. Tenants — depends on currencies (base_currency_id)
+ * 4. Users — depends on tenants & roles
+ * 5. Projects — depends on tenants, users, currencies
+ * 6. Employees — depends on tenants, projects, currencies
+ * 7. Tasks — depends on tenants, projects, users
+ * 8. Expenses — depends on tenants, projects, employees, currencies, users
*/
public function run(): void
{
- // User::factory(10)->create();
-
- User::factory()->create([
- 'name' => 'Test User',
- 'email' => 'test@example.com',
+ $this->call([
+ CurrencySeeder::class,
+ RolePermissionSeeder::class,
+ TenantSeeder::class,
+ UserSeeder::class,
+ ProjectSeeder::class,
+ EmployeeSeeder::class,
+ TaskSeeder::class,
+ ExpenseSeeder::class,
]);
}
}
diff --git a/database/seeders/EmployeeSeeder.php b/database/seeders/EmployeeSeeder.php
new file mode 100644
index 00000000..ec1c5141
--- /dev/null
+++ b/database/seeders/EmployeeSeeder.php
@@ -0,0 +1,297 @@
+firstOrFail();
+ $tenant2 = Tenant::where('slug', 'global')->firstOrFail();
+
+ $irr = Currency::where('code', 'IRR')->firstOrFail();
+ $usd = Currency::where('code', 'USD')->firstOrFail();
+
+ $tenant1Projects = $tenant1->projects()->orderBy('id')->get();
+ $tenant2Projects = $tenant2->projects()->orderBy('id')->get();
+
+ // ─────────────────────────────────────────────
+ // Tenant 1 — Sepideh Employees (Persian)
+ // ─────────────────────────────────────────────
+
+ $tenant1Employees = [
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id, // ساختمان مسکونی گلستان
+ 'national_code' => '0012345678',
+ 'first_name' => 'حسین',
+ 'last_name' => 'محمدی',
+ 'phone' => '09121234567',
+ 'job_title' => 'مهندس عمران',
+ 'contract_type' => 'permanent',
+ 'hire_date' => '2022-03-01',
+ 'status' => 'active',
+ 'base_salary' => 85000000, // 85M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-1234-5678',
+ 'notes' => null,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id,
+ 'national_code' => '0023456789',
+ 'first_name' => 'رضا',
+ 'last_name' => 'کریمی',
+ 'phone' => '09132345678',
+ 'job_title' => 'کارگر ساختمانی',
+ 'contract_type' => 'temporary',
+ 'hire_date' => '2024-04-01',
+ 'status' => 'active',
+ 'base_salary' => 45000000, // 45M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-2345-6789',
+ 'notes' => null,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[1]->id, // پل عابر سپید
+ 'national_code' => '0034567890',
+ 'first_name' => 'مهدی',
+ 'last_name' => 'حسینی',
+ 'phone' => '09143456789',
+ 'job_title' => 'مهندس راه و ترابری',
+ 'contract_type' => 'permanent',
+ 'hire_date' => '2023-06-15',
+ 'status' => 'active',
+ 'base_salary' => 92000000, // 92M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-3456-7890',
+ 'notes' => null,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[2]->id, // مجتمع تجاری مروارید
+ 'national_code' => '0045678901',
+ 'first_name' => 'فاطمه',
+ 'last_name' => 'احمدی',
+ 'phone' => '09154567890',
+ 'job_title' => 'حسابدار',
+ 'contract_type' => 'permanent',
+ 'hire_date' => '2022-09-01',
+ 'status' => 'active',
+ 'base_salary' => 78000000, // 78M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-4567-8901',
+ 'notes' => null,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[2]->id,
+ 'national_code' => '0056789012',
+ 'first_name' => 'امیر',
+ 'last_name' => 'نجفی',
+ 'phone' => '09165678901',
+ 'job_title' => 'تاسیساتکار',
+ 'contract_type' => 'contractor',
+ 'hire_date' => '2024-01-10',
+ 'status' => 'active',
+ 'base_salary' => 65000000, // 65M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-5678-9012',
+ 'notes' => 'پیمانکار تاسیسات مکانیکی',
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[3]->id, // استخر و سونا سلامت
+ 'national_code' => '0067890123',
+ 'first_name' => 'سعید',
+ 'last_name' => 'موسوی',
+ 'phone' => '09176789012',
+ 'job_title' => 'برقکار',
+ 'contract_type' => 'temporary',
+ 'hire_date' => '2023-08-20',
+ 'status' => 'terminated',
+ 'termination_date' => '2024-05-30',
+ 'base_salary' => 55000000, // 55M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-6789-0123',
+ 'notes' => 'پروژه به اتمام رسیده — تسویه شده',
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[4]->id, // بازسازی بیمارستان شفا
+ 'national_code' => '0078901234',
+ 'first_name' => 'زهرا',
+ 'last_name' => 'صادقی',
+ 'phone' => '09187890123',
+ 'job_title' => 'معمار',
+ 'contract_type' => 'permanent',
+ 'hire_date' => '2023-01-15',
+ 'status' => 'on_leave',
+ 'base_salary' => 88000000, // 88M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-7890-1234',
+ 'notes' => 'مرخصی استعلاجی از ۱۴۰۳/۰۴/۰۱ تا ۱۴۰۳/۰۶/۳۱',
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[4]->id,
+ 'national_code' => '0089012345',
+ 'first_name' => 'محمد',
+ 'last_name' => 'جعفری',
+ 'phone' => '09198901234',
+ 'job_title' => 'نجار',
+ 'contract_type' => 'contractor',
+ 'hire_date' => '2024-03-01',
+ 'status' => 'active',
+ 'base_salary' => 50000000, // 50M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-8901-2345',
+ 'notes' => 'پیمانکار کارهای چوب و دکوراسیون',
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => null,
+ 'national_code' => '0090123456',
+ 'first_name' => 'علی',
+ 'last_name' => 'عباسی',
+ 'phone' => '09209012345',
+ 'job_title' => 'راننده',
+ 'contract_type' => 'temporary',
+ 'hire_date' => '2024-02-15',
+ 'status' => 'active',
+ 'base_salary' => 40000000, // 40M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-9012-3456',
+ 'notes' => 'راننده کامیون حمل مصالح',
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id,
+ 'national_code' => '0101234567',
+ 'first_name' => 'مریم',
+ 'last_name' => 'رحیمی',
+ 'phone' => '09210123456',
+ 'job_title' => 'مسئول انبار',
+ 'contract_type' => 'permanent',
+ 'hire_date' => '2023-11-01',
+ 'status' => 'active',
+ 'base_salary' => 52000000, // 52M IRR
+ 'currency_id' => $irr->id,
+ 'bank_account' => '6037-9975-0123-4567',
+ 'notes' => null,
+ ],
+ ];
+
+ foreach ($tenant1Employees as $empData) {
+ Employee::create($empData);
+ }
+
+ // ─────────────────────────────────────────────
+ // Tenant 2 — Global Employees (English)
+ // ─────────────────────────────────────────────
+
+ $tenant2Employees = [
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[0]->id, // Riverside Tower
+ 'national_code' => '1000000001',
+ 'first_name' => 'David',
+ 'last_name' => 'Johnson',
+ 'phone' => '+1-555-0101',
+ 'job_title' => 'Civil Engineer',
+ 'contract_type' => 'permanent',
+ 'hire_date' => '2023-01-15',
+ 'status' => 'active',
+ 'base_salary' => 8500, // 8,500 USD
+ 'currency_id' => $usd->id,
+ 'bank_account' => 'US12-3456-7890-1234',
+ 'notes' => null,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[0]->id,
+ 'national_code' => '1000000002',
+ 'first_name' => 'Sarah',
+ 'last_name' => 'Williams',
+ 'phone' => '+1-555-0102',
+ 'job_title' => 'Project Coordinator',
+ 'contract_type' => 'permanent',
+ 'hire_date' => '2023-06-01',
+ 'status' => 'active',
+ 'base_salary' => 6200,
+ 'currency_id' => $usd->id,
+ 'bank_account' => 'US12-3456-7890-5678',
+ 'notes' => null,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[1]->id, // Industrial Warehouse
+ 'national_code' => '1000000003',
+ 'first_name' => 'Michael',
+ 'last_name' => 'Brown',
+ 'phone' => '+1-555-0103',
+ 'job_title' => 'Steel Erector',
+ 'contract_type' => 'contractor',
+ 'hire_date' => '2024-03-10',
+ 'status' => 'active',
+ 'base_salary' => 5500,
+ 'currency_id' => $usd->id,
+ 'bank_account' => 'US12-3456-7890-9012',
+ 'notes' => 'Subcontractor — structural steelwork',
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => null,
+ 'national_code' => '1000000004',
+ 'first_name' => 'Emily',
+ 'last_name' => 'Davis',
+ 'phone' => '+1-555-0104',
+ 'job_title' => 'Accountant',
+ 'contract_type' => 'permanent',
+ 'hire_date' => '2022-09-15',
+ 'status' => 'active',
+ 'base_salary' => 5800,
+ 'currency_id' => $usd->id,
+ 'bank_account' => 'US12-3456-7890-3456',
+ 'notes' => null,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[2]->id, // Greenfield School
+ 'national_code' => '1000000005',
+ 'first_name' => 'Robert',
+ 'last_name' => 'Taylor',
+ 'phone' => '+1-555-0105',
+ 'job_title' => 'Electrician',
+ 'contract_type' => 'temporary',
+ 'hire_date' => '2024-01-20',
+ 'status' => 'terminated',
+ 'termination_date' => '2024-07-15',
+ 'base_salary' => 4500,
+ 'currency_id' => $usd->id,
+ 'bank_account' => 'US12-3456-7890-7890',
+ 'notes' => 'Project completed — contract ended',
+ ],
+ ];
+
+ foreach ($tenant2Employees as $empData) {
+ Employee::create($empData);
+ }
+
+ $total = count($tenant1Employees) + count($tenant2Employees);
+ $this->command->info("Employees seeded: {$total} records created.");
+ }
+}
diff --git a/database/seeders/ExpenseSeeder.php b/database/seeders/ExpenseSeeder.php
new file mode 100644
index 00000000..d3bf02b9
--- /dev/null
+++ b/database/seeders/ExpenseSeeder.php
@@ -0,0 +1,376 @@
+firstOrFail();
+ $tenant2 = Tenant::where('slug', 'global')->firstOrFail();
+
+ $irr = Currency::where('code', 'IRR')->firstOrFail();
+ $usd = Currency::where('code', 'USD')->firstOrFail();
+
+ $tenant1Projects = $tenant1->projects()->orderBy('id')->get();
+ $tenant2Projects = $tenant2->projects()->orderBy('id')->get();
+
+ $tenant1Employees = $tenant1->employees()->orderBy('id')->get();
+ $tenant2Employees = $tenant2->employees()->orderBy('id')->get();
+
+ $admin1 = User::where('email', 'admin@sepideh.ir')->firstOrFail();
+ $manager = User::where('email', 'manager@sepideh.ir')->firstOrFail();
+ $admin2 = User::where('email', 'admin@global.com')->firstOrFail();
+
+ // ─────────────────────────────────────────────
+ // Tenant 1 — Sepideh Expenses (IRR)
+ // ─────────────────────────────────────────────
+
+ $tenant1Expenses = [
+ // Project: ساختمان مسکونی گلستان
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => $tenant1Employees[0]->id ?? null,
+ 'amount' => 2500000000, // 2.5B IRR — میلگرد
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'خرید میلگرد آج از ذوب آهن اصفهان — ۵۰ تن',
+ 'expense_date' => '2024-05-10',
+ 'category' => 'materials',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin1->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => $tenant1Employees[1]->id ?? null,
+ 'amount' => 850000000, // 850M IRR — سیمان
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'خرید سیمان تهران — ۲۰۰ تن تیپ ۲ و ۵',
+ 'expense_date' => '2024-05-25',
+ 'category' => 'materials',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin1->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 320000000, // 320M IRR — اجاره جرثقیل
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'اجاره جرثقیل ۵۰ تنی برای مدت ۲۰ روز',
+ 'expense_date' => '2024-06-15',
+ 'category' => 'equipment',
+ 'receipt_path' => null,
+ 'status' => 'pending',
+ 'approved_by' => null,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 45000000, // 45M IRR — حمل و نقل
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'هزینه حمل مصالح از کارخانه تا محل پروژه — ۳ سفر کامیون',
+ 'expense_date' => '2024-06-20',
+ 'category' => 'transport',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $manager->id,
+ ],
+ // Project: مجتمع تجاری مروارید
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[2]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => $tenant1Employees[4]->id ?? null,
+ 'amount' => 1800000000, // 1.8B IRR — چیلر
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'خرید چیلر ۵۰۰ تنی و نصب سیستم تهویه',
+ 'expense_date' => '2024-07-05',
+ 'category' => 'equipment',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin1->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[2]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => $tenant1Employees[3]->id ?? null,
+ 'amount' => 120000000, // 120M IRR — خوراک
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'وعده غذایی کارگران — ۳ ماهه دوم ۱۴۰۳',
+ 'expense_date' => '2024-07-15',
+ 'category' => 'food',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $manager->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[2]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 600000000, // 600M IRR — نیروی کار
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'حقوق کارگران ساختمانی پروژه مروارید — تیرماه ۱۴۰۳',
+ 'expense_date' => '2024-07-22',
+ 'category' => 'labor',
+ 'receipt_path' => null,
+ 'status' => 'pending',
+ 'approved_by' => null,
+ ],
+ // Project: بازسازی بیمارستان شفا
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[4]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 950000000, // 950M IRR — مصالح
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'خرید مصالح بهداشتی بیمارستانی — کفپوش، رنگ ضدعفونیکننده و دربهای اتوماتیک',
+ 'expense_date' => '2024-06-10',
+ 'category' => 'materials',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin1->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[4]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 200000000, // 200M IRR — تجهیزات
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'تأمین و نصب ژنراتور اضطراری ۵۰۰ کیلووات',
+ 'expense_date' => '2024-07-01',
+ 'category' => 'equipment',
+ 'receipt_path' => null,
+ 'status' => 'rejected',
+ 'approved_by' => $admin1->id,
+ ],
+ // Project: استخر و سونا سلامت
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[3]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 350000000, // 350M IRR — مصالح
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'خرید سرامیک و موزاییک استخر — مارک آلفا',
+ 'expense_date' => '2024-02-15',
+ 'category' => 'materials',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin1->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[3]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 75000000, // 75M IRR — حمل و نقل
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'هزینه حمل و نصب سیستم فیلتراسیون آب استخر',
+ 'expense_date' => '2024-03-20',
+ 'category' => 'transport',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $manager->id,
+ ],
+ // Project: پل عابر سپید
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[1]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => $tenant1Employees[2]->id ?? null,
+ 'amount' => 150000000, // 150M IRR — گمانهبوری
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'هزینه گمانهبوری و مطالعات ژئوتکنیک پل عابر',
+ 'expense_date' => '2024-07-10',
+ 'category' => 'other',
+ 'receipt_path' => null,
+ 'status' => 'pending',
+ 'approved_by' => null,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[1]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 280000000, // 280M IRR — تجهیزات
+ 'currency_id' => $irr->id,
+ 'original_amount' => null,
+ 'description' => 'خرید و نصب داربست و قالب فلزی برای ساخت پایههای پل',
+ 'expense_date' => '2024-07-18',
+ 'category' => 'equipment',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin1->id,
+ ],
+ ];
+
+ foreach ($tenant1Expenses as $expenseData) {
+ Expense::create($expenseData);
+ }
+
+ // ─────────────────────────────────────────────
+ // Tenant 2 — Global Expenses (USD)
+ // ─────────────────────────────────────────────
+
+ $tenant2Expenses = [
+ // Project: Riverside Tower
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[0]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => $tenant2Employees[0]->id ?? null,
+ 'amount' => 1250000, // $1.25M — concrete
+ 'currency_id' => $usd->id,
+ 'original_amount' => null,
+ 'description' => 'Ready-mix concrete supply for foundation pouring — 3,000 cubic yards',
+ 'expense_date' => '2024-04-15',
+ 'category' => 'materials',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin2->id,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[0]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 340000, // $340K — steel
+ 'currency_id' => $usd->id,
+ 'original_amount' => null,
+ 'description' => 'Structural steel reinforcement bars — Grade 60, 500 tons',
+ 'expense_date' => '2024-05-20',
+ 'category' => 'materials',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin2->id,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[0]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => $tenant2Employees[2]->id ?? null,
+ 'amount' => 85000, // $85K — equipment rental
+ 'currency_id' => $usd->id,
+ 'original_amount' => null,
+ 'description' => 'Tower crane rental — 3 months',
+ 'expense_date' => '2024-06-01',
+ 'category' => 'equipment',
+ 'receipt_path' => null,
+ 'status' => 'pending',
+ 'approved_by' => null,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[0]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 22500, // $22.5K — labor
+ 'currency_id' => $usd->id,
+ 'original_amount' => null,
+ 'description' => 'Labor wages for concrete finishing crew — June 2024',
+ 'expense_date' => '2024-06-30',
+ 'category' => 'labor',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin2->id,
+ ],
+ // Project: Industrial Warehouse Complex
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[1]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 45000, // $45K — site survey
+ 'currency_id' => $usd->id,
+ 'original_amount' => null,
+ 'description' => 'Geotechnical survey and soil testing across the warehouse site',
+ 'expense_date' => '2024-07-10',
+ 'category' => 'other',
+ 'receipt_path' => null,
+ 'status' => 'pending',
+ 'approved_by' => null,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[1]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => null,
+ 'amount' => 15000, // $15K — transport
+ 'currency_id' => $usd->id,
+ 'original_amount' => null,
+ 'description' => 'Transportation of heavy equipment to warehouse site',
+ 'expense_date' => '2024-07-15',
+ 'category' => 'transport',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin2->id,
+ ],
+ // Project: Greenfield School Campus
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[2]->id,
+ 'petty_cash_id' => null,
+ 'employee_id' => $tenant2Employees[4]->id ?? null,
+ 'amount' => 62000, // $62K — electrical
+ 'currency_id' => $usd->id,
+ 'original_amount' => null,
+ 'description' => 'Electrical wiring, panels and fire alarm system for Building A',
+ 'expense_date' => '2024-04-10',
+ 'category' => 'equipment',
+ 'receipt_path' => null,
+ 'status' => 'approved',
+ 'approved_by' => $admin2->id,
+ ],
+ ];
+
+ foreach ($tenant2Expenses as $expenseData) {
+ Expense::create($expenseData);
+ }
+
+ $total = count($tenant1Expenses) + count($tenant2Expenses);
+ $this->command->info("Expenses seeded: {$total} records created.");
+ }
+}
diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php
new file mode 100644
index 00000000..caf5b799
--- /dev/null
+++ b/database/seeders/PermissionSeeder.php
@@ -0,0 +1,55 @@
+ $permission, 'guard_name' => 'web']);
+ }
+
+ // Assign all permissions to admin role
+ $adminRole = Role::where('name', 'admin')->first();
+ if ($adminRole) {
+ $adminRole->syncPermissions(Permission::all());
+ }
+
+ // Assign basic permissions to manager role
+ $managerRole = Role::where('name', 'manager')->first();
+ if ($managerRole) {
+ $managerRole->syncPermissions([
+ 'manage projects',
+ 'manage expenses',
+ 'manage petty cash',
+ 'manage employees',
+ 'manage tasks',
+ 'manage inventory',
+ 'manage attachments',
+ 'view reports',
+ 'view dashboard',
+ ]);
+ }
+
+ $this->command->info('Permissions seeded successfully!');
+ }
+}
\ No newline at end of file
diff --git a/database/seeders/ProjectSeeder.php b/database/seeders/ProjectSeeder.php
new file mode 100644
index 00000000..e8b950af
--- /dev/null
+++ b/database/seeders/ProjectSeeder.php
@@ -0,0 +1,169 @@
+firstOrFail();
+ $tenant2 = Tenant::where('slug', 'global')->firstOrFail();
+
+ $irr = Currency::where('code', 'IRR')->firstOrFail();
+ $usd = Currency::where('code', 'USD')->firstOrFail();
+
+ $tenant1Manager = User::where('email', 'manager@sepideh.ir')->firstOrFail();
+ $tenant1Admin = User::where('email', 'admin@sepideh.ir')->firstOrFail();
+ $tenant2Admin = User::where('email', 'admin@global.com')->firstOrFail();
+
+ // ─────────────────────────────────────────────
+ // Tenant 1 — Sepideh (Persian construction projects)
+ // ─────────────────────────────────────────────
+
+ $persianProjects = [
+ [
+ 'tenant_id' => $tenant1->id,
+ 'name' => 'ساختمان مسکونی گلستان',
+ 'code' => 'SPD-1403-001',
+ 'description' => 'ساخت مجتمع مسکونی ۱۲ طبقه شامل ۹۶ واحد در منطقه ۲ تهران',
+ 'status' => 'in_progress',
+ 'start_date' => '2024-04-20',
+ 'end_date' => '2025-10-20',
+ 'progress' => 45,
+ 'budget' => 850000000000, // 850 billion IRR
+ 'default_currency_id' => $irr->id,
+ 'budget_usd' => 20238095.24,
+ 'manager_id' => $tenant1Manager->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'name' => 'پل عابر سپید',
+ 'code' => 'SPD-1403-002',
+ 'description' => 'ساخت پل عابر پیاده با طول ۴۵ متر و عرض ۵ متر در بزرگراه همت',
+ 'status' => 'planning',
+ 'start_date' => '2024-09-01',
+ 'end_date' => '2025-06-30',
+ 'progress' => 10,
+ 'budget' => 120000000000, // 120 billion IRR
+ 'default_currency_id' => $irr->id,
+ 'budget_usd' => 2857142.86,
+ 'manager_id' => $tenant1Admin->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'name' => 'مجتمع تجاری مروارید',
+ 'code' => 'SPD-1403-003',
+ 'description' => 'ساخت مجتمع تجاری ۸ طبقه شامل ۱۲۰ واحد تجاری و پارکینگ ۳ طبقه',
+ 'status' => 'in_progress',
+ 'start_date' => '2024-01-15',
+ 'end_date' => '2025-12-31',
+ 'progress' => 62,
+ 'budget' => 1200000000000, // 1.2 trillion IRR
+ 'default_currency_id' => $irr->id,
+ 'budget_usd' => 28571428.57,
+ 'manager_id' => $tenant1Manager->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'name' => 'استخر و سونا سلامت',
+ 'code' => 'SPD-1403-004',
+ 'description' => 'ساخت مجموعه ورزشی شامل استخر المپیک، سونا خشک و بخار و سالن بدنسازی',
+ 'status' => 'completed',
+ 'start_date' => '2023-06-01',
+ 'end_date' => '2024-05-30',
+ 'progress' => 100,
+ 'budget' => 350000000000, // 350 billion IRR
+ 'default_currency_id' => $irr->id,
+ 'budget_usd' => 8333333.33,
+ 'manager_id' => $tenant1Admin->id,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'name' => 'بازسازی بیمارستان شفا',
+ 'code' => 'SPD-1403-005',
+ 'description' => 'بازسازی و نوسازی بخشهای داخلی بیمارستان شفا شامل اورژانس، ICU و بخش جراحی',
+ 'status' => 'on_hold',
+ 'start_date' => '2024-03-01',
+ 'end_date' => '2025-03-01',
+ 'progress' => 25,
+ 'budget' => 500000000000, // 500 billion IRR
+ 'default_currency_id' => $irr->id,
+ 'budget_usd' => 11904761.90,
+ 'manager_id' => $tenant1Manager->id,
+ ],
+ ];
+
+ foreach ($persianProjects as $projectData) {
+ Project::create($projectData);
+ }
+
+ // ─────────────────────────────────────────────
+ // Tenant 2 — Global (English construction projects)
+ // ─────────────────────────────────────────────
+
+ $englishProjects = [
+ [
+ 'tenant_id' => $tenant2->id,
+ 'name' => 'Riverside Tower',
+ 'code' => 'GLO-2024-001',
+ 'description' => 'Construction of a 20-story mixed-use tower with retail, office, and residential spaces along the waterfront',
+ 'status' => 'in_progress',
+ 'start_date' => '2024-02-01',
+ 'end_date' => '2026-06-30',
+ 'progress' => 35,
+ 'budget' => 45000000, // 45M USD
+ 'default_currency_id' => $usd->id,
+ 'budget_usd' => 45000000.00,
+ 'manager_id' => $tenant2Admin->id,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'name' => 'Industrial Warehouse Complex',
+ 'code' => 'GLO-2024-002',
+ 'description' => 'Building a 50,000 sqm warehouse complex with loading docks and logistics center',
+ 'status' => 'planning',
+ 'start_date' => '2024-08-01',
+ 'end_date' => '2025-04-30',
+ 'progress' => 5,
+ 'budget' => 12000000, // 12M USD
+ 'default_currency_id' => $usd->id,
+ 'budget_usd' => 12000000.00,
+ 'manager_id' => $tenant2Admin->id,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'name' => 'Greenfield School Campus',
+ 'code' => 'GLO-2024-003',
+ 'description' => 'Construction of a modern school campus with three buildings, sports facilities, and parking area',
+ 'status' => 'completed',
+ 'start_date' => '2023-05-01',
+ 'end_date' => '2024-07-15',
+ 'progress' => 100,
+ 'budget' => 8500000, // 8.5M USD
+ 'default_currency_id' => $usd->id,
+ 'budget_usd' => 8500000.00,
+ 'manager_id' => $tenant2Admin->id,
+ ],
+ ];
+
+ foreach ($englishProjects as $projectData) {
+ Project::create($projectData);
+ }
+
+ $total = count($persianProjects) + count($englishProjects);
+ $this->command->info("Projects seeded: {$total} records created.");
+ }
+}
diff --git a/database/seeders/RolePermissionSeeder.php b/database/seeders/RolePermissionSeeder.php
new file mode 100644
index 00000000..de11a1e2
--- /dev/null
+++ b/database/seeders/RolePermissionSeeder.php
@@ -0,0 +1,216 @@
+getPermissions();
+
+ foreach ($permissions as $name) {
+ Permission::firstOrCreate(
+ ['name' => $name, 'guard_name' => 'web']
+ );
+ }
+
+ $this->command->info('Permissions seeded: ' . count($permissions) . ' created.');
+
+ // 2. Create Roles
+ $roles = ['super-admin', 'admin', 'project-manager', 'accountant', 'employee'];
+
+ // Super-admin role at the platform level (no tenant)
+ Role::firstOrCreate(
+ ['name' => 'super-admin', 'guard_name' => 'web', 'tenant_id' => null]
+ )->syncPermissions($permissions);
+
+ // Tenant-scoped roles
+ $tenants = Tenant::all();
+
+ foreach ($tenants as $tenant) {
+ foreach ($roles as $roleName) {
+ if ($roleName === 'super-admin') {
+ continue;
+ }
+
+ Role::firstOrCreate(
+ ['name' => $roleName, 'guard_name' => 'web', 'tenant_id' => $tenant->id]
+ );
+ }
+ }
+
+ // 3. Assign Permissions to Tenant-Scoped Roles
+ foreach ($tenants as $tenant) {
+ $this->assignRolePermissions($tenant);
+ }
+
+ $this->command->info('Roles seeded for ' . $tenants->count() . ' tenants.');
+ }
+
+ protected function getPermissions(): array
+ {
+ return [
+ // Projects
+ 'projects.view',
+ 'projects.create',
+ 'projects.update',
+ 'projects.delete',
+
+ // Tasks
+ 'tasks.view',
+ 'tasks.create',
+ 'tasks.update',
+ 'tasks.delete',
+ 'tasks.update-status',
+
+ // Employees
+ 'employees.view',
+ 'employees.create',
+ 'employees.update',
+ 'employees.delete',
+ 'employees.view-financials',
+
+ // Expenses
+ 'expenses.view',
+ 'expenses.create',
+ 'expenses.update',
+ 'expenses.delete',
+ 'expenses.approve',
+
+ // Inventory
+ 'inventory.view',
+ 'inventory.create',
+ 'inventory.update',
+ 'inventory.delete',
+
+ // Letters (Correspondence)
+ 'letters.view',
+ 'letters.create',
+ 'letters.update',
+ 'letters.delete',
+ 'letters.manage',
+
+ // Documents
+ 'documents.view',
+ 'documents.create',
+ 'documents.update',
+ 'documents.delete',
+ 'documents.manage',
+
+ // Payroll
+ 'payroll.view',
+ 'payroll.create',
+ 'payroll.manage',
+
+ // Reports
+ 'reports.view',
+ 'reports.export',
+
+ // Petty Cash
+ 'petty-cash.view',
+ 'petty-cash.create',
+ 'petty-cash.update',
+ 'petty-cash.delete',
+ 'petty-cash.approve',
+ ];
+ }
+
+ protected function assignRolePermissions(Tenant $tenant): void
+ {
+ // Admin — full access within the tenant
+ $admin = Role::where('name', 'admin')
+ ->where('tenant_id', $tenant->id)
+ ->first();
+
+ if ($admin) {
+ $admin->syncPermissions($this->getPermissions());
+ }
+
+ // Project Manager
+ $pm = Role::where('name', 'project-manager')
+ ->where('tenant_id', $tenant->id)
+ ->first();
+
+ if ($pm) {
+ $pm->syncPermissions([
+ 'projects.view',
+ 'projects.create',
+ 'projects.update',
+ 'tasks.view',
+ 'tasks.create',
+ 'tasks.update',
+ 'tasks.delete',
+ 'tasks.update-status',
+ 'employees.view',
+ 'expenses.view',
+ 'expenses.create',
+ 'expenses.update',
+ 'expenses.approve',
+ 'letters.view',
+ 'letters.create',
+ 'letters.update',
+ 'documents.view',
+ 'documents.create',
+ 'documents.update',
+ 'petty-cash.view',
+ 'petty-cash.create',
+ 'reports.view',
+ 'reports.export',
+ ]);
+ }
+
+ // Accountant
+ $accountant = Role::where('name', 'accountant')
+ ->where('tenant_id', $tenant->id)
+ ->first();
+
+ if ($accountant) {
+ $accountant->syncPermissions([
+ 'projects.view',
+ 'tasks.view',
+ 'employees.view',
+ 'employees.view-financials',
+ 'expenses.view',
+ 'expenses.create',
+ 'expenses.update',
+ 'expenses.approve',
+ 'petty-cash.view',
+ 'petty-cash.create',
+ 'petty-cash.approve',
+ 'payroll.view',
+ 'payroll.create',
+ 'payroll.manage',
+ 'letters.view',
+ 'documents.view',
+ 'reports.view',
+ 'reports.export',
+ ]);
+ }
+
+ // Employee
+ $employee = Role::where('name', 'employee')
+ ->where('tenant_id', $tenant->id)
+ ->first();
+
+ if ($employee) {
+ $employee->syncPermissions([
+ 'projects.view',
+ 'tasks.view',
+ 'tasks.update-status',
+ 'expenses.view',
+ 'expenses.create',
+ 'letters.view',
+ 'documents.view',
+ ]);
+ }
+ }
+}
diff --git a/database/seeders/TaskSeeder.php b/database/seeders/TaskSeeder.php
new file mode 100644
index 00000000..897269ad
--- /dev/null
+++ b/database/seeders/TaskSeeder.php
@@ -0,0 +1,247 @@
+firstOrFail();
+ $tenant2 = Tenant::where('slug', 'global')->firstOrFail();
+
+ $tenant1Projects = $tenant1->projects()->orderBy('id')->get();
+ $tenant2Projects = $tenant2->projects()->orderBy('id')->get();
+
+ $manager = User::where('email', 'manager@sepideh.ir')->firstOrFail();
+ $admin1 = User::where('email', 'admin@sepideh.ir')->firstOrFail();
+ $admin2 = User::where('email', 'admin@global.com')->firstOrFail();
+
+ // ─────────────────────────────────────────────
+ // Tenant 1 — Sepideh Tasks
+ // ─────────────────────────────────────────────
+
+ $tenant1Tasks = [
+ // Project: ساختمان مسکونی گلستان
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id,
+ 'title' => 'تأمین مصالح مرحله اسکلت',
+ 'description' => 'خرید و تحویل میلگرد، سیمان و آجر برای اسکلت طبقات ۵ تا ۸',
+ 'status' => 'in_progress',
+ 'priority' => 'high',
+ 'assignee_id' => $manager->id,
+ 'due_date' => '2024-08-15',
+ 'progress' => 60,
+ 'estimated_hours' => 120.00,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[0]->id,
+ 'title' => 'نصب قالببندی طبقه ۶',
+ 'description' => 'قالببندی ستونها و دال طبقه ششم با نظارت مهندس ناظر',
+ 'status' => 'todo',
+ 'priority' => 'medium',
+ 'assignee_id' => $admin1->id,
+ 'due_date' => '2024-09-01',
+ 'progress' => 0,
+ 'estimated_hours' => 80.00,
+ ],
+ // Project: پل عابر سپید
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[1]->id,
+ 'title' => 'مطالعات ژئوتکنیک',
+ 'description' => 'انجاد گمانههای ژئوتکنیک و تهیه گزارش خاکشناسی محوطه پروژه',
+ 'status' => 'in_progress',
+ 'priority' => 'urgent',
+ 'assignee_id' => $manager->id,
+ 'due_date' => '2024-07-30',
+ 'progress' => 80,
+ 'estimated_hours' => 200.00,
+ ],
+ // Project: مجتمع تجاری مروارید
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[2]->id,
+ 'title' => 'نصب سیستم تهویه مطبوع',
+ 'description' => 'نصب چیلر، داکتها و ایرواشر طبقات تجاری',
+ 'status' => 'in_progress',
+ 'priority' => 'high',
+ 'assignee_id' => $manager->id,
+ 'due_date' => '2024-11-30',
+ 'progress' => 35,
+ 'estimated_hours' => 480.00,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[2]->id,
+ 'title' => 'تأسیسات برقی فاز ۲',
+ 'description' => 'سیمکشی تابلوهای برق اصلی و پنلهای کنترلی طبقات ۴ تا ۸',
+ 'status' => 'review',
+ 'priority' => 'medium',
+ 'assignee_id' => $admin1->id,
+ 'due_date' => '2024-08-20',
+ 'progress' => 90,
+ 'estimated_hours' => 160.00,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[2]->id,
+ 'title' => 'نازککاری دیوارهای داخلی',
+ 'description' => 'گچکاری و نازککاری دیوارهای داخلی واحدهای تجاری طبقات ۱ تا ۳',
+ 'status' => 'todo',
+ 'priority' => 'low',
+ 'assignee_id' => null,
+ 'due_date' => '2025-01-15',
+ 'progress' => 0,
+ 'estimated_hours' => 320.00,
+ ],
+ // Project: استخر و سونا سلامت
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[3]->id,
+ 'title' => 'تست فشار لولهکشی',
+ 'description' => 'تست هیدرواستاتیک لولههای آب سرد و گرم و سیستم فاضلاب',
+ 'status' => 'done',
+ 'priority' => 'high',
+ 'assignee_id' => $admin1->id,
+ 'due_date' => '2024-04-15',
+ 'progress' => 100,
+ 'estimated_hours' => 40.00,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[3]->id,
+ 'title' => 'تحویل نهایی به کارفرما',
+ 'description' => 'آمادهسازی اسناد تحویل، لیست عیوب و رفع آنها و تحویل رسمی',
+ 'status' => 'done',
+ 'priority' => 'medium',
+ 'assignee_id' => $manager->id,
+ 'due_date' => '2024-05-30',
+ 'progress' => 100,
+ 'estimated_hours' => 24.00,
+ ],
+ // Project: بازسازی بیمارستان شفا
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[4]->id,
+ 'title' => 'تخریب دیوارهای داخلی بخش اورژانس',
+ 'description' => 'تخریب و حمل مصالح دیوارهای غیرسازهای بخش اورژانس فعلی',
+ 'status' => 'todo',
+ 'priority' => 'urgent',
+ 'assignee_id' => $manager->id,
+ 'due_date' => '2024-09-15',
+ 'progress' => 0,
+ 'estimated_hours' => 96.00,
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'project_id' => $tenant1Projects[4]->id,
+ 'title' => 'تهیه نقشههای اجرایی',
+ 'description' => 'تکمیل نقشههای اجرایی معماری، تأسیسات و برق برای بخش ICU',
+ 'status' => 'in_progress',
+ 'priority' => 'high',
+ 'assignee_id' => $admin1->id,
+ 'due_date' => '2024-08-10',
+ 'progress' => 50,
+ 'estimated_hours' => 160.00,
+ ],
+ ];
+
+ foreach ($tenant1Tasks as $taskData) {
+ Task::create($taskData);
+ }
+
+ // ─────────────────────────────────────────────
+ // Tenant 2 — Global Tasks
+ // ─────────────────────────────────────────────
+
+ $tenant2Tasks = [
+ // Project: Riverside Tower
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[0]->id,
+ 'title' => 'Foundation Pouring — Phase 2',
+ 'description' => 'Complete concrete pouring for the tower foundation section B including reinforcement inspection',
+ 'status' => 'in_progress',
+ 'priority' => 'urgent',
+ 'assignee_id' => $admin2->id,
+ 'due_date' => '2024-08-30',
+ 'progress' => 70,
+ 'estimated_hours' => 240.00,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[0]->id,
+ 'title' => 'Structural Steel Erection — Floors 5-8',
+ 'description' => 'Erect and weld structural steel frames for floors 5 through 8 of the tower',
+ 'status' => 'todo',
+ 'priority' => 'high',
+ 'assignee_id' => $admin2->id,
+ 'due_date' => '2024-11-15',
+ 'progress' => 0,
+ 'estimated_hours' => 360.00,
+ ],
+ // Project: Industrial Warehouse Complex
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[1]->id,
+ 'title' => 'Site Survey & Soil Testing',
+ 'description' => 'Conduct topographic survey and soil bearing capacity tests across the 50,000 sqm site',
+ 'status' => 'in_progress',
+ 'priority' => 'high',
+ 'assignee_id' => $admin2->id,
+ 'due_date' => '2024-09-15',
+ 'progress' => 45,
+ 'estimated_hours' => 120.00,
+ ],
+ // Project: Greenfield School Campus
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[2]->id,
+ 'title' => 'Final Inspection & Handover',
+ 'description' => 'Complete final building inspection, fire safety certification, and handover to the school board',
+ 'status' => 'done',
+ 'priority' => 'medium',
+ 'assignee_id' => $admin2->id,
+ 'due_date' => '2024-07-10',
+ 'progress' => 100,
+ 'estimated_hours' => 40.00,
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'project_id' => $tenant2Projects[2]->id,
+ 'title' => 'Landscaping & Parking Lot Completion',
+ 'description' => 'Final grading, asphalt paving for parking area, and installation of outdoor lighting and signage',
+ 'status' => 'review',
+ 'priority' => 'low',
+ 'assignee_id' => null,
+ 'due_date' => '2024-07-15',
+ 'progress' => 95,
+ 'estimated_hours' => 80.00,
+ ],
+ ];
+
+ foreach ($tenant2Tasks as $taskData) {
+ Task::create($taskData);
+ }
+
+ $total = count($tenant1Tasks) + count($tenant2Tasks);
+ $this->command->info("Tasks seeded: {$total} records created.");
+ }
+}
diff --git a/database/seeders/TenantSeeder.php b/database/seeders/TenantSeeder.php
new file mode 100644
index 00000000..3378d238
--- /dev/null
+++ b/database/seeders/TenantSeeder.php
@@ -0,0 +1,64 @@
+firstOrFail();
+ $usd = Currency::where('code', 'USD')->firstOrFail();
+
+ $tenants = [
+ [
+ 'name' => 'سازندگی سپید',
+ 'slug' => 'sepideh',
+ 'domain' => 'sepideh.Vernova.ir',
+ 'is_active' => true,
+ 'default_locale' => 'fa',
+ 'default_calendar' => 'jalali',
+ 'base_currency_id' => $irr->id,
+ 'allow_multi_currency' => true,
+ 'logo_path' => null,
+ 'settings' => [
+ 'week_start' => 'saturday',
+ 'working_days' => ['saturday', 'sunday', 'monday', 'tuesday', 'wednesday'],
+ 'fiscal_year_start' => '1403-01-01',
+ ],
+ ],
+ [
+ 'name' => 'Global Construction Co.',
+ 'slug' => 'global',
+ 'domain' => 'global.Vernova.ir',
+ 'is_active' => true,
+ 'default_locale' => 'en',
+ 'default_calendar' => 'gregorian',
+ 'base_currency_id' => $usd->id,
+ 'allow_multi_currency' => true,
+ 'logo_path' => null,
+ 'settings' => [
+ 'week_start' => 'monday',
+ 'working_days' => ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
+ 'fiscal_year_start' => '2024-01-01',
+ ],
+ ],
+ ];
+
+ foreach ($tenants as $tenantData) {
+ Tenant::create($tenantData);
+ }
+
+ $this->command->info('Tenants seeded: ' . count($tenants) . ' records created.');
+ }
+}
diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php
new file mode 100644
index 00000000..87f05b6a
--- /dev/null
+++ b/database/seeders/UserSeeder.php
@@ -0,0 +1,103 @@
+firstOrFail();
+ $tenant2 = Tenant::where('slug', 'global')->firstOrFail();
+
+ $users = [
+ [
+ 'tenant_id' => null,
+ 'name' => 'Super Admin',
+ 'email' => 'admin@projectra.ir',
+ 'password' => Hash::make('password'),
+ 'locale' => 'fa',
+ 'preferred_calendar' => 'jalali',
+ 'is_active' => true,
+ 'email_verified_at' => now(),
+ 'role' => 'super-admin',
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'name' => 'محمد احمدی',
+ 'email' => 'admin@sepideh.ir',
+ 'password' => Hash::make('password'),
+ 'locale' => 'fa',
+ 'preferred_calendar' => 'jalali',
+ 'is_active' => true,
+ 'email_verified_at' => now(),
+ 'role' => 'admin',
+ ],
+ [
+ 'tenant_id' => $tenant1->id,
+ 'name' => 'علی رضایی',
+ 'email' => 'manager@sepideh.ir',
+ 'password' => Hash::make('password'),
+ 'locale' => 'fa',
+ 'preferred_calendar' => 'jalali',
+ 'is_active' => true,
+ 'email_verified_at' => now(),
+ 'role' => 'project-manager',
+ ],
+ [
+ 'tenant_id' => $tenant2->id,
+ 'name' => 'John Smith',
+ 'email' => 'admin@global.com',
+ 'password' => Hash::make('password'),
+ 'locale' => 'en',
+ 'preferred_calendar' => 'gregorian',
+ 'is_active' => true,
+ 'email_verified_at' => now(),
+ 'role' => 'admin',
+ ],
+ ];
+
+ foreach ($users as $userData) {
+ $roleName = $userData['role'];
+ unset($userData['role']);
+
+ $user = User::create($userData);
+
+ // Assign the Spatie role. For tenant-scoped users, scope the role
+ // to their tenant; for super-admin, use the global (null tenant) role.
+ if ($user->tenant_id) {
+ $role = Role::where('name', $roleName)
+ ->where('tenant_id', $user->tenant_id)
+ ->first();
+ } else {
+ $role = Role::where('name', $roleName)
+ ->whereNull('tenant_id')
+ ->first();
+ }
+
+ if ($role) {
+ $user->assignRole($role);
+ } else {
+ $this->command->warn("Role '{$roleName}' not found for user {$user->email}. Skipping role assignment.");
+ }
+ }
+
+ $this->command->info('Users seeded: ' . count($users) . ' records created with roles.');
+ }
+}
diff --git a/move-files.bat b/move-files.bat
new file mode 100644
index 00000000..e69de29b
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..21608e70
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2546 @@
+{
+ "name": "Vernova",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "@tailwindcss/forms": "^0.5.7",
+ "alpinejs": "^3.13",
+ "autoprefixer": "^10.4",
+ "axios": "^1.7",
+ "laravel-vite-plugin": "^1.0",
+ "postcss": "^8.4",
+ "tailwindcss": "^3.4",
+ "vite": "^5.0"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz",
+ "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz",
+ "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz",
+ "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz",
+ "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz",
+ "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz",
+ "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz",
+ "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz",
+ "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz",
+ "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz",
+ "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz",
+ "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz",
+ "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz",
+ "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz",
+ "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz",
+ "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz",
+ "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz",
+ "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz",
+ "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz",
+ "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz",
+ "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz",
+ "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz",
+ "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz",
+ "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz",
+ "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz",
+ "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@tailwindcss/forms": {
+ "version": "0.5.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.11.tgz",
+ "integrity": "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mini-svg-data-uri": "^1.2.3"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz",
+ "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.1.5"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz",
+ "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/alpinejs": {
+ "version": "3.15.12",
+ "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.12.tgz",
+ "integrity": "sha512-nJvPAQVNPdZZ0NrExJ/kzQco3ijR8LwvCOadQecllESiqT4NyZ/57sN9V2XyvhlBGAbmlKYgeWZvYdKq99ij/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "~3.1.1"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz",
+ "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.2",
+ "caniuse-lite": "^1.0.30001787",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
+ "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.5",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.32",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz",
+ "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001793",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
+ "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.364",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz",
+ "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.2",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
+ "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.7",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/laravel-vite-plugin": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.3.0.tgz",
+ "integrity": "sha512-P5qyG56YbYxM8OuYmK2OkhcKe0AksNVJUjq9LUZ5tOekU9fBn9LujYyctI4t9XoLjuMvHJXXpCoPntY1oKltuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picocolors": "^1.0.0",
+ "vite-plugin-full-reload": "^1.1.0"
+ },
+ "bin": {
+ "clean-orphaned-assets": "bin/clean.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.0.0 || ^6.0.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mini-svg-data-uri": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+ "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mini-svg-data-uri": "cli.js"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.46",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz",
+ "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
+ "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
+ "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.1.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "jiti": ">=1.21.0",
+ "postcss": ">=8.0.9",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.12",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz",
+ "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.60.4",
+ "@rollup/rollup-android-arm64": "4.60.4",
+ "@rollup/rollup-darwin-arm64": "4.60.4",
+ "@rollup/rollup-darwin-x64": "4.60.4",
+ "@rollup/rollup-freebsd-arm64": "4.60.4",
+ "@rollup/rollup-freebsd-x64": "4.60.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.60.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.60.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.60.4",
+ "@rollup/rollup-linux-arm64-musl": "4.60.4",
+ "@rollup/rollup-linux-loong64-gnu": "4.60.4",
+ "@rollup/rollup-linux-loong64-musl": "4.60.4",
+ "@rollup/rollup-linux-ppc64-gnu": "4.60.4",
+ "@rollup/rollup-linux-ppc64-musl": "4.60.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.60.4",
+ "@rollup/rollup-linux-riscv64-musl": "4.60.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.60.4",
+ "@rollup/rollup-linux-x64-gnu": "4.60.4",
+ "@rollup/rollup-linux-x64-musl": "4.60.4",
+ "@rollup/rollup-openbsd-x64": "4.60.4",
+ "@rollup/rollup-openharmony-arm64": "4.60.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.60.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.60.4",
+ "@rollup/rollup-win32-x64-gnu": "4.60.4",
+ "@rollup/rollup-win32-x64-msvc": "4.60.4",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.1",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+ "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "tinyglobby": "^0.2.11",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.19",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
+ "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.6.0",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.2",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.7",
+ "lilconfig": "^3.1.3",
+ "micromatch": "^4.0.8",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.4.47",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
+ "postcss-nested": "^6.2.0",
+ "postcss-selector-parser": "^6.1.2",
+ "resolve": "^1.22.8",
+ "sucrase": "^3.35.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-full-reload": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz",
+ "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picocolors": "^1.0.0",
+ "picomatch": "^2.3.1"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 4e934caa..48de9a10 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,13 @@
"build": "vite build"
},
"devDependencies": {
- "axios": "^1.6.4",
+ "@tailwindcss/forms": "^0.5.7",
+ "alpinejs": "^3.13",
+ "autoprefixer": "^10.4",
+ "axios": "^1.7",
"laravel-vite-plugin": "^1.0",
+ "postcss": "^8.4",
+ "tailwindcss": "^3.4",
"vite": "^5.0"
}
}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 00000000..49c0612d
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/public/css/jalali-datepicker.css b/public/css/jalali-datepicker.css
new file mode 100644
index 00000000..c5dd9cc2
--- /dev/null
+++ b/public/css/jalali-datepicker.css
@@ -0,0 +1,135 @@
+/* Vernova - Jalali Date Picker Styles */
+.jalali-picker-dropdown {
+ background: #fff;
+ border: 1px solid #e5e7eb;
+ border-radius: 12px;
+ box-shadow: 0 10px 25px rgba(0,0,0,0.12);
+ padding: 0;
+ z-index: 99999;
+ width: 280px;
+ font-family: 'Vazirmatn', sans-serif;
+ direction: rtl;
+ user-select: none;
+}
+
+.jp-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 12px;
+ background: #14b8a6;
+ color: #fff;
+ border-radius: 12px 12px 0 0;
+}
+
+.jp-title {
+ font-size: 14px;
+ font-weight: 600;
+}
+
+.jp-nav {
+ background: rgba(255,255,255,0.2);
+ border: none;
+ color: #fff;
+ width: 28px;
+ height: 28px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background 0.15s;
+}
+
+.jp-nav:hover {
+ background: rgba(255,255,255,0.35);
+}
+
+.jp-weekdays {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ padding: 8px 8px 2px;
+ gap: 2px;
+}
+
+.jp-wd {
+ text-align: center;
+ font-size: 11px;
+ color: #9ca3af;
+ font-weight: 500;
+ padding: 4px 0;
+}
+
+.jp-days {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ padding: 2px 8px 8px;
+ gap: 2px;
+}
+
+.jp-day {
+ width: 34px;
+ height: 34px;
+ border: none;
+ background: transparent;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 13px;
+ font-family: 'Vazirmatn', sans-serif;
+ color: #374151;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.15s;
+ margin: 0 auto;
+}
+
+.jp-day:hover {
+ background: #f0fdfa;
+ color: #0d9488;
+}
+
+.jp-day.jp-today {
+ background: #fef3c7;
+ color: #92400e;
+ font-weight: 600;
+}
+
+.jp-day.jp-selected {
+ background: #14b8a6;
+ color: #fff;
+ font-weight: 600;
+}
+
+.jp-day.jp-selected:hover {
+ background: #0d9488;
+ color: #fff;
+}
+
+.jp-empty {
+ width: 34px;
+ height: 34px;
+}
+
+.jp-footer {
+ border-top: 1px solid #f3f4f6;
+ padding: 6px 8px;
+ text-align: center;
+}
+
+.jp-today-btn {
+ background: #f0fdfa;
+ border: 1px solid #ccfbf1;
+ color: #0d9488;
+ padding: 4px 16px;
+ border-radius: 6px;
+ font-size: 12px;
+ font-family: 'Vazirmatn', sans-serif;
+ cursor: pointer;
+ transition: all 0.15s;
+}
+
+.jp-today-btn:hover {
+ background: #ccfbf1;
+}
diff --git a/public/js/jalali-datepicker.js b/public/js/jalali-datepicker.js
new file mode 100644
index 00000000..3fea1351
--- /dev/null
+++ b/public/js/jalali-datepicker.js
@@ -0,0 +1,326 @@
+/**
+ * Vernova - Lightweight Vanilla JS Jalali Date Picker
+ * No jQuery, no eval, no external dependencies. CSP-safe.
+ */
+(function () {
+ 'use strict';
+
+ // ─── Jalali ↔ Gregorian Conversion ─────────────────────────
+ var JalaliConverter = {
+ gregorianToJalali: function (gy, gm, gd) {
+ var g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
+ var gy2 = (gm > 2) ? (gy + 1) : gy;
+ var days = 355666 + (365 * gy) + Math.floor((gy2 + 3) / 4) - Math.floor((gy2 + 99) / 100) + Math.floor((gy2 + 399) / 400) + gd + g_d_m[gm - 1];
+ var jy = -1595 + (33 * Math.floor(days / 12053));
+ days %= 12053;
+ jy += 4 * Math.floor(days / 1461);
+ days %= 1461;
+ if (days > 365) {
+ jy += Math.floor((days - 1) / 365);
+ days = (days - 1) % 365;
+ }
+ var jm, jd;
+ if (days < 186) {
+ jm = 1 + Math.floor(days / 31);
+ jd = 1 + (days % 31);
+ } else {
+ jm = 7 + Math.floor((days - 186) / 30);
+ jd = 1 + ((days - 186) % 30);
+ }
+ return [jy, jm, jd];
+ },
+ jalaliToGregorian: function (jy, jm, jd) {
+ var jy2 = jy - 979;
+ var jm2 = jm - 1;
+ var jd2 = jd - 1;
+ var days = 365 * jy2 + Math.floor(jy2 / 33) * 8 + Math.floor((jy2 % 33 + 3) / 4) + 78 + jd2 + ((jm2 < 7) ? (jm2 * 31) : ((jm2 * 30) + 6));
+ var gy = 1600 + 400 * Math.floor(days / 146097);
+ days %= 146097;
+ if (days > 36524) {
+ gy += 100 * Math.floor(--days / 36524);
+ days %= 36524;
+ if (days >= 365) days++;
+ }
+ gy += 4 * Math.floor(days / 1461);
+ days %= 1461;
+ if (days > 365) {
+ gy += Math.floor((days - 1) / 365);
+ days = (days - 1) % 365;
+ }
+ var gd = days + 1;
+ var sal_a = [0, 31, ((gy % 4 === 0 && gy % 100 !== 0) || (gy % 400 === 0)) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ var gm;
+ for (gm = 0; gm < 13 && gd > sal_a[gm]; gm++) gd -= sal_a[gm];
+ return [gy, gm, gd];
+ },
+ isJalaliLeap: function (jy) {
+ var breaks = [1, 5, 9, 13, 17, 22, 26, 30];
+ var r = jy % 33;
+ return breaks.indexOf(r) !== -1;
+ },
+ jalaliMonthDays: function (jy, jm) {
+ if (jm <= 6) return 31;
+ if (jm <= 11) return 30;
+ return this.isJalaliLeap(jy) ? 30 : 29;
+ },
+ gregorianMonthDays: function (gy, gm) {
+ var days = [0, 31, ((gy % 4 === 0 && gy % 100 !== 0) || (gy % 400 === 0)) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ return days[gm];
+ }
+ };
+
+ // ─── Jalali Month/Day Names ────────────────────────────────
+ var jalaliMonths = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'];
+ var jalaliWeekDaysShort = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'];
+ var jalaliWeekDaysLong = ['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'];
+
+ // ─── Pad helper ────────────────────────────────────────────
+ function pad(n) { return n < 10 ? '0' + n : '' + n; }
+
+ // ─── Date Picker Class ─────────────────────────────────────
+ function JalaliDatePicker(inputEl, hiddenEl) {
+ this.inputEl = inputEl;
+ this.hiddenEl = hiddenEl;
+ this.container = null;
+ this.isOpen = false;
+ this.currentYear = 1404;
+ this.currentMonth = 1;
+ this.selectedYear = null;
+ this.selectedMonth = null;
+ this.selectedDay = null;
+
+ this._init();
+ }
+
+ JalaliDatePicker.prototype = {
+ _init: function () {
+ // Parse initial value
+ var val = this.inputEl.value;
+ if (val && /^\d{4}\/\d{1,2}\/\d{1,2}$/.test(val)) {
+ var parts = val.split('/');
+ this.selectedYear = this.currentYear = parseInt(parts[0]);
+ this.selectedMonth = this.currentMonth = parseInt(parts[1]);
+ this.selectedDay = parseInt(parts[2]);
+ } else {
+ // Default to today
+ var now = new Date();
+ var today = JalaliConverter.gregorianToJalali(now.getFullYear(), now.getMonth() + 1, now.getDate());
+ this.currentYear = today[0];
+ this.currentMonth = today[1];
+ }
+
+ // Create container
+ this.container = document.createElement('div');
+ this.container.className = 'jalali-picker-dropdown';
+ this.container.style.display = 'none';
+ document.body.appendChild(this.container);
+
+ // Bind events
+ var self = this;
+ this.inputEl.addEventListener('focus', function (e) { self.open(); });
+ this.inputEl.addEventListener('click', function (e) { e.stopPropagation(); });
+ this.container.addEventListener('click', function (e) { e.stopPropagation(); });
+
+ // Close on outside click
+ document.addEventListener('click', function (e) {
+ if (self.isOpen && e.target !== self.inputEl && !self.container.contains(e.target)) {
+ self.close();
+ }
+ });
+
+ // Handle manual input
+ this.inputEl.addEventListener('blur', function () {
+ self._syncFromInput();
+ });
+
+ // Handle keyboard
+ this.inputEl.addEventListener('keydown', function (e) {
+ if (e.key === 'Escape') self.close();
+ if (e.key === 'Enter') { self._syncFromInput(); self.close(); }
+ });
+ },
+
+ _syncFromInput: function () {
+ var val = this.inputEl.value.trim();
+ if (/^\d{4}\/\d{1,2}\/\d{1,2}$/.test(val)) {
+ var parts = val.split('/');
+ var jy = parseInt(parts[0]), jm = parseInt(parts[1]), jd = parseInt(parts[2]);
+ var maxDay = JalaliConverter.jalaliMonthDays(jy, jm);
+ if (jd > maxDay) jd = maxDay;
+ this.selectedYear = jy;
+ this.selectedMonth = jm;
+ this.selectedDay = jd;
+ this.inputEl.value = pad(jy) + '/' + pad(jm) + '/' + pad(jd);
+ this._updateHidden();
+ } else if (val === '') {
+ this.selectedYear = null;
+ this.selectedMonth = null;
+ this.selectedDay = null;
+ if (this.hiddenEl) this.hiddenEl.value = '';
+ }
+ },
+
+ _updateHidden: function () {
+ if (this.selectedYear && this.selectedMonth && this.selectedDay) {
+ var g = JalaliConverter.jalaliToGregorian(this.selectedYear, this.selectedMonth, this.selectedDay);
+ var gregStr = pad(g[0]) + '-' + pad(g[1]) + '-' + pad(g[2]);
+ if (this.hiddenEl) this.hiddenEl.value = gregStr;
+ }
+ },
+
+ open: function () {
+ this._render();
+ this.container.style.display = 'block';
+ this.isOpen = true;
+ this._position();
+ },
+
+ close: function () {
+ this.container.style.display = 'none';
+ this.isOpen = false;
+ },
+
+ _position: function () {
+ var rect = this.inputEl.getBoundingClientRect();
+ var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
+ var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
+ var pickerHeight = this.container.offsetHeight;
+ var spaceBelow = window.innerHeight - rect.bottom;
+ var spaceAbove = rect.top;
+
+ this.container.style.position = 'absolute';
+ if (spaceBelow < pickerHeight && spaceAbove > spaceBelow) {
+ this.container.style.top = (rect.top + scrollTop - pickerHeight - 4) + 'px';
+ } else {
+ this.container.style.top = (rect.bottom + scrollTop + 4) + 'px';
+ }
+ // RTL: align to right edge
+ this.container.style.left = (rect.right + scrollLeft - this.container.offsetWidth) + 'px';
+ },
+
+ _render: function () {
+ var self = this;
+ var y = this.currentYear;
+ var m = this.currentMonth;
+
+ // First day of month in Gregorian
+ var firstGreg = JalaliConverter.jalaliToGregorian(y, m, 1);
+ var firstDate = new Date(firstGreg[0], firstGreg[1] - 1, firstGreg[2]);
+ // Day of week: 0=Sun, adjust to Saturday=0 for Jalali
+ var dow = firstDate.getDay(); // 0=Sun
+ var jalaliDow = (dow + 1) % 7; // 0=Sat
+
+ var daysInMonth = JalaliConverter.jalaliMonthDays(y, m);
+
+ // Today
+ var now = new Date();
+ var todayJ = JalaliConverter.gregorianToJalali(now.getFullYear(), now.getMonth() + 1, now.getDate());
+
+ var html = '';
+
+ // Week day headers
+ html += '';
+ for (var i = 0; i < 7; i++) {
+ html += '' + jalaliWeekDaysShort[i] + ' ';
+ }
+ html += '
';
+
+ // Days grid
+ html += '';
+ // Empty cells before first day
+ for (var i = 0; i < jalaliDow; i++) {
+ html += ' ';
+ }
+ // Day cells
+ for (var d = 1; d <= daysInMonth; d++) {
+ var classes = 'jp-day';
+ if (y === todayJ[0] && m === todayJ[1] && d === todayJ[2]) classes += ' jp-today';
+ if (y === this.selectedYear && m === this.selectedMonth && d === this.selectedDay) classes += ' jp-selected';
+ html += '' + d + ' ';
+ }
+ html += '
';
+
+ // Footer
+ html += '';
+
+ this.container.innerHTML = html;
+
+ // Bind navigation
+ this.container.querySelector('.jp-prev').addEventListener('click', function () { self._navigate(-1); });
+ this.container.querySelector('.jp-next').addEventListener('click', function () { self._navigate(1); });
+ this.container.querySelector('.jp-today-btn').addEventListener('click', function () { self._goToday(); });
+
+ // Bind day clicks
+ var dayBtns = this.container.querySelectorAll('.jp-day:not(.jp-empty)');
+ for (var i = 0; i < dayBtns.length; i++) {
+ dayBtns[i].addEventListener('click', function () {
+ var day = parseInt(this.getAttribute('data-day'));
+ self._selectDay(self.currentYear, self.currentMonth, day);
+ });
+ }
+ },
+
+ _navigate: function (dir) {
+ this.currentMonth += dir;
+ if (this.currentMonth > 12) {
+ this.currentMonth = 1;
+ this.currentYear++;
+ } else if (this.currentMonth < 1) {
+ this.currentMonth = 12;
+ this.currentYear--;
+ }
+ this._render();
+ this._position();
+ },
+
+ _selectDay: function (y, m, d) {
+ this.selectedYear = y;
+ this.selectedMonth = m;
+ this.selectedDay = d;
+ this.inputEl.value = pad(y) + '/' + pad(m) + '/' + pad(d);
+ this._updateHidden();
+ this.close();
+ // Trigger change event
+ var event = new Event('change', { bubbles: true });
+ this.inputEl.dispatchEvent(event);
+ if (this.hiddenEl) {
+ this.hiddenEl.dispatchEvent(new Event('change', { bubbles: true }));
+ }
+ },
+
+ _goToday: function () {
+ var now = new Date();
+ var today = JalaliConverter.gregorianToJalali(now.getFullYear(), now.getMonth() + 1, now.getDate());
+ this._selectDay(today[0], today[1], today[2]);
+ }
+ };
+
+ // ─── Initialize all date pickers ───────────────────────────
+ function initPickers() {
+ var inputs = document.querySelectorAll('.jalali-datepicker-input');
+ for (var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if (input._jalaliPickerInitialized) continue;
+
+ var hiddenId = input.getAttribute('data-gregorian-target');
+ var hidden = hiddenId ? document.getElementById(hiddenId) : null;
+
+ new JalaliDatePicker(input, hidden);
+ input._jalaliPickerInitialized = true;
+ }
+ }
+
+ // Run on DOM ready
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initPickers);
+ } else {
+ initPickers();
+ }
+
+})();
diff --git a/resources/css/app.css b/resources/css/app.css
index e69de29b..9d0a8678 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -0,0 +1,50 @@
+@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800&family=Inter:wght@300;400;500;600;700&display=swap');
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ [dir="rtl"] {
+ font-family: 'Vazirmatn', sans-serif;
+ }
+ [dir="ltr"] {
+ font-family: 'Inter', sans-serif;
+ }
+}
+
+@layer components {
+ .sidebar-link {
+ @apply w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm transition-colors;
+ }
+ .sidebar-link-active {
+ @apply bg-sidebar-primary text-sidebar-primary-foreground font-medium;
+ }
+ .sidebar-link-inactive {
+ @apply text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
+ }
+ .kpi-card {
+ @apply relative overflow-hidden rounded-xl border bg-card text-card-foreground;
+ }
+ .status-badge {
+ @apply inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium;
+ }
+}
+
+@layer utilities {
+ .ps-custom { padding-inline-start: 1rem; }
+ .pe-custom { padding-inline-end: 1rem; }
+ .ms-custom { margin-inline-start: 0.5rem; }
+ .me-custom { margin-inline-end: 0.5rem; }
+ .border-s-custom { border-inline-start-width: 1px; }
+ .border-e-custom { border-inline-end-width: 1px; }
+}
+
+::-webkit-scrollbar { width: 6px; height: 6px; }
+::-webkit-scrollbar-track { background: transparent; }
+::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
+::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
+
+@media print {
+ .no-print { display: none !important; }
+}
diff --git a/resources/js/app.js b/resources/js/app.js
index e59d6a0a..4b7b9e91 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -1 +1,62 @@
-import './bootstrap';
+import Alpine from 'alpinejs';
+import axios from 'axios';
+
+window.Alpine = Alpine;
+
+Alpine.store('app', {
+ sidebarOpen: window.innerWidth >= 768,
+ darkMode: localStorage.getItem('darkMode') === 'true',
+ mobileMenuOpen: false,
+
+ toggleSidebar() {
+ this.sidebarOpen = !this.sidebarOpen;
+ },
+ toggleDarkMode() {
+ this.darkMode = !this.darkMode;
+ localStorage.setItem('darkMode', this.darkMode);
+ document.documentElement.classList.toggle('dark', this.darkMode);
+ },
+ toggleMobileMenu() {
+ this.mobileMenuOpen = !this.mobileMenuOpen;
+ },
+});
+
+Alpine.data('dropdown', () => ({
+ open: false,
+ toggle() { this.open = !this.open; },
+ close() { this.open = false; },
+}));
+
+Alpine.data('confirmDialog', () => ({
+ showing: false,
+ message: '',
+ onConfirm: null,
+ show(message, onConfirm) {
+ this.message = message;
+ this.onConfirm = onConfirm;
+ this.showing = true;
+ },
+ confirm() {
+ if (this.onConfirm) this.onConfirm();
+ this.showing = false;
+ },
+ cancel() { this.showing = false; },
+}));
+
+Alpine.data('toast', () => ({
+ toasts: [],
+ add(message, type = 'success', duration = 3000) {
+ const id = Date.now();
+ this.toasts.push({ id, message, type });
+ setTimeout(() => { this.toasts = this.toasts.filter(t => t.id !== id); }, duration);
+ },
+}));
+
+axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
+axios.defaults.headers.common['Accept'] = 'application/json';
+axios.defaults.headers.common['X-Locale'] = document.documentElement.lang || 'fa';
+
+const token = document.querySelector('meta[name="csrf-token"]');
+if (token) axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
+
+Alpine.start();
diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js
deleted file mode 100644
index 5f1390b0..00000000
--- a/resources/js/bootstrap.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import axios from 'axios';
-window.axios = axios;
-
-window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
diff --git a/resources/lang/en.json b/resources/lang/en.json
new file mode 100644
index 00000000..13986329
--- /dev/null
+++ b/resources/lang/en.json
@@ -0,0 +1,761 @@
+{
+ "common.appName": "Projectra",
+ "common.projectManagement": "Project Management",
+ "common.save": "Save",
+ "common.save_changes": "Save Changes",
+ "common.cancel": "Cancel",
+ "common.delete": "Delete",
+ "common.edit": "Edit",
+ "common.create": "Create",
+ "common.search": "Search...",
+ "common.filter": "Filter",
+ "common.export": "Export",
+ "common.exportExcel": "Export Excel",
+ "common.exportPdf": "Export PDF",
+ "common.confirm": "Confirm",
+ "common.reject": "Reject",
+ "common.approve": "Approve",
+ "common.back": "Back",
+ "common.actions": "Actions",
+ "common.status": "Status",
+ "common.all": "All",
+ "common.noResults": "No results found",
+ "common.loading": "Loading...",
+ "common.yes": "Yes",
+ "common.no": "No",
+ "common.areYouSure": "Are you sure?",
+ "common.from": "From",
+ "common.to": "To",
+ "common.select": "Select",
+ "common.reset": "Reset",
+ "common.all_rights_reserved": "All rights reserved",
+ "common.comingSoon": "Coming Soon",
+ "common.success": "Operation completed successfully",
+ "common.error": "An error occurred",
+ "common.view": "View",
+ "common.view_all": "View All",
+ "common.details": "Details",
+ "common.no_data": "No data available",
+ "common.total": "Total",
+ "common.count": "Count",
+ "common.date": "Date",
+ "common.amount": "Amount",
+ "common.description": "Description",
+ "common.notes": "Notes",
+ "common.project": "Project",
+ "common.currency": "Currency",
+ "common.upload": "Upload File",
+ "common.choose_file": "Choose File",
+ "common.no_file": "No file selected",
+ "common.confirm_delete": "Are you sure you want to delete?",
+ "common.good_morning": "Good Morning",
+ "common.good_afternoon": "Good Afternoon",
+ "common.good_evening": "Good Evening",
+ "common.none": "None",
+ "common.no_records": "No records found",
+ "common.today": "Today",
+
+ "nav.dashboard": "Dashboard",
+ "nav.projects": "Projects",
+ "nav.tasks": "Tasks",
+ "nav.expenses": "Expenses",
+ "nav.employees": "Employees",
+ "nav.warehouse": "Warehouse",
+ "nav.pettyCash": "Petty Cash",
+ "nav.letters": "Correspondence",
+ "nav.documents": "Documents",
+ "nav.reports": "Reports",
+ "nav.payroll": "Payroll",
+ "nav.settings": "Settings",
+ "nav.help": "Help",
+
+ "auth.login": "Login",
+ "auth.logout": "Logout",
+ "auth.email": "Email",
+ "auth.password": "Password",
+ "auth.rememberMe": "Remember Me",
+ "auth.forgotPassword": "Forgot Password?",
+ "auth.invalidCredentials": "Invalid email or password",
+
+ "dashboard.title": "Dashboard",
+ "dashboard.active_projects": "Active Projects",
+ "dashboard.total_budget": "Total Budget",
+ "dashboard.pending_tasks": "Pending Tasks",
+ "dashboard.active_employees": "Active Employees",
+ "dashboard.addProject": "Add Project",
+ "dashboard.addExpense": "Add Expense",
+ "dashboard.addEmployee": "Add Employee",
+ "dashboard.viewReports": "View Reports",
+ "dashboard.recent_activity": "Recent Activity",
+ "dashboard.vs_last_month": "vs Last Month",
+ "dashboard.budgetGrowth": "Budget Growth",
+ "dashboard.urgent": "Urgent",
+ "dashboard.newHires": "New Hires",
+ "dashboard.my_tasks": "My Tasks",
+ "dashboard.no_activity": "No activities recorded",
+ "dashboard.no_tasks": "No tasks",
+ "dashboard.no_projects": "No projects",
+ "dashboard.projects_progress": "Projects Progress",
+ "dashboard.calendar": "Calendar",
+ "dashboard.numbers": "Numbers",
+ "dashboard.currency": "Currency",
+ "dashboard.today": "Today",
+ "dashboard.short": "Short",
+ "dashboard.calendar_system": "Calendar System",
+ "dashboard.calendarType": "Calendar Type",
+ "dashboard.integer": "Integer",
+ "dashboard.decimal": "Decimal",
+ "dashboard.percentage": "Percentage",
+ "dashboard.number_format": "Number Format",
+ "dashboard.numeralSystem": "Numeral System",
+ "dashboard.conversionSample": "Conversion Sample",
+ "dashboard.currency_format": "Currency Format",
+ "dashboard.localization_info": "Localization Info",
+ "dashboard.i18nFeatures": "Internationalization Features",
+ "dashboard.i18nDesc": "Live preview of date, number and currency conversion based on locale settings",
+
+ "project.create": "Create Project",
+ "project.new": "New Project",
+ "project.edit": "Edit Project",
+ "project.name": "Project Name",
+ "project.code": "Project Code",
+ "project.description": "Description",
+ "project.status": "Status",
+ "project.startDate": "Start Date",
+ "project.endDate": "End Date",
+ "project.start_date": "Start Date",
+ "project.end_date": "End Date",
+ "project.budget": "Budget",
+ "project.manager": "Project Manager",
+ "project.currency": "Currency",
+ "project.members": "Project Members",
+ "project.location": "Project Location",
+ "project.progress": "Progress",
+ "project.activeProjects": "Active Projects",
+ "project.no_projects": "No projects found",
+ "project.status_planning": "Planning",
+ "project.status_in_progress": "In Progress",
+ "project.status_on_hold": "On Hold",
+ "project.status_completed": "Completed",
+ "project.status_cancelled": "Cancelled",
+ "project.status_active": "Active",
+ "project.created_successfully": "Project created successfully",
+ "project.updated_successfully": "Project updated successfully",
+ "project.deleted_successfully": "Project deleted successfully",
+ "project.status_updated": "Project status updated",
+
+ "task.create": "Create Task",
+ "task.new": "New Task",
+ "task.edit": "Edit Task",
+ "task.title": "Title",
+ "task.description": "Description",
+ "task.priority": "Priority",
+ "task.dueDate": "Due Date",
+ "task.assignee": "Assignee",
+ "task.estimatedHours": "Estimated Hours",
+ "task.status": "Status",
+ "task.start_date": "Start Date",
+ "task.completedAt": "Completed At",
+ "task.activityLog": "Activity Log",
+ "task.logCreated": "Task created",
+ "task.logStatusChanged": "Status changed",
+ "task.parent_task": "Parent Task",
+ "task.progress": "Progress",
+ "task.duration_days": "Duration (days)",
+ "task.sub_tasks": "Sub Tasks",
+ "task.status_updated": "Status updated",
+ "task.status_pending": "Pending",
+ "task.status_in_progress": "In Progress",
+ "task.status_review": "In Review",
+ "task.status_done": "Done",
+ "task.status_cancelled": "Cancelled",
+ "task.priority_low": "Low",
+ "task.priority_medium": "Medium",
+ "task.priority_high": "High",
+ "task.priority_urgent": "Urgent",
+ "task.kanban": "Kanban View",
+ "task.listView": "List View",
+ "task.gantt_view": "Gantt Chart",
+ "task.gantt": "Gantt",
+ "task.no_tasks": "No tasks",
+ "task.blocked": "Blocked",
+ "task.can_start": "Can Start",
+ "task.dependencies": "Dependencies",
+ "task.add_dependency": "Add Dependency",
+ "task.remove_dependency": "Remove Dependency",
+ "task.dependency_type": "Dependency Type",
+ "task.finish_to_start": "Finish to Start",
+ "task.start_to_start": "Start to Start",
+ "task.finish_to_finish": "Finish to Finish",
+ "task.start_to_finish": "Start to Finish",
+ "task.blocking_tasks": "Blocking Tasks",
+ "task.dependent_tasks": "Dependent Tasks",
+ "task.circular_dependency_error": "Circular dependency — cannot add",
+ "task.dependency_added": "Dependency added successfully",
+ "task.dependency_removed": "Dependency removed",
+ "task.created_successfully": "Task created successfully",
+ "task.updated_successfully": "Task updated successfully",
+ "task.deleted_successfully": "Task deleted successfully",
+
+ "employee.create": "Add Employee",
+ "employee.new": "New Employee",
+ "employee.edit": "Edit Employee",
+ "employee.firstName": "First Name",
+ "employee.lastName": "Last Name",
+ "employee.nationalCode": "National Code",
+ "employee.phone": "Phone",
+ "employee.jobTitle": "Job Title",
+ "employee.contractType": "Contract Type",
+ "employee.hireDate": "Hire Date",
+ "employee.baseSalary": "Base Salary",
+ "employee.salary_currency": "Salary Currency",
+ "employee.bankAccount": "Bank Account",
+ "employee.status": "Status",
+ "employee.status_active": "Active",
+ "employee.status_inactive": "Inactive",
+ "employee.status_on_leave": "On Leave",
+ "employee.project": "Project",
+ "employee.workLogs": "Work Logs",
+ "employee.financials": "Financials",
+ "employee.totalHours": "Total Hours",
+ "employee.overtimeHours": "Overtime Hours",
+ "employee.hoursWorked": "Hours Worked",
+ "employee.date": "Date",
+ "employee.description": "Description",
+ "employee.totalPaid": "Total Paid",
+ "employee.totalUnpaid": "Total Unpaid",
+ "employee.salary": "Salary",
+ "employee.bonus": "Bonus",
+ "employee.deduction": "Deduction",
+ "employee.overtime": "Overtime",
+ "employee.advance": "Advance",
+ "employee.reimbursement": "Reimbursement",
+ "employee.isPaid": "Paid",
+ "employee.isUnpaid": "Unpaid",
+ "employee.type": "Type",
+ "employee.amount": "Amount",
+ "employee.effectiveDate": "Effective Date",
+ "employee.termination_date": "Termination Date",
+ "employee.noWorkLogs": "No work logs recorded",
+ "employee.noFinancials": "No financial records",
+ "employee.contract_full_time": "Full-time",
+ "employee.contract_part_time": "Part-time",
+ "employee.contract_contractor": "Contractor",
+ "employee.contract_intern": "Intern",
+ "employee.contract_consultant": "Consultant",
+ "employee.created_successfully": "Employee added successfully",
+ "employee.updated_successfully": "Employee updated successfully",
+ "employee.deleted_successfully": "Employee deleted successfully",
+
+ "expense.create": "Create Expense",
+ "expense.create_expense": "Create Expense",
+ "expense.edit_expense": "Edit Expense",
+ "expense.new": "New Expense",
+ "expense.edit": "Edit Expense",
+ "expense.expenses": "Expenses",
+ "expense.title": "Title",
+ "expense.amount": "Amount",
+ "expense.original_amount": "Original Amount",
+ "expense.category": "Category",
+ "expense.date": "Expense Date",
+ "expense.description": "Description",
+ "expense.receipt": "Receipt/Attachment",
+ "expense.current_receipt": "Current Receipt",
+ "expense.base_currency": "Base Currency",
+ "expense.currency_conversion_note": "Amount is automatically converted to base currency",
+ "expense.invoice_number": "Invoice Number",
+ "expense.currency": "Currency",
+ "expense.project": "Project",
+ "expense.petty_cash": "Related Petty Cash",
+ "expense.requested_by": "Requested By",
+ "expense.approved_by": "Approved By",
+ "expense.approval_date": "Approval Date",
+ "expense.from_date": "From Date",
+ "expense.to_date": "To Date",
+ "expense.confirm_reject": "Are you sure you want to reject this expense?",
+ "expense.status_pending": "Pending",
+ "expense.status_approved": "Approved",
+ "expense.status_rejected": "Rejected",
+ "expense.status_paid": "Paid",
+ "expense.category_materials": "Materials",
+ "expense.category_labor": "Labor",
+ "expense.category_equipment": "Equipment",
+ "expense.category_transport": "Transport",
+ "expense.category_services": "Services",
+ "expense.category_food": "Food",
+ "expense.category_accommodation": "Accommodation",
+ "expense.category_other": "Other",
+ "expense.created_successfully": "Expense created successfully",
+ "expense.updated_successfully": "Expense updated successfully",
+ "expense.deleted_successfully": "Expense deleted successfully",
+ "expense.approved_successfully": "Expense approved",
+ "expense.rejected_successfully": "Expense rejected",
+ "expense.settled_successfully": "Petty cash settled",
+ "expense.no_expenses": "No expenses recorded",
+
+ "inventory.inventory": "Inventory",
+ "inventory.dashboard": "Dashboard",
+ "inventory.items": "Inventory Items",
+ "inventory.item_name": "Item Name",
+ "inventory.item_code": "Item Code",
+ "inventory.unit": "Unit",
+ "inventory.category": "Category",
+ "inventory.category_placeholder": "e.g., Materials, Tools, Hardware...",
+ "inventory.warehouses": "Warehouses",
+ "inventory.warehouse_name": "Warehouse Name",
+ "inventory.location": "Location",
+ "inventory.active": "Active",
+ "inventory.inactive": "Inactive",
+ "inventory.quantity": "Quantity",
+ "inventory.unit_price": "Unit Price",
+ "inventory.total_price": "Total Price",
+ "inventory.reference": "Reference Number",
+ "inventory.notes": "Notes",
+ "inventory.description": "Description",
+ "inventory.current_stock": "Current Stock",
+ "inventory.min_stock": "Min Stock",
+ "inventory.min_stock_help": "Alert when stock falls below this amount",
+ "inventory.low_stock": "Low Stock",
+ "inventory.low_stock_items": "Low Stock Items",
+ "inventory.low_stock_alert": "Stock Alert",
+ "inventory.active_items": "Active Items",
+ "inventory.all_stock_ok": "All items have sufficient stock",
+ "inventory.recent_transactions": "Recent Transactions",
+ "inventory.items_count": "Items Count",
+ "inventory.transactions": "Transactions",
+ "inventory.transaction_type": "Transaction Type",
+ "inventory.type_in": "Stock In",
+ "inventory.type_out": "Stock Out",
+ "inventory.type_transfer": "Transfer",
+ "inventory.type_return": "Return",
+ "inventory.type_adjustment": "Adjustment",
+ "inventory.stock_in": "Stock In",
+ "inventory.stock_out": "Stock Out",
+ "inventory.transfer": "Transfer Between Warehouses",
+ "inventory.from_warehouse": "From Warehouse",
+ "inventory.to_warehouse": "To Warehouse",
+ "inventory.current_stock_source": "Source Stock",
+ "inventory.from_date": "From Date",
+ "inventory.to_date": "To Date",
+ "inventory.warehouse": "Warehouse",
+ "inventory.item": "Item",
+ "inventory.project": "Project",
+ "inventory.create_warehouse": "Add New Warehouse",
+ "inventory.create_item": "Add New Item",
+ "inventory.create_transaction": "New Transaction",
+ "inventory.new_transaction": "New Transaction",
+ "inventory.edit_item": "Edit Item",
+ "inventory.edit_warehouse": "Edit Warehouse",
+ "inventory.stock_in_success": "Stock in recorded successfully",
+ "inventory.stock_out_success": "Stock out recorded successfully",
+ "inventory.transfer_success": "Transfer completed successfully",
+ "inventory.insufficient_stock": "Insufficient stock",
+ "inventory.insufficient_stock_source": "Source warehouse has insufficient stock",
+ "inventory.no_items": "No items recorded",
+ "inventory.no_warehouses": "No warehouses recorded",
+ "inventory.no_transactions": "No transactions recorded",
+ "inventory.warehouse_created": "Warehouse created successfully",
+ "inventory.item_created": "Item created successfully",
+ "inventory.transaction_created": "Transaction recorded successfully",
+ "inventory.item_updated": "Item updated successfully",
+ "inventory.item_deleted": "Item deleted successfully",
+ "inventory.item_has_transactions": "This item has transactions and cannot be deleted",
+ "inventory.warehouse_updated": "Warehouse updated successfully",
+ "inventory.warehouse_deleted": "Warehouse deleted successfully",
+ "inventory.warehouse_has_transactions": "This warehouse has transactions and cannot be deleted",
+
+ "pettyCash.pettyCash": "Petty Cash",
+ "pettyCash.title": "Title",
+ "pettyCash.amount": "Amount",
+ "pettyCash.original_amount": "Original Amount",
+ "pettyCash.remaining": "Remaining",
+ "pettyCash.description": "Description",
+ "pettyCash.project": "Project",
+ "pettyCash.currency": "Currency",
+ "pettyCash.base_currency": "Base Currency",
+ "pettyCash.currency_conversion_note": "Amount is automatically converted to base currency",
+ "pettyCash.request_date": "Request Date",
+ "pettyCash.approval_date": "Approval Date",
+ "pettyCash.requested_by": "Requested By",
+ "pettyCash.approved_by": "Approved By",
+ "pettyCash.status": "Status",
+ "pettyCash.pending": "Pending",
+ "pettyCash.approved": "Approved",
+ "pettyCash.rejected": "Rejected",
+ "pettyCash.settled": "Settled",
+ "pettyCash.create": "Create Petty Cash",
+ "pettyCash.edit": "Edit Petty Cash",
+ "pettyCash.settle": "Settle",
+ "pettyCash.receipt": "Receipt/Attachment",
+ "pettyCash.current_receipt": "Current Receipt",
+ "pettyCash.receipt_help": "Image or PDF file (max 10MB)",
+ "pettyCash.expenses": "Related Expenses",
+ "pettyCash.no_petty_cashes": "No petty cash records",
+ "pettyCash.created_successfully": "Petty cash created successfully",
+ "pettyCash.updated_successfully": "Petty cash updated successfully",
+ "pettyCash.deleted_successfully": "Petty cash deleted successfully",
+ "pettyCash.approved_successfully": "Petty cash approved",
+ "pettyCash.rejected_successfully": "Petty cash rejected",
+ "pettyCash.settled_successfully": "Petty cash settled",
+
+ "letters.letters": "Correspondence",
+ "letters.title": "Correspondence",
+ "letters.create_title": "New Letter",
+ "letters.edit_title": "Edit Letter",
+ "letters.show_title": "View Letter",
+ "letters.list_title": "Letters List",
+ "letters.type": "Letter Type",
+ "letters.type_incoming": "Incoming",
+ "letters.type_outgoing": "Outgoing",
+ "letters.incoming": "Incoming Letter",
+ "letters.outgoing": "Outgoing Letter",
+ "letters.new_incoming": "New Incoming",
+ "letters.new_outgoing": "New Outgoing",
+ "letters.status": "Status",
+ "letters.status_draft": "Draft",
+ "letters.status_sent": "Sent",
+ "letters.status_received": "Received",
+ "letters.status_archived": "Archived",
+ "letters.draft": "Draft",
+ "letters.sent": "Sent",
+ "letters.received": "Received",
+ "letters.archived": "Archived",
+ "letters.subject": "Subject",
+ "letters.letter_number": "Letter Number",
+ "letters.letter_date": "Letter Date",
+ "letters.sender": "Sender",
+ "letters.recipient": "Recipient",
+ "letters.content": "Content",
+ "letters.description": "Description",
+ "letters.reference_number": "Reference Number",
+ "letters.priority": "Priority",
+ "letters.category": "Category",
+ "letters.project": "Project",
+ "letters.parent_letter": "Related Letter",
+ "letters.attachments": "Attachments",
+ "letters.has_attachment": "Has Attachment",
+ "letters.normal": "Normal",
+ "letters.urgent": "Urgent",
+ "letters.very_urgent": "Very Urgent",
+ "letters.from_date": "From Date",
+ "letters.to_date": "To Date",
+ "letters.create": "New Letter",
+ "letters.edit": "Edit",
+ "letters.delete": "Delete",
+ "letters.archive": "Archive",
+ "letters.download": "Download",
+ "letters.upload_attachment": "Upload Attachment",
+ "letters.delete_attachment": "Delete Attachment",
+ "letters.save": "Save",
+ "letters.cancel": "Cancel",
+ "letters.back": "Back",
+ "letters.search": "Search",
+ "letters.filter": "Filter",
+ "letters.print": "Print",
+ "letters.send": "Send",
+ "letters.view_letter": "View Letter",
+ "letters.no_letters": "No letters found",
+ "letters.created_successfully": "Letter created successfully",
+ "letters.updated_successfully": "Letter updated successfully",
+ "letters.deleted_successfully": "Letter deleted successfully",
+ "letters.archived_successfully": "Letter archived successfully",
+
+ "letter.letters": "Correspondence",
+ "letter.create_letter": "New Letter",
+ "letter.edit_letter": "Edit Letter",
+ "letter.incoming": "Incoming",
+ "letter.outgoing": "Outgoing",
+ "letter.type_incoming": "Incoming",
+ "letter.type_outgoing": "Outgoing",
+ "letter.subject": "Subject",
+ "letter.content": "Content",
+ "letter.sender": "Sender",
+ "letter.recipient": "Recipient",
+ "letter.letter_date": "Letter Date",
+ "letter.reference_number": "Reference Number",
+ "letter.type": "Letter Type",
+ "letter.project": "Related Project",
+ "letter.parent_letter": "Original Letter",
+ "letter.parent_letter_help": "The letter this letter is a reply to",
+ "letter.status": "Status",
+ "letter.status_draft": "Draft",
+ "letter.status_sent": "Sent",
+ "letter.status_received": "Received",
+ "letter.status_archived": "Archived",
+ "letter.attachments": "Attachments",
+ "letter.attachments_help": "Max 5 files (image, PDF — max 10MB)",
+ "letter.current_attachments": "Current Attachments",
+ "letter.replies": "Replies",
+ "letter.people": "Correspondence Info",
+ "letter.no_letters": "No letters found",
+ "letter.no_replies": "No replies yet",
+ "letter.archive": "Archive",
+ "letter.download": "Download",
+ "letter.search_placeholder": "Search subject, reference number, sender...",
+ "letter.confirm_archive": "Are you sure you want to archive this letter?",
+ "letter.file_name": "File Name",
+ "letter.file_size": "File Size",
+ "letter.remove_attachment": "Remove Attachment",
+ "letter.created_successfully": "Letter created successfully",
+ "letter.updated_successfully": "Letter updated successfully",
+ "letter.deleted_successfully": "Letter deleted successfully",
+ "letter.archived_successfully": "Letter archived",
+
+ "document.documents": "Documents",
+ "document.upload_document": "Upload Document",
+ "document.edit_document": "Edit Document",
+ "document.title": "Title",
+ "document.description": "Description",
+ "document.project": "Project",
+ "document.file": "File",
+ "document.file_type": "File Type",
+ "document.uploaded_by": "Uploaded By",
+ "document.upload_date": "Upload Date",
+ "document.current_file": "Current File",
+ "document.max_file_size_help": "Max 50MB",
+ "document.no_documents": "No documents uploaded",
+ "document.download": "Download",
+ "document.type_pdf": "PDF",
+ "document.type_image": "Image",
+ "document.type_word": "Word",
+ "document.type_excel": "Excel",
+ "document.type_text": "Text",
+ "document.type_other": "Other",
+ "document.file_not_found": "File not found",
+ "document.created_successfully": "Document uploaded successfully",
+ "document.updated_successfully": "Document updated successfully",
+ "document.deleted_successfully": "Document deleted successfully",
+
+ "report.reports": "Reports",
+ "report.financial_report": "Financial Report",
+ "report.employee_worklog_report": "Worklog Report",
+ "report.inventory_report": "Inventory Report",
+ "report.payroll_report": "Payroll Report",
+ "report.financial_description": "Financial report of projects including budget, expenses and balance",
+ "report.worklog_description": "Employee work hours report by project",
+ "report.inventory_description": "Inventory stock report and shortage alerts",
+ "report.payroll_description": "Salary, bonus and deductions report",
+ "report.project_code": "Project Code",
+ "report.project_name": "Project Name",
+ "report.budget": "Budget",
+ "report.total_expenses": "Total Expenses",
+ "report.total_petty_cash": "Total Petty Cash",
+ "report.remaining": "Remaining",
+ "report.utilization_percent": "Utilization %",
+ "report.manager": "Project Manager",
+ "report.employee_name": "Employee Name",
+ "report.job_title": "Job Title",
+ "report.project": "Project",
+ "report.date": "Date",
+ "report.hours_worked": "Hours Worked",
+ "report.overtime_hours": "Overtime Hours",
+ "report.task": "Task",
+ "report.item_code": "Item Code",
+ "report.item_name": "Item Name",
+ "report.warehouse": "Warehouse",
+ "report.total_in": "Total In",
+ "report.total_out": "Total Out",
+ "report.current_stock": "Current Stock",
+ "report.min_quantity": "Min Quantity",
+ "report.unit_price": "Unit Price",
+ "report.stock_value": "Stock Value",
+ "report.status": "Status",
+ "report.low_stock": "Low Stock",
+ "report.ok": "OK",
+ "report.contract_type": "Contract Type",
+ "report.salary": "Salary",
+ "report.bonus": "Bonus",
+ "report.overtime": "Overtime",
+ "report.deduction": "Deduction",
+ "report.other": "Other",
+ "report.net_pay": "Net Pay",
+ "report.total_paid": "Total Paid",
+ "report.total_unpaid": "Total Unpaid",
+ "report.employee_count": "Employee Count",
+ "report.date_from": "From Date",
+ "report.date_to": "To Date",
+ "report.no_data": "No data available to display",
+ "report.low_stock_alerts": "Low Stock Alerts",
+ "report.total_inventory_value": "Total Inventory Value",
+ "report.total_items": "Total Items",
+ "report.grand_totals": "Grand Totals",
+
+ "reports.reports": "Reports",
+ "reports.financial": "Financial Report",
+ "reports.financial_description": "Financial report of projects including budget, expenses and balance",
+ "reports.employee_worklog": "Worklog Report",
+ "reports.employee_worklog_description": "Employee work hours report by project",
+ "reports.inventory": "Inventory Report",
+ "reports.inventory_description": "Inventory stock report and shortage alerts",
+ "reports.payroll": "Payroll Report",
+ "reports.payroll_description": "Salary, bonus and deductions report",
+ "reports.total_expenses": "Total Expenses",
+ "reports.approved_amount": "Approved Amount",
+ "reports.pending_amount": "Pending Amount",
+ "reports.export_coming_soon": "Export coming soon",
+ "reports.detailed_view_coming_soon": "Detailed view coming soon",
+
+ "payroll.payroll": "Payroll",
+ "payroll.calculate_payroll": "Calculate Payroll",
+ "payroll.final_settlement": "Final Settlement",
+ "payroll.total_salaries": "Total Salaries",
+ "payroll.total_paid": "Total Paid",
+ "payroll.total_unpaid": "Total Unpaid",
+ "payroll.total_deductions": "Total Deductions",
+ "payroll.month": "Month",
+ "payroll.year": "Year",
+ "payroll.employee": "Employee",
+ "payroll.select_employee": "Select Employee",
+ "payroll.type": "Type",
+ "payroll.amount": "Amount",
+ "payroll.currency": "Currency",
+ "payroll.date": "Date",
+ "payroll.effective_date": "Effective Date",
+ "payroll.is_paid": "Paid",
+ "payroll.pay": "Pay",
+ "payroll.pay_bulk": "Bulk Pay",
+ "payroll.settle_all": "Settle All",
+ "payroll.settlement": "Settlement",
+ "payroll.new_entry": "New Entry",
+ "payroll.load": "Load",
+ "payroll.show": "Show",
+ "payroll.base_salary": "Base Salary",
+ "payroll.existing_records": "Existing Records",
+ "payroll.all_settled": "All Settled",
+ "payroll.unpaid_count": "Unpaid Count",
+ "payroll.confirm_settlement": "Are you sure you want to settle all outstanding payments?",
+ "payroll.type_salary": "Salary",
+ "payroll.type_bonus": "Bonus",
+ "payroll.type_deduction": "Deduction",
+ "payroll.type_advance": "Advance",
+ "payroll.type_overtime": "Overtime",
+ "payroll.type_allowance": "Allowance",
+ "payroll.no_records": "No records found",
+ "payroll.settlement_coming_soon": "Full settlement coming soon",
+ "payroll.detailed_view_coming_soon": "Detailed view coming soon",
+ "payroll.record_created_successfully": "Financial record created successfully",
+ "payroll.created_successfully": "Financial record created successfully",
+ "payroll.marked_as_paid": "Payment recorded successfully",
+ "payroll.already_paid": "This record is already paid",
+ "payroll.bulk_paid_successfully": "Bulk payments recorded successfully",
+ "payroll.settlement_processed": "Settlement processed successfully",
+ "payroll.settlement_processed_successfully": "Settlement processed successfully",
+ "payroll.total_gross": "Total Gross",
+ "payroll.total_net": "Net Pay",
+ "payroll.unpaid_employees": "Unpaid Employees",
+ "payroll.payment_status": "Payment Status",
+ "payroll.paid_percent": "paid",
+ "payroll.financial_records": "Financial Records",
+ "payroll.confirm_bulk_pay": "Are you sure you want to bulk pay selected items?",
+ "payroll.confirm_pay": "Are you sure you want to mark this as paid?",
+ "payroll.paid": "Paid",
+ "payroll.unpaid": "Unpaid",
+ "payroll.manual_entry": "Manual Entry",
+ "payroll.auto_calculate": "Auto Calculate",
+ "payroll.auto_calculate_btn": "Calculate & Create",
+ "payroll.auto_calculate_help_title": "Auto Calculate Payroll",
+ "payroll.auto_calculate_help": "Based on each employee's base salary, a salary record will be created for the selected month. If salary for the current month already exists, that employee will be skipped.",
+ "payroll.auto_salary_description": "Salary for month :month year :year",
+ "payroll.auto_overtime_description": "Overtime :hours hours",
+ "payroll.auto_calculated": ":created records created, :skipped skipped (already exist)",
+ "payroll.fill_base_salary": "Base Salary",
+ "payroll.base_salary_short": "Base",
+ "payroll.include_worklogs": "Calculate overtime from work logs",
+ "payroll.include_worklogs_help": "Overtime hours from recorded work logs will be calculated and converted to overtime pay",
+ "payroll.select_employees": "Select Employees",
+ "payroll.notes": "Notes",
+ "payroll.payroll_year": "Payroll Year :year",
+ "payroll.no_records_year": "No records found for this year",
+ "payroll.settlement_type": "Settlement Type",
+ "payroll.settlement_resignation": "Resignation",
+ "payroll.settlement_termination": "Termination",
+ "payroll.settlement_end_of_contract": "End of Contract",
+ "payroll.settlement_date": "Settlement Date",
+ "payroll.last_working_date": "Last Working Date",
+ "payroll.settlement_preview": "Settlement Preview",
+ "payroll.preview": "Preview",
+ "payroll.process_settlement": "Process Settlement",
+ "payroll.settlement_warning_title": "Attention!",
+ "payroll.settlement_warning": "By processing the settlement, all unpaid records will be marked as paid and the employee status will change to 'Terminated'. This action cannot be undone.",
+ "payroll.total_payable": "Total Payable",
+ "payroll.net_payable": "Net Payable",
+ "payroll.months_worked": "Months Worked",
+ "payroll.no_unpaid_records": "No unpaid records",
+ "payroll.cannot_delete_paid": "Paid records cannot be deleted",
+ "payroll.deleted_successfully": "Record deleted successfully",
+
+ "employee.contract_daily": "Daily Wage",
+ "employee.status_terminated": "Terminated",
+
+ "calendar.jalali": "Jalali",
+ "calendar.gregorian": "Gregorian",
+ "calendar.hijri": "Hijri",
+
+ "currency.IRR": "Rial",
+ "currency.USD": "Dollar",
+ "currency.EUR": "Euro",
+ "currency.AED": "Dirham",
+ "currency.code_irr": "Rial",
+ "currency.code_usd": "Dollar",
+ "currency.code_eur": "Euro",
+ "currency.code_aed": "Dirham",
+
+ "attachment.attachments": "Attachments",
+ "attachment.add_file": "Add File",
+ "attachment.additional_files": "Additional Attachment Files",
+ "attachment.max_files_help": "Max 5 files (image, PDF, Word — max 10MB each)",
+ "attachment.no_attachments": "No attachments",
+ "attachment.download": "Download",
+ "attachment.uploaded_successfully": "File uploaded successfully",
+ "attachment.deleted_successfully": "Attachment deleted successfully",
+ "attachment.upload_failed": "File upload failed",
+ "attachment.delete_failed": "Failed to delete attachment",
+ "attachment.invalid_model_type": "Invalid model type",
+ "attachment.model_not_found": "Model not found",
+
+ "settings.settings": "Settings",
+ "settings.organization": "Organization",
+ "settings.users": "Users",
+ "settings.currencies": "Currencies",
+ "settings.org_name": "Organization Name",
+ "settings.domain": "Domain",
+ "settings.base_currency": "Base Currency",
+ "settings.default_locale": "Default Language",
+ "settings.default_calendar": "Default Calendar",
+ "settings.allow_multi_currency": "Allow Multi-Currency",
+ "settings.logo": "Logo",
+ "settings.logo_help": "Allowed formats: JPEG, PNG, SVG — max 512KB",
+ "settings.organization_updated": "Organization settings updated successfully",
+ "settings.add_user": "Add User",
+ "settings.edit_user": "Edit User",
+ "settings.user_name": "Name",
+ "settings.user_email": "Email",
+ "settings.user_role": "Role",
+ "settings.role_admin": "System Admin",
+ "settings.role_manager": "Project Manager",
+ "settings.role_accountant": "Accountant",
+ "settings.role_employee": "Employee",
+ "settings.active": "Active",
+ "settings.inactive": "Inactive",
+ "settings.activate": "Activate",
+ "settings.deactivate": "Deactivate",
+ "settings.confirm": "Confirm",
+ "settings.password_help": "Leave blank if you don't want to change it",
+ "settings.user_created": "User created successfully",
+ "settings.user_updated": "User updated successfully",
+ "settings.user_status_updated": "User status updated",
+ "settings.no_users": "No users found",
+ "settings.add_currency": "Add Currency",
+ "settings.edit_currency": "Edit Currency",
+ "settings.currency_code": "Currency Code",
+ "settings.currency_name": "Currency Name",
+ "settings.currency_symbol": "Symbol",
+ "settings.exchange_rate": "Exchange Rate to USD",
+ "settings.exchange_rate_help": "Conversion rate of one unit of this currency to US Dollar",
+ "settings.decimal_places": "Decimal Places",
+ "settings.currency_created": "Currency created successfully",
+ "settings.currency_updated": "Currency updated successfully",
+ "settings.currency_status_updated": "Currency status updated",
+ "settings.no_currencies": "No currencies found",
+ "settings.full_settings_coming_soon": "Full settings page coming soon"
+}
diff --git a/resources/lang/fa.json b/resources/lang/fa.json
new file mode 100644
index 00000000..fc4b8cee
--- /dev/null
+++ b/resources/lang/fa.json
@@ -0,0 +1,826 @@
+{
+ "common.appName": "ورنوا",
+ "common.projectManagement": "مدیریت پروژه",
+ "common.save": "ذخیره",
+ "common.save_changes": "ذخیره تغییرات",
+ "common.cancel": "انصراف",
+ "common.delete": "حذف",
+ "common.edit": "ویرایش",
+ "common.create": "ایجاد",
+ "common.search": "جستجو...",
+ "common.filter": "فیلتر",
+ "common.export": "خروجی",
+ "common.exportExcel": "خروجی اکسل",
+ "common.exportPdf": "خروجی PDF",
+ "common.confirm": "تأیید",
+ "common.reject": "رد",
+ "common.approve": "تأیید",
+ "common.back": "بازگشت",
+ "common.actions": "عملیات",
+ "common.status": "وضعیت",
+ "common.all": "همه",
+ "common.noResults": "نتیجهای یافت نشد",
+ "common.loading": "در حال بارگذاری...",
+ "common.yes": "بله",
+ "common.no": "خیر",
+ "common.areYouSure": "آیا مطمئن هستید؟",
+ "common.from": "از",
+ "common.to": "تا",
+ "common.select": "انتخاب کنید",
+ "common.reset": "بازنشانی",
+ "common.all_rights_reserved": "تمامی حقوق محفوظ است",
+ "common.comingSoon": "بهزودی",
+ "common.success": "عملیات با موفقیت انجام شد",
+ "common.error": "خطایی رخ داد",
+ "common.view": "مشاهده",
+ "common.view_all": "مشاهده همه",
+ "common.details": "جزئیات",
+ "common.no_data": "دادهای موجود نیست",
+ "common.total": "مجموع",
+ "common.count": "تعداد",
+ "common.date": "تاریخ",
+ "common.amount": "مبلغ",
+ "common.description": "شرح",
+ "common.notes": "توضیحات",
+ "common.project": "پروژه",
+ "common.currency": "ارز",
+ "common.upload": "آپلود فایل",
+ "common.choose_file": "انتخاب فایل",
+ "common.no_file": "فایلی انتخاب نشده",
+ "common.confirm_delete": "آیا از حذف مطمئن هستید؟",
+ "common.good_morning": "صبح بخیر",
+ "common.good_afternoon": "ظهر بخیر",
+ "common.good_evening": "عصر بخیر",
+ "common.none": "هیچکدام",
+ "common.no_records": "رکوردی یافت نشد",
+ "common.today": "امروز",
+
+ "nav.dashboard": "داشبورد",
+ "nav.projects": "پروژهها",
+ "nav.tasks": "وظایف",
+ "nav.expenses": "هزینهها",
+ "nav.employees": "نیروها",
+ "nav.warehouse": "انبار",
+ "nav.pettyCash": "تنخواه",
+ "nav.letters": "نامهنگاری",
+ "nav.documents": "مستندات",
+ "nav.reports": "گزارشها",
+ "nav.payroll": "حقوق و دستمزد",
+ "nav.settings": "تنظیمات",
+ "nav.help": "راهنما",
+
+ "auth.login": "ورود",
+ "auth.logout": "خروج",
+ "auth.email": "ایمیل",
+ "auth.password": "رمز عبور",
+ "auth.rememberMe": "مرا به خاطر بسپار",
+ "auth.forgotPassword": "رمز عبور را فراموش کردهاید؟",
+ "auth.invalidCredentials": "ایمیل یا رمز عبور اشتباه است",
+
+ "dashboard.title": "داشبورد",
+ "dashboard.active_projects": "پروژههای فعال",
+ "dashboard.total_budget": "بودجه کل",
+ "dashboard.pending_tasks": "وظایف در انتظار",
+ "dashboard.active_employees": "نیروهای فعال",
+ "dashboard.addProject": "افزودن پروژه",
+ "dashboard.addExpense": "ثبت هزینه",
+ "dashboard.addEmployee": "افزودن نیرو",
+ "dashboard.viewReports": "مشاهده گزارشها",
+ "dashboard.recent_activity": "فعالیتهای اخیر",
+ "dashboard.vs_last_month": "نسبت به ماه قبل",
+ "dashboard.budgetGrowth": "رشد بودجه",
+ "dashboard.urgent": "فوری",
+ "dashboard.newHires": "نیروی جدید",
+ "dashboard.my_tasks": "وظایف من",
+ "dashboard.no_activity": "فعالیتی ثبت نشده است",
+ "dashboard.no_tasks": "وظیفهای نیست",
+ "dashboard.no_projects": "پروژهای نیست",
+ "dashboard.projects_progress": "پیشرفت پروژهها",
+ "dashboard.calendar": "تقویم",
+ "dashboard.numbers": "اعداد",
+ "dashboard.currency": "ارز",
+ "dashboard.today": "امروز",
+ "dashboard.short": "کوتاه",
+ "dashboard.calendar_system": "سیستم تقویم",
+ "dashboard.calendarType": "نوع تقویم",
+ "dashboard.integer": "عدد صحیح",
+ "dashboard.decimal": "عدد اعشاری",
+ "dashboard.percentage": "درصد",
+ "dashboard.number_format": "فرمت اعداد",
+ "dashboard.numeralSystem": "سیستم رقم",
+ "dashboard.conversionSample": "تبدیل نمونه",
+ "dashboard.currency_format": "فرمت ارز",
+ "dashboard.localization_info": "اطلاعات بومیسازی",
+ "dashboard.i18nFeatures": "قابلیتهای بینالمللیسازی",
+ "dashboard.i18nDesc": "نمایش زنده تبدیل تاریخ، عدد و ارز بر اساس تنظیمات منطقهای",
+
+ "project.create": "ایجاد پروژه",
+ "project.new": "پروژه جدید",
+ "project.edit": "ویرایش پروژه",
+ "project.name": "نام پروژه",
+ "project.code": "کد پروژه",
+ "project.description": "توضیحات",
+ "project.status": "وضعیت",
+ "project.startDate": "تاریخ شروع",
+ "project.endDate": "تاریخ پایان",
+ "project.start_date": "تاریخ شروع",
+ "project.end_date": "تاریخ پایان",
+ "project.budget": "بودجه",
+ "project.manager": "مدیر پروژه",
+ "project.currency": "ارز",
+ "project.members": "اعضای پروژه",
+ "project.location": "محل پروژه",
+ "project.progress": "پیشرفت",
+ "project.activeProjects": "پروژههای فعال",
+ "project.no_projects": "پروژهای ثبت نشده است",
+ "project.status_planning": "برنامهریزی",
+ "project.status_in_progress": "در حال اجرا",
+ "project.status_on_hold": "متوقف",
+ "project.status_completed": "تکمیلشده",
+ "project.status_cancelled": "لغوشده",
+ "project.status_active": "فعال",
+ "project.created_successfully": "پروژه با موفقیت ایجاد شد",
+ "project.updated_successfully": "پروژه با موفقیت بروزرسانی شد",
+ "project.deleted_successfully": "پروژه با موفقیت حذف شد",
+ "project.status_updated": "وضعیت پروژه بروزرسانی شد",
+
+ "task.create": "ایجاد وظیفه",
+ "task.new": "وظیفه جدید",
+ "task.edit": "ویرایش وظیفه",
+ "task.title": "عنوان",
+ "task.description": "توضیحات",
+ "task.priority": "اولویت",
+ "task.dueDate": "مهلت",
+ "task.assignee": "مسئول",
+ "task.estimatedHours": "ساعت تخمینی",
+ "task.status": "وضعیت",
+ "task.start_date": "تاریخ شروع",
+ "task.completedAt": "تاریخ تکمیل",
+ "task.activityLog": "تاریخچه تغییرات",
+ "task.logCreated": "وظیفه ایجاد شد",
+ "task.logStatusChanged": "تغییر وضعیت",
+ "task.parent_task": "وظیفه والد",
+ "task.progress": "پیشرفت",
+ "task.duration_days": "مدت (روز)",
+ "task.sub_tasks": "زیروظایف",
+ "task.status_updated": "وضعیت بروزرسانی شد",
+ "task.status_pending": "در انتظار",
+ "task.status_in_progress": "در حال انجام",
+ "task.status_review": "در بررسی",
+ "task.status_done": "انجامشده",
+ "task.status_cancelled": "لغوشده",
+ "task.priority_low": "کم",
+ "task.priority_medium": "متوسط",
+ "task.priority_high": "زیاد",
+ "task.priority_urgent": "فوری",
+ "task.kanban": "نمایش کانبان",
+ "task.listView": "نمایش لیست",
+ "task.gantt_view": "نمودار گانت",
+ "task.gantt": "گانت",
+ "task.no_tasks": "وظیفهای نیست",
+ "task.blocked": "مسدود",
+ "task.can_start": "قابل شروع",
+ "task.dependencies": "وابستگیها",
+ "task.add_dependency": "افزودن وابستگی",
+ "task.remove_dependency": "حذف وابستگی",
+ "task.dependency_type": "نوع وابستگی",
+ "task.finish_to_start": "پایان به شروع",
+ "task.start_to_start": "شروع به شروع",
+ "task.finish_to_finish": "پایان به پایان",
+ "task.start_to_finish": "شروع به پایان",
+ "task.blocking_tasks": "وظایف مسدودکننده",
+ "task.dependent_tasks": "وظایف وابسته",
+ "task.circular_dependency_error": "وابستگی چرخشی — امکان ثبت نیست",
+ "task.dependency_added": "وابستگی با موفقیت ثبت شد",
+ "task.dependency_removed": "وابستگی حذف شد",
+ "task.created_successfully": "وظیفه با موفقیت ایجاد شد",
+ "task.updated_successfully": "وظیفه با موفقیت بروزرسانی شد",
+ "task.deleted_successfully": "وظیفه با موفقیت حذف شد",
+
+ "employee.create": "افزودن نیرو",
+ "employee.new": "نیروی جدید",
+ "employee.edit": "ویرایش نیرو",
+ "employee.firstName": "نام",
+ "employee.lastName": "نام خانوادگی",
+ "employee.nationalCode": "کد ملی",
+ "employee.phone": "تلفن",
+ "employee.jobTitle": "عنوان شغلی",
+ "employee.contractType": "نوع قرارداد",
+ "employee.hireDate": "تاریخ استخدام",
+ "employee.baseSalary": "حقوق پایه",
+ "employee.salary_currency": "ارز حقوق",
+ "employee.bankAccount": "شماره حساب",
+ "employee.status": "وضعیت",
+ "employee.status_active": "فعال",
+ "employee.status_inactive": "غیرفعال",
+ "employee.status_on_leave": "مرخصی",
+ "employee.project": "پروژه",
+ "employee.workLogs": "کارکرد",
+ "employee.financials": "مالی",
+ "employee.totalHours": "جمع ساعات",
+ "employee.overtimeHours": "ساعت اضافهکار",
+ "employee.hoursWorked": "ساعت کار",
+ "employee.date": "تاریخ",
+ "employee.description": "شرح",
+ "employee.totalPaid": "جمع پرداختی",
+ "employee.totalUnpaid": "جمع پرداختنشده",
+ "employee.salary": "حقوق",
+ "employee.bonus": "پاداش",
+ "employee.deduction": "کسورات",
+ "employee.overtime": "اضافهکار",
+ "employee.advance": "مساعده",
+ "employee.reimbursement": "بازپرداخت",
+ "employee.isPaid": "پرداختشده",
+ "employee.isUnpaid": "پرداختنشده",
+ "employee.type": "نوع",
+ "employee.amount": "مبلغ",
+ "employee.effectiveDate": "تاریخ اجرا",
+ "employee.termination_date": "تاریخ تسویه",
+ "employee.noWorkLogs": "کارکردی ثبت نشده است",
+ "employee.noFinancials": "رکورد مالی ثبت نشده است",
+ "employee.contract_full_time": "تماموقت",
+ "employee.contract_part_time": "پارهوقت",
+ "employee.contract_contractor": "پیمانکار",
+ "employee.contract_intern": "کارآموز",
+ "employee.contract_consultant": "مشاور",
+ "employee.created_successfully": "نیرو با موفقیت اضافه شد",
+ "employee.updated_successfully": "نیرو با موفقیت بروزرسانی شد",
+ "employee.deleted_successfully": "نیرو با موفقیت حذف شد",
+
+ "expense.create": "ثبت هزینه",
+ "expense.create_expense": "ثبت هزینه",
+ "expense.edit_expense": "ویرایش هزینه",
+ "expense.new": "هزینه جدید",
+ "expense.edit": "ویرایش هزینه",
+ "expense.expenses": "هزینهها",
+ "expense.title": "عنوان",
+ "expense.amount": "مبلغ",
+ "expense.original_amount": "مبلغ اصلی",
+ "expense.category": "دستهبندی",
+ "expense.date": "تاریخ هزینه",
+ "expense.description": "شرح هزینه",
+ "expense.receipt": "رسید/پیوست",
+ "expense.current_receipt": "رسید فعلی",
+ "expense.base_currency": "ارز پایه",
+ "expense.currency_conversion_note": "مبلغ به صورت خودکار به ارز پایه تبدیل میشود",
+ "expense.invoice_number": "شماره فاکتور",
+ "expense.currency": "ارز",
+ "expense.project": "پروژه",
+ "expense.petty_cash": "تنخواه مربوطه",
+ "expense.requested_by": "درخواستکننده",
+ "expense.approved_by": "تأییدکننده",
+ "expense.approval_date": "تاریخ تأیید",
+ "expense.from_date": "از تاریخ",
+ "expense.to_date": "تا تاریخ",
+ "expense.confirm_reject": "آیا از رد این هزینه مطمئن هستید؟",
+ "expense.status_pending": "در انتظار",
+ "expense.status_approved": "تأییدشده",
+ "expense.status_rejected": "ردشده",
+ "expense.status_paid": "پرداختشده",
+ "expense.category_materials": "مصالح",
+ "expense.category_labor": "نیروی کار",
+ "expense.category_equipment": "تجهیزات",
+ "expense.category_transport": "حملونقل",
+ "expense.category_services": "خدمات",
+ "expense.category_food": "خوراک",
+ "expense.category_accommodation": "اسکان",
+ "expense.category_other": "سایر",
+ "expense.created_successfully": "هزینه با موفقیت ثبت شد",
+ "expense.updated_successfully": "هزینه با موفقیت بروزرسانی شد",
+ "expense.deleted_successfully": "هزینه با موفقیت حذف شد",
+ "expense.approved_successfully": "هزینه تأیید شد",
+ "expense.rejected_successfully": "هزینه رد شد",
+ "expense.settled_successfully": "تنخواه تسویه شد",
+ "expense.no_expenses": "هزینهای ثبت نشده است",
+
+ "inventory.inventory": "انبار",
+ "inventory.dashboard": "داشبورد",
+ "inventory.items": "اقلام انبار",
+ "inventory.item_name": "نام قلم",
+ "inventory.item_code": "کد قلم",
+ "inventory.unit": "واحد",
+ "inventory.category": "دستهبندی",
+ "inventory.category_placeholder": "مثال: مصالح، ابزار، یراقآلات،...",
+ "inventory.warehouses": "انبارها",
+ "inventory.warehouse_name": "نام انبار",
+ "inventory.location": "محل",
+ "inventory.active": "فعال",
+ "inventory.inactive": "غیرفعال",
+ "inventory.quantity": "تعداد",
+ "inventory.unit_price": "قیمت واحد",
+ "inventory.total_price": "مبلغ کل",
+ "inventory.reference": "شماره مرجع",
+ "inventory.notes": "توضیحات",
+ "inventory.description": "توضیحات",
+ "inventory.current_stock": "موجودی فعلی",
+ "inventory.min_stock": "حداقل موجودی",
+ "inventory.min_stock_help": "هشدار وقتی موجودی کمتر از این مقدار شود",
+ "inventory.low_stock": "موجودی کم",
+ "inventory.low_stock_items": "اقلام کمموجودی",
+ "inventory.low_stock_alert": "هشدار موجودی",
+ "inventory.active_items": "اقلام فعال",
+ "inventory.all_stock_ok": "همه اقلام موجودی کافی دارند",
+ "inventory.recent_transactions": "آخرین تراکنشها",
+ "inventory.items_count": "تعداد اقلام",
+ "inventory.transactions": "رسید و حواله",
+ "inventory.transaction_type": "نوع حواله",
+ "inventory.type_in": "رسید",
+ "inventory.type_out": "حواله",
+ "inventory.type_transfer": "انتقال",
+ "inventory.type_return": "برگشت",
+ "inventory.type_adjustment": "تعدیل",
+ "inventory.stock_in": "ثبت رسید",
+ "inventory.stock_out": "ثبت حواله",
+ "inventory.transfer": "انتقال بین انبارها",
+ "inventory.from_warehouse": "انبار مبدأ",
+ "inventory.to_warehouse": "انبار مقصد",
+ "inventory.current_stock_source": "موجودی مبدأ",
+ "inventory.from_date": "از تاریخ",
+ "inventory.to_date": "تا تاریخ",
+ "inventory.warehouse": "انبار",
+ "inventory.item": "قلم",
+ "inventory.project": "پروژه",
+ "inventory.create_warehouse": "افزودن انبار جدید",
+ "inventory.create_item": "افزودن قلم جدید",
+ "inventory.create_transaction": "حواله جدید",
+ "inventory.new_transaction": "ثبت رسید/حواله",
+ "inventory.edit_item": "ویرایش قلم",
+ "inventory.edit_warehouse": "ویرایش انبار",
+ "inventory.stock_in_success": "رسید با موفقیت ثبت شد",
+ "inventory.stock_out_success": "حواله با موفقیت ثبت شد",
+ "inventory.transfer_success": "انتقال با موفقیت انجام شد",
+ "inventory.insufficient_stock": "موجودی کافی نیست",
+ "inventory.insufficient_stock_source": "موجودی انبار مبدأ کافی نیست",
+ "inventory.no_items": "قلمی ثبت نشده است",
+ "inventory.no_warehouses": "انباری ثبت نشده است",
+ "inventory.no_transactions": "تراکنشی ثبت نشده است",
+ "inventory.warehouse_created": "انبار با موفقیت ایجاد شد",
+ "inventory.item_created": "قلم با موفقیت ایجاد شد",
+ "inventory.transaction_created": "حواله با موفقیت ثبت شد",
+ "inventory.item_updated": "قلم با موفقیت بروزرسانی شد",
+ "inventory.item_deleted": "قلم با موفقیت حذف شد",
+ "inventory.item_has_transactions": "این قلم دارای تراکنش است و قابل حذف نیست",
+ "inventory.warehouse_updated": "انبار با موفقیت بروزرسانی شد",
+ "inventory.warehouse_deleted": "انبار با موفقیت حذف شد",
+ "inventory.warehouse_has_transactions": "این انبار دارای تراکنش است و قابل حذف نیست",
+
+ "pettyCash.pettyCash": "تنخواه",
+ "pettyCash.title": "عنوان",
+ "pettyCash.amount": "مبلغ",
+ "pettyCash.original_amount": "مبلغ اصلی",
+ "pettyCash.remaining": "مانده",
+ "pettyCash.description": "شرح",
+ "pettyCash.project": "پروژه",
+ "pettyCash.currency": "ارز",
+ "pettyCash.base_currency": "ارز پایه",
+ "pettyCash.currency_conversion_note": "مبلغ به صورت خودکار به ارز پایه تبدیل میشود",
+ "pettyCash.request_date": "تاریخ درخواست",
+ "pettyCash.approval_date": "تاریخ تأیید",
+ "pettyCash.requested_by": "درخواستکننده",
+ "pettyCash.approved_by": "تأییدکننده",
+ "pettyCash.status": "وضعیت",
+ "pettyCash.pending": "در انتظار",
+ "pettyCash.approved": "تأییدشده",
+ "pettyCash.rejected": "ردشده",
+ "pettyCash.settled": "تسویهشده",
+ "pettyCash.create": "ثبت تنخواه",
+ "pettyCash.edit": "ویرایش تنخواه",
+ "pettyCash.settle": "تسویه",
+ "pettyCash.receipt": "رسید/پیوست",
+ "pettyCash.current_receipt": "رسید فعلی",
+ "pettyCash.receipt_help": "فایل تصویر یا PDF (حداکثر ۱۰ مگابایت)",
+ "pettyCash.expenses": "هزینههای مرتبط",
+ "pettyCash.no_petty_cashes": "تنخواهی ثبت نشده است",
+ "pettyCash.created_successfully": "تنخواه با موفقیت ثبت شد",
+ "pettyCash.updated_successfully": "تنخواه با موفقیت بروزرسانی شد",
+ "pettyCash.deleted_successfully": "تنخواه با موفقیت حذف شد",
+ "pettyCash.approved_successfully": "تنخواه تأیید شد",
+ "pettyCash.rejected_successfully": "تنخواه رد شد",
+ "pettyCash.settled_successfully": "تنخواه تسویه شد",
+
+ "letters.letters": "نامه نگاری",
+ "letters.title": "نامه نگاری",
+ "letters.create_title": "ثبت نامه جدید",
+ "letters.edit_title": "ویرایش نامه",
+ "letters.show_title": "مشاهده نامه",
+ "letters.list_title": "لیست نامهها",
+ "letters.type": "نوع نامه",
+ "letters.type_incoming": "دریافتی",
+ "letters.type_outgoing": "ارسالی",
+ "letters.incoming": "نامه دریافتی",
+ "letters.outgoing": "نامه ارسالی",
+ "letters.new_incoming": "نامه دریافتی جدید",
+ "letters.new_outgoing": "نامه ارسالی جدید",
+ "letters.status": "وضعیت",
+ "letters.status_draft": "پیشنویس",
+ "letters.status_sent": "ارسال شده",
+ "letters.status_received": "دریافت شده",
+ "letters.status_archived": "بایگانی شده",
+ "letters.draft": "پیشنویس",
+ "letters.sent": "ارسال شده",
+ "letters.received": "دریافت شده",
+ "letters.archived": "بایگانی شده",
+ "letters.subject": "موضوع",
+ "letters.letter_number": "شماره نامه",
+ "letters.letter_date": "تاریخ نامه",
+ "letters.sender": "فرستنده",
+ "letters.recipient": "گیرنده",
+ "letters.content": "متن نامه",
+ "letters.description": "توضیحات",
+ "letters.reference_number": "شماره مرجع",
+ "letters.priority": "اولویت",
+ "letters.category": "دستهبندی",
+ "letters.project": "پروژه",
+ "letters.parent_letter": "نامه مرتبط",
+ "letters.attachments": "پیوستها",
+ "letters.has_attachment": "دارای پیوست",
+ "letters.normal": "عادی",
+ "letters.urgent": "فوری",
+ "letters.very_urgent": "خیلی فوری",
+ "letters.from_date": "از تاریخ",
+ "letters.to_date": "تا تاریخ",
+ "letters.create": "ثبت نامه جدید",
+ "letters.edit": "ویرایش",
+ "letters.delete": "حذف",
+ "letters.archive": "بایگانی",
+ "letters.download": "دانلود",
+ "letters.upload_attachment": "آپلود پیوست",
+ "letters.delete_attachment": "حذف پیوست",
+ "letters.save": "ذخیره",
+ "letters.cancel": "انصراف",
+ "letters.back": "بازگشت",
+ "letters.search": "جستجو",
+ "letters.filter": "فیلتر",
+ "letters.print": "چاپ",
+ "letters.send": "ارسال",
+ "letters.view_letter": "مشاهده نامه",
+ "letters.no_letters": "هیچ نامهای یافت نشد",
+ "letters.created_successfully": "نامه با موفقیت ثبت شد",
+ "letters.updated_successfully": "نامه با موفقیت بروزرسانی شد",
+ "letters.deleted_successfully": "نامه با موفقیت حذف شد",
+ "letters.archived_successfully": "نامه با موفقیت بایگانی شد",
+
+ "letter.letters": "نامهنگاری",
+ "letter.create_letter": "ثبت نامه جدید",
+ "letter.edit_letter": "ویرایش نامه",
+ "letter.incoming": "وارده",
+ "letter.outgoing": "صادره",
+ "letter.type_incoming": "وارده",
+ "letter.type_outgoing": "صادره",
+ "letter.subject": "موضوع",
+ "letter.content": "متن نامه",
+ "letter.sender": "فرستنده",
+ "letter.recipient": "گیرنده",
+ "letter.letter_date": "تاریخ نامه",
+ "letter.reference_number": "شماره مرجع",
+ "letter.type": "نوع نامه",
+ "letter.project": "پروژه مرتبط",
+ "letter.parent_letter": "نامه اصلی",
+ "letter.parent_letter_help": "نامهای که این نامه پاسخ آن است",
+ "letter.status": "وضعیت",
+ "letter.status_draft": "پیشنویس",
+ "letter.status_sent": "ارسالشده",
+ "letter.status_received": "دریافتشده",
+ "letter.status_archived": "بایگانیشده",
+ "letter.attachments": "پیوستها",
+ "letter.attachments_help": "حداکثر ۵ فایل (تصویر، PDF - حداکثر ۱۰ مگابایت)",
+ "letter.current_attachments": "پیوستهای فعلی",
+ "letter.replies": "پاسخها",
+ "letter.people": "اطلاعات مراسله",
+ "letter.no_letters": "نامهای ثبت نشده است",
+ "letter.no_replies": "پاسخی ثبت نشده است",
+ "letter.archive": "بایگانی",
+ "letter.download": "دانلود",
+ "letter.search_placeholder": "جستجو در موضوع، شماره مرجع، فرستنده...",
+ "letter.confirm_archive": "آیا از بایگانی این نامه مطمئن هستید؟",
+ "letter.file_name": "نام فایل",
+ "letter.file_size": "اندازه فایل",
+ "letter.remove_attachment": "حذف پیوست",
+ "letter.created_successfully": "نامه با موفقیت ثبت شد",
+ "letter.updated_successfully": "نامه با موفقیت بروزرسانی شد",
+ "letter.deleted_successfully": "نامه با موفقیت حذف شد",
+ "letter.archived_successfully": "نامه بایگانی شد",
+
+ "document.documents": "مستندات",
+ "document.upload_document": "آپلود مستند",
+ "document.edit_document": "ویرایش مستند",
+ "document.title": "عنوان",
+ "document.description": "شرح",
+ "document.project": "پروژه",
+ "document.file": "فایل",
+ "document.file_type": "نوع فایل",
+ "document.uploaded_by": "آپلودکننده",
+ "document.upload_date": "تاریخ آپلود",
+ "document.current_file": "فایل فعلی",
+ "document.max_file_size_help": "حداکثر ۵۰ مگابایت",
+ "document.no_documents": "مستندی آپلود نشده است",
+ "document.download": "دانلود",
+ "document.type_pdf": "PDF",
+ "document.type_image": "تصویر",
+ "document.type_word": "Word",
+ "document.type_excel": "Excel",
+ "document.type_text": "متنی",
+ "document.type_other": "سایر",
+ "document.file_not_found": "فایل یافت نشد",
+ "document.created_successfully": "مستند با موفقیت آپلود شد",
+ "document.updated_successfully": "مستند با موفقیت بروزرسانی شد",
+ "document.deleted_successfully": "مستند با موفقیت حذف شد",
+
+ "report.reports": "گزارشها",
+ "report.financial_report": "گزارش مالی",
+ "report.employee_worklog_report": "گزارش کارکرد",
+ "report.inventory_report": "گزارش انبار",
+ "report.payroll_report": "گزارش حقوق و دستمزد",
+ "report.financial_description": "گزارش مالی پروژهها شامل بودجه، هزینهها و مانده",
+ "report.worklog_description": "گزارش ساعات کار نیروها به تفکیک پروژه",
+ "report.inventory_description": "گزارش موجودی انبار و هشدار کمبود",
+ "report.payroll_description": "گزارش حقوق، پاداش و کسورات نیروها",
+ "report.project_code": "کد پروژه",
+ "report.project_name": "نام پروژه",
+ "report.budget": "بودجه",
+ "report.total_expenses": "جمع هزینهها",
+ "report.total_petty_cash": "جمع تنخواه",
+ "report.remaining": "مانده",
+ "report.utilization_percent": "درصد مصرف",
+ "report.manager": "مدیر پروژه",
+ "report.employee_name": "نام نیرو",
+ "report.job_title": "عنوان شغلی",
+ "report.project": "پروژه",
+ "report.date": "تاریخ",
+ "report.hours_worked": "ساعات کار",
+ "report.overtime_hours": "ساعت اضافهکار",
+ "report.task": "وظیفه",
+ "report.item_code": "کد قلم",
+ "report.item_name": "نام قلم",
+ "report.warehouse": "انبار",
+ "report.total_in": "جمع ورودی",
+ "report.total_out": "جمع خروجی",
+ "report.current_stock": "موجودی فعلی",
+ "report.min_quantity": "حداقل موجودی",
+ "report.unit_price": "قیمت واحد",
+ "report.stock_value": "ارزش موجودی",
+ "report.status": "وضعیت",
+ "report.low_stock": "کمبود موجودی",
+ "report.ok": "مطلوب",
+ "report.contract_type": "نوع قرارداد",
+ "report.salary": "حقوق",
+ "report.bonus": "پاداش",
+ "report.overtime": "اضافهکار",
+ "report.deduction": "کسورات",
+ "report.other": "سایر",
+ "report.net_pay": "خالص پرداختی",
+ "report.total_paid": "جمع پرداختی",
+ "report.total_unpaid": "جمع پرداختنشده",
+ "report.employee_count": "تعداد نیروها",
+ "report.date_from": "از تاریخ",
+ "report.date_to": "تا تاریخ",
+ "report.no_data": "دادهای برای نمایش وجود ندارد",
+ "report.low_stock_alerts": "هشدار کمبود موجودی",
+ "report.total_inventory_value": "ارزش کل موجودی",
+ "report.total_items": "تعداد اقلام",
+ "report.grand_totals": "جمع کل",
+
+ "reports.reports": "گزارشها",
+ "reports.financial": "گزارش مالی",
+ "reports.financial_description": "گزارش مالی پروژهها شامل بودجه، هزینهها و مانده",
+ "reports.employee_worklog": "گزارش کارکرد",
+ "reports.employee_worklog_description": "گزارش ساعات کار نیروها به تفکیک پروژه",
+ "reports.inventory": "گزارش انبار",
+ "reports.inventory_description": "گزارش موجودی انبار و هشدار کمبود",
+ "reports.payroll": "گزارش حقوق",
+ "reports.payroll_description": "گزارش حقوق، پاداش و کسورات نیروها",
+ "reports.total_expenses": "جمع هزینهها",
+ "reports.approved_amount": "مبلغ تأییدشده",
+ "reports.pending_amount": "مبلغ در انتظار",
+ "reports.export_coming_soon": "خروجی بهزودی اضافه میشود",
+ "reports.detailed_view_coming_soon": "نمایش تفصیلی بهزودی اضافه میشود",
+
+ "payroll.payroll": "حقوق و دستمزد",
+ "payroll.calculate_payroll": "محاسبه حقوق",
+ "payroll.final_settlement": "تسویه حساب",
+ "payroll.total_salaries": "جمع حقوق",
+ "payroll.total_paid": "جمع پرداختی",
+ "payroll.total_unpaid": "جمع پرداختنشده",
+ "payroll.total_deductions": "جمع کسورات",
+ "payroll.month": "ماه",
+ "payroll.year": "سال",
+ "payroll.employee": "نیرو",
+ "payroll.select_employee": "انتخاب نیرو",
+ "payroll.type": "نوع",
+ "payroll.amount": "مبلغ",
+ "payroll.currency": "ارز",
+ "payroll.date": "تاریخ",
+ "payroll.effective_date": "تاریخ اجرا",
+ "payroll.is_paid": "پرداختشده",
+ "payroll.pay": "پرداخت",
+ "payroll.pay_bulk": "پرداخت دستهای",
+ "payroll.settle_all": "تسویه همه",
+ "payroll.settlement": "تسویه حساب",
+ "payroll.new_entry": "ثبت رکورد جدید",
+ "payroll.load": "بارگذاری",
+ "payroll.show": "نمایش",
+ "payroll.base_salary": "حقوق پایه",
+ "payroll.existing_records": "رکوردهای موجود",
+ "payroll.all_settled": "همه تسویه شده",
+ "payroll.unpaid_count": "تعداد پرداختنشده",
+ "payroll.confirm_settlement": "آیا از تسویه همه پرداختهای معوق مطمئن هستید؟",
+ "payroll.type_salary": "حقوق",
+ "payroll.type_bonus": "پاداش",
+ "payroll.type_deduction": "کسورات",
+ "payroll.type_advance": "مساعده",
+ "payroll.type_overtime": "اضافهکار",
+ "payroll.type_allowance": "مزایا",
+ "payroll.no_records": "رکوردی ثبت نشده است",
+ "payroll.settlement_coming_soon": "تسویه حساب کامل بهزودی اضافه میشود",
+ "payroll.detailed_view_coming_soon": "نمایش تفصیلی بهزودی اضافه میشود",
+ "payroll.record_created_successfully": "رکورد مالی با موفقیت ثبت شد",
+ "payroll.created_successfully": "رکورد مالی با موفقیت ثبت شد",
+ "payroll.marked_as_paid": "پرداخت با موفقیت ثبت شد",
+ "payroll.already_paid": "این رکورد قبلاً پرداخت شده است",
+ "payroll.bulk_paid_successfully": "پرداختهای دستهای با موفقیت ثبت شد",
+ "payroll.settlement_processed": "تسویه حساب با موفقیت انجام شد",
+ "payroll.settlement_processed_successfully": "تسویه حساب با موفقیت انجام شد",
+ "payroll.total_gross": "جمع ناخالص",
+ "payroll.total_net": "خالص پرداختی",
+ "payroll.unpaid_employees": "نیروهای پرداختنشده",
+ "payroll.payment_status": "وضعیت پرداخت",
+ "payroll.paid_percent": "پرداخت شده",
+ "payroll.financial_records": "رکوردهای مالی",
+ "payroll.confirm_bulk_pay": "آیا از پرداخت دستهای موارد انتخابشده مطمئن هستید؟",
+ "payroll.confirm_pay": "آیا از ثبت پرداخت مطمئن هستید؟",
+ "payroll.paid": "پرداختشده",
+ "payroll.unpaid": "پرداختنشده",
+ "payroll.manual_entry": "ثبت دستی",
+ "payroll.auto_calculate": "محاسبه خودکار",
+ "payroll.auto_calculate_btn": "محاسبه و ایجاد",
+ "payroll.auto_calculate_help_title": "محاسبه خودکار حقوق",
+ "payroll.auto_calculate_help": "بر اساس حقوق پایه هر نیرو، رکورد حقوقی ماه انتخابشده ایجاد میشود. اگر قبلاً حقوق ماه جاری ثبت شده باشد، آن نیرو رد میشود.",
+ "payroll.auto_salary_description": "حقوق ماه :month سال :year",
+ "payroll.auto_overtime_description": "اضافهکار :hours ساعت",
+ "payroll.auto_calculated": ":created رکورد ایجاد شد، :skipped رکورد رد شد (قبلاً ثبت شده)",
+ "payroll.fill_base_salary": "حقوق پایه",
+ "payroll.base_salary_short": "پایه",
+ "payroll.include_worklogs": "محاسبه اضافهکار از کارکرد",
+ "payroll.include_worklogs_help": "ساعات اضافهکار از کارکرد ثبتشده محاسبه و به مبلغ اضافهکار تبدیل میشود",
+ "payroll.select_employees": "انتخاب نیروها",
+ "payroll.notes": "یادداشت",
+ "payroll.payroll_year": "حقوق سال :year",
+ "payroll.no_records_year": "رکوردی برای این سال ثبت نشده است",
+ "payroll.settlement_type": "نوع تسویه",
+ "payroll.settlement_resignation": "استعفا",
+ "payroll.settlement_termination": "اخراج",
+ "payroll.settlement_end_of_contract": "پایان قرارداد",
+ "payroll.settlement_date": "تاریخ تسویه",
+ "payroll.last_working_date": "آخرین روز کاری",
+ "payroll.settlement_preview": "پیشنمایش تسویه",
+ "payroll.preview": "پیشنمایش",
+ "payroll.process_settlement": "انجام تسویه",
+ "payroll.settlement_warning_title": "توجه!",
+ "payroll.settlement_warning": "با انجام تسویه حساب، تمام رکوردهای پرداختنشده نیرو به عنوان پرداختشده علامتگذاری شده و وضعیت نیرو به «تسویهشده» تغییر میکند. این عمل قابل بازگشت نیست.",
+ "payroll.total_payable": "جمع قابل پرداخت",
+ "payroll.net_payable": "خالص قابل پرداخت",
+ "payroll.months_worked": "ماه کارکرد",
+ "payroll.no_unpaid_records": "رکورد پرداختنشدهای وجود ندارد",
+ "payroll.cannot_delete_paid": "رکورد پرداختشده قابل حذف نیست",
+ "payroll.deleted_successfully": "رکورد با موفقیت حذف شد",
+
+ "employee.contract_daily": "روزمزد",
+ "employee.status_terminated": "تسویهشده",
+
+ "calendar.jalali": "شمسی",
+ "calendar.gregorian": "میلادی",
+ "calendar.hijri": "قمری",
+
+ "currency.IRR": "ریال",
+ "currency.USD": "دلار",
+ "currency.EUR": "یورو",
+ "currency.AED": "درهم",
+ "currency.code_irr": "ریال",
+ "currency.code_usd": "دلار",
+ "currency.code_eur": "یورو",
+ "currency.code_aed": "درهم",
+
+ "attachment.attachments": "پیوستها",
+ "attachment.add_file": "افزودن فایل",
+ "attachment.additional_files": "فایلهای پیوست اضافی",
+ "attachment.max_files_help": "حداکثر ۵ فایل (تصویر، PDF، Word - حداکثر ۱۰ مگابایت هر کدام)",
+ "attachment.no_attachments": "پیوستی وجود ندارد",
+ "attachment.download": "دانلود",
+ "attachment.uploaded_successfully": "فایل با موفقیت آپلود شد",
+ "attachment.deleted_successfully": "پیوست با موفقیت حذف شد",
+ "attachment.upload_failed": "خطا در آپلود فایل",
+ "attachment.delete_failed": "خطا در حذف پیوست",
+ "attachment.invalid_model_type": "نوع مدل نامعتبر است",
+ "attachment.model_not_found": "مدل یافت نشد",
+
+ "settings.settings": "تنظیمات",
+ "settings.organization": "سازمان",
+ "settings.users": "کاربران",
+ "settings.currencies": "ارزها",
+ "settings.org_name": "نام سازمان",
+ "settings.domain": "دامنه",
+ "settings.base_currency": "ارز پایه",
+ "settings.default_locale": "زبان پیشفرض",
+ "settings.default_calendar": "تقویم پیشفرض",
+ "settings.allow_multi_currency": "اجازه استفاده از چند ارز",
+ "settings.logo": "لوگو",
+ "settings.logo_help": "فرمتهای مجاز: JPEG, PNG, SVG — حداکثر ۵۱۲ کیلوبایت",
+ "settings.organization_updated": "تنظیمات سازمان با موفقیت بروزرسانی شد",
+ "settings.add_user": "افزودن کاربر",
+ "settings.edit_user": "ویرایش کاربر",
+ "settings.user_name": "نام",
+ "settings.user_email": "ایمیل",
+ "settings.user_role": "نقش",
+ "settings.role_admin": "مدیر سیستم",
+ "settings.role_manager": "مدیر پروژه",
+ "settings.role_accountant": "حسابدار",
+ "settings.role_employee": "کارمند",
+ "settings.active": "فعال",
+ "settings.inactive": "غیرفعال",
+ "settings.activate": "فعالسازی",
+ "settings.deactivate": "غیرفعالسازی",
+ "settings.confirm": "تأیید",
+ "settings.password_help": "خالی بگذارید اگر نمیخواهید تغییر کنید",
+ "settings.user_created": "کاربر با موفقیت ایجاد شد",
+ "settings.user_updated": "کاربر با موفقیت بروزرسانی شد",
+ "settings.user_status_updated": "وضعیت کاربر تغییر کرد",
+ "settings.no_users": "کاربری ثبت نشده است",
+ "settings.add_currency": "افزودن ارز",
+ "settings.edit_currency": "ویرایش ارز",
+ "settings.currency_code": "کد ارز",
+ "settings.currency_name": "نام ارز",
+ "settings.currency_symbol": "نماد",
+ "settings.exchange_rate": "نرخ تبدیل به دلار",
+ "settings.exchange_rate_help": "نرخ تبدیل هر واحد این ارز به دلار آمریکا",
+ "settings.decimal_places": "تعداد اعشار",
+ "settings.currency_created": "ارز با موفقیت ایجاد شد",
+ "settings.currency_updated": "ارز با موفقیت بروزرسانی شد",
+ "settings.currency_status_updated": "وضعیت ارز تغییر کرد",
+ "settings.no_currencies": "ارزی ثبت نشده است",
+ "settings.full_settings_coming_soon": "صفحه کامل تنظیمات بهزودی اضافه میشود",
+ "employee.notes": "یادداشت ها",
+ "project.projects": "پروژه ها",
+ "task.gantt_no_tasks": "هیچ وظیفه ای برای نمایش وجود ندارد",
+ "task.gantt_day": "روز",
+ "task.gantt_week": "هفته",
+ "task.gantt_month": "ماه",
+ "task.gantt_year": "سال",
+ "task.gantt_all": "همه",
+ "task.gantt_loading": "در حال بارگذاری...",
+ "task.gantt_no_data": "هیچ داده ای برای نمایش وجود ندارد",
+ "task.gantt_today": "امروز",
+ "auth.login_subtitle": "به سامانه مدریرت پروژه ورنوا خوش آمدید",
+ "auth.logout_success": "خروج با موفقیت انجام شد",
+ "auth.register": "ثبت نام",
+ "auth.password_reset": "بازیابی رمز عبور",
+ "auth.password_reset_subtitle": "رمز عبور خود را وارد کنید",
+ "auth.password_reset_success": "رمز عبور شما با موفقیت تغییر کرد",
+ "auth.login_success": "به حساب کاربری خودتون خوش اومدید",
+ "auth.email_placeholder": "ایمیل",
+ "auth.password_placeholder": "رمز عبور",
+ "auth.register_success": "ثبت نام با موفقیت انجام شد",
+ "auth.register_subtitle": "حساب کاربری خود را بسازید",
+ "auth.register_email_exists": "این ایمیل قبلا ثبت شده است",
+ "auth.remember_me": "مرا به خاطر بسپار",
+ "auth.sign_in": "ورود",
+ "project.create_project": "ایجاد پروژه جدید",
+ "task.project": "پروژه",
+ "task.kanban_view": "نمایش کانبان",
+ "task.create_task": "ایجاد وظیفه جدید",
+ "task.due_date": "تاریخ پایان",
+ "task.estimated_hours": "ساعت تخمینی",
+ "task.task_name": "نام وظیفه",
+ "gantt_duration": "مدت زمان",
+
+ "expense.status": "وضعیت",
+ "expense.pending": "در انتظار",
+ "expense.approv": "تایید شده",
+ "expense.reject": "رد شده",
+ "expense.approved": "تأیید",
+
+ "employee.employees": "کارمندان",
+ "employee.search_placeholder": "جستجوی کارمند",
+ "employee.contract_type": "نوع قرارداد",
+ "employee.inactive": "وضعیت غیرفعال",
+ "employee.create_employee": "ایجاد کارمند جدید",
+ "employee.first_name": "نام",
+ "employee.last_name": "نام خانوادگی",
+ "employee.email": "ایمیل",
+ "employee.address": "آدرس",
+ "employee.active": "فعال",
+ "employee.hire_date": "تاریخ استخدام",
+ "employee.position": "سمت و عنوان شغلی",
+ "employee.department": "واحد فعالیت",
+ "employee.base_salary": "حقوق پایه",
+ "employee.national_code": "کد ملی",
+
+ "pettyCash.total_amount": "مجموع مبلغ تنخواه",
+ "pettyCash.total_expenses": "مجموع هزینه های تنخواه",
+ "pettyCash.total_remaining": "مجموع باقیمانده تنخواه",
+
+
+ "letters.create_letter": "نامه جدید",
+ "letters.attachments_help": "فایل های مربوط به نامه با فرمت مناسب pdf یا تصویر رو بارگزاری کنید."
+
+
+}
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php
new file mode 100644
index 00000000..7e9b8602
--- /dev/null
+++ b/resources/views/auth/login.blade.php
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+ {{ __('auth.login') }} - Vernova
+
+
+
+ @vite(['resources/css/app.css'])
+
+
+
+
+
+
+
+
Vernova
+
{{ __('auth.login_subtitle') }}
+
+
+
+
+ @if(session('success'))
+
+ {{ session('success') }}
+
+ @endif
+
+
+
+
+
+
© {{ date('Y') }} Vernova. {{ __('common.all_rights_reserved') }}
+
+
+
diff --git a/resources/views/components/currency-display.blade.php b/resources/views/components/currency-display.blade.php
new file mode 100644
index 00000000..43193034
--- /dev/null
+++ b/resources/views/components/currency-display.blade.php
@@ -0,0 +1,7 @@
+@props(['amount' => 0, 'currency' => null])
+
+@php
+ $formatted = \App\Helpers\CurrencyHelper::formatWithSymbol((float) $amount, $currency);
+@endphp
+
+merge(['class' => 'font-medium']) }}>{{ $formatted }}
diff --git a/resources/views/components/date-input.blade.php b/resources/views/components/date-input.blade.php
new file mode 100644
index 00000000..a79ac59b
--- /dev/null
+++ b/resources/views/components/date-input.blade.php
@@ -0,0 +1,55 @@
+@props(['name', 'value' => null, 'id' => null, 'class' => '', 'required' => false])
+
+@php
+$calendar = session('calendar', 'jalali');
+$inputId = $id ?? $name;
+$gregorianValue = null;
+$displayValue = '';
+
+// Normalize the value to a Gregorian Y-m-d string
+if ($value) {
+ if ($value instanceof \DateTimeInterface) {
+ $gregorianValue = $value->format('Y-m-d');
+ } elseif (is_string($value) && strlen($value) >= 10) {
+ $gregorianValue = substr($value, 0, 10);
+ }
+
+ if ($calendar === 'jalali' && $gregorianValue) {
+ try {
+ $displayValue = \Morilog\Jalali\Jalalian::fromDateTime($gregorianValue)->format('Y/m/d');
+ } catch (\Exception $e) {
+ $displayValue = $gregorianValue;
+ }
+ } else {
+ $displayValue = $gregorianValue ?? '';
+ }
+}
+
+$defaultClass = 'w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500 focus:border-teal-500';
+$cssClass = $class ?: $defaultClass;
+@endphp
+
+@if($calendar === 'jalali')
+
+
+
+
+@else
+
+@endif
diff --git a/resources/views/components/localized-date.blade.php b/resources/views/components/localized-date.blade.php
new file mode 100644
index 00000000..f9f7ec3e
--- /dev/null
+++ b/resources/views/components/localized-date.blade.php
@@ -0,0 +1,13 @@
+@props(['date' => null, 'format' => null, 'calendar' => null])
+
+@php
+ $displayDate = '';
+ if ($date) {
+ $displayDate = \App\Helpers\CalendarHelper::formatDate($date, $format, $calendar);
+ if (app()->getLocale() === 'fa') {
+ $displayDate = persian_num($displayDate);
+ }
+ }
+@endphp
+
+merge(['class' => '']) }}>{{ $displayDate }}
diff --git a/resources/views/components/priority-badge.blade.php b/resources/views/components/priority-badge.blade.php
new file mode 100644
index 00000000..b47db288
--- /dev/null
+++ b/resources/views/components/priority-badge.blade.php
@@ -0,0 +1,25 @@
+@props(['priority' => 'medium'])
+
+@php
+ $colors = [
+ 'low' => 'bg-gray-100 text-gray-600',
+ 'medium' => 'bg-amber-50 text-amber-600',
+ 'high' => 'bg-orange-50 text-orange-600',
+ 'urgent' => 'bg-rose-50 text-rose-600',
+ ];
+
+ $dots = [
+ 'low' => 'bg-gray-400',
+ 'medium' => 'bg-amber-400',
+ 'high' => 'bg-orange-500',
+ 'urgent' => 'bg-rose-500',
+ ];
+
+ $colorClass = $colors[$priority] ?? $colors['medium'];
+ $dotClass = $dots[$priority] ?? $dots['medium'];
+@endphp
+
+
+
+ {{ __('task.priority_' . $priority) }}
+
diff --git a/resources/views/components/status-badge.blade.php b/resources/views/components/status-badge.blade.php
new file mode 100644
index 00000000..14424e38
--- /dev/null
+++ b/resources/views/components/status-badge.blade.php
@@ -0,0 +1,39 @@
+@props(['type' => 'project', 'status' => ''])
+
+@php
+ $colors = [
+ 'project' => [
+ 'planning' => 'bg-blue-50 text-blue-700 border-blue-200',
+ 'active' => 'bg-emerald-50 text-emerald-700 border-emerald-200',
+ 'on_hold' => 'bg-amber-50 text-amber-700 border-amber-200',
+ 'completed' => 'bg-gray-50 text-gray-700 border-gray-200',
+ 'cancelled' => 'bg-red-50 text-red-700 border-red-200',
+ ],
+ 'task' => [
+ 'pending' => 'bg-gray-50 text-gray-700 border-gray-200',
+ 'in_progress' => 'bg-blue-50 text-blue-700 border-blue-200',
+ 'review' => 'bg-amber-50 text-amber-700 border-amber-200',
+ 'done' => 'bg-emerald-50 text-emerald-700 border-emerald-200',
+ 'cancelled' => 'bg-red-50 text-red-700 border-red-200',
+ ],
+ 'expense' => [
+ 'pending' => 'bg-amber-50 text-amber-700 border-amber-200',
+ 'approved' => 'bg-emerald-50 text-emerald-700 border-emerald-200',
+ 'rejected' => 'bg-red-50 text-red-700 border-red-200',
+ 'paid' => 'bg-blue-50 text-blue-700 border-blue-200',
+ ],
+ 'petty_cash' => [
+ 'pending' => 'bg-amber-50 text-amber-700 border-amber-200',
+ 'approved' => 'bg-emerald-50 text-emerald-700 border-emerald-200',
+ 'rejected' => 'bg-red-50 text-red-700 border-red-200',
+ 'settled' => 'bg-blue-50 text-blue-700 border-blue-200',
+ ],
+ ];
+
+ $colorClass = $colors[$type][$status] ?? 'bg-gray-50 text-gray-700 border-gray-200';
+ $label = __($type . '.status_' . $status);
+@endphp
+
+merge(['class' => "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium border {$colorClass}"]) }}>
+ {{ $label }}
+
diff --git a/resources/views/dashboard/index.blade.php b/resources/views/dashboard/index.blade.php
new file mode 100644
index 00000000..53d801ff
--- /dev/null
+++ b/resources/views/dashboard/index.blade.php
@@ -0,0 +1,242 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
{{ __('dashboard.title') }}
+
{{ $todayDisplay }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ __('dashboard.active_projects') }}
+
+ {{ app()->getLocale() === 'fa' ? persian_num($activeProjectsCount) : $activeProjectsCount }}
+
+
+
+
+ @if($projectTrend != 0)
+
+ {{ $projectTrend > 0 ? '↑' : '↓' }} {{ abs($projectTrend) }}% {{ __('dashboard.vs_last_month') }}
+
+ @endif
+
+
+
+
+
+
+
+
{{ __('dashboard.total_budget') }}
+
{{ $totalBudgetFormatted }}
+
+
+
+
+
+
+
+
+
+
+
{{ __('dashboard.pending_tasks') }}
+
+ {{ app()->getLocale() === 'fa' ? persian_num($pendingTasksCount) : $pendingTasksCount }}
+
+
+
+
+ @if($taskTrend != 0)
+
+ {{ $taskTrend > 0 ? '↑' : '↓' }} {{ abs($taskTrend) }}% {{ __('dashboard.vs_last_month') }}
+
+ @endif
+
+
+
+
+
+
+
+
{{ __('dashboard.active_employees') }}
+
+ {{ app()->getLocale() === 'fa' ? persian_num($activeEmployeesCount) : $activeEmployeesCount }}
+
+
+
+
+ @if($employeeTrend != 0)
+
+ {{ $employeeTrend > 0 ? '↑' : '↓' }} {{ abs($employeeTrend) }}% {{ __('dashboard.vs_last_month') }}
+
+ @endif
+
+
+
+
+
+
+
+
+
+ @forelse($projectsInProgress as $project)
+
+
+
+
+ {{ $project->name }}
+
+
+ {{ app()->getLocale() === 'fa' ? persian_num($project->progress) : $project->progress }}%
+
+
+
+
+ {{ $project->code }}
+ @if($project->manager)
+ {{ $project->manager->name }}
+ @endif
+
+
+
+ @empty
+
{{ __('dashboard.no_projects') }}
+ @endforelse
+
+
+
+
+
+
+
+
+
+
+
+
{{ __('dashboard.recent_activity') }}
+
+
+ @forelse($recentActivities as $activity)
+
+
+ {{ mb_strtoupper(mb_substr($activity->user?->name ?? '?', 0, 1)) }}
+
+
+
{{ $activity->user?->name }} — {{ $activity->description }}
+
{{ calendar_date($activity->created_at) }}
+
+
+ @empty
+
{{ __('dashboard.no_activity') }}
+ @endforelse
+
+
+
+
+
+
+
+
{{ __('dashboard.localization_info') }}
+
+
+
{{ __('dashboard.calendar_system') }}
+
{{ $currentCalendar === 'jalali' ? __('calendar.jalali') : __('calendar.gregorian') }}
+
{{ __('dashboard.today') }}: {{ $todayDisplay }}
+
+
+
{{ __('dashboard.number_format') }}
+
+ {{ app()->getLocale() === 'fa' ? persian_num(1234567.89) : number_format(1234567.89, 2) }}
+
+
+
+
{{ __('dashboard.currency_format') }}
+
{{ format_currency(150000000, $currencyCode) }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/documents/form.blade.php b/resources/views/documents/form.blade.php
new file mode 100644
index 00000000..f6b1ef8b
--- /dev/null
+++ b/resources/views/documents/form.blade.php
@@ -0,0 +1,55 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
{{ isset($document) ? __('document.edit_document') : __('document.upload_document') }}
+
+
+
+
+@endsection
diff --git a/resources/views/documents/index.blade.php b/resources/views/documents/index.blade.php
new file mode 100644
index 00000000..bd8a8491
--- /dev/null
+++ b/resources/views/documents/index.blade.php
@@ -0,0 +1,91 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ __('document.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('document.file_type') }}
+
+ {{ __('common.all') }}
+ @foreach($fileTypes as $type)
+ {{ __('document.type_' . $type) }}
+ @endforeach
+
+
+
+ {{ __('common.search') }}
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+ {{ __('document.title') }}
+ {{ __('document.file_type') }}
+ {{ __('document.project') }}
+ {{ __('document.uploaded_by') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($documents as $document)
+
+
+ {{ $document->title }}
+
+
+ {{ __('document.type_' . $document->file_type) }}
+
+ {{ $document->project?->name ?? '-' }}
+ {{ $document->uploader?->name ?? '-' }}
+
+
+
+
+ @empty
+
+ {{ __('document.no_documents') }}
+
+ @endforelse
+
+
+
+
+
+
+ {{ $documents->withQueryString()->links() }}
+
+
+@endsection
diff --git a/resources/views/documents/show.blade.php b/resources/views/documents/show.blade.php
new file mode 100644
index 00000000..490fec5a
--- /dev/null
+++ b/resources/views/documents/show.blade.php
@@ -0,0 +1,45 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
{{ $document->title }}
+
+
+
+
+
+
+
+
{{ __('document.title') }}
+
{{ $document->title }}
+
+
+
{{ __('document.file_type') }}
+
{{ __('document.type_' . $document->file_type) }}
+
+
+
{{ __('document.project') }}
+
{{ $document->project?->name ?? '-' }}
+
+
+
{{ __('document.uploaded_by') }}
+
{{ $document->uploader?->name ?? '-' }}
+
+ @if($document->description)
+
+
{{ __('document.description') }}
+
{{ $document->description }}
+
+ @endif
+
+
+
+@endsection
diff --git a/resources/views/employees/create.blade.php b/resources/views/employees/create.blade.php
new file mode 100644
index 00000000..868f54e0
--- /dev/null
+++ b/resources/views/employees/create.blade.php
@@ -0,0 +1,18 @@
+@extends('layouts.app')
+
+@section('title', __('employee.create'))
+
+@section('content')
+
+
+
{{ __('employee.create') }}
+
+
+
+
+ @csrf
+ @include('employees.form')
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/employees/edit.blade.php b/resources/views/employees/edit.blade.php
new file mode 100644
index 00000000..0503f784
--- /dev/null
+++ b/resources/views/employees/edit.blade.php
@@ -0,0 +1,19 @@
+@extends('layouts.app')
+
+@section('title', __('employee.edit'))
+
+@section('content')
+
+
+
{{ __('employee.edit') }}
+
+
+
+
+ @csrf
+ @method('PUT')
+ @include('employees.form')
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/employees/financials.blade.php b/resources/views/employees/financials.blade.php
new file mode 100644
index 00000000..9b15a409
--- /dev/null
+++ b/resources/views/employees/financials.blade.php
@@ -0,0 +1,113 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+
{{ __('employee.financials') }}
+
{{ $employee->first_name }} {{ $employee->last_name }}
+
+
+
+
+
+
+
+
{{ __('employee.totalPaid') }}
+
{{ format_currency((float)$totalPaid, $employee->currency?->code) }}
+
+
+
{{ __('employee.totalUnpaid') }}
+
{{ format_currency((float)$totalUnpaid, $employee->currency?->code) }}
+
+
+
+
+
+
+
+ {{ __('employee.type') }}
+
+ {{ __('common.all') }}
+ @foreach($financialTypes as $ft)
+ {{ __('employee.' . $ft) }}
+ @endforeach
+
+
+
+ {{ __('common.status') }}
+
+ {{ __('common.all') }}
+ {{ __('employee.isPaid') }}
+ {{ __('employee.isUnpaid') }}
+
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('employee.effectiveDate') }}
+ {{ __('employee.type') }}
+ {{ __('employee.amount') }}
+ {{ __('common.status') }}
+ {{ __('employee.description') }}
+
+
+
+ @forelse($financials as $fin)
+
+ {{ $fin->effective_date ? calendar_date($fin->effective_date) : '-' }}
+
+
+ {{ __('employee.' . $fin->type) }}
+
+
+
+ {{ in_array($fin->type, ['deduction', 'advance']) ? '-' : '+' }}{{ format_currency((float)$fin->amount, $fin->currency?->code) }}
+
+
+ @if(isset($fin->is_paid))
+
+ {{ $fin->is_paid ? __('employee.isPaid') : __('employee.isUnpaid') }}
+
+ @else
+ -
+ @endif
+
+ {{ $fin->description ?? '-' }}
+
+ @empty
+
+ {{ __('employee.noFinancials') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $financials->withQueryString()->links() }}
+
+
+@endsection
diff --git a/resources/views/employees/form.blade.php b/resources/views/employees/form.blade.php
new file mode 100644
index 00000000..1aee6ea4
--- /dev/null
+++ b/resources/views/employees/form.blade.php
@@ -0,0 +1,138 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($employee) ? __('employee.edit_employee') : __('employee.create_employee') }}
+
+
+
+
+
+ @csrf
+ @if(isset($employee))
+ @method('PUT')
+ @endif
+
+
+
+
+
{{ __('employee.first_name') }} *
+
+ @error('first_name')
{{ $message }}
@enderror
+
+
+
+
+
{{ __('employee.last_name') }} *
+
+ @error('last_name')
{{ $message }}
@enderror
+
+
+
+
+ {{ __('employee.contract_type') }} *
+
+ @foreach($contractTypes as $type)
+ contract_type ?? '') === $type ? 'selected' : '' }}>{{ __('employee.contract_' . $type) }}
+ @endforeach
+
+
+
+
+
+ {{ __('employee.hire_date') }} *
+
+
+
+
+
+ {{ __('employee.position') }}
+
+
+
+
+
+ {{ __('employee.department') }}
+
+
+
+
+
+ {{ __('employee.project') }}
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ project_id ?? '') == $project->id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('employee.base_salary') }}
+
+
+
+
+
+ {{ __('employee.salary_currency') }}
+
+ {{ __('common.select') }}
+ @foreach($currencies as $currency)
+ currency_id ?? '') == $currency->id ? 'selected' : '' }}>{{ $currency->code }} - {{ $currency->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('employee.phone') }}
+
+
+
+
+
+ {{ __('employee.email') }}
+
+
+
+
+
+ {{ __('employee.national_code') }}
+
+
+
+
+
+ {{ __('employee.notes') }}
+ {{ old('notes', $employee->notes ?? '') }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/employees/index.blade.php b/resources/views/employees/index.blade.php
new file mode 100644
index 00000000..6b4ab5a2
--- /dev/null
+++ b/resources/views/employees/index.blade.php
@@ -0,0 +1,78 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+ {{ __('common.search') }}
+
+
+
+ {{ __('employee.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('employee.contract_type') }}
+
+ {{ __('common.all') }}
+ @foreach($contractTypes as $type)
+ {{ __('employee.contract_' . $type) }}
+ @endforeach
+
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+ {{ $employees->withQueryString()->links() }}
+
+
+@endsection
diff --git a/resources/views/employees/show.blade.php b/resources/views/employees/show.blade.php
new file mode 100644
index 00000000..d3009fd6
--- /dev/null
+++ b/resources/views/employees/show.blade.php
@@ -0,0 +1,125 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
{{ $employee->full_name }}
+
{{ $employee->job_title ?? '-' }}
+
+
+
+
+
+
+
+
+
{{ __('employee.firstName') }}
+
{{ $employee->first_name }}
+
+
+
{{ __('employee.lastName') }}
+
{{ $employee->last_name }}
+
+
+
{{ __('employee.jobTitle') }}
+
{{ $employee->job_title ?? '-' }}
+
+
+
{{ __('employee.contractType') }}
+
{{ __('employee.' . $employee->contract_type) ?? $employee->contract_type ?? '-' }}
+
+
+
{{ __('employee.phone') }}
+
{{ $employee->phone ?? '-' }}
+
+
+
{{ __('employee.nationalCode') }}
+
{{ $employee->national_code ?? '-' }}
+
+
+
{{ __('employee.hireDate') }}
+
{{ $employee->hire_date ? calendar_date($employee->hire_date) : '-' }}
+
+
+
{{ __('employee.project') }}
+
{{ $employee->project?->name ?? '-' }}
+
+
+
{{ __('employee.baseSalary') }}
+
{{ $employee->base_salary ? format_currency((float)$employee->base_salary, $employee->currency?->code) : '-' }}
+
+
+
{{ __('employee.bankAccount') }}
+
{{ $employee->bank_account ?? '-' }}
+
+
+
{{ __('employee.status') }}
+
+ {{ __('employee.' . $employee->status) ?? $employee->status }}
+
+
+
+
+
+
+ @if($employee->workLogs->count() > 0)
+
+
+
+ @foreach($employee->workLogs->take(5) as $log)
+
+
+
{{ calendar_date($log->date) }}
+
{{ $log->project?->name ?? '-' }}
+
+
{{ persian_num(number_format($log->hours_worked, 1)) }} {{ __('employee.hoursWorked') }}
+
+ @endforeach
+
+
+ @endif
+
+
+ @if($employee->financials->count() > 0)
+
+
+
+ @foreach($employee->financials->take(5) as $fin)
+
+
+
{{ __('employee.' . $fin->type) }}
+
{{ $fin->effective_date ? calendar_date($fin->effective_date) : '-' }}
+
+
+ {{ format_currency((float)$fin->amount, $fin->currency?->code) }}
+
+
+ @endforeach
+
+
+ @endif
+
+@endsection
diff --git a/resources/views/employees/work-logs.blade.php b/resources/views/employees/work-logs.blade.php
new file mode 100644
index 00000000..17637c89
--- /dev/null
+++ b/resources/views/employees/work-logs.blade.php
@@ -0,0 +1,97 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+
{{ __('employee.workLogs') }}
+
{{ $employee->first_name }} {{ $employee->last_name }}
+
+
+
+
+
+
+
+
{{ __('employee.totalHours') }}
+
{{ persian_num(number_format($totalHours, 1)) }}
+
+
+
{{ __('employee.overtimeHours') }}
+
{{ persian_num(number_format($overtimeHours, 1)) }}
+
+
+
+
+
+
+
+ {{ __('common.from') }}
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+ {{ __('common.to') }}
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+ {{ __('employee.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('employee.date') }}
+ {{ __('employee.project') }}
+ {{ __('employee.hoursWorked') }}
+ {{ __('employee.overtimeHours') }}
+ {{ __('employee.description') }}
+
+
+
+ @forelse($workLogs as $log)
+
+ {{ calendar_date($log->date) }}
+ {{ $log->project?->name ?? '-' }}
+ {{ persian_num(number_format($log->hours_worked, 1)) }}
+
+ {{ $log->overtime_hours > 0 ? persian_num(number_format($log->overtime_hours, 1)) : '-' }}
+
+ {{ $log->description ?? '-' }}
+
+ @empty
+
+ {{ __('employee.noWorkLogs') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $workLogs->withQueryString()->links() }}
+
+
+@endsection
diff --git a/resources/views/expenses/expense-form.blade.php b/resources/views/expenses/expense-form.blade.php
new file mode 100644
index 00000000..30afb260
--- /dev/null
+++ b/resources/views/expenses/expense-form.blade.php
@@ -0,0 +1,126 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($expense) ? __('expense.edit_expense') : __('expense.create_expense') }}
+
+
+
+
+
+ @csrf
+ @if(isset($expense))
+ @method('PUT')
+ @endif
+
+
+
+
+ {{ __('expense.title') }} *
+
+
+
+
+
+ {{ __('expense.project') }} *
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ project_id ?? '') == $project->id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('expense.category') }} *
+
+ @foreach($categories as $cat)
+ category ?? '') === $cat ? 'selected' : '' }}>{{ __('expense.category_' . $cat) }}
+ @endforeach
+
+
+
+
+
+ {{ __('expense.amount') }} *
+
+
+
+
+
+
{{ __('expense.currency') }} *
+
+ @foreach($currencies as $currency)
+ currency_id ?? $baseCurrency?->id ?? '') == $currency->id ? 'selected' : '' }}>
+ {{ $currency->code }} - {{ $currency->name }}
+ @if($currency->id === $baseCurrency?->id) ({{ __('expense.base_currency') }})@endif
+
+ @endforeach
+
+
{{ __('expense.currency_conversion_note') }}
+
+
+
+
+ {{ __('expense.date') }} *
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500" required>
+
+
+
+
+ {{ __('expense.invoice_number') }}
+
+
+
+
+
+
{{ __('expense.receipt') }}
+ @if(isset($expense) && $expense?->receipt_path)
+
+ @endif
+
+
+
+
+
+ {{ __('expense.description') }}
+ {{ old('description', $expense?->description ?? '') }}
+
+
+
+
+
{{ __('attachment.additional_files') }}
+
+
{{ __('attachment.max_files_help') }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/expenses/expense-index.blade.php b/resources/views/expenses/expense-index.blade.php
new file mode 100644
index 00000000..e273c81d
--- /dev/null
+++ b/resources/views/expenses/expense-index.blade.php
@@ -0,0 +1,167 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+ {{ __('expense.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('expense.category') }}
+
+ {{ __('common.all') }}
+ @foreach($categories as $cat)
+ {{ __('expense.category_' . $cat) }}
+ @endforeach
+
+
+
+ {{ __('expense.status') }}
+
+ {{ __('common.all') }}
+ @foreach($statuses as $status)
+ {{ __('expense.status_' . $status) }}
+ @endforeach
+
+
+
+ {{ __('expense.from_date') }}
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+ {{ __('expense.to_date') }}
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('expense.title') }}
+ {{ __('expense.project') }}
+ {{ __('expense.amount') }}
+ {{ __('expense.category') }}
+ {{ __('expense.date') }}
+ {{ __('expense.status') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($expenses as $expense)
+
+
+
+
+ {{ $expense->title ?? '-' }}
+
+ {{-- Attachment indicator --}}
+ @if($expense->attachments_count > 0 || $expense->receipt_path)
+
+
+ @if($expense->attachments_count > 0)
+ {{ $expense->attachments_count }}
+ @endif
+
+ @endif
+
+
+ {{ $expense->project?->name }}
+
+ {{ format_currency((float)$expense->amount, $expense->currency?->code) }}
+ @if($expense->original_amount)
+ ({{ format_currency((float)$expense->original_amount, $expense->currency?->code) }})
+ @endif
+
+ {{ __('expense.category_' . $expense->category) }}
+ {{ $expense->expense_date ? calendar_date($expense->expense_date) : '-' }}
+
+
+ {{ $expense->status_label }}
+
+
+
+
+ {{-- View button --}}
+
+
+
+ {{-- Edit button --}}
+
+
+
+ @if($expense->status === 'pending')
+
+
+
+
+
+
+ @endif
+
+
+
+ @empty
+
+ {{ __('expense.no_expenses') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $expenses->withQueryString()->links() }}
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/expenses/expense-show.blade.php b/resources/views/expenses/expense-show.blade.php
new file mode 100644
index 00000000..91ab6ccc
--- /dev/null
+++ b/resources/views/expenses/expense-show.blade.php
@@ -0,0 +1,292 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
{{ $expense->title }}
+
+ {{ $expense->status_label }}
+
+
+
+
+ {{ __('common.edit') }}
+
+ @if($expense->status === 'pending')
+
+
+ {{ __('expense.approve') }}
+
+
+
+ {{ __('expense.reject') }}
+
+ @endif
+
+
+
+
+
+
+
+
+
+
{{ __('expense.project') }}
+
{{ $expense->project?->name ?? '-' }}
+
+
+
{{ __('expense.category') }}
+
{{ __('expense.category_' . $expense->category) }}
+
+
+
{{ __('expense.amount') }}
+
{{ format_currency((float)$expense->amount, $expense->currency?->code) }}
+ @if($expense->original_amount && $expense->original_amount != $expense->amount)
+
{{ __('expense.original_amount') ?? 'مبلغ اصلی' }}: {{ format_currency((float)$expense->original_amount, $expense->currency?->code) }}
+ @endif
+
+
+
{{ __('expense.date') }}
+
{{ $expense->expense_date ? calendar_date($expense->expense_date) : '-' }}
+
+
+
+
+
+ @if($expense->invoice_number)
+
+
{{ __('expense.invoice_number') }}
+
{{ $expense->invoice_number }}
+
+ @endif
+
+
{{ __('expense.requested_by') ?? 'درخواستکننده' }}
+
{{ $expense->requestedBy?->name ?? '-' }}
+
+ @if($expense->approvedBy)
+
+
{{ __('expense.approved_by') ?? 'تأییدکننده' }}
+
{{ $expense->approvedBy?->name }}
+
+ @endif
+ @if($expense->approval_date)
+
+
{{ __('expense.approval_date') ?? 'تاریخ تأیید' }}
+
{{ calendar_date($expense->approval_date) }}
+
+ @endif
+ @if($expense->pettyCash)
+
+ @endif
+
+
+
+
+ @if($expense->description)
+
+
{{ __('expense.description') }}
+
{{ $expense->description }}
+
+ @endif
+
+
+
+ @if($expense->receipt_path)
+
+
+
+ {{ __('expense.receipt') }}
+
+
+ @if(str_ends_with(strtolower($expense->receipt_path), '.pdf'))
+
+ @else
+
+
+
+ @endif
+
+
{{ basename($expense->receipt_path) }}
+
+
+
+ {{ __('letter.download') }}
+
+
+
+ @endif
+
+
+
+
+
+
+ {{ __('attachment.attachments') }}
+ @if($expense->attachments->count() > 0)
+ {{ $expense->attachments->count() }}
+ @endif
+
+
+
+ {{ __('attachment.add_file') }}
+
+
+
+
+
+
+ @if($expense->attachments->count() > 0)
+
+ @foreach($expense->attachments as $attachment)
+
+ @if($attachment->is_image)
+
+
+
+ @elseif($attachment->is_pdf)
+
+ @else
+
+ @endif
+
+
+
+
+
+
+
+ @can('delete', $attachment)
+
+
+
+ @endcan
+
+
+ @endforeach
+
+ @else
+
+
+
{{ __('attachment.no_attachments') }}
+
+ @endif
+
+
+
+
+
+
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/expenses/form.blade.php b/resources/views/expenses/form.blade.php
new file mode 100644
index 00000000..3d177180
--- /dev/null
+++ b/resources/views/expenses/form.blade.php
@@ -0,0 +1,125 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($expense) ? __('expense.edit_expense') : __('expense.create_expense') }}
+
+
+
+
+
+ @csrf
+ @if(isset($expense))
+ @method('PUT')
+ @endif
+
+
+
+
+ {{ __('expense.title') }} *
+
+
+
+
+
+ {{ __('expense.project') }} *
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ project_id ?? '') == $project->id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('expense.category') }} *
+
+ @foreach($categories as $cat)
+ category ?? '') === $cat ? 'selected' : '' }}>{{ __('expense.category_' . $cat) }}
+ @endforeach
+
+
+
+
+
+ {{ __('expense.amount') }} *
+
+
+
+
+
+
{{ __('expense.currency') }} *
+
+ @foreach($currencies as $currency)
+ currency_id ?? $baseCurrency?->id ?? '') == $currency->id ? 'selected' : '' }}>
+ {{ $currency->code }} - {{ $currency->name }}
+ @if($currency->id === $baseCurrency?->id) ({{ __('expense.base_currency') }})@endif
+
+ @endforeach
+
+
{{ __('expense.currency_conversion_note') }}
+
+
+
+
+ {{ __('expense.date') }} *
+
+
+
+
+
+ {{ __('expense.invoice_number') }}
+
+
+
+
+
+
{{ __('expense.receipt') }}
+ @if(isset($expense) && $expense?->receipt_path)
+
+ @endif
+
+
+
+
+
+ {{ __('expense.description') }}
+ {{ old('description', $expense?->description ?? '') }}
+
+
+
+
+
{{ __('attachment.additional_files') }}
+
+
{{ __('attachment.max_files_help') }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/expenses/index.blade.php b/resources/views/expenses/index.blade.php
new file mode 100644
index 00000000..f16cebb4
--- /dev/null
+++ b/resources/views/expenses/index.blade.php
@@ -0,0 +1,140 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+ {{ __('expense.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('expense.category') }}
+
+ {{ __('common.all') }}
+ @foreach($categories as $cat)
+ {{ __('expense.category_' . $cat) }}
+ @endforeach
+
+
+
+ {{ __('expense.status') }}
+
+ {{ __('common.all') }}
+ @foreach($statuses as $status)
+ {{ __('expense.status_' . $status) }}
+ @endforeach
+
+
+
+ {{ __('expense.from_date') }}
+
+
+
+ {{ __('expense.to_date') }}
+
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('expense.title') }}
+ {{ __('expense.project') }}
+ {{ __('expense.amount') }}
+ {{ __('expense.category') }}
+ {{ __('expense.date') }}
+ {{ __('expense.status') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($expenses as $expense)
+
+
+ {{ $expense->title }}
+
+ {{ $expense->project?->name }}
+
+ {{ format_currency((float)$expense->amount, $expense->currency?->code) }}
+ @if($expense->original_amount)
+ ({{ format_currency((float)$expense->original_amount, $expense->currency?->code) }})
+ @endif
+
+ {{ __('expense.category_' . $expense->category) }}
+ {{ calendar_date($expense->expense_date) }}
+
+
+ {{ $expense->status_label }}
+
+
+
+
+ @if($expense->status === 'pending')
+ {{ __('expense.approve') }}
+ {{ __('expense.reject') }}
+ @endif
+
+
+
+ @empty
+
+ {{ __('expense.no_expenses') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $expenses->withQueryString()->links() }}
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/expenses/show.blade.php b/resources/views/expenses/show.blade.php
new file mode 100644
index 00000000..02239d72
--- /dev/null
+++ b/resources/views/expenses/show.blade.php
@@ -0,0 +1,262 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
{{ $expense->title }}
+
+ {{ $expense->status_label }}
+
+
+
+ {{ __('common.edit') }}
+
+ @if($expense->status === 'pending')
+
+ {{ __('expense.approve') }}
+
+
+ {{ __('expense.reject') }}
+
+ @endif
+
+
+
+
+
+
+
+
+
+
{{ __('expense.project') }}
+
{{ $expense->project?->name ?? '-' }}
+
+
+
{{ __('expense.category') }}
+
{{ __('expense.category_' . $expense->category) }}
+
+
+
{{ __('expense.amount') }}
+
{{ format_currency((float)$expense->amount, $expense->currency?->code) }}
+ @if($expense->original_amount && $expense->original_amount != $expense->amount)
+
({{ format_currency((float)$expense->original_amount, $expense->currency?->code) }})
+ @endif
+
+
+
{{ __('expense.date') }}
+
{{ $expense->expense_date ? calendar_date($expense->expense_date) : '-' }}
+
+
+
+
+
+ @if($expense->invoice_number)
+
+
{{ __('expense.invoice_number') }}
+
{{ $expense->invoice_number }}
+
+ @endif
+
+
{{ __('expense.requested_by') ?? __('common.project') }}
+
{{ $expense->requestedBy?->name ?? '-' }}
+
+ @if($expense->approvedBy)
+
+
{{ __('expense.approved_by') ?? 'تأییدکننده' }}
+
{{ $expense->approvedBy?->name }}
+
+ @endif
+ @if($expense->approval_date)
+
+
{{ __('expense.approval_date') ?? 'تاریخ تأیید' }}
+
{{ calendar_date($expense->approval_date) }}
+
+ @endif
+
+
+
+
+ @if($expense->description)
+
+
{{ __('expense.description') }}
+
{{ $expense->description }}
+
+ @endif
+
+
+
+ @if($expense->receipt_path)
+
+
{{ __('expense.receipt') }}
+
+ @if(str_ends_with(strtolower($expense->receipt_path), '.pdf'))
+
+ @else
+
+
+
+ @endif
+
+
{{ basename($expense->receipt_path) }}
+
+
+ {{ __('letter.download') }}
+
+
+
+ @endif
+
+
+
+
+
{{ __('attachment.attachments') }}
+
+ + {{ __('attachment.add_file') }}
+
+
+
+
+
+
+ @csrf
+
+
+
+
+
+
+ {{ __('common.upload') }}
+
+
+ {{ __('attachment.max_files_help') }}
+
+
+
+
+
+ @forelse($expense->attachments as $attachment)
+
+ @if($attachment->is_image)
+
+
+
+ @elseif($attachment->is_pdf)
+
+ @else
+
+ @endif
+
+
+
{{ $attachment->file_name }}
+
{{ $attachment->formatted_size }} • {{ $attachment->created_at?->format('Y/m/d H:i') }}
+
+
+
+
+ @empty
+
+
+
{{ __('attachment.no_attachments') }}
+
+ @endforelse
+
+
+
+
+
+
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/help/index.blade.php b/resources/views/help/index.blade.php
new file mode 100644
index 00000000..ca5351b1
--- /dev/null
+++ b/resources/views/help/index.blade.php
@@ -0,0 +1,43 @@
+@extends('layouts.app')
+
+@section('title', __('nav.help'))
+
+@section('content')
+
+
+ {{ __('nav.help') }}
+
+
+
+
+
Vernova - راهنمای سیستم
+
سیستم مدیریت پروژه چندمستاجری با پشتیبانی از چندزبانی، چندتقویمی و چندارزی.
+
+
+
+
🟢 پروژهها
+
مدیریت پروژهها، وضعیت، پیشرفت و بودجه
+
+
+
+
📋 وظایف
+
مدیریت وظایف با نمای لیست و کانبان، اولویتبندی و تخصیص
+
+
+
+
💰 هزینهها
+
ثبت و تایید هزینهها با پشتیبانی از چند ارز
+
+
+
+
👥 کارکنان
+
مدیریت پرسنل، قراردادها و سوابق کاری
+
+
+
+
📦 انبار
+
مدیریت اقلام، انبارها و تراکنشهای انبارداری
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory-transactions/form.blade.php b/resources/views/inventory-transactions/form.blade.php
new file mode 100644
index 00000000..b3062a08
--- /dev/null
+++ b/resources/views/inventory-transactions/form.blade.php
@@ -0,0 +1,199 @@
+@extends('layouts.app')
+
+@section('title', __('inventory.new_transaction'))
+
+@section('content')
+
+
+
+
+
+
+
{{ __('inventory.new_transaction') }}
+
+
+
+
+
+ @csrf
+
+
+
+
+
+ {{ __('inventory.transaction_type') }} *
+
+
+ @foreach($types as $key => $type)
+ @php
+ $tk = 'inventory.type_' . $type;
+ $tl = __($tk);
+ if ($tl === $tk) $tl = $type;
+ @endphp
+ {{ $tl }}
+ @endforeach
+
+ @error('type')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('inventory.date') }} *
+
+
+ value="{{ old('date', date('Y-m-d')) }}"
+ class="w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500"
+ required>
+ @error('date')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+
+
+ {{ __('inventory.name') }} *
+
+
+ {{ __('common.select') }}
+ @foreach($items as $item)
+ id ? 'selected' : '' }}>{{ $item->name }}
+ @endforeach
+
+ @error('item_id')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('warehouse.warehouses') }} *
+
+
+ {{ __('common.select') }}
+ @foreach($warehouses as $wh)
+ id ? 'selected' : '' }}>{{ $wh->name }}
+ @endforeach
+
+ @error('warehouse_id')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+
+
+ {{ __('inventory.quantity') }} *
+
+
+ @error('quantity')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('expense.project') }}
+
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+ @error('project_id')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+
+
+ {{ __('inventory.unit_price') }}
+
+
+ @error('unit_price')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('expense.currency') }}
+
+
+ {{ __('common.select') }}
+ @foreach($currencies as $currency)
+ id ? 'selected' : '' }}>{{ $currency->name }} ({{ $currency->code }})
+ @endforeach
+
+ @error('currency_id')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+
+ {{ __('inventory.reference') }}
+
+
+ @error('reference')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+ {{ __('common.description') }}
+
+
{{ old('description') }}
+ @error('description')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory-transactions/index.blade.php b/resources/views/inventory-transactions/index.blade.php
new file mode 100644
index 00000000..f1d5c898
--- /dev/null
+++ b/resources/views/inventory-transactions/index.blade.php
@@ -0,0 +1,106 @@
+@extends('layouts.app')
+
+@section('title', __('inventory.transactions'))
+
+@section('content')
+
+
+
+
+
+
+
+
+ {{ __('common.all') }} {{ __('inventory.transaction_type') }}
+ @foreach($types as $t)
+ @php
+ $tk = 'inventory.type_' . $t;
+ $tl = __($tk);
+ if ($tl === $tk) $tl = $t;
+ @endphp
+ {{ $tl }}
+ @endforeach
+
+
+ {{ __('common.all') }} {{ __('inventory.items') }}
+ @foreach($items as $item)
+ id ? 'selected' : '' }}>{{ $item->name }}
+ @endforeach
+
+
+ {{ __('common.all') }} {{ __('warehouse.warehouses') }}
+ @foreach($warehouses as $wh)
+ id ? 'selected' : '' }}>{{ $wh->name }}
+ @endforeach
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('inventory.transaction_type') }}
+ {{ __('inventory.name') }}
+ {{ __('warehouse.warehouses') }}
+ {{ __('inventory.quantity') }}
+ {{ __('inventory.date') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($transactions as $tr)
+
+
+ @php
+ $ttk = 'inventory.type_' . $tr->type;
+ $ttl = __($ttk);
+ if ($ttl === $ttk) $ttl = $tr->type;
+ @endphp
+
+ {{ $ttl }}
+
+
+ {{ $tr->item?->name ?? '-' }}
+ {{ $tr->warehouse?->name ?? '-' }}
+ {{ $tr->quantity }}
+ {{ $tr->date ? calendar_date($tr->date) : '-' }}
+
+
+
+
+ @empty
+
+ {{ __('inventory.no_transactions') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $transactions->withQueryString()->links() }}
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory-transactions/show.blade.php b/resources/views/inventory-transactions/show.blade.php
new file mode 100644
index 00000000..1b134d85
--- /dev/null
+++ b/resources/views/inventory-transactions/show.blade.php
@@ -0,0 +1,103 @@
+@extends('layouts.app')
+
+@section('title', __('inventory.transactions') . ' #' . $inventoryTransaction->id)
+
+@section('content')
+
+
+
+
+
+
+
+
+
{{ __('inventory.transactions') }} #{{ $inventoryTransaction->id }}
+
+
+
+ @csrf @method('DELETE')
+
+ {{ __('common.delete') }}
+
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.transaction_type') }}
+ @php
+ $ttk = 'inventory.type_' . $inventoryTransaction->type;
+ $ttl = __($ttk);
+ if ($ttl === $ttk) $ttl = $inventoryTransaction->type;
+ @endphp
+
+ {{ $ttl }}
+
+
+
+
{{ __('inventory.name') }}
+
{{ $inventoryTransaction->item?->name ?? '-' }}
+
+
+
{{ __('warehouse.warehouses') }}
+
{{ $inventoryTransaction->warehouse?->name ?? '-' }}
+
+
+
{{ __('inventory.quantity') }}
+
{{ $inventoryTransaction->quantity }}
+
+
+
{{ __('inventory.unit_price') }}
+
{{ $inventoryTransaction->unit_price ? number_format($inventoryTransaction->unit_price) : '-' }}
+
+
+
{{ __('inventory.date') }}
+
{{ $inventoryTransaction->date ? calendar_date($inventoryTransaction->date) : '-' }}
+
+
+
+ @if($inventoryTransaction->description)
+
+
{{ __('common.description') }}
+
{{ $inventoryTransaction->description }}
+
+ @endif
+
+
+
+
+
+ @if($inventoryTransaction->project)
+
+ @endif
+
+ @if($inventoryTransaction->reference)
+
+
{{ __('inventory.reference') }}
+
{{ $inventoryTransaction->reference }}
+
+ @endif
+
+ @if($inventoryTransaction->total_price)
+
+
{{ __('inventory.total_price') }}
+
{{ number_format($inventoryTransaction->total_price) }}
+ @if($inventoryTransaction->currency)
+
{{ $inventoryTransaction->currency->code }}
+ @endif
+
+ @endif
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory/create-item.blade.php b/resources/views/inventory/create-item.blade.php
new file mode 100644
index 00000000..e20e0df5
--- /dev/null
+++ b/resources/views/inventory/create-item.blade.php
@@ -0,0 +1,71 @@
+@extends('layouts.app')
+
+@section('title', 'افزودن قلم جدید')
+
+@section('content')
+
+
+
+
+
+ @csrf
+
+
+
نام قلم *
+
+ @error('name')
{{ $message }}
@enderror
+
+
+
+
+
+
+
+
+
+ توضیحات
+ {{ old('description') }}
+
+
+
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory/create-transaction.blade.php b/resources/views/inventory/create-transaction.blade.php
new file mode 100644
index 00000000..9e34063a
--- /dev/null
+++ b/resources/views/inventory/create-transaction.blade.php
@@ -0,0 +1,77 @@
+@extends('layouts.app')
+
+@section('title', 'حواله جدید')
+
+@section('content')
+
+
+
+
+
+ @csrf
+
+
+ نوع حواله *
+
+ ورود به انبار (رسید)
+ خروج از انبار (حواله)
+
+
+
+
+
+ قلم *
+
+ انتخاب...
+ @foreach($items as $item)
+ id ? 'selected' : '' }}>{{ $item->name }} {{ $item->code ? '(' . $item->code . ')' : '' }}
+ @endforeach
+
+
+
+ انبار *
+
+ انتخاب...
+ @foreach($warehouses as $warehouse)
+ id ? 'selected' : '' }}>{{ $warehouse->name }}
+ @endforeach
+
+
+
+
+
+
+ مقدار *
+
+
+
+ پروژه
+
+ انتخاب...
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+
+
+ توضیحات
+ {{ old('notes') }}
+
+
+
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory/create-warehouse.blade.php b/resources/views/inventory/create-warehouse.blade.php
new file mode 100644
index 00000000..4aaa5fde
--- /dev/null
+++ b/resources/views/inventory/create-warehouse.blade.php
@@ -0,0 +1,48 @@
+@extends('layouts.app')
+
+@section('title', 'افزودن انبار جدید')
+
+@section('content')
+
+
+
+
+
+ @csrf
+
+
+
نام انبار *
+
+ @error('name')
{{ $message }}
@enderror
+
+
+
+ موقعیت
+
+
+
+
+ توضیحات
+ {{ old('description') }}
+
+
+
+
+ فعال
+
+
+
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory/form.blade.php b/resources/views/inventory/form.blade.php
new file mode 100644
index 00000000..da0a6a67
--- /dev/null
+++ b/resources/views/inventory/form.blade.php
@@ -0,0 +1,132 @@
+@extends('layouts.app')
+
+@section('title', isset($item) ? __('inventory.edit_item') : __('inventory.add_item'))
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($item) ? __('inventory.edit_item') : __('inventory.add_item') }}
+
+
+
+
+
+
+ @csrf
+ @if(isset($item))
+ @method('PUT')
+ @endif
+
+
+
+
+
+ {{ __('inventory.name') }} *
+
+
+ @error('name')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('inventory.code') }}
+
+
+ @error('code')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+
+
+ {{ __('inventory.category') }}
+
+
+ {{ __('common.select') }}
+ @foreach($categories as $cat)
+ category : '') == $cat ? 'selected' : '' }}>
+ @php
+ $ck = 'inventory.category_' . $cat;
+ $cl = __($ck);
+ if ($cl === $ck) $cl = $cat;
+ @endphp
+ {{ $cl }}
+
+ @endforeach
+
+ @error('category')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('inventory.unit') }}
+
+
+ {{ __('common.select') }}
+ @foreach($units as $u)
+ unit : '') == $u ? 'selected' : '' }}>
+ {{ __('inventory.unit_' . $u) }}
+
+ @endforeach
+
+ @error('unit')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+
+ {{ __('common.description') }}
+
+
{{ old('description', isset($item) ? $item->description : '') }}
+ @error('description')
+
{{ $message }}
+ @enderror
+
+
+
+
+ is_active : true) ? 'checked' : '' }}
+ class="rounded border-gray-300 text-teal-600 focus:ring-teal-500">
+ {{ __('common.active') }}
+
+
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory/index.blade.php b/resources/views/inventory/index.blade.php
new file mode 100644
index 00000000..19f6965b
--- /dev/null
+++ b/resources/views/inventory/index.blade.php
@@ -0,0 +1,157 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
{{ __('inventory.inventory') ?? 'انبار' }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.items') }}
+
{{ persian_num($totalItems) }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.active_items') ?? 'اقلام فعال' }}
+
{{ persian_num($activeItems) }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.warehouses') }}
+
{{ persian_num($totalWarehouses) }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.low_stock_items') ?? 'موجودی کم' }}
+
{{ persian_num($lowStockItems->count()) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ @if($recentTransactions->count() > 0)
+
+ @foreach($recentTransactions as $txn)
+
+
+ {{ __('inventory.type_' . $txn->type) }}
+
+
+
{{ $txn->item?->name ?? '-' }}
+
{{ $txn->warehouse?->name ?? '-' }}
+
+
+
+ {{ $txn->type === 'out' ? '-' : '+' }}{{ persian_num(number_format($txn->quantity, 2)) }}
+
+
{{ $txn->created_at ? calendar_date($txn->created_at) : '' }}
+
+
+ @endforeach
+
+ @else
+
+ {{ __('inventory.no_transactions') ?? 'تراکنشی ثبت نشده است' }}
+
+ @endif
+
+
+
+
+
+ @if($lowStockItems->count() > 0)
+
+ @foreach($lowStockItems as $item)
+
+
+
+
{{ $item->name }}
+
{{ $item->code ?? '-' }} · {{ $item->unit ?? '-' }}
+
+
+
+ {{ persian_num(number_format($item->current_stock, 2)) }}
+
+
{{ __('inventory.min_stock') ?? 'حداقل' }}: {{ persian_num(number_format($item->min_stock ?? 0, 2)) }}
+
+
+ @endforeach
+
+ @else
+
+
+
{{ __('inventory.all_stock_ok') ?? 'همه اقلام موجودی کافی دارند' }}
+
+ @endif
+
+
+
+@endsection
diff --git a/resources/views/inventory/inventory-index.blade.php b/resources/views/inventory/inventory-index.blade.php
new file mode 100644
index 00000000..b07aebeb
--- /dev/null
+++ b/resources/views/inventory/inventory-index.blade.php
@@ -0,0 +1,138 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
{{ __('inventory.inventory') ?? 'انبار' }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.items') }}
+
{{ persian_num($totalItems) }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.active_items') ?? 'اقلام فعال' }}
+
{{ persian_num($activeItems) }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.warehouses') }}
+
{{ persian_num($totalWarehouses) }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.low_stock_items') ?? 'موجودی کم' }}
+
{{ persian_num($lowStockItems->count()) }}
+
+
+
+
+
+
+
+
+
+ @if($recentTransactions->count() > 0)
+
+ @foreach($recentTransactions as $txn)
+
+
+ {{ __('inventory.type_' . $txn->type) }}
+
+
+
{{ $txn->item?->name ?? '-' }}
+
{{ $txn->warehouse?->name ?? '-' }}
+
+
+
+ {{ $txn->type === 'out' ? '-' : '+' }}{{ persian_num(number_format($txn->quantity, 2)) }}
+
+
{{ $txn->created_at ? calendar_date($txn->created_at) : '' }}
+
+
+ @endforeach
+
+ @else
+
+ {{ __('inventory.no_transactions') }}
+
+ @endif
+
+
+
+
+
+ @if($lowStockItems->count() > 0)
+
+ @foreach($lowStockItems as $item)
+
+
+
+
{{ $item->name }}
+
{{ $item->code ?? '-' }} · {{ $item->unit ?? '-' }}
+
+
+
+ {{ persian_num(number_format($item->current_stock, 2)) }}
+
+
{{ __('inventory.min_stock') ?? 'حداقل' }}: {{ persian_num(number_format($item->min_stock ?? 0, 2)) }}
+
+
+ @endforeach
+
+ @else
+
+
+
{{ __('inventory.all_stock_ok') ?? 'همه اقلام موجودی کافی دارند' }}
+
+ @endif
+
+
+
+@endsection
diff --git a/resources/views/inventory/item-form.blade.php b/resources/views/inventory/item-form.blade.php
new file mode 100644
index 00000000..2308c0e4
--- /dev/null
+++ b/resources/views/inventory/item-form.blade.php
@@ -0,0 +1,116 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($item) && $item ? __('inventory.edit_item') : __('inventory.create_item') }}
+
+
+
+
+
+ @csrf
+ @if(isset($item) && $item)
+ @method('PUT')
+ @endif
+
+
+
+
+
{{ __('inventory.item_name') }} *
+
+ @error('name')
+
{{ $message }}
+ @enderror
+
+
+
+
+
{{ __('inventory.item_code') }}
+
+ @error('code')
+
{{ $message }}
+ @enderror
+
+
+
+
+
{{ __('inventory.unit') }} *
+
+ unit : '') === 'عدد' ? 'selected' : '' }}>عدد
+ unit : '') === 'کیلوگرم' ? 'selected' : '' }}>کیلوگرم
+ unit : '') === 'متر' ? 'selected' : '' }}>متر
+ unit : '') === 'مترمربع' ? 'selected' : '' }}>مترمربع
+ unit : '') === 'لیتر' ? 'selected' : '' }}>لیتر
+ unit : '') === 'رول' ? 'selected' : '' }}>رول
+ unit : '') === 'بسته' ? 'selected' : '' }}>بسته
+ unit : '') === 'دستگاه' ? 'selected' : '' }}>دستگاه
+ unit : '') === 'تن' ? 'selected' : '' }}>تن
+ unit : '') === 'ساعت' ? 'selected' : '' }}>ساعت
+
+ @error('unit')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('inventory.category') }}
+
+
+ @foreach($categories as $cat)
+
+ @endforeach
+
+
+
+
+
+
{{ __('inventory.min_stock') ?? 'حداقل موجودی' }}
+
+
{{ __('inventory.min_stock_help') ?? 'هشدار کاهش موجودی' }}
+
+
+
+
+
+ is_active : true) ? 'checked' : '' }}
+ class="sr-only peer">
+
+ {{ __('inventory.active') }}
+
+
+
+
+
+ {{ __('inventory.description') ?? 'توضیحات' }}
+ {{ old('description', isset($item) ? $item->description : '') }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/inventory/items.blade.php b/resources/views/inventory/items.blade.php
new file mode 100644
index 00000000..5607aa24
--- /dev/null
+++ b/resources/views/inventory/items.blade.php
@@ -0,0 +1,120 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+ {{ __('common.search') }}
+
+
+
+ {{ __('inventory.category') }}
+
+ {{ __('common.all') }}
+ @foreach($categories as $cat)
+ {{ $cat }}
+ @endforeach
+
+
+
+ {{ __('common.status') }}
+
+ {{ __('common.all') }}
+ {{ __('inventory.active') }}
+ {{ __('inventory.inactive') }}
+
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('inventory.item_code') }}
+ {{ __('inventory.item_name') }}
+ {{ __('inventory.category') }}
+ {{ __('inventory.unit') }}
+ {{ __('inventory.current_stock') }}
+ {{ __('common.status') }}
+
+
+
+
+ @forelse($items as $item)
+
+ {{ $item->code ?? '-' }}
+ {{ $item->name }}
+ {{ $item->category ?? '-' }}
+ {{ $item->unit ?? '-' }}
+
+ @php
+ $stock = $item->current_stock ?? 0;
+ $minStock = $item->min_stock ?? 0;
+ @endphp
+
+ {{ persian_num(number_format($stock, 2)) }}
+
+ @if($stock <= $minStock && $stock > 0)
+
+ @elseif($stock <= 0)
+
+ @endif
+
+
+
+ {{ $item->is_active ? __('inventory.active') : __('inventory.inactive') }}
+
+
+
+
+
+
+
+ @if($item->inventoryTransactions()->count() === 0)
+
+ @csrf @method('DELETE')
+
+
+
+
+ @endif
+
+
+
+ @empty
+
+ {{ __('inventory.no_items') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $items->withQueryString()->links() }}
+
+
+@endsection
diff --git a/resources/views/inventory/show.blade.php b/resources/views/inventory/show.blade.php
new file mode 100644
index 00000000..bcc47501
--- /dev/null
+++ b/resources/views/inventory/show.blade.php
@@ -0,0 +1,77 @@
+@extends('layouts.app')
+
+@section('title', $item->name)
+
+@section('content')
+
+
+
+
+
+
+
+
+
{{ $item->name }}
+
{{ $item->code }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.code') }}
+
{{ $item->code ?? '-' }}
+
+
+
{{ __('inventory.category') }}
+ @if($item->category)
+ @php
+ $ck = 'inventory.category_' . $item->category;
+ $cl = __($ck);
+ if ($cl === $ck) $cl = $item->category;
+ @endphp
+
{{ $cl }}
+ @else
+
-
+ @endif
+
+
+
{{ __('inventory.unit') }}
+
{{ $item->unit ?? '-' }}
+
+
+
{{ __('common.status') }}
+ @if($item->is_active)
+
{{ __('common.active') }}
+ @else
+
{{ __('common.inactive') }}
+ @endif
+
+
+
+ @if($item->description)
+
+
{{ __('common.description') }}
+
{{ $item->description }}
+
+ @endif
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/inventory/transactions.blade.php b/resources/views/inventory/transactions.blade.php
new file mode 100644
index 00000000..6471969e
--- /dev/null
+++ b/resources/views/inventory/transactions.blade.php
@@ -0,0 +1,467 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
{{ __('inventory.transactions') }}
+
+
+
+ {{ __('inventory.stock_in') }}
+
+
+
+ {{ __('inventory.stock_out') }}
+
+
+
+ {{ __('inventory.transfer') ?? 'انتقال' }}
+
+
+
+
+
+
+
+
+ {{ __('inventory.item') }}
+
+ {{ __('common.all') }}
+ @foreach($items as $item)
+ id ? 'selected' : '' }}>{{ $item->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.warehouse') }}
+
+ {{ __('common.all') }}
+ @foreach($warehouses as $wh)
+ id ? 'selected' : '' }}>{{ $wh->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.transaction_type') }}
+
+ {{ __('common.all') }}
+ @foreach($types as $t)
+ {{ __('inventory.type_' . $t) }}
+ @endforeach
+
+
+
+ {{ __('inventory.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.from_date') }}
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+ {{ __('inventory.to_date') }}
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('inventory.transaction_type') }}
+ {{ __('inventory.item') }}
+ {{ __('inventory.warehouse') }}
+ {{ __('inventory.quantity') }}
+ {{ __('inventory.unit_price') }}
+ {{ __('inventory.total_price') }}
+ {{ __('inventory.reference') }}
+ {{ __('common.date') ?? 'تاریخ' }}
+
+
+
+ @forelse($transactions as $txn)
+
+
+
+ {{ __('inventory.type_' . $txn->type) }}
+
+
+ {{ $txn->item?->name ?? '-' }}
+ {{ $txn->warehouse?->name ?? '-' }}
+
+ {{ $txn->type === 'out' ? '-' : '+' }}{{ persian_num(number_format($txn->quantity, 2)) }}
+ @if($txn->item?->unit)
+ {{ $txn->item->unit }}
+ @endif
+
+
+ @if($txn->unit_price)
+ {{ format_currency((float)$txn->unit_price, $txn->currency?->code) }}
+ @else
+ -
+ @endif
+
+
+ @if($txn->total_price)
+ {{ format_currency((float)$txn->total_price, $txn->currency?->code) }}
+ @else
+ -
+ @endif
+
+ {{ $txn->reference_number ?? $txn->reference ?? '-' }}
+ {{ $txn->created_at ? calendar_date($txn->created_at) : '-' }}
+
+ @empty
+
+ {{ __('inventory.no_transactions') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $transactions->withQueryString()->links() }}
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.stock_in') }}
+
+
+
+
+
+ @csrf
+
+
+ {{ __('inventory.item') }} *
+
+ {{ __('common.select') }}
+ @foreach($items as $item)
+ {{ $item->name }} {{ $item->code ? '(' . $item->code . ')' : '' }}
+ @endforeach
+
+
+
+ {{ __('inventory.warehouse') }} *
+
+ {{ __('common.select') }}
+ @foreach($warehouses as $wh)
+ {{ $wh->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.quantity') }} *
+
+
+
+ {{ __('inventory.unit_price') }}
+
+
+ @if(isset($currencies) && $currencies->count() > 0)
+
+ {{ __('settings.currency') ?? 'ارز' }}
+
+ {{ __('common.select') }}
+ @foreach($currencies as $currency)
+ is_default ? 'selected' : '' }}>{{ $currency->name }} ({{ $currency->code }})
+ @endforeach
+
+
+ @endif
+
+ {{ __('inventory.project') }}
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ {{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.reference') }}
+
+
+
+ {{ __('inventory.notes') }}
+
+
+
+
+ {{ __('inventory.current_stock') }}:
+ -
+
+
+ {{ __('common.cancel') }}
+ {{ __('inventory.stock_in') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.stock_out') }}
+
+
+
+
+
+ @csrf
+
+
+ {{ __('inventory.item') }} *
+
+ {{ __('common.select') }}
+ @foreach($items as $item)
+ {{ $item->name }} {{ $item->code ? '(' . $item->code . ')' : '' }}
+ @endforeach
+
+
+
+ {{ __('inventory.warehouse') }} *
+
+ {{ __('common.select') }}
+ @foreach($warehouses as $wh)
+ {{ $wh->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.quantity') }} *
+
+
+
+ {{ __('inventory.unit_price') }}
+
+
+ @if(isset($currencies) && $currencies->count() > 0)
+
+ {{ __('settings.currency') ?? 'ارز' }}
+
+ {{ __('common.select') }}
+ @foreach($currencies as $currency)
+ is_default ? 'selected' : '' }}>{{ $currency->name }} ({{ $currency->code }})
+ @endforeach
+
+
+ @endif
+
+ {{ __('inventory.project') }}
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ {{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.reference') }}
+
+
+
+ {{ __('inventory.notes') }}
+
+
+
+
+ {{ __('inventory.current_stock') }}:
+ -
+
+ @error('quantity')
+ {{ $message }}
+ @enderror
+
+ {{ __('common.cancel') }}
+ {{ __('inventory.stock_out') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ __('inventory.transfer') ?? 'انتقال بین انبارها' }}
+
+
+
+
+
+ @csrf
+
+
+ {{ __('inventory.item') }} *
+
+ {{ __('common.select') }}
+ @foreach($items as $item)
+ {{ $item->name }} {{ $item->code ? '(' . $item->code . ')' : '' }}
+ @endforeach
+
+
+
+ {{ __('inventory.quantity') }} *
+
+
+
+ {{ __('inventory.from_warehouse') ?? 'انبار مبدأ' }} *
+
+ {{ __('common.select') }}
+ @foreach($warehouses as $wh)
+ {{ $wh->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.to_warehouse') ?? 'انبار مقصد' }} *
+
+ {{ __('common.select') }}
+ @foreach($warehouses as $wh)
+ {{ $wh->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.project') }}
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ {{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('inventory.notes') }}
+
+
+
+
+ {{ __('inventory.current_stock_source') ?? 'موجودی مبدأ' }}:
+ -
+
+ @error('quantity')
+ {{ $message }}
+ @enderror
+
+ {{ __('common.cancel') }}
+ {{ __('inventory.transfer') ?? 'انتقال' }}
+
+
+
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/inventory/warehouse-form.blade.php b/resources/views/inventory/warehouse-form.blade.php
new file mode 100644
index 00000000..8bb8bbc2
--- /dev/null
+++ b/resources/views/inventory/warehouse-form.blade.php
@@ -0,0 +1,76 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($warehouse) && $warehouse ? __('inventory.edit_warehouse') : __('inventory.create_warehouse') }}
+
+
+
+
+
+ @csrf
+ @if(isset($warehouse) && $warehouse)
+ @method('PUT')
+ @endif
+
+
+
+
+
{{ __('inventory.warehouse_name') }} *
+
+ @error('name')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('inventory.location') }}
+
+
+
+
+
+ {{ __('inventory.project') }}
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ project_id : '') == $project->id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+
+
+
+ is_active : true) ? 'checked' : '' }}
+ class="sr-only peer">
+
+ {{ __('inventory.active') }}
+
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/inventory/warehouses.blade.php b/resources/views/inventory/warehouses.blade.php
new file mode 100644
index 00000000..20988847
--- /dev/null
+++ b/resources/views/inventory/warehouses.blade.php
@@ -0,0 +1,99 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+ {{ __('inventory.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('common.status') }}
+
+ {{ __('common.all') }}
+ {{ __('inventory.active') }}
+ {{ __('inventory.inactive') }}
+
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('inventory.warehouse_name') }}
+ {{ __('inventory.location') }}
+ {{ __('inventory.project') }}
+ {{ __('inventory.items_count') ?? 'تعداد اقلام' }}
+ {{ __('common.status') }}
+
+
+
+
+ @forelse($warehouses as $warehouse)
+
+ {{ $warehouse->name }}
+ {{ $warehouse->location ?? '-' }}
+ {{ $warehouse->project?->name ?? '-' }}
+ {{ persian_num($warehouse->inventoryTransactions()->distinct('item_id')->count('item_id')) }}
+
+
+ {{ $warehouse->is_active ? __('inventory.active') : __('inventory.inactive') }}
+
+
+
+
+
+
+
+ @if($warehouse->inventoryTransactions()->count() === 0)
+
+ @csrf @method('DELETE')
+
+
+
+
+ @endif
+
+
+
+ @empty
+
+ {{ __('inventory.no_warehouses') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $warehouses->withQueryString()->links() }}
+
+
+@endsection
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
new file mode 100644
index 00000000..93477c4b
--- /dev/null
+++ b/resources/views/layouts/app.blade.php
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+ {{ isset($pageTitle) ? $pageTitle . ' - ' : '' }}{{ config('app.name') }}
+
+
+
+
+
+
+
+ @vite(['resources/css/app.css', 'resources/js/app.js'])
+
+
+
+ @stack('styles')
+
+
+ @if(session('calendar', 'jalali') === 'jalali')
+
+ @endif
+
+
+
+
+
+
+
+ @include('layouts.sidebar')
+
+
+
+
+
+ @include('layouts.header')
+
+
+
+ @if(session('success'))
+
+
{{ session('success') }}
+
+
+
+
+ @endif
+
+ @if(session('error'))
+
+
{{ session('error') }}
+
+
+
+
+ @endif
+
+ @yield('content')
+
+
+
+
+
+
© {{ persian_num(date('Y')) }} Vernova — {{ __('common.all_rights_reserved') }}
+
+
+
+ {{ session('calendar', config('Vernova.defaults.calendar', 'jalali')) === 'jalali' ? __('calendar.jalali') : __('calendar.gregorian') }}
+
+
+
+ {{ strtoupper(session('currency', config('Vernova.defaults.currency', 'IRR'))) }}
+
+
{{ app()->getLocale() === 'fa' ? 'فارسی' : 'English' }}
+
+
+
+
+
+
+ @stack('scripts')
+
+
+ @if(session('calendar', 'jalali') === 'jalali')
+
+ @endif
+
+
diff --git a/resources/views/layouts/header.blade.php b/resources/views/layouts/header.blade.php
new file mode 100644
index 00000000..5fbce96b
--- /dev/null
+++ b/resources/views/layouts/header.blade.php
@@ -0,0 +1,113 @@
+
+
diff --git a/resources/views/layouts/sidebar.blade.php b/resources/views/layouts/sidebar.blade.php
new file mode 100644
index 00000000..909b7e94
--- /dev/null
+++ b/resources/views/layouts/sidebar.blade.php
@@ -0,0 +1,241 @@
+
+
+
+
+
diff --git a/resources/views/letters/form.blade.php b/resources/views/letters/form.blade.php
new file mode 100644
index 00000000..ea4c895e
--- /dev/null
+++ b/resources/views/letters/form.blade.php
@@ -0,0 +1,141 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($letter) && $letter ? __('letters.edit_letter') : __('letters.create_letter') }}
+
+
+
+
+ @if(isset($parentLetter) && $parentLetter)
+
+
+
+
{{ __('letters.reply_to') }}:
+
{{ $parentLetter->subject }}
+
+
+ @endif
+
+
+
+ @csrf
+ @if(isset($letter) && $letter)
+ @method('PUT')
+ @endif
+
+
+
+
+ {{ __('letters.type') }} *
+
+ type : ($prefillType ?? '')) === 'incoming' ? 'selected' : '' }}>{{ __('letters.type_incoming') }}
+ type : ($prefillType ?? '')) === 'outgoing' ? 'selected' : '' }}>{{ __('letters.type_outgoing') }}
+
+
+
+
+
+ {{ __('common.status') }} *
+
+ @foreach(['draft', 'sent', 'received'] as $s)
+ status : 'draft') === $s ? 'selected' : '' }}>{{ __('letters.status_' . $s) }}
+ @endforeach
+ @if(isset($letter) && $letter)
+ status) === 'archived' ? 'selected' : '' }}>{{ __('letters.status_archived') }}
+ @endif
+
+
+
+
+
+
+
+
+
+
+ {{ __('letters.reference_number') }}
+
+
+
+
+
+ {{ __('letters.sender') }} *
+
+
+
+
+
+ {{ __('letters.recipient') }} *
+
+
+
+
+
+ {{ __('letters.project') }}
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ project_id : '') == $project->id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('letters.subject') }} *
+
+
+
+
+
+ {{ __('letters.content') }}
+ {{ old('content', isset($letter) ? $letter->content : '') }}
+
+
+
+ @if(!isset($letter) || !$letter)
+
+
{{ __('letters.attachments') }}
+
+
{{ __('letters.attachments_help') }}
+
+ @endif
+
+
+ @if(isset($parentLetter) && $parentLetter)
+
+ @endif
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/letters/index.blade.php b/resources/views/letters/index.blade.php
new file mode 100644
index 00000000..938c72f5
--- /dev/null
+++ b/resources/views/letters/index.blade.php
@@ -0,0 +1,139 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
{{ __('letters.letters') }}
+
+
+
+
+
+
+
+ {{ __('common.search') }}
+
+
+
+ {{ __('letters.type') }}
+
+ {{ __('common.all') }}
+ @foreach($types as $t)
+ {{ __('letters.type_' . $t) }}
+ @endforeach
+
+
+
+ {{ __('common.status') }}
+
+ {{ __('common.all') }}
+ @foreach($statuses as $s)
+ {{ __('letters.status_' . $s) }}
+ @endforeach
+
+
+
+ {{ __('letters.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('letters.from_date') }}
+
+
+
+ {{ __('letters.to_date') }}
+
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('letters.type') }}
+ {{ __('letters.reference_number') }}
+ {{ __('letters.subject') }}
+ {{ __('letters.sender') }}
+ {{ __('letters.recipient') }}
+ {{ __('common.status') }}
+ {{ __('letters.letter_date') }}
+
+
+
+
+ @forelse($letters as $letter)
+
+
+
+ {{ __('letters.type_' . $letter->type) }}
+
+
+ {{ $letter->reference_number ?? '-' }}
+ {{ $letter->subject }}
+ {{ $letter->sender }}
+ {{ $letter->recipient }}
+
+
+ {{ __('letters.status_' . $letter->status) }}
+
+
+ {{ $letter->letter_date ? calendar_date($letter->letter_date) : '-' }}
+
+
+ @if($letter->attachments->count() > 0)
+
+
+
+ @endif
+
+
+
+
+
+
+ @empty
+
+ {{ __('letters.no_letters') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $letters->withQueryString()->links() }}
+
+
+@endsection
diff --git a/resources/views/letters/show.blade.php b/resources/views/letters/show.blade.php
new file mode 100644
index 00000000..b88eba7b
--- /dev/null
+++ b/resources/views/letters/show.blade.php
@@ -0,0 +1,224 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
{{ __('letters.letter_details') }}
+
+ {{ __('letters.type_' . $letter->type) }}
+
+
+
+
+
+
+
+
+
+
{{ $letter->subject }}
+
+ {{ __('letters.status_' . $letter->status) }}
+
+
+
+ @if($letter->content)
+
+ {{ $letter->content }}
+
+ @endif
+
+
+
+
+
+
+
+
+
{{ __('letters.attachments') }}
+ @can('update', $letter)
+
+
+ {{ __('letters.add_attachment') }}
+
+
+ @endcan
+
+
+ @if($letter->attachments->count() > 0)
+
+ @foreach($letter->attachments as $attachment)
+
+
+ @if(str_starts_with($attachment->mime_type, 'image/'))
+
+ @elseif(str_starts_with($attachment->mime_type, 'application/pdf'))
+
+ @else
+
+ @endif
+
+
+
{{ $attachment->file_name }}
+
{{ number_format($attachment->file_size / 1024, 1) }} KB
+
+
+
+
+
+ @can('update', $letter)
+
+ @csrf @method('DELETE')
+
+
+
+
+ @endcan
+
+
+ @endforeach
+
+ @else
+
{{ __('letters.no_attachments') }}
+ @endif
+
+
+
+ @if($letter->replies->count() > 0)
+
+
{{ __('letters.replies') }}
+
+ @foreach($letter->replies as $reply)
+
+
+
{{ $reply->letter_date ? calendar_date($reply->letter_date) : '' }} · {{ $reply->reference_number ?? '-' }}
+
+ @endforeach
+
+
+ @endif
+
+
+
+
+
+
{{ __('letters.letter_info') }}
+
+
+
{{ __('letters.reference_number') }}
+
{{ $letter->reference_number ?? '-' }}
+
+
+
+
{{ __('letters.letter_date') }}
+
{{ $letter->letter_date ? calendar_date($letter->letter_date) : '-' }}
+
+
+
+
{{ __('letters.sender') }}
+
{{ $letter->sender }}
+
+
+
+
{{ __('letters.recipient') }}
+
{{ $letter->recipient }}
+
+
+ @if($letter->project)
+
+ @endif
+
+ @if($letter->parentLetter)
+
+ @endif
+
+
+
{{ __('letters.registered_by') }}
+
{{ $letter->createdBy?->name ?? '-' }}
+
+
+
+
{{ __('letters.registered_at') }}
+
{{ $letter->created_at ? calendar_date($letter->created_at) : '-' }}
+
+
+
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/payroll/calculate.blade.php b/resources/views/payroll/calculate.blade.php
new file mode 100644
index 00000000..ba0e53c4
--- /dev/null
+++ b/resources/views/payroll/calculate.blade.php
@@ -0,0 +1,170 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
{{ __('payroll.new_entry') }}
+
+
+
+
+
+ {{ __('payroll.manual_entry') }}
+ {{ __('payroll.auto_calculate') }}
+
+
+
+
+
+
+ @csrf
+
+
+ {{ __('payroll.employee') }} *
+
+ {{ __('common.select') }}
+ @foreach($employees as $emp)
+ {{ $emp->first_name }} {{ $emp->last_name }}
+ @endforeach
+
+
+
+ {{ __('payroll.type') }} *
+
+ @foreach(['salary', 'overtime', 'bonus', 'deduction', 'advance', 'allowance'] as $type)
+ {{ __('payroll.type_' . $type) }}
+ @endforeach
+
+
+
+ {{ __('payroll.amount') }} *
+
+
+
+ {{ __('common.currency') }}
+
+ @foreach($currencies as $currency)
+ is_default ? 'selected' : '' }}>{{ $currency->code }}
+ @endforeach
+
+
+
+ {{ __('payroll.effective_date') }} *
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500" required>
+
+
+
+ {{ __('payroll.month') }} *
+
+ @foreach(range(1, 12) as $m)
+ {{ $m }}
+ @endforeach
+
+
+
+ {{ __('payroll.year') }} *
+
+
+
+
+ {{ __('common.description') }}
+
+
+
+ {{ __('payroll.notes') }}
+
+
+
+
+
+
+
+
+
+
+ @csrf
+
+ {{ __('payroll.auto_calculate_help_title') }}
+ {{ __('payroll.auto_calculate_help') }}
+
+
+
+ {{ __('payroll.month') }} *
+
+ @foreach(range(1, 12) as $m)
+ {{ $m }}
+ @endforeach
+
+
+
+ {{ __('payroll.year') }} *
+
+
+
+
+
+ {{ __('payroll.include_worklogs') }}
+
+
{{ __('payroll.include_worklogs_help') }}
+
+
+
+
+
+
+ {{ __('payroll.select_employees') }}
+ {{ __('common.all') }} / {{ __('common.none') }}
+
+
+ @foreach($employees as $emp)
+
+
+
+ {{ $emp->first_name }} {{ $emp->last_name }}
+ {{ $emp->job_title ?? '' }}
+
+
+ @endforeach
+
+
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/payroll/index.blade.php b/resources/views/payroll/index.blade.php
new file mode 100644
index 00000000..4c8c2d19
--- /dev/null
+++ b/resources/views/payroll/index.blade.php
@@ -0,0 +1,179 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
{{ __('payroll.payroll') }}
+
+
+
+
+
+
+
{{ __('payroll.total_gross') }}
+
{{ format_currency($totalGross) }}
+
+
+
{{ __('payroll.total_deductions') }}
+
{{ format_currency($totalDeductions + $totalAdvances) }}
+
+
+
{{ __('payroll.total_net') }}
+
{{ format_currency($totalNet) }}
+
+
+
{{ __('payroll.unpaid_employees') }}
+
{{ $unpaidEmployeeCount }} / {{ $activeEmployeeCount }}
+
+
+
+
+
+
+ {{ __('payroll.payment_status') }}
+ {{ __('payroll.total_paid') }}: {{ format_currency($totalPaid) }} | {{ __('payroll.total_unpaid') }}: {{ format_currency($totalUnpaid) }}
+
+ @php $paidPercent = $totalGross > 0 ? round(($totalPaid / $totalGross) * 100) : 0; @endphp
+
+
{{ $paidPercent }}% {{ __('payroll.paid_percent') }}
+
+
+
+
+
+
+ {{ __('payroll.month') }}
+
+ @foreach($months as $m)
+ {{ $persianMonths[$m] ?? $m }}
+ @endforeach
+
+
+
+ {{ __('payroll.year') }}
+
+ @foreach($years as $y)
+ {{ $y }}
+ @endforeach
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+ @csrf
+
+
+
+ @if($financials->count() > 0)
+
+ {{ __('payroll.financial_records') }} — {{ $persianMonths[$month] ?? $month }} {{ $year }}
+
+ {{ __('payroll.pay_bulk') }}
+
+
+ @endif
+
+
+
+
+
+
+ {{ $financials->withQueryString()->links() }}
+
+
+
+
+@endsection
diff --git a/resources/views/payroll/settlement.blade.php b/resources/views/payroll/settlement.blade.php
new file mode 100644
index 00000000..d53932d4
--- /dev/null
+++ b/resources/views/payroll/settlement.blade.php
@@ -0,0 +1,148 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
{{ __('payroll.final_settlement') }}
+
+
+
+ {{ __('payroll.settlement_warning_title') }}
+ {{ __('payroll.settlement_warning') }}
+
+
+
+
+
+ @csrf
+
+
+ {{ __('payroll.employee') }} *
+
+ {{ __('common.select') }}
+ @foreach($employees as $emp)
+ {{ $emp->full_name ?? ($emp->first_name . ' ' . $emp->last_name) }} {{ $emp->job_title ? '(' . $emp->job_title . ')' : '' }}
+ @endforeach
+
+
+
+ {{ __('payroll.settlement_type') }} *
+
+ {{ __('payroll.settlement_resignation') }}
+ {{ __('payroll.settlement_termination') }}
+ {{ __('payroll.settlement_end_of_contract') }}
+
+
+
+ {{ __('payroll.settlement_date') }} *
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500" required>
+
+
+ {{ __('payroll.last_working_date') }}
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+ {{ __('payroll.notes') }}
+
+
+
+
+
+
+
+
{{ __('payroll.settlement_preview') }}
+
+
+
+
+
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/payroll/show.blade.php b/resources/views/payroll/show.blade.php
new file mode 100644
index 00000000..9c56b2f2
--- /dev/null
+++ b/resources/views/payroll/show.blade.php
@@ -0,0 +1,151 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
{{ $employee->full_name ?? ($employee->first_name . ' ' . $employee->last_name) }}
+
{{ $employee->job_title ?? '' }} — {{ __('payroll.payroll_year', ['year' => $year]) }}
+
+
+
+
+
+
+
+
{{ __('employee.contractType') }}
+
{{ __('employee.contract_' . ($employee->contract_type ?? 'full_time')) }}
+
+
+
{{ __('employee.baseSalary') }}
+
{{ number_format($employee->base_salary ?? 0) }}
+
+
+
{{ __('employee.hireDate') }}
+
{{ $employee->hire_date ? calendar_date($employee->hire_date) : '-' }}
+
+
+
{{ __('employee.status') }}
+
+
+ {{ __('employee.status_' . $employee->status) }}
+
+
+
+
+
+
+
+
+
+
{{ __('payroll.total_gross') }}
+
{{ number_format($yearlyGross) }}
+
+
+
{{ __('payroll.total_deductions') }}
+
{{ number_format($yearlyDeductions + $yearlyAdvances) }}
+
+
+
{{ __('payroll.total_net') }}
+
{{ number_format($yearlyNet) }}
+
+
+
{{ __('payroll.total_paid') }}
+
{{ number_format($yearlyPaid) }}
+
+
+
{{ __('payroll.total_unpaid') }}
+
{{ number_format($yearlyUnpaid) }}
+
+
+
+
+
+
+
+ {{ __('payroll.year') }}
+
+ @foreach(range(now()->year - 2, now()->year + 1) as $y)
+ {{ $y }}
+ @endforeach
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+ @foreach(range(1, 12) as $m)
+ @isset($financials[$m])
+
+
+
{{ $persianMonths[$m] ?? $m }}
+ @php
+ $monthFinancials = $financials[$m];
+ $monthGross = $monthFinancials->whereIn('type', ['salary', 'overtime', 'bonus', 'allowance'])->sum('amount');
+ $monthDeductions = $monthFinancials->where('type', 'deduction')->sum('amount');
+ $monthAdvances = $monthFinancials->where('type', 'advance')->sum('amount');
+ $monthNet = $monthGross - $monthDeductions - $monthAdvances;
+ $monthPaid = $monthFinancials->where('is_paid', true)->whereIn('type', ['salary', 'overtime', 'bonus', 'allowance'])->sum('amount');
+ @endphp
+
+ {{ __('payroll.total_gross') }}: {{ number_format($monthGross) }}
+ {{ __('payroll.total_deductions') }}: {{ number_format($monthDeductions + $monthAdvances) }}
+ {{ __('payroll.total_net') }}: {{ number_format($monthNet) }}
+ @if($monthPaid < $monthGross)
+ {{ __('payroll.unpaid') }}: {{ number_format($monthGross - $monthPaid) }}
+ @endif
+
+
+
+
+ @foreach($monthFinancials as $f)
+
+
+
+ {{ __('payroll.type_' . $f->type) }}
+
+
+
+ {{ in_array($f->type, ['deduction', 'advance']) ? '-' : '' }}{{ number_format($f->amount) }}
+
+ {{ $f->description ?? '-' }}
+ {{ $f->effective_date ? calendar_date($f->effective_date) : '-' }}
+
+ @if($f->is_paid)
+
+
+ {{ __('payroll.paid') }}
+
+ @else
+ {{ __('payroll.pay') }}
+ @endif
+
+
+ @endforeach
+
+
+
+ @endisset
+ @endforeach
+
+ @if($financials->isEmpty())
+
+ {{ __('payroll.no_records_year') }}
+
+ @endif
+
+
+@endsection
diff --git a/resources/views/petty-cashes/PettyCashController.php b/resources/views/petty-cashes/PettyCashController.php
new file mode 100644
index 00000000..6cf5347a
--- /dev/null
+++ b/resources/views/petty-cashes/PettyCashController.php
@@ -0,0 +1,255 @@
+authorize('viewAny', PettyCash::class);
+
+ $query = PettyCash::with(['project', 'currency', 'requestedBy', 'approvedBy'])->withCount('attachments');
+
+ if ($request->filled('project_id')) {
+ $query->where('project_id', $request->project_id);
+ }
+
+ if ($request->filled('status')) {
+ $query->byStatus($request->status);
+ }
+
+ $query->orderBy('request_date', 'desc');
+
+ $pettyCashes = $query->paginate(20)->appends($request->query());
+ $projects = Project::whereIn('status', ['planning', 'active'])->get();
+ $statuses = ['pending', 'approved', 'rejected', 'settled'];
+
+ return view('petty-cashes.index', compact('pettyCashes', 'projects', 'statuses'));
+ }
+
+ /**
+ * Show the form for creating a new petty cash request.
+ */
+ public function create()
+ {
+ $this->authorize('create', PettyCash::class);
+
+ $pettyCash = null;
+ $projects = Project::whereIn('status', ['planning', 'active'])->get();
+ $currencies = Currency::where('is_active', true)->get();
+ $baseCurrency = CurrencyHelper::getBaseCurrency();
+
+ return view('petty-cashes.form', compact('pettyCash', 'projects', 'currencies', 'baseCurrency'));
+ }
+
+ /**
+ * Store a newly created petty cash request.
+ */
+ public function store(Request $request)
+ {
+ $this->authorize('create', PettyCash::class);
+
+ $validated = $request->validate([
+ 'project_id' => 'required|exists:projects,id',
+ 'title' => 'required|string|max:255',
+ 'description' => 'nullable|string',
+ 'amount' => 'required|numeric|min:0',
+ 'currency_id' => 'required|exists:currencies,id',
+ 'request_date' => 'required|date',
+ 'receipt' => 'nullable|file|mimes:jpg,jpeg,png,pdf,doc,docx|max:10240',
+ 'attachments' => 'nullable|array|max:5',
+ 'attachments.*' => 'file|mimes:jpg,jpeg,png,pdf,doc,docx|max:10240',
+ ]);
+
+ $validated['tenant_id'] = $this->getTenantId();
+ $validated['requested_by'] = Auth::id();
+ $validated['status'] = 'pending';
+
+ // Handle currency conversion
+ $baseCurrency = CurrencyHelper::getBaseCurrency();
+ $selectedCurrency = Currency::find($validated['currency_id']);
+
+ if ($selectedCurrency && $baseCurrency && $selectedCurrency->id !== $baseCurrency->id) {
+ $validated['original_amount'] = $validated['amount'];
+ $validated['amount'] = CurrencyHelper::convert(
+ (float) $validated['amount'],
+ $selectedCurrency->code,
+ $baseCurrency->code
+ );
+ }
+
+ // Handle receipt upload (legacy)
+ if ($request->hasFile('receipt')) {
+ $validated['receipt_path'] = $request->file('receipt')->store('receipts/petty-cash', 'public');
+ }
+
+ $pettyCash = PettyCash::create($validated);
+
+ // Handle multiple file attachments
+ if ($request->hasFile('attachments')) {
+ $pettyCash->attachFiles($request->file('attachments'), 'receipt');
+ }
+
+ return redirect()
+ ->route('petty-cashes.show', $pettyCash)
+ ->with('success', __('pettyCash.created_successfully'));
+ }
+
+ /**
+ * Display the specified petty cash request.
+ */
+ public function show(PettyCash $pettyCash)
+ {
+ $this->authorize('view', $pettyCash);
+
+ $pettyCash->load(['project', 'currency', 'requestedBy', 'approvedBy', 'expenses', 'attachments']);
+
+ return view('petty-cashes.show', compact('pettyCash'));
+ }
+
+ /**
+ * Show the form for editing the specified petty cash request.
+ */
+ public function edit(PettyCash $pettyCash)
+ {
+ $this->authorize('update', $pettyCash);
+
+ $projects = Project::whereIn('status', ['planning', 'active'])->get();
+ $currencies = Currency::where('is_active', true)->get();
+ $baseCurrency = CurrencyHelper::getBaseCurrency();
+
+ return view('petty-cashes.form', compact('pettyCash', 'projects', 'currencies', 'baseCurrency'));
+ }
+
+ /**
+ * Update the specified petty cash request.
+ */
+ public function update(Request $request, PettyCash $pettyCash)
+ {
+ $this->authorize('update', $pettyCash);
+
+ $validated = $request->validate([
+ 'project_id' => 'required|exists:projects,id',
+ 'title' => 'required|string|max:255',
+ 'description' => 'nullable|string',
+ 'amount' => 'required|numeric|min:0',
+ 'currency_id' => 'required|exists:currencies,id',
+ 'request_date' => 'required|date',
+ 'receipt' => 'nullable|file|mimes:jpg,jpeg,png,pdf,doc,docx|max:10240',
+ 'attachments' => 'nullable|array|max:5',
+ 'attachments.*' => 'file|mimes:jpg,jpeg,png,pdf,doc,docx|max:10240',
+ ]);
+
+ // Handle currency conversion
+ $baseCurrency = CurrencyHelper::getBaseCurrency();
+ $selectedCurrency = Currency::find($validated['currency_id']);
+
+ if ($selectedCurrency && $baseCurrency && $selectedCurrency->id !== $baseCurrency->id) {
+ $validated['original_amount'] = $validated['amount'];
+ $validated['amount'] = CurrencyHelper::convert(
+ (float) $validated['amount'],
+ $selectedCurrency->code,
+ $baseCurrency->code
+ );
+ }
+
+ // Handle receipt upload
+ if ($request->hasFile('receipt')) {
+ // Delete old receipt if exists
+ if ($pettyCash->receipt_path) {
+ \Illuminate\Support\Facades\Storage::disk('public')->delete($pettyCash->receipt_path);
+ }
+ $validated['receipt_path'] = $request->file('receipt')->store('receipts/petty-cash', 'public');
+ }
+
+ $pettyCash->update($validated);
+
+ // Handle multiple file attachments
+ if ($request->hasFile('attachments')) {
+ $pettyCash->attachFiles($request->file('attachments'), 'receipt');
+ }
+
+ return redirect()
+ ->route('petty-cashes.show', $pettyCash)
+ ->with('success', __('pettyCash.updated_successfully'));
+ }
+
+ /**
+ * Remove the specified petty cash request.
+ */
+ public function destroy(PettyCash $pettyCash)
+ {
+ $this->authorize('delete', $pettyCash);
+
+ $pettyCash->delete();
+
+ return redirect()
+ ->route('petty-cashes.index')
+ ->with('success', __('pettyCash.deleted_successfully'));
+ }
+
+ /**
+ * Approve a petty cash request.
+ */
+ public function approve(PettyCash $pettyCash)
+ {
+ $this->authorize('approve', $pettyCash);
+
+ $pettyCash->update([
+ 'status' => 'approved',
+ 'approved_by' => Auth::id(),
+ 'approval_date' => now(),
+ ]);
+
+ return response()->json([
+ 'success' => true,
+ 'message' => __('pettyCash.approved_successfully'),
+ ]);
+ }
+
+ /**
+ * Reject a petty cash request.
+ */
+ public function reject(PettyCash $pettyCash)
+ {
+ $this->authorize('approve', $pettyCash);
+
+ $pettyCash->update([
+ 'status' => 'rejected',
+ 'approved_by' => Auth::id(),
+ 'approval_date' => now(),
+ ]);
+
+ return response()->json([
+ 'success' => true,
+ 'message' => __('pettyCash.rejected_successfully'),
+ ]);
+ }
+
+ /**
+ * Settle a petty cash request (mark as settled after usage).
+ */
+ public function settle(PettyCash $pettyCash)
+ {
+ $this->authorize('approve', $pettyCash);
+
+ $pettyCash->update([
+ 'status' => 'settled',
+ ]);
+
+ return response()->json([
+ 'success' => true,
+ 'message' => __('pettyCash.settled_successfully'),
+ ]);
+ }
+}
diff --git a/resources/views/petty-cashes/create.blade.php b/resources/views/petty-cashes/create.blade.php
new file mode 100644
index 00000000..c44f5924
--- /dev/null
+++ b/resources/views/petty-cashes/create.blade.php
@@ -0,0 +1,18 @@
+@extends('layouts.app')
+
+@section('title', __('petty_cash.create'))
+
+@section('content')
+
+
+
{{ __('petty_cash.create') }}
+
+
+
+
+ @csrf
+ @include('petty-cashes.form')
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/petty-cashes/expense-form.blade.php b/resources/views/petty-cashes/expense-form.blade.php
new file mode 100644
index 00000000..5ee2126f
--- /dev/null
+++ b/resources/views/petty-cashes/expense-form.blade.php
@@ -0,0 +1,112 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
{{ __('pettyCash.add_expense') }}
+
+
+
+
+
+
+
{{ $pettyCash->title }}
+
{{ $pettyCash->project?->name }}
+
+
+
{{ __('pettyCash.remaining') }}
+
{{ format_currency((float)$pettyCash->remaining, $pettyCash->currency?->code) }}
+
+
+
+
+
+
+ @csrf
+
+
+
+
+ {{ __('expense.title') }} *
+
+
+
+
+
+ {{ __('expense.category') }} *
+
+ @foreach($categories as $cat)
+ {{ __('expense.category_' . $cat) }}
+ @endforeach
+
+
+
+
+
+ {{ __('expense.amount') }} *
+
+
+
+
+
+
{{ __('pettyCash.currency') }} *
+
+ @foreach($currencies as $currency)
+ currency_id ?? $baseCurrency?->id ?? '') == $currency->id ? 'selected' : '' }}>
+ {{ $currency->code }} - {{ $currency->name }}
+ @if($currency->id === $baseCurrency?->id) ({{ __('pettyCash.base_currency') }})@endif
+
+ @endforeach
+
+
{{ __('pettyCash.currency_conversion_note') }}
+
+
+
+
+ {{ __('expense.date') }} *
+
+
+
+
+
+ {{ __('expense.description') }}
+ {{ old('description') }}
+
+
+
+
+
{{ __('pettyCash.receipt') }}
+
+
{{ __('pettyCash.receipt_help') }}
+
+
+
+
+
{{ __('attachment.additional_files') }}
+
+
{{ __('attachment.max_files_help') }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/petty-cashes/form.blade.php b/resources/views/petty-cashes/form.blade.php
new file mode 100644
index 00000000..164a0f40
--- /dev/null
+++ b/resources/views/petty-cashes/form.blade.php
@@ -0,0 +1,108 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($pettyCash) ? __('pettyCash.edit') : __('pettyCash.create') }}
+
+
+
+
+
+ @csrf
+ @if(isset($pettyCash))
+ @method('PUT')
+ @endif
+
+
+
+
+ {{ __('pettyCash.title') }} *
+
+
+
+
+
+ {{ __('pettyCash.project') }} *
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ project_id ?? '') == $project->id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('pettyCash.amount') }} *
+
+
+
+
+
+
{{ __('pettyCash.currency') }} *
+
+ @foreach($currencies as $currency)
+ currency_id ?? $baseCurrency?->id ?? '') == $currency->id ? 'selected' : '' }}>
+ {{ $currency->code }} - {{ $currency->name }}
+ @if($currency->id === $baseCurrency?->id) ({{ __('pettyCash.base_currency') }})@endif
+
+ @endforeach
+
+
{{ __('pettyCash.currency_conversion_note') }}
+
+
+
+
+
+
+
+
+
+ {{ __('pettyCash.description') }}
+ {{ old('description', $pettyCash?->description ?? '') }}
+
+
+
+
+
{{ __('pettyCash.receipt') }}
+ @if(isset($pettyCash) && $pettyCash?->receipt_path)
+
+ @endif
+
+
{{ __('pettyCash.receipt_help') }}
+
+
+
+
+
{{ __('attachment.additional_files') }}
+
+
{{ __('attachment.max_files_help') }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/petty-cashes/index.blade.php b/resources/views/petty-cashes/index.blade.php
new file mode 100644
index 00000000..d38ad020
--- /dev/null
+++ b/resources/views/petty-cashes/index.blade.php
@@ -0,0 +1,164 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
{{ __('pettyCash.total_amount') }}
+
{{ format_currency((float)$totalAmount) }}
+
+
+
{{ __('pettyCash.total_expenses') }}
+
{{ format_currency((float)$totalExpenses) }}
+
+
+
{{ __('pettyCash.total_remaining') }}
+
{{ format_currency((float)$totalRemaining) }}
+
+
+
+
+
+
+
+ {{ __('pettyCash.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('pettyCash.status') }}
+
+ {{ __('common.all') }}
+ @foreach($statuses as $status)
+ {{ __('pettyCash.' . $status) }}
+ @endforeach
+
+
+
+ {{ __('common.from') }}
+
+
+
+ {{ __('common.to') }}
+
+
+
+ {{ __('common.filter') }}
+
+ {{ __('common.reset') }}
+
+
+
+
+
+
+
+
+
+ {{ __('pettyCash.title') }}
+ {{ __('pettyCash.project') }}
+ {{ __('pettyCash.amount') }}
+ {{ __('pettyCash.total_expenses') }}
+ {{ __('pettyCash.remaining') }}
+ {{ __('pettyCash.request_date') }}
+ {{ __('pettyCash.status') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($pettyCashes as $pc)
+
+
+ {{ $pc->title ?? '-' }}
+
+ {{ $pc->project?->name ?? '-' }}
+
+ {{ format_currency((float)$pc->amount, $pc->currency?->code) }}
+
+
+ {{ format_currency((float)$pc->total_expenses, $pc->currency?->code) }}
+
+
+ {{ $pc->remaining !== null ? format_currency((float)$pc->remaining, $pc->currency?->code) : '-' }}
+
+ {{ $pc->request_date ? calendar_date($pc->request_date) : '-' }}
+
+
+ {{ $pc->status_label }}
+
+
+
+
+ @if($pc->status === 'pending')
+
{{ __('common.approve') }}
+
{{ __('common.reject') }}
+ @elseif($pc->status === 'approved')
+
{{ __('pettyCash.add_expense') }}
+
{{ __('pettyCash.settle') }}
+ @endif
+
+
+
+ @empty
+
+ {{ __('pettyCash.no_petty_cashes') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $pettyCashes->withQueryString()->links() }}
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/petty-cashes/petty-cash-index.blade.php b/resources/views/petty-cashes/petty-cash-index.blade.php
new file mode 100644
index 00000000..ff279627
--- /dev/null
+++ b/resources/views/petty-cashes/petty-cash-index.blade.php
@@ -0,0 +1,165 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+ {{ __('pettyCash.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('pettyCash.status') }}
+
+ {{ __('common.all') }}
+ @foreach($statuses as $status)
+ {{ __('pettyCash.' . $status) }}
+ @endforeach
+
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('pettyCash.title') }}
+ {{ __('pettyCash.project') }}
+ {{ __('pettyCash.amount') }}
+ {{ __('pettyCash.remaining') }}
+ {{ __('pettyCash.request_date') }}
+ {{ __('pettyCash.status') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($pettyCashes as $pc)
+
+
+
+
+ {{ $pc->title ?? '-' }}
+
+ {{-- Attachment indicator --}}
+ @if($pc->attachments_count > 0 || $pc->receipt_path)
+
+
+ @if($pc->attachments_count > 0)
+ {{ $pc->attachments_count }}
+ @endif
+
+ @endif
+
+
+ {{ $pc->project?->name ?? '-' }}
+
+ {{ format_currency((float)$pc->amount, $pc->currency?->code) }}
+ @if($pc->original_amount && $pc->original_amount != $pc->amount)
+ ({{ format_currency((float)$pc->original_amount, $pc->currency?->code) }})
+ @endif
+
+
+ @if($pc->remaining !== null)
+ {{ format_currency((float)$pc->remaining, $pc->currency?->code) }}
+ @else
+ -
+ @endif
+
+ {{ $pc->request_date ? calendar_date($pc->request_date) : '-' }}
+
+
+ {{ $pc->status_label }}
+
+
+
+
+ {{-- View button --}}
+
+
+
+ {{-- Edit button --}}
+
+
+
+ @if($pc->status === 'pending')
+
+
+
+
+
+
+ @elseif($pc->status === 'approved')
+
+
+
+ @endif
+
+
+
+ @empty
+
+ {{ __('pettyCash.no_petty_cashes') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $pettyCashes->withQueryString()->links() }}
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/petty-cashes/show.blade.php b/resources/views/petty-cashes/show.blade.php
new file mode 100644
index 00000000..3a66abf1
--- /dev/null
+++ b/resources/views/petty-cashes/show.blade.php
@@ -0,0 +1,291 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
{{ $pettyCash->title ?? __('pettyCash.pettyCash') }}
+
+ {{ $pettyCash->status_label }}
+
+
+
+
+
+
+
{{ __('pettyCash.amount') }}
+
{{ format_currency((float)$pettyCash->amount, $pettyCash->currency?->code) }}
+ @if($pettyCash->original_amount && $pettyCash->original_amount != $pettyCash->amount)
+
{{ __('pettyCash.original_amount') }}: {{ format_currency((float)$pettyCash->original_amount, $pettyCash->currency?->code) }}
+ @endif
+
+
+
{{ __('pettyCash.total_expenses') }}
+
{{ format_currency((float)$pettyCash->total_expenses, $pettyCash->currency?->code) }}
+
+
+
{{ __('pettyCash.remaining') }}
+
+ {{ format_currency((float)$pettyCash->remaining, $pettyCash->currency?->code) }}
+
+
+
+
+
+
+
+ {{ __('pettyCash.utilization') }}
+ {{ $pettyCash->utilization }}%
+
+
+
+
+
+
+
+
+
{{ __('pettyCash.project') }}
+
{{ $pettyCash->project?->name ?? '-' }}
+
+
+
{{ __('pettyCash.request_date') }}
+
{{ $pettyCash->request_date ? calendar_date($pettyCash->request_date) : '-' }}
+
+
+
{{ __('pettyCash.requested_by') }}
+
{{ $pettyCash->requestedBy?->name ?? '-' }}
+
+ @if($pettyCash->approved_by)
+
+
{{ __('pettyCash.approved_by') }}
+
{{ $pettyCash->approvedBy?->name ?? '-' }}
+
+ @endif
+ @if($pettyCash->approval_date)
+
+
{{ __('pettyCash.approval_date') }}
+
{{ calendar_date($pettyCash->approval_date) }}
+
+ @endif
+ @if($pettyCash->description)
+
+
{{ __('pettyCash.description') }}
+
{{ $pettyCash->description }}
+
+ @endif
+
+
+
+
+
+
+
+
+
+ @if($pettyCash->expenses->count() > 0)
+
+
+
+
+ {{ __('expense.title') }}
+ {{ __('expense.category') }}
+ {{ __('expense.amount') }}
+ {{ __('expense.date') }}
+ {{ __('expense.status') }}
+
+
+
+ @foreach($pettyCash->expenses as $expense)
+
+ {{ $expense->title ?? '-' }}
+ {{ __('expense.category_' . $expense->category) }}
+ {{ format_currency((float)$expense->amount, $expense->currency?->code) }}
+ {{ $expense->expense_date ? calendar_date($expense->expense_date) : '-' }}
+
+
+ {{ $expense->status_label ?? $expense->status }}
+
+
+
+ @endforeach
+
+
+
+ @else
+
+ {{ __('pettyCash.no_expenses_yet') }}
+
+ @endif
+
+
+
+
+
+
{{ __('attachment.attachments') }}
+
+
+ {{ __('attachment.add_file') }}
+
+
+
+
+ @if($pettyCash->attachments->count() > 0)
+
+ @foreach($pettyCash->attachments as $attachment)
+
+
+ @if($attachment->is_image)
+
+
+
+ @elseif($attachment->is_pdf)
+
+ @else
+
+ @endif
+
+
+
+
+
+
+ @endforeach
+
+ @else
+
+ {{ __('attachment.no_attachments') }}
+
+ @endif
+
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/projects/form.blade.php b/resources/views/projects/form.blade.php
new file mode 100644
index 00000000..8b4a585d
--- /dev/null
+++ b/resources/views/projects/form.blade.php
@@ -0,0 +1,118 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($project) ? __('project.edit_project') : __('project.create_project') }}
+
+
+
+
+
+ @csrf
+ @if(isset($project))
+ @method('PUT')
+ @endif
+
+
+
+
+
{{ __('project.name') }} *
+
+ @error('name')
{{ $message }}
@enderror
+
+
+
+
+
{{ __('project.code') }} *
+
+ @error('code')
{{ $message }}
@enderror
+
+
+
+
+ {{ __('project.status') }}
+
+ @foreach($statuses as $status)
+ status ?? 'planning') === $status ? 'selected' : '' }}>{{ __('project.status_' . $status) }}
+ @endforeach
+
+
+
+
+
+ {{ __('project.manager') }}
+
+ {{ __('common.select') }}
+ @foreach($managers as $manager)
+ manager_id ?? '') == $manager->id ? 'selected' : '' }}>{{ $manager->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('project.budget') }}
+
+
+
+
+
+ {{ __('project.currency') }}
+
+ {{ __('common.select') }}
+ @foreach($currencies as $currency)
+ default_currency_id ?? '') == $currency->id ? 'selected' : '' }}>{{ $currency->code }} - {{ $currency->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('project.start_date') }}
+
+
+
+
+
+ {{ __('project.end_date') }}
+
+
+
+
+
+ {{ __('project.location') }}
+
+
+
+
+
+ {{ __('project.description') }}
+ {{ old('description', $project->description ?? '') }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/projects/index.blade.php b/resources/views/projects/index.blade.php
new file mode 100644
index 00000000..d809dc36
--- /dev/null
+++ b/resources/views/projects/index.blade.php
@@ -0,0 +1,95 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+ {{ __('common.search') }}
+
+
+
+ {{ __('project.status') }}
+
+ {{ __('common.all') }}
+ @foreach(['planning', 'active', 'on_hold', 'completed', 'cancelled'] as $status)
+ {{ __('project.status_' . $status) }}
+ @endforeach
+
+
+
+ {{ __('common.filter') }}
+
+ {{ __('common.reset') }}
+
+
+
+
+
+
+
+
+ {{ $projects->withQueryString()->links() }}
+
+
+@endsection
diff --git a/resources/views/projects/show.blade.php b/resources/views/projects/show.blade.php
new file mode 100644
index 00000000..644a798c
--- /dev/null
+++ b/resources/views/projects/show.blade.php
@@ -0,0 +1,208 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
+
{{ $project->name }}
+
{{ $project->code }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ __('project.status') }}
+
+ {{ $project->status_label }}
+
+
+
+
{{ __('project.budget') }}
+
{{ format_currency((float)$project->budget) }}
+
+
+
{{ __('project.manager') }}
+
{{ $project->manager?->name ?? '-' }}
+
+
+
{{ __('project.progress') }}
+
+
+
+
{{ $project->progress }}%
+
+
+
+
+
{{ __('project.start_date') }}
+
{{ $project->start_date ? calendar_date($project->start_date) : '-' }}
+
+
+
{{ __('project.end_date') }}
+
{{ $project->end_date ? calendar_date($project->end_date) : '-' }}
+
+
+
{{ __('project.location') }}
+
{{ $project->location ?? '-' }}
+
+
+
{{ __('project.currency') }}
+
{{ $project->defaultCurrency?->code ?? '-' }}
+
+
+
+ @if($project->description)
+
+
{{ __('project.description') }}
+
{{ $project->description }}
+
+ @endif
+
+
+ @php
+ $projectEmployees = $project->tasks->pluck('assignee')->filter()->unique('id');
+ @endphp
+
+
+
+
+
+
+
+ {{ __('task.tasks') }} ({{ $taskStats['total'] }})
+
+
+ {{ __('expense.expenses') }}
+
+
+ {{ __('employee.employees') }}
+
+
+
+
+
+
+
+ @forelse($project->tasks as $task)
+
+ @empty
+
{{ __('task.no_tasks') }}
+ @endforelse
+
+
+
+
+ @forelse($project->expenses as $expense)
+
+ {{ $expense->title }}
+ {{ format_currency((float)$expense->amount, $expense->currency?->code) }}
+ {{ $expense->status_label }}
+
+ @empty
+
{{ __('expense.no_expenses') }}
+ @endforelse
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ __('project.task_overview') }}
+
+
+ {{ __('task.status_pending') }}
+ {{ $taskStats['pending'] }}
+
+
+ {{ __('task.status_in_progress') }}
+ {{ $taskStats['in_progress'] }}
+
+
+ {{ __('task.status_review') }}
+ {{ $taskStats['review'] }}
+
+
+ {{ __('task.status_done') }}
+ {{ $taskStats['done'] }}
+
+
+
+
+
+
+
{{ __('project.team') }}
+
+ @forelse($project->users as $member)
+
+
+ {{ mb_strtoupper(mb_substr($member->name, 0, 1)) }}
+
+
{{ $member->name }}
+
+ @empty
+
{{ __('project.no_team_members') }}
+ @endforelse
+
+
+
+
+
+@endsection
diff --git a/resources/views/reports/employee-worklog.blade.php b/resources/views/reports/employee-worklog.blade.php
new file mode 100644
index 00000000..6bc34c72
--- /dev/null
+++ b/resources/views/reports/employee-worklog.blade.php
@@ -0,0 +1,13 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
{{ __('reports.employee_worklog') }}
+
+
{{ __('reports.detailed_view_coming_soon') }}
+
+@endsection
diff --git a/resources/views/reports/financial-pdf.blade.php b/resources/views/reports/financial-pdf.blade.php
new file mode 100644
index 00000000..1c7aa2ac
--- /dev/null
+++ b/resources/views/reports/financial-pdf.blade.php
@@ -0,0 +1,295 @@
+
+
+
+
+
+ {{ __('reports.financial') }} - Vernova
+
+
+
+
+
+
+
+
+
+
+
+ @if(count($reportData ?? []) > 0)
+
+
+
+ #
+ {{ __('project.code') }}
+ {{ __('project.name') }}
+ {{ __('project.budget') }}
+ {{ __('reports.total_expenses') }}
+ {{ __('reports.total_petty_cash') }}
+ {{ __('reports.remaining') }}
+ {{ __('reports.utilization') }}
+ {{ __('project.manager') }}
+
+
+
+ @foreach($reportData as $index => $row)
+
+ {{ persian_num($loop->iteration) }}
+ {{ $row['code'] ?? '-' }}
+ {{ $row['name'] ?? '-' }}
+ {{ format_currency((float)($row['budget'] ?? 0)) }}
+ {{ format_currency((float)($row['total_expenses'] ?? 0)) }}
+ {{ format_currency((float)($row['total_petty_cash'] ?? 0)) }}
+
+ {{ format_currency((float)($row['remaining'] ?? 0)) }}
+
+
+
+
+
{{ persian_num(round($row['utilization'] ?? 0)) }}%
+
+
+ {{ $row['manager'] ?? '-' }}
+
+ @endforeach
+
+
+
+
+ {{ __('reports.total') }}
+ {{ format_currency((float)($summary['total_budget'] ?? 0)) }}
+ {{ format_currency((float)($summary['total_expenses'] ?? 0)) }}
+ {{ format_currency((float)($summary['total_petty_cash'] ?? 0)) }}
+
+ {{ format_currency((float)($summary['total_remaining'] ?? 0)) }}
+
+
+ {{ persian_num(round($summary['avg_utilization'] ?? 0)) }}%
+
+
+
+
+
+ @else
+
+ {{ __('reports.no_financial_data') }}
+
+ @endif
+
+
+
+
+
+
diff --git a/resources/views/reports/financial.blade.php b/resources/views/reports/financial.blade.php
new file mode 100644
index 00000000..65b0fbc7
--- /dev/null
+++ b/resources/views/reports/financial.blade.php
@@ -0,0 +1,30 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
{{ __('reports.financial') }}
+
+
+
+
+
+
{{ __('reports.total_expenses') }}
+
{{ number_format($totalAmount) }}
+
+
+
{{ __('reports.approved_amount') }}
+
{{ number_format($approvedAmount) }}
+
+
+
{{ __('reports.pending_amount') }}
+
{{ number_format($pendingAmount) }}
+
+
+
+
{{ __('reports.detailed_view_coming_soon') }}
+
+@endsection
diff --git a/resources/views/reports/index.blade.php b/resources/views/reports/index.blade.php
new file mode 100644
index 00000000..9364ce38
--- /dev/null
+++ b/resources/views/reports/index.blade.php
@@ -0,0 +1,41 @@
+@extends('layouts.app')
+
+@section('content')
+
+
{{ __('reports.reports') }}
+
+
+
+@endsection
diff --git a/resources/views/reports/inventory.blade.php b/resources/views/reports/inventory.blade.php
new file mode 100644
index 00000000..4d98aaff
--- /dev/null
+++ b/resources/views/reports/inventory.blade.php
@@ -0,0 +1,13 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
{{ __('reports.inventory') }}
+
+
{{ __('reports.detailed_view_coming_soon') }}
+
+@endsection
diff --git a/resources/views/reports/payroll.blade.php b/resources/views/reports/payroll.blade.php
new file mode 100644
index 00000000..2344712c
--- /dev/null
+++ b/resources/views/reports/payroll.blade.php
@@ -0,0 +1,13 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
{{ __('reports.payroll') }}
+
+
{{ __('reports.detailed_view_coming_soon') }}
+
+@endsection
diff --git a/resources/views/settings/index.blade.php b/resources/views/settings/index.blade.php
new file mode 100644
index 00000000..7679fd02
--- /dev/null
+++ b/resources/views/settings/index.blade.php
@@ -0,0 +1,8 @@
+@extends('layouts.app')
+
+@section('content')
+
+
{{ __('settings.settings') }}
+
{{ __('settings.full_settings_coming_soon') }}
+
+@endsection
diff --git a/resources/views/settings/partials/currencies.blade.php b/resources/views/settings/partials/currencies.blade.php
new file mode 100644
index 00000000..60f50940
--- /dev/null
+++ b/resources/views/settings/partials/currencies.blade.php
@@ -0,0 +1,173 @@
+
+
+
+
+
+ {{ __('settings.add_currency') }}
+
+
+
+
+
+
+
+
+ {{ __('settings.currency_code') }}
+ {{ __('settings.currency_name') }}
+ {{ __('settings.currency_symbol') }}
+ {{ __('settings.exchange_rate') }}
+ {{ __('settings.decimal_places') }}
+ {{ __('common.status') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($allCurrencies as $currency)
+
+ {{ $currency->code }}
+ {{ $currency->name }}
+ {{ $currency->symbol }}
+ {{ number_format($currency->exchange_rate_to_usd, 4) }}
+ {{ persian_num($currency->decimal_places) }}
+
+
+ {{ $currency->is_active ? __('settings.active') : __('settings.inactive') }}
+
+
+
+
+
+ {{ __('common.edit') }}
+
+
+ @csrf @method('PATCH')
+
+ {{ $currency->is_active ? __('settings.deactivate') : __('settings.activate') }}
+
+
+
+
+
+ @empty
+
+ {{ __('settings.no_currencies') }}
+
+ @endforelse
+
+
+
+
+
+
+
+
+
+
+
+@push('scripts')
+
+@endpush
diff --git a/resources/views/settings/partials/organization.blade.php b/resources/views/settings/partials/organization.blade.php
new file mode 100644
index 00000000..bdc67aff
--- /dev/null
+++ b/resources/views/settings/partials/organization.blade.php
@@ -0,0 +1,84 @@
+
+ @csrf
+ @method('PUT')
+
+
+
+
+ {{ __('settings.org_name') }}
+
+
+
+
+
+ {{ __('settings.domain') }}
+
+
+
+
+
+ {{ __('settings.base_currency') }}
+
+ — {{ __('common.select') }} —
+ @foreach($currencies as $currency)
+ base_currency_id) == $currency->id ? 'selected' : '' }}>
+ {{ $currency->name }} ({{ $currency->code }})
+
+ @endforeach
+
+
+
+
+
+ {{ __('settings.default_locale') }}
+
+ default_locale ?? 'fa') === 'fa' ? 'selected' : '' }}>فارسی
+ default_locale) === 'en' ? 'selected' : '' }}>English
+ default_locale) === 'ar' ? 'selected' : '' }}>العربية
+
+
+
+
+
+ {{ __('settings.default_calendar') }}
+
+ default_calendar ?? 'jalali') === 'jalali' ? 'selected' : '' }}>{{ __('calendar.jalali') }}
+ default_calendar) === 'gregorian' ? 'selected' : '' }}>{{ __('calendar.gregorian') }}
+
+
+
+
+
+ allow_multi_currency ?? true) ? 'checked' : '' }}
+ class="w-4 h-4 rounded border-gray-300 text-teal-600 focus:ring-teal-500">
+ {{ __('settings.allow_multi_currency') }}
+
+
+
+
+
+
{{ __('settings.logo') }}
+
+ @if($tenant?->logo_path)
+
+ @endif
+
+
+
{{ __('settings.logo_help') }}
+
+
+
+
+ {{ __('common.save_changes') }}
+
+
+
diff --git a/resources/views/settings/partials/users.blade.php b/resources/views/settings/partials/users.blade.php
new file mode 100644
index 00000000..90ad6739
--- /dev/null
+++ b/resources/views/settings/partials/users.blade.php
@@ -0,0 +1,203 @@
+
+
+
+
+
+ {{ __('settings.add_user') }}
+
+
+
+
+
+
+
+
+ {{ __('settings.user_name') }}
+ {{ __('settings.user_email') }}
+ {{ __('settings.user_role') }}
+ {{ __('common.status') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($users as $user)
+
+
+
+
+ {{ mb_strtoupper(mb_substr($user->name, 0, 1)) }}
+
+
{{ $user->name }}
+
+
+ {{ $user->email }}
+
+ @foreach($user->roles as $role)
+
+ {{ $role->name }}
+
+ @endforeach
+ @if($user->roles->isEmpty())
+ —
+ @endif
+
+
+
+ {{ $user->is_active ? __('settings.active') : __('settings.inactive') }}
+
+
+
+
+
+ {{ __('common.edit') }}
+
+ @if($user->id !== auth()->id())
+
+ @csrf @method('PATCH')
+
+ {{ $user->is_active ? __('settings.deactivate') : __('settings.activate') }}
+
+
+ @endif
+
+
+
+ @empty
+
+ {{ __('settings.no_users') }}
+
+ @endforelse
+
+
+
+
+
+
+
+
+
+
+
+@push('scripts')
+
+@endpush
diff --git a/resources/views/tasks/form.blade.php b/resources/views/tasks/form.blade.php
new file mode 100644
index 00000000..974d1394
--- /dev/null
+++ b/resources/views/tasks/form.blade.php
@@ -0,0 +1,113 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($task) ? __('task.edit_task') : __('task.create_task') }}
+
+
+
+
+
+ @csrf
+ @if(isset($task))
+ @method('PUT')
+ @endif
+
+
+
+
+
{{ __('task.title') }} *
+
+ @error('title')
{{ $message }}
@enderror
+
+
+
+
+
{{ __('task.project') }} *
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ project_id ?? $preselectedProject ?? '') == $project->id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+ @error('project_id')
{{ $message }}
@enderror
+
+
+
+
+ {{ __('task.assignee') }}
+
+ {{ __('common.select') }}
+ @foreach($users as $user)
+ assignee_id ?? '') == $user->id ? 'selected' : '' }}>{{ $user->name }}
+ @endforeach
+
+
+
+
+
+ {{ __('task.status') }}
+
+ @foreach($statuses as $status)
+ status ?? 'pending') === $status ? 'selected' : '' }}>{{ __('task.status_' . $status) }}
+ @endforeach
+
+
+
+
+
+ {{ __('task.priority') }}
+
+ @foreach($priorities as $priority)
+ priority ?? 'medium') === $priority ? 'selected' : '' }}>{{ __('task.priority_' . $priority) }}
+ @endforeach
+
+
+
+
+
+ {{ __('task.start_date') }}
+
+
+
+
+
+ {{ __('task.due_date') }}
+
+
+
+
+
+ {{ __('task.estimated_hours') }}
+
+
+
+
+
+ {{ __('task.description') }}
+ {{ old('description', $task->description ?? '') }}
+
+
+
+
+
+
+
+@endsection
diff --git a/resources/views/tasks/gantt.blade.php b/resources/views/tasks/gantt.blade.php
new file mode 100644
index 00000000..4b10e7c5
--- /dev/null
+++ b/resources/views/tasks/gantt.blade.php
@@ -0,0 +1,150 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
{{ __('task.gantt') }}
+
+
+ {{ __('common.all') }} {{ __('project.projects') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+
+ @if(empty($ganttData))
+
+
+
{{ __('task.gantt_no_tasks') }}
+
+ @else
+
+
+
+
+
+
{{ __('task.gantt_legend') }}
+
+ {{ __('task.status_pending') }}
+ {{ __('task.status_in_progress') }}
+ {{ __('task.status_review') }}
+ {{ __('task.status_done') }}
+ {{ __('task.status_cancelled') }}
+
+
+ @endif
+
+
+@if(!empty($ganttData))
+@push('styles')
+
+
+@endpush
+
+@push('scripts')
+
+
+
+@endpush
+@endif
+@endsection
diff --git a/resources/views/tasks/index.blade.php b/resources/views/tasks/index.blade.php
new file mode 100644
index 00000000..b862c114
--- /dev/null
+++ b/resources/views/tasks/index.blade.php
@@ -0,0 +1,135 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
{{ __('task.tasks') }}
+
+
+
+
+
+
+
+ {{ __('task.project') }}
+
+ {{ __('common.all') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('task.status') }}
+
+ {{ __('common.all') }}
+ @foreach($statuses as $status)
+ {{ __('task.status_' . $status) }}
+ @endforeach
+
+
+
+ {{ __('task.priority') }}
+
+ {{ __('common.all') }}
+ @foreach($priorities as $priority)
+ {{ __('task.priority_' . $priority) }}
+ @endforeach
+
+
+
+ {{ __('task.assignee') }}
+
+ {{ __('common.all') }}
+ @foreach($users as $user)
+ id ? 'selected' : '' }}>{{ $user->name }}
+ @endforeach
+
+
+
+ {{ __('common.filter') }}
+
+ {{ __('common.reset') }}
+
+
+
+
+
+
+ @forelse($tasks as $task)
+
+
+
+
+
+
+
{{ $task->title }}
+
+ {{ $task->project?->name }}
+ @if($task->due_date)
+ •
+ {{ calendar_date($task->due_date) }}
+ @endif
+ @if($task->assignee)
+ •
+ {{ $task->assignee->name }}
+ @endif
+
+
+
+
+
+ @foreach($statuses as $s)
+ status === $s ? 'selected' : '' }}>{{ __('task.status_' . $s) }}
+ @endforeach
+
+
+ @empty
+
+
+
{{ __('task.no_tasks') }}
+
+ @endforelse
+
+
+
+
+
+ {{ $tasks->withQueryString()->links() }}
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/tasks/kanban.blade.php b/resources/views/tasks/kanban.blade.php
new file mode 100644
index 00000000..3cc2e486
--- /dev/null
+++ b/resources/views/tasks/kanban.blade.php
@@ -0,0 +1,153 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+ {{ __('common.all') }} {{ __('nav.projects') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+
+ {{ __('task.create') }}
+
+
+
+
+
+
+ @foreach([
+ 'pending' => ['color' => 'gray', 'icon' => 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z'],
+ 'in_progress' => ['color' => 'blue', 'icon' => 'M13 10V3L4 14h7v7l9-11h-7z'],
+ 'review' => ['color' => 'amber', 'icon' => 'M15 12a3 3 0 11-6 0 3 3 0 016 0z'],
+ 'done' => ['color' => 'emerald', 'icon' => 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z']
+ ] as $status => $config)
+
+
+
+
+
{{ __('task.status_' . $status) }}
+
{{ $kanbanColumns[$status]->count() }}
+
+
+
+
+ @forelse($kanbanColumns[$status] as $task)
+
+
+
+ @if($task->project)
+
{{ $task->project->name }}
+ @endif
+
+
+
+ @if($task->due_date)
+
+
+ {{ calendar_date($task->due_date) }}
+
+ @endif
+
+
+ @if($task->assignee)
+
+ {{ mb_strtoupper(mb_substr($task->assignee->name, 0, 1)) }}
+
+ @endif
+ {{-- Quick status change buttons --}}
+
+ @if($status !== 'pending')
+
+
+
+ @endif
+ @if($status !== 'in_progress')
+
+
+
+ @endif
+ @if($status !== 'review')
+
+
+
+ @endif
+ @if($status !== 'done')
+
+
+
+ @endif
+
+
+
+
+ @empty
+
+ {{ __('task.no_tasks') }}
+
+ @endforelse
+
+
+ @endforeach
+
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/tasks/show.blade.php b/resources/views/tasks/show.blade.php
new file mode 100644
index 00000000..e052699a
--- /dev/null
+++ b/resources/views/tasks/show.blade.php
@@ -0,0 +1,158 @@
+@extends('layouts.app')
+
+@section('content')
+
+
+
+
+
+
+
+
{{ $task->title }}
+
+ @if($task->project)
+ {{ $task->project->name }}
+ •
+ @endif
+
+ {{ __('task.status_' . $task->status) }}
+
+
+ {{ __('task.' . $task->priority) }}
+
+
+
+
+
+
+
+
+
+
+
{{ __('task.assignee') }}
+
{{ $task->assignee?->name ?? '-' }}
+
+
+
{{ __('task.dueDate') }}
+
+ {{ $task->due_date ? calendar_date($task->due_date) : '-' }}
+
+
+ @if($task->start_date)
+
+
{{ __('project.startDate') }}
+
{{ calendar_date($task->start_date) }}
+
+ @endif
+ @if($task->completed_at)
+
+
{{ __('task.completedAt') }}
+
{{ calendar_date($task->completed_at) }}
+
+ @endif
+ @if($task->description)
+
+
{{ __('task.description') }}
+
{{ $task->description }}
+
+ @endif
+
+
+
+
+
{{ __('task.status') }}:
+ @foreach(['pending', 'in_progress', 'review', 'done'] as $s)
+
+ {{ __('task.status_' . $s) }}
+
+ @endforeach
+
+
+
+
+ @if($task->children->count() > 0)
+
+
+
زیروظایف
+
+
+ @foreach($task->children as $child)
+
+
+ {{ __('task.' . $child->priority) }}
+
+
{{ $child->title }}
+
+ {{ __('task.status_' . $child->status) }}
+
+
{{ $child->assignee?->name }}
+
+ @endforeach
+
+
+ @endif
+
+
+ @if($task->logs->count() > 0)
+
+
+
{{ __('task.activityLog') }}
+
+
+ @foreach($task->logs as $log)
+
+
+ {{ $log->user?->name ?? '-' }}
+ {{ $log->created_at ? calendar_date($log->created_at) : '' }}
+
+
+ @if($log->field_changed === 'created')
+ {{ __('task.logCreated') }}
+ @elseif($log->field_changed === 'status')
+ {{ __('task.logStatusChanged') }}: {{ __('task.status_' . $log->old_value) }} → {{ __('task.status_' . $log->new_value) }}
+ @else
+ {{ $log->field_changed }}: {{ $log->old_value }} → {{ $log->new_value }}
+ @endif
+
+
+ @endforeach
+
+
+ @endif
+
+
+@push('scripts')
+
+@endpush
+@endsection
diff --git a/resources/views/warehouses/form.blade.php b/resources/views/warehouses/form.blade.php
new file mode 100644
index 00000000..aed33092
--- /dev/null
+++ b/resources/views/warehouses/form.blade.php
@@ -0,0 +1,95 @@
+@extends('layouts.app')
+
+@section('title', isset($warehouse) ? __('warehouse.edit_warehouse') : __('warehouse.add_warehouse'))
+
+@section('content')
+
+
+
+
+
+
+
+ {{ isset($warehouse) ? __('warehouse.edit_warehouse') : __('warehouse.add_warehouse') }}
+
+
+
+
+
+
+ @csrf
+ @if(isset($warehouse))
+ @method('PUT')
+ @endif
+
+
+
+
+
+ {{ __('warehouse.name') }} *
+
+
+ @error('name')
+
{{ $message }}
+ @enderror
+
+
+
+
+ {{ __('warehouse.location') }}
+
+
+ @error('location')
+
{{ $message }}
+ @enderror
+
+
+
+
+
+
+ {{ __('expense.project') }}
+
+
+ {{ __('common.select') }}
+ @foreach($projects as $project)
+ project_id : '') == $project->id ? 'selected' : '' }}>
+ {{ $project->name }}
+
+ @endforeach
+
+ @error('project_id')
+
{{ $message }}
+ @enderror
+
+
+
+
+ is_active : true) ? 'checked' : '' }}
+ class="rounded border-gray-300 text-teal-600 focus:ring-teal-500">
+ {{ __('common.active') }}
+
+
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/warehouses/index.blade.php b/resources/views/warehouses/index.blade.php
new file mode 100644
index 00000000..e4ccb61e
--- /dev/null
+++ b/resources/views/warehouses/index.blade.php
@@ -0,0 +1,92 @@
+@extends('layouts.app')
+
+@section('title', __('warehouse.warehouses'))
+
+@section('content')
+
+
+
+
+
+
+
+
+
+ {{ __('common.all') }} {{ __('expense.project') }}
+ @foreach($projects as $project)
+ id ? 'selected' : '' }}>{{ $project->name }}
+ @endforeach
+
+
+ {{ __('common.all') }}
+ {{ __('common.active') }}
+ {{ __('common.inactive') }}
+
+
+ {{ __('common.filter') }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('warehouse.name') }}
+ {{ __('warehouse.location') }}
+ {{ __('expense.project') }}
+ {{ __('common.status') }}
+ {{ __('common.actions') }}
+
+
+
+ @forelse($warehouses as $wh)
+
+
+ {{ $wh->name }}
+
+ {{ $wh->location ?? '-' }}
+ {{ $wh->project?->name ?? '-' }}
+
+ @if($wh->is_active)
+ {{ __('common.active') }}
+ @else
+ {{ __('common.inactive') }}
+ @endif
+
+
+
+
+
+ @empty
+
+ {{ __('warehouse.no_warehouses') }}
+
+ @endforelse
+
+
+
+
+
+
+
+ {{ $warehouses->withQueryString()->links() }}
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/warehouses/show.blade.php b/resources/views/warehouses/show.blade.php
new file mode 100644
index 00000000..d2795e65
--- /dev/null
+++ b/resources/views/warehouses/show.blade.php
@@ -0,0 +1,64 @@
+@extends('layouts.app')
+
+@section('title', $warehouse->name)
+
+@section('content')
+
+
+
+
+
+
+
+
+
{{ $warehouse->name }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ __('warehouse.name') }}
+
{{ $warehouse->name }}
+
+
+
{{ __('warehouse.location') }}
+
{{ $warehouse->location ?? '-' }}
+
+
+
+
{{ __('common.status') }}
+ @if($warehouse->is_active)
+
{{ __('common.active') }}
+ @else
+
{{ __('common.inactive') }}
+ @endif
+
+
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php
deleted file mode 100644
index 7b626b14..00000000
--- a/resources/views/welcome.blade.php
+++ /dev/null
@@ -1,172 +0,0 @@
-
-
-
-
-
-
- Laravel
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Documentation
-
-
- Laravel has wonderful documentation covering every aspect of the framework. Whether you are a newcomer or have prior experience with Laravel, we recommend reading our documentation from beginning to end.
-
-
-
-
-
-
-
-
-
-
-
-
-
Laracasts
-
-
- Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process.
-
-
-
-
-
-
-
-
-
-
-
Laravel News
-
-
- Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials.
-
-
-
-
-
-
-
-
-
-
-
Vibrant Ecosystem
-
-
- Laravel's robust library of first-party tools and libraries, such as Forge , Vapor , Nova , Envoyer , and Herd help you take your projects to the next level. Pair them with powerful open source libraries like Cashier , Dusk , Echo , Horizon , Sanctum , Telescope , and more.
-
-
-
-
-
-
-
- Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }})
-
-
-
-
-
-
diff --git a/routes/api.php b/routes/api.php
new file mode 100644
index 00000000..498218a8
--- /dev/null
+++ b/routes/api.php
@@ -0,0 +1,79 @@
+group(function () {
+
+ // Current user with roles, permissions, and tenant
+ Route::get('/user', function (\Illuminate\Http\Request $request) {
+ return $request->user()->load('tenant', 'roles.permissions', 'permissions');
+ });
+
+ // Dashboard stats
+ Route::get('/dashboard/stats', [DashboardController::class, 'index'])->name('api.dashboard.stats');
+
+ // Projects API
+ Route::apiResource('projects', ProjectController::class);
+ Route::put('projects/{project}/status', [ProjectController::class, 'updateStatus']);
+ Route::get('projects-export/excel', [ProjectController::class, 'exportExcel']);
+ Route::get('projects-export/pdf', [ProjectController::class, 'exportPdf']);
+
+ // Tasks API
+ Route::apiResource('tasks', TaskController::class);
+ Route::put('tasks/{task}/status', [TaskController::class, 'updateStatus']);
+ Route::get('tasks-kanban', [TaskController::class, 'kanban']);
+
+ // Employees API
+ Route::apiResource('employees', EmployeeController::class);
+ Route::get('employees/{employee}/work-logs', [EmployeeController::class, 'workLogs']);
+ Route::get('employees/{employee}/financials', [EmployeeController::class, 'financials']);
+
+ // Expenses API
+ Route::apiResource('expenses', ExpenseController::class);
+ Route::put('expenses/{expense}/approve', [ExpenseController::class, 'approve']);
+ Route::put('expenses/{expense}/reject', [ExpenseController::class, 'reject']);
+
+ // Inventory API
+ Route::prefix('inventory')->group(function () {
+ Route::get('items', [InventoryController::class, 'items']);
+ Route::get('warehouses', [InventoryController::class, 'warehouses']);
+ Route::get('transactions', [InventoryController::class, 'transactions']);
+ Route::post('stock-in', [InventoryController::class, 'stockIn']);
+ Route::post('stock-out', [InventoryController::class, 'stockOut']);
+ Route::get('current-stock', [InventoryController::class, 'currentStock']);
+ });
+
+ // Locale/Settings API
+ Route::post('/locale', function (\Illuminate\Http\Request $request) {
+ $locale = $request->validate(['locale' => 'required|in:fa,en,ar'])['locale'];
+ session(['locale' => $locale]);
+ app()->setLocale($locale);
+ if ($request->user()) {
+ $request->user()->update(['locale' => $locale]);
+ }
+ return response()->json(['locale' => $locale]);
+ });
+
+ Route::post('/calendar', function (\Illuminate\Http\Request $request) {
+ $calendar = $request->validate(['calendar' => 'required|in:jalali,gregorian'])['calendar'];
+ session(['calendar' => $calendar]);
+ return response()->json(['calendar' => $calendar]);
+ });
+});
diff --git a/routes/console.php b/routes/console.php
index eff2ed24..3c9adf1a 100644
--- a/routes/console.php
+++ b/routes/console.php
@@ -5,4 +5,4 @@ use Illuminate\Support\Facades\Artisan;
Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
-})->purpose('Display an inspiring quote')->hourly();
+})->purpose('Display an inspiring quote');
diff --git a/routes/letters-routes.php b/routes/letters-routes.php
new file mode 100644
index 00000000..78f4a26c
--- /dev/null
+++ b/routes/letters-routes.php
@@ -0,0 +1,10 @@
+name('letters.archive');
+Route::get('letters-attachment/{attachment}/download', [LetterController::class, 'downloadAttachment'])->name('letters.attachment.download');
+Route::delete('letters-attachment/{attachment}', [LetterController::class, 'deleteAttachment'])->name('letters.attachment.delete');
diff --git a/routes/web.php b/routes/web.php
index 86a06c53..47a6b25a 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -1,7 +1,188 @@
name('login');
+Route::post('/login', [AuthController::class, 'login'])->name('login.post');
+Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
+
+// Protected Routes (auth required)
+Route::middleware(['auth', \App\Http\Middleware\SetTenant::class, \App\Http\Middleware\SetLocale::class, \App\Http\Middleware\AuditLogMiddleware::class])->group(function () {
+
+ // Dashboard
+ Route::get('/', [DashboardController::class, 'index'])->name('dashboard');
+ Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard.index');
+
+ // Locale & Settings
+ Route::post('/locale', function (\Illuminate\Http\Request $request) {
+ $locale = $request->validate(['locale' => 'required|in:fa,en,ar'])['locale'];
+ session(['locale' => $locale]);
+ app()->setLocale($locale);
+ if ($request->user()) {
+ $request->user()->update(['locale' => $locale]);
+ }
+ return redirect()->back();
+ })->name('locale.update');
+
+ Route::post('/calendar', function (\Illuminate\Http\Request $request) {
+ $calendar = $request->validate(['calendar' => 'required|in:jalali,gregorian'])['calendar'];
+ session(['calendar' => $calendar]);
+ if ($request->user()) {
+ $request->user()->update(['preferred_calendar' => $calendar]);
+ }
+ return redirect()->back();
+ })->name('calendar.update');
+
+ Route::post('/currency', function (\Illuminate\Http\Request $request) {
+ $currency = $request->validate(['currency' => 'required|in:IRR,USD,EUR,AED'])['currency'];
+ session(['currency' => $currency]);
+ return redirect()->back();
+ })->name('currency.update');
+
+ // Projects
+ Route::resource('projects', ProjectController::class);
+ Route::put('projects/{project}/status', [ProjectController::class, 'updateStatus'])->name('projects.update-status');
+ Route::get('projects-export/excel', [ProjectController::class, 'exportExcel'])->name('projects.export.excel');
+ Route::get('projects-export/pdf', [ProjectController::class, 'exportPdf'])->name('projects.export.pdf');
+
+ // Tasks
+ Route::resource('tasks', TaskController::class);
+ Route::put('tasks/{task}/status', [TaskController::class, 'updateStatus'])->name('tasks.update-status');
+ Route::get('tasks-kanban', [TaskController::class, 'kanban'])->name('tasks.kanban');
+ Route::get('tasks-gantt', [TaskController::class, 'gantt'])->name('tasks.gantt');
+ Route::get('tasks/{task}/gantt', [TaskController::class, 'gantt'])->name('tasks.gantt-project');
+
+ // Employees
+ Route::resource('employees', EmployeeController::class);
+ Route::get('employees/{employee}/work-logs', [EmployeeController::class, 'workLogs'])->name('employees.work-logs');
+ Route::get('employees/{employee}/financials', [EmployeeController::class, 'financials'])->name('employees.financials');
+
+ // Expenses
+ Route::resource('expenses', ExpenseController::class);
+ Route::put('expenses/{expense}/approve', [ExpenseController::class, 'approve'])->name('expenses.approve');
+ Route::put('expenses/{expense}/reject', [ExpenseController::class, 'reject'])->name('expenses.reject');
+
+ // Petty Cash
+ Route::resource('petty-cashes', PettyCashController::class);
+ Route::get('petty-cashes/{pettyCash}/create-expense', [PettyCashController::class, 'createExpense'])->name('petty-cashes.create-expense');
+ Route::post('petty-cashes/{pettyCash}/store-expense', [PettyCashController::class, 'storeExpense'])->name('petty-cashes.store-expense');
+ Route::put('petty-cashes/{pettyCash}/approve', [PettyCashController::class, 'approve'])->name('petty-cashes.approve');
+ Route::put('petty-cashes/{pettyCash}/reject', [PettyCashController::class, 'reject'])->name('petty-cashes.reject');
+ Route::put('petty-cashes/{pettyCash}/settle', [PettyCashController::class, 'settle'])->name('petty-cashes.settle');
+
+ // Inventory
+ Route::prefix('inventory')->name('inventory.')->group(function () {
+ // Dashboard
+ Route::get('/', [InventoryController::class, 'index'])->name('index');
+
+ // Items CRUD
+ Route::get('items', [InventoryController::class, 'items'])->name('items');
+ Route::get('items/create', [InventoryController::class, 'createItem'])->name('items.create');
+ Route::post('items', [InventoryController::class, 'storeItem'])->name('items.store');
+ Route::get('items/{item}/edit', [InventoryController::class, 'editItem'])->name('items.edit');
+ Route::put('items/{item}', [InventoryController::class, 'updateItem'])->name('items.update');
+ Route::delete('items/{item}', [InventoryController::class, 'destroyItem'])->name('items.destroy');
+
+ // Warehouses CRUD
+ Route::get('warehouses', [InventoryController::class, 'warehouses'])->name('warehouses');
+ Route::get('warehouses/create', [InventoryController::class, 'createWarehouse'])->name('warehouses.create');
+ Route::post('warehouses', [InventoryController::class, 'storeWarehouse'])->name('warehouses.store');
+ Route::get('warehouses/{warehouse}/edit', [InventoryController::class, 'editWarehouse'])->name('warehouses.edit');
+ Route::put('warehouses/{warehouse}', [InventoryController::class, 'updateWarehouse'])->name('warehouses.update');
+ Route::delete('warehouses/{warehouse}', [InventoryController::class, 'destroyWarehouse'])->name('warehouses.destroy');
+
+ // Transactions
+ Route::get('transactions', [InventoryController::class, 'transactions'])->name('transactions');
+
+ // Stock operations
+ Route::post('stock-in', [InventoryController::class, 'stockIn'])->name('stock-in');
+ Route::post('stock-out', [InventoryController::class, 'stockOut'])->name('stock-out');
+ Route::post('transfer', [InventoryController::class, 'stockTransfer'])->name('transfer');
+ Route::get('current-stock', [InventoryController::class, 'currentStock'])->name('current-stock');
+ });
+
+ // Letters (Correspondence)
+ Route::resource('letters', LetterController::class);
+ Route::put('letters/{letter}/archive', [LetterController::class, 'archive'])->name('letters.archive');
+
+ // Letters (Correspondence)
+ Route::resource('letters', LetterController::class);
+ Route::put('letters/{letter}/archive', [LetterController::class, 'archive'])->name('letters.archive');
+
+ // Documents
+ Route::resource('documents', DocumentController::class);
+ Route::get('documents/{document}/download', [DocumentController::class, 'download'])->name('documents.download');
+
+ // Reports
+ Route::prefix('reports')->name('reports.')->group(function () {
+ Route::get('/', [ReportController::class, 'index'])->name('index');
+ Route::get('financial', [ReportController::class, 'financial'])->name('financial');
+ Route::get('financial/excel', [ReportController::class, 'financialExportExcel'])->name('financial.excel');
+ Route::get('financial/pdf', [ReportController::class, 'financialExportPdf'])->name('financial.pdf');
+ Route::get('employee-worklog', [ReportController::class, 'employeeWorklog'])->name('employee-worklog');
+ Route::get('employee-worklog/excel', [ReportController::class, 'employeeWorklogExportExcel'])->name('employee-worklog.excel');
+ Route::get('inventory', [ReportController::class, 'inventory'])->name('inventory');
+ Route::get('inventory/excel', [ReportController::class, 'inventoryExportExcel'])->name('inventory.excel');
+ Route::get('payroll', [ReportController::class, 'payroll'])->name('payroll');
+ Route::get('payroll/excel', [ReportController::class, 'payrollExportExcel'])->name('payroll.excel');
+ });
+
+ Route::prefix('payroll')->name('payroll.')->group(function () {
+ Route::get('/', [PayrollController::class, 'index'])->name('index');
+ Route::get('/calculate', [PayrollController::class, 'calculate'])->name('calculate');
+ Route::post('/auto-calculate', [PayrollController::class, 'autoCalculate'])->name('auto-calculate');
+ Route::post('/store', [PayrollController::class, 'store'])->name('store');
+ Route::get('/settlement', [PayrollController::class, 'settlement'])->name('settlement');
+ Route::post('/preview-settlement', [PayrollController::class, 'previewSettlement'])->name('preview-settlement');
+ Route::post('/process-settlement', [PayrollController::class, 'processSettlement'])->name('process-settlement');
+ Route::post('/pay-bulk', [PayrollController::class, 'payBulk'])->name('pay-bulk');
+ Route::get('/{employee}', [PayrollController::class, 'show'])->name('show');
+ Route::post('/{financial}/pay', [PayrollController::class, 'pay'])->name('pay');
+ Route::delete('/{financial}', [PayrollController::class, 'destroy'])->name('destroy');
+ });
+
+ // Settings
+ Route::prefix('settings')->name('settings.')->group(function () {
+ Route::get('/', [SettingsController::class, 'index'])->name('index');
+ Route::put('organization', [SettingsController::class, 'updateOrganization'])->name('organization.update');
+ Route::post('users', [SettingsController::class, 'storeUser'])->name('users.store');
+ Route::put('users/{user}', [SettingsController::class, 'updateUser'])->name('users.update');
+ Route::patch('users/{user}/toggle', [SettingsController::class, 'toggleUser'])->name('users.toggle');
+ Route::post('currencies', [SettingsController::class, 'storeCurrency'])->name('currencies.store');
+ Route::put('currencies/{currency}', [SettingsController::class, 'updateCurrency'])->name('currencies.update');
+ Route::patch('currencies/{currency}/toggle', [SettingsController::class, 'toggleCurrency'])->name('currencies.toggle');
+ });
+
+
+ // Attachments
+ Route::post('attachments', [AttachmentController::class, 'store'])->name('attachments.store');
+ Route::delete('attachments/{attachment}', [AttachmentController::class, 'destroy'])->name('attachments.destroy');
+ Route::get('attachments/{attachment}/download', [AttachmentController::class, 'download'])->name('attachments.download');
});
diff --git a/sidebar-addition.txt b/sidebar-addition.txt
new file mode 100644
index 00000000..f25e93a4
--- /dev/null
+++ b/sidebar-addition.txt
@@ -0,0 +1,16 @@
+This file contains the sidebar section to be added.
+Replace the section between "{{-- Warehouse with sub-menu --}}" and "{{-- Remaining nav items --}}"
+with the following additional nav item added AFTER the warehouse section:
+
+ {{-- Letters --}}
+ @php
+ $isLettersActive = request()->routeIs('letters.*');
+ @endphp
+
+
+
+
+ {{ __('nav.letters') }}
+
diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore
deleted file mode 100644
index d6b7ef32..00000000
--- a/storage/framework/views/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
diff --git a/storage/framework/views/0815a55745834eb105f466067e7b4e86.php b/storage/framework/views/0815a55745834eb105f466067e7b4e86.php
new file mode 100644
index 00000000..df31cade
--- /dev/null
+++ b/storage/framework/views/0815a55745834eb105f466067e7b4e86.php
@@ -0,0 +1,55 @@
+ 'medium']));
+
+foreach ($attributes->all() as $__key => $__value) {
+ if (in_array($__key, $__propNames)) {
+ $$__key = $$__key ?? $__value;
+ } else {
+ $__newAttributes[$__key] = $__value;
+ }
+}
+
+$attributes = new \Illuminate\View\ComponentAttributeBag($__newAttributes);
+
+unset($__propNames);
+unset($__newAttributes);
+
+foreach (array_filter((['priority' => 'medium']), 'is_string', ARRAY_FILTER_USE_KEY) as $__key => $__value) {
+ $$__key = $$__key ?? $__value;
+}
+
+$__defined_vars = get_defined_vars();
+
+foreach ($attributes->all() as $__key => $__value) {
+ if (array_key_exists($__key, $__defined_vars)) unset($$__key);
+}
+
+unset($__defined_vars); ?>
+
+ 'bg-gray-100 text-gray-600',
+ 'medium' => 'bg-amber-50 text-amber-600',
+ 'high' => 'bg-orange-50 text-orange-600',
+ 'urgent' => 'bg-rose-50 text-rose-600',
+ ];
+
+ $dots = [
+ 'low' => 'bg-gray-400',
+ 'medium' => 'bg-amber-400',
+ 'high' => 'bg-orange-500',
+ 'urgent' => 'bg-rose-500',
+ ];
+
+ $colorClass = $colors[$priority] ?? $colors['medium'];
+ $dotClass = $dots[$priority] ?? $dots['medium'];
+?>
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/storage/framework/views/0dc35d574885e9ed87093ad3cc8d1de5.php b/storage/framework/views/0dc35d574885e9ed87093ad3cc8d1de5.php
new file mode 100644
index 00000000..ed1f4fdc
--- /dev/null
+++ b/storage/framework/views/0dc35d574885e9ed87093ad3cc8d1de5.php
@@ -0,0 +1,183 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $t): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $s): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'from_date','value' => request('from_date')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'from_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(request('from_date'))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'to_date','value' => request('to_date')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'to_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(request('to_date'))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $letter): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+
+ type)); ?>
+
+
+
+ reference_number ?? '-'); ?>
+ subject); ?>
+ sender); ?>
+ recipient); ?>
+
+
+ status)); ?>
+
+
+
+ letter_date ? calendar_date($letter->letter_date) : '-'); ?>
+
+
+ attachments->count() > 0): ?>
+
+
+
+
+
+
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/15dc9805bd50ffbccd898a04c0572745.php b/storage/framework/views/15dc9805bd50ffbccd898a04c0572745.php
new file mode 100644
index 00000000..59777464
--- /dev/null
+++ b/storage/framework/views/15dc9805bd50ffbccd898a04c0572745.php
@@ -0,0 +1,183 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $cat): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $status): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'from_date','value' => request('from_date')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'from_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(request('from_date'))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'to_date','value' => request('to_date')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'to_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(request('to_date'))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $expense): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+ title); ?>
+
+ project?->name); ?>
+
+ amount, $expense->currency?->code)); ?>
+
+ original_amount): ?>
+ (original_amount, $expense->currency?->code)); ?>)
+
+
+ category)); ?>
+ expense_date)); ?>
+
+
+ status_label); ?>
+
+
+
+
+
+ status === 'pending'): ?>
+
+
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+
+startPush('scripts'); ?>
+
+stopPush(); ?>
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/42fce2afce1274cac2c89c79739f9fc3.php b/storage/framework/views/42fce2afce1274cac2c89c79739f9fc3.php
new file mode 100644
index 00000000..c234138b
--- /dev/null
+++ b/storage/framework/views/42fce2afce1274cac2c89c79739f9fc3.php
@@ -0,0 +1,118 @@
+
+
+
\ No newline at end of file
diff --git a/storage/framework/views/448a787e655d73dc75d1ab0fcc84b86f.php b/storage/framework/views/448a787e655d73dc75d1ab0fcc84b86f.php
new file mode 100644
index 00000000..ded36391
--- /dev/null
+++ b/storage/framework/views/448a787e655d73dc75d1ab0fcc84b86f.php
@@ -0,0 +1,188 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+ : | :
+
+ 0 ? round(($totalPaid / $totalGross) * 100) : 0; ?>
+
+
%
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $m): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $y): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ count() > 0): ?>
+
+ —
+
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/46e7c1d03b179af2b30bd5f0a8a09de0.php b/storage/framework/views/46e7c1d03b179af2b30bd5f0a8a09de0.php
new file mode 100644
index 00000000..1c79d07a
--- /dev/null
+++ b/storage/framework/views/46e7c1d03b179af2b30bd5f0a8a09de0.php
@@ -0,0 +1,269 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
title); ?>
+
+ status_label); ?>
+
+
+
+
+
+
+
+ status === 'pending'): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
project?->name ?? '-'); ?>
+
+
+
+
+
amount, $expense->currency?->code)); ?>
+ original_amount && $expense->original_amount != $expense->amount): ?>
+
(original_amount, $expense->currency?->code)); ?>)
+
+
+
+
+
expense_date ? calendar_date($expense->expense_date) : '-'); ?>
+
+
+
+
+
+ invoice_number): ?>
+
+
+
invoice_number); ?>
+
+
+
+
+
requestedBy?->name ?? '-'); ?>
+
+ approvedBy): ?>
+
+
+
approvedBy?->name); ?>
+
+
+ approval_date): ?>
+
+
+
approval_date)); ?>
+
+
+
+
+
+
+ description): ?>
+
+
+
+
+
+ receipt_path): ?>
+
+
+
+ receipt_path), '.pdf')): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ attachments; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $attachment): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+ is_image): ?>
+
+
+
+ is_pdf): ?>
+
+
+
+
+
+
+
file_name); ?>
+
formatted_size); ?> • created_at?->format('Y/m/d H:i')); ?>
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+startPush('scripts'); ?>
+
+stopPush(); ?>
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/4ec001dbe225074190f1867b3ff08122.php b/storage/framework/views/4ec001dbe225074190f1867b3ff08122.php
new file mode 100644
index 00000000..0c49d771
--- /dev/null
+++ b/storage/framework/views/4ec001dbe225074190f1867b3ff08122.php
@@ -0,0 +1,8 @@
+startSection('content'); ?>
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/5625be61c087bf17bbc0b8985f4cf4b2.php b/storage/framework/views/5625be61c087bf17bbc0b8985f4cf4b2.php
new file mode 100644
index 00000000..f5a44357
--- /dev/null
+++ b/storage/framework/views/5625be61c087bf17bbc0b8985f4cf4b2.php
@@ -0,0 +1,30 @@
+startSection('content'); ?>
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/5c57a35cb8207405007d3ffb6056da18.php b/storage/framework/views/5c57a35cb8207405007d3ffb6056da18.php
new file mode 100644
index 00000000..1071f16d
--- /dev/null
+++ b/storage/framework/views/5c57a35cb8207405007d3ffb6056da18.php
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ yieldContent('title'); ?>
+
+
+
+
+
+
+
+
+
+
+ yieldContent('code'); ?>
+
+
+
+ yieldContent('message'); ?>
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/storage/framework/views/63827679d7679f25341f7abe0c3ec203.php b/storage/framework/views/63827679d7679f25341f7abe0c3ec203.php
new file mode 100644
index 00000000..d3b6bf4a
--- /dev/null
+++ b/storage/framework/views/63827679d7679f25341f7abe0c3ec203.php
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ yieldPushContent('styles'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ make('layouts.sidebar', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
+
+
+
+
+
+ make('layouts.header', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
+
+
+
+
+
+
+
+
+
+
+
+ yieldContent('content'); ?>
+
+
+
+
+
+
© Vernova —
+
+
+
+
+
+
+
+
+
+
+
+
getLocale() === 'fa' ? 'فارسی' : 'English'); ?>
+
+
+
+
+
+
+ yieldPushContent('scripts'); ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/storage/framework/views/65f6c99804a6a0edbd8042acc1b80c49.php b/storage/framework/views/65f6c99804a6a0edbd8042acc1b80c49.php
new file mode 100644
index 00000000..e82eb74e
--- /dev/null
+++ b/storage/framework/views/65f6c99804a6a0edbd8042acc1b80c49.php
@@ -0,0 +1,250 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/storage/framework/views/663a1a1fea235ffe5ad3f8ff45ecda31.php b/storage/framework/views/663a1a1fea235ffe5ad3f8ff45ecda31.php
new file mode 100644
index 00000000..41e2faec
--- /dev/null
+++ b/storage/framework/views/663a1a1fea235ffe5ad3f8ff45ecda31.php
@@ -0,0 +1,93 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $type): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $document): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+ title); ?>
+
+
+ file_type)); ?>
+
+ project?->name ?? '-'); ?>
+ uploader?->name ?? '-'); ?>
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/7379735e393d6b0e3866bee72506a75c.php b/storage/framework/views/7379735e393d6b0e3866bee72506a75c.php
new file mode 100644
index 00000000..12e6b7d5
--- /dev/null
+++ b/storage/framework/views/7379735e393d6b0e3866bee72506a75c.php
@@ -0,0 +1,41 @@
+startSection('content'); ?>
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/86a592b10d7a65e83deaa9802eeb20fb.php b/storage/framework/views/86a592b10d7a65e83deaa9802eeb20fb.php
new file mode 100644
index 00000000..dae3dc89
--- /dev/null
+++ b/storage/framework/views/86a592b10d7a65e83deaa9802eeb20fb.php
@@ -0,0 +1,163 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+
+ type : ($prefillType ?? '')) === 'incoming' ? 'selected' : ''); ?>>
+ type : ($prefillType ?? '')) === 'outgoing' ? 'selected' : ''); ?>>
+
+
+
+
+
+ *
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $s): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ status : 'draft') === $s ? 'selected' : ''); ?>>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+ status) === 'archived' ? 'selected' : ''); ?>>
+
+
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'letter_date','value' => old('letter_date', $letter?->letter_date?->format('Y-m-d')),'required' => 'true']] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'letter_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(old('letter_date', $letter?->letter_date?->format('Y-m-d'))),'required' => 'true']); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+
+
+
+
+
+ *
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ project_id : '') == $project->id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+ *
+
+
+
+
+
+
+ content : '')); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/8978f0f93ca9bd010ca93ff26a7c11ff.php b/storage/framework/views/8978f0f93ca9bd010ca93ff26a7c11ff.php
new file mode 100644
index 00000000..b16ee077
--- /dev/null
+++ b/storage/framework/views/8978f0f93ca9bd010ca93ff26a7c11ff.php
@@ -0,0 +1,118 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
first_name); ?> last_name); ?>
+
+
+
+
+
+
+
+
+
currency?->code)); ?>
+
+
+
+
currency?->code)); ?>
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $ft): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ >
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $fin): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+ effective_date ? calendar_date($fin->effective_date) : '-'); ?>
+
+
+ type)); ?>
+
+
+
+
+ type, ['deduction', 'advance']) ? '-' : '+'); ?>amount, $fin->currency?->code)); ?>
+
+
+
+ is_paid)): ?>
+
+ is_paid ? __('employee.isPaid') : __('employee.isUnpaid')); ?>
+
+
+
+ -
+
+
+ description ?? '-'); ?>
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/90d43165439e87034f1fc57adfb2dd86.php b/storage/framework/views/90d43165439e87034f1fc57adfb2dd86.php
new file mode 100644
index 00000000..97ad23c2
--- /dev/null
+++ b/storage/framework/views/90d43165439e87034f1fc57adfb2dd86.php
@@ -0,0 +1,150 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+startPush('styles'); ?>
+
+
+stopPush(); ?>
+
+startPush('scripts'); ?>
+
+
+
+stopPush(); ?>
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/95971f91e644c2c50f9234aa4ccd2329.php b/storage/framework/views/95971f91e644c2c50f9234aa4ccd2329.php
new file mode 100644
index 00000000..d11d8bfe
--- /dev/null
+++ b/storage/framework/views/95971f91e644c2c50f9234aa4ccd2329.php
@@ -0,0 +1,103 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ >
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $warehouse): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+ name); ?>
+ location ?? '-'); ?>
+ project?->name ?? '-'); ?>
+ inventoryTransactions()->distinct('item_id')->count('item_id'))); ?>
+
+
+ is_active ? __('inventory.active') : __('inventory.inactive')); ?>
+
+
+
+
+
+
+
+
+ inventoryTransactions()->count() === 0): ?>
+
+
+
+
+
+
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/98463ca068fc45f46d96fd75afccf591.php b/storage/framework/views/98463ca068fc45f46d96fd75afccf591.php
new file mode 100644
index 00000000..0fe1893c
--- /dev/null
+++ b/storage/framework/views/98463ca068fc45f46d96fd75afccf591.php
@@ -0,0 +1,168 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
*
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ project_id ?? $preselectedProject ?? '') == $project->id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $user): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ assignee_id ?? '') == $user->id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $status): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ status ?? 'pending') === $status ? 'selected' : ''); ?>>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $priority): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ priority ?? 'medium') === $priority ? 'selected' : ''); ?>>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'start_date','value' => old('start_date', isset($task) && $task->start_date ? $task->start_date->format('Y-m-d') : '')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'start_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(old('start_date', isset($task) && $task->start_date ? $task->start_date->format('Y-m-d') : ''))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'due_date','value' => old('due_date', isset($task) && $task->due_date ? $task->due_date->format('Y-m-d') : '')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'due_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(old('due_date', isset($task) && $task->due_date ? $task->due_date->format('Y-m-d') : ''))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ description ?? '')); ?>
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/9b62b89896774f58ae7160b9f9c3f8f5.php b/storage/framework/views/9b62b89896774f58ae7160b9f9c3f8f5.php
new file mode 100644
index 00000000..4d7374a8
--- /dev/null
+++ b/storage/framework/views/9b62b89896774f58ae7160b9f9c3f8f5.php
@@ -0,0 +1,7 @@
+
+
+startSection('title', __('Forbidden')); ?>
+startSection('code', '403'); ?>
+startSection('message', __($exception->getMessage() ?: 'Forbidden')); ?>
+
+make('errors::minimal', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/a03636601cf2476624659c1e63087b5d.php b/storage/framework/views/a03636601cf2476624659c1e63087b5d.php
new file mode 100644
index 00000000..ef8ca49a
--- /dev/null
+++ b/storage/framework/views/a03636601cf2476624659c1e63087b5d.php
@@ -0,0 +1,251 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLocale() === 'fa' ? persian_num($activeProjectsCount) : $activeProjectsCount); ?>
+
+
+
+
+
+
+
+ 0 ? '↑' : '↓'); ?> %
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLocale() === 'fa' ? persian_num($pendingTasksCount) : $pendingTasksCount); ?>
+
+
+
+
+
+
+
+ 0 ? '↑' : '↓'); ?> %
+
+
+
+
+
+
+
+
+
+
+
+
+ getLocale() === 'fa' ? persian_num($activeEmployeesCount) : $activeEmployeesCount); ?>
+
+
+
+
+
+
+
+ 0 ? '↑' : '↓'); ?> %
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+
+
+ name); ?>
+
+
+
+ getLocale() === 'fa' ? persian_num($project->progress) : $project->progress); ?>%
+
+
+
+
+ code); ?>
+ manager): ?>
+ manager->name); ?>
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $activity): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+ user?->name ?? '?', 0, 1))); ?>
+
+
+
user?->name); ?> — description); ?>
+
created_at)); ?>
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getLocale() === 'fa' ? persian_num(1234567.89) : number_format(1234567.89, 2)); ?>
+
+
+
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/a11f4e6f418ca75d82ec12f95844d120.php b/storage/framework/views/a11f4e6f418ca75d82ec12f95844d120.php
new file mode 100644
index 00000000..2d19419c
--- /dev/null
+++ b/storage/framework/views/a11f4e6f418ca75d82ec12f95844d120.php
@@ -0,0 +1,174 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
*
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+ *
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $type): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ contract_type ?? '') === $type ? 'selected' : ''); ?>>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+ *
+
+
+ 'components.date-input','data' => ['name' => 'hire_date','value' => old('hire_date', isset($employee) && $employee->hire_date ? $employee->hire_date->format('Y-m-d') : ''),'required' => 'true']] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'hire_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(old('hire_date', isset($employee) && $employee->hire_date ? $employee->hire_date->format('Y-m-d') : '')),'required' => 'true']); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ project_id ?? '') == $project->id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $currency): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ currency_id ?? '') == $currency->id ? 'selected' : ''); ?>>code); ?> - name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ notes ?? '')); ?>
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/a767be18f85f06a40736e43baa9c1c94.php b/storage/framework/views/a767be18f85f06a40736e43baa9c1c94.php
new file mode 100644
index 00000000..8e09b411
--- /dev/null
+++ b/storage/framework/views/a767be18f85f06a40736e43baa9c1c94.php
@@ -0,0 +1,100 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $status): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/abc1086b0925e0d145a47b081ba4eb8c.php b/storage/framework/views/abc1086b0925e0d145a47b081ba4eb8c.php
new file mode 100644
index 00000000..0bd7f329
--- /dev/null
+++ b/storage/framework/views/abc1086b0925e0d145a47b081ba4eb8c.php
@@ -0,0 +1,164 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ count() > 0): ?>
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $txn): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+
+
+ type)); ?>
+
+
+
+
item?->name ?? '-'); ?>
+
warehouse?->name ?? '-'); ?>
+
+
+
+ type === 'out' ? '-' : '+'); ?>quantity, 2))); ?>
+
+
+
created_at ? calendar_date($txn->created_at) : ''); ?>
+
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ count() > 0): ?>
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
name); ?>
+
code ?? '-'); ?> · unit ?? '-'); ?>
+
+
+
+ current_stock, 2))); ?>
+
+
+
: min_stock ?? 0, 2))); ?>
+
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/b1e4ba57b9976591108a1c9b3ffe68a8.php b/storage/framework/views/b1e4ba57b9976591108a1c9b3ffe68a8.php
new file mode 100644
index 00000000..ad063ce6
--- /dev/null
+++ b/storage/framework/views/b1e4ba57b9976591108a1c9b3ffe68a8.php
@@ -0,0 +1,83 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $type): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/b3640ad9e480045721de9d9873d8e305.php b/storage/framework/views/b3640ad9e480045721de9d9873d8e305.php
new file mode 100644
index 00000000..db8ed1ea
--- /dev/null
+++ b/storage/framework/views/b3640ad9e480045721de9d9873d8e305.php
@@ -0,0 +1,131 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+
+
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ project_id ?? '') == $project->id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+ *
+
+
+
+
+
+
*
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $currency): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ currency_id ?? $baseCurrency?->id ?? '') == $currency->id ? 'selected' : ''); ?>>
+ code); ?> - name); ?>
+
+ id === $baseCurrency?->id): ?> ()
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'request_date','value' => old('request_date', $pettyCash?->request_date?->format('Y-m-d')),'required' => 'true']] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'request_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(old('request_date', $pettyCash?->request_date?->format('Y-m-d'))),'required' => 'true']); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ description ?? '')); ?>
+
+
+
+
+
+ receipt_path): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/b70330c10889c719d7f5f7c4f8975d50.php b/storage/framework/views/b70330c10889c719d7f5f7c4f8975d50.php
new file mode 100644
index 00000000..79b57f60
--- /dev/null
+++ b/storage/framework/views/b70330c10889c719d7f5f7c4f8975d50.php
@@ -0,0 +1,235 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
name); ?>
+
code); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ status_label); ?>
+
+
+
+
+
+
+
manager?->name ?? '-'); ?>
+
+
+
+
+
start_date ? calendar_date($project->start_date) : '-'); ?>
+
+
+
+
end_date ? calendar_date($project->end_date) : '-'); ?>
+
+
+
+
location ?? '-'); ?>
+
+
+
+
defaultCurrency?->code ?? '-'); ?>
+
+
+
+ description): ?>
+
+
+
+
+ tasks->pluck('assignee')->filter()->unique('id');
+ ?>
+
+
+
+
+
+
+
+ ()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tasks; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $task): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+
+ 'components.priority-badge','data' => ['priority' => $task->priority]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('priority-badge'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['priority' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute($task->priority)]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
title); ?>
+
+ status_label); ?>
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+ expenses; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $expense): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+ title); ?>
+ amount, $expense->currency?->code)); ?>
+ status_label); ?>
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ users; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $member): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+ name, 0, 1))); ?>
+
+
+
name); ?>
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/c2413d3aa9744e18fe67bd7a0d816197.php b/storage/framework/views/c2413d3aa9744e18fe67bd7a0d816197.php
new file mode 100644
index 00000000..ace7bcdd
--- /dev/null
+++ b/storage/framework/views/c2413d3aa9744e18fe67bd7a0d816197.php
@@ -0,0 +1,85 @@
+ null, 'id' => null, 'class' => '', 'required' => false]));
+
+foreach ($attributes->all() as $__key => $__value) {
+ if (in_array($__key, $__propNames)) {
+ $$__key = $$__key ?? $__value;
+ } else {
+ $__newAttributes[$__key] = $__value;
+ }
+}
+
+$attributes = new \Illuminate\View\ComponentAttributeBag($__newAttributes);
+
+unset($__propNames);
+unset($__newAttributes);
+
+foreach (array_filter((['name', 'value' => null, 'id' => null, 'class' => '', 'required' => false]), 'is_string', ARRAY_FILTER_USE_KEY) as $__key => $__value) {
+ $$__key = $$__key ?? $__value;
+}
+
+$__defined_vars = get_defined_vars();
+
+foreach ($attributes->all() as $__key => $__value) {
+ if (array_key_exists($__key, $__defined_vars)) unset($$__key);
+}
+
+unset($__defined_vars); ?>
+
+format('Y-m-d');
+ } elseif (is_string($value) && strlen($value) >= 10) {
+ $gregorianValue = substr($value, 0, 10);
+ }
+
+ if ($calendar === 'jalali' && $gregorianValue) {
+ try {
+ $displayValue = \Morilog\Jalali\Jalalian::fromDateTime($gregorianValue)->format('Y/m/d');
+ } catch (\Exception $e) {
+ $displayValue = $gregorianValue;
+ }
+ } else {
+ $displayValue = $gregorianValue ?? '';
+ }
+}
+
+$defaultClass = 'w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500 focus:border-teal-500';
+$cssClass = $class ?: $defaultClass;
+?>
+
+
+
+
+
+ dir="ltr" />
+
+
+
+ />
+
+
\ No newline at end of file
diff --git a/storage/framework/views/cc7cee66c4891fd4b5e2d2b84bf0b502.php b/storage/framework/views/cc7cee66c4891fd4b5e2d2b84bf0b502.php
new file mode 100644
index 00000000..5c4e4b6d
--- /dev/null
+++ b/storage/framework/views/cc7cee66c4891fd4b5e2d2b84bf0b502.php
@@ -0,0 +1,171 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $status): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $pc): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+ title ?? '-'); ?>
+
+ project?->name ?? '-'); ?>
+
+ amount, $pc->currency?->code)); ?>
+
+
+
+ total_expenses, $pc->currency?->code)); ?>
+
+
+
+ remaining !== null ? format_currency((float)$pc->remaining, $pc->currency?->code) : '-'); ?>
+
+
+ request_date ? calendar_date($pc->request_date) : '-'); ?>
+
+
+ status_label); ?>
+
+
+
+
+
+ status === 'pending'): ?>
+
+
+ status === 'approved'): ?>
+
+
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+
+startPush('scripts'); ?>
+
+stopPush(); ?>
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/cf61b670207f0f276880b2ae809d4364.php b/storage/framework/views/cf61b670207f0f276880b2ae809d4364.php
new file mode 100644
index 00000000..338c877f
--- /dev/null
+++ b/storage/framework/views/cf61b670207f0f276880b2ae809d4364.php
@@ -0,0 +1,130 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
full_name); ?>
+
job_title ?? '-'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
job_title ?? '-'); ?>
+
+
+
+
contract_type) ?? $employee->contract_type ?? '-'); ?>
+
+
+
+
+
national_code ?? '-'); ?>
+
+
+
+
hire_date ? calendar_date($employee->hire_date) : '-'); ?>
+
+
+
+
project?->name ?? '-'); ?>
+
+
+
+
base_salary ? format_currency((float)$employee->base_salary, $employee->currency?->code) : '-'); ?>
+
+
+
+
bank_account ?? '-'); ?>
+
+
+
+
+ status) ?? $employee->status); ?>
+
+
+
+
+
+
+
+ workLogs->count() > 0): ?>
+
+
+
+ workLogs->take(5); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $log): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+
+
+
date)); ?>
+
project?->name ?? '-'); ?>
+
+
hours_worked, 1))); ?>
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+ financials->count() > 0): ?>
+
+
+
+ financials->take(5); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $fin): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+
+
+
type)); ?>
+
effective_date ? calendar_date($fin->effective_date) : '-'); ?>
+
+
+ amount, $fin->currency?->code)); ?>
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/d0cbbc094801c6a7d98901eb18134bcd.php b/storage/framework/views/d0cbbc094801c6a7d98901eb18134bcd.php
new file mode 100644
index 00000000..3aafa222
--- /dev/null
+++ b/storage/framework/views/d0cbbc094801c6a7d98901eb18134bcd.php
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+ - Vernova
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
© Vernova.
+
+
+
+
\ No newline at end of file
diff --git a/storage/framework/views/d1152c3fd7ed43b88a51d21a99ef2690.php b/storage/framework/views/d1152c3fd7ed43b88a51d21a99ef2690.php
new file mode 100644
index 00000000..34b1e571
--- /dev/null
+++ b/storage/framework/views/d1152c3fd7ed43b88a51d21a99ef2690.php
@@ -0,0 +1,173 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
*
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $status): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ status ?? 'planning') === $status ? 'selected' : ''); ?>>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $manager): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ manager_id ?? '') == $manager->id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $currency): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ default_currency_id ?? '') == $currency->id ? 'selected' : ''); ?>>code); ?> - name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'start_date','value' => old('start_date', $project?->start_date?->format('Y-m-d'))]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'start_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(old('start_date', $project?->start_date?->format('Y-m-d')))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'end_date','value' => old('end_date', $project?->end_date?->format('Y-m-d'))]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'end_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(old('end_date', $project?->end_date?->format('Y-m-d')))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ description ?? '')); ?>
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/d2d7153ed22b6afa438b863b8619689d.php b/storage/framework/views/d2d7153ed22b6afa438b863b8619689d.php
new file mode 100644
index 00000000..c16ac2f4
--- /dev/null
+++ b/storage/framework/views/d2d7153ed22b6afa438b863b8619689d.php
@@ -0,0 +1,141 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
*
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+
*
+
+ unit : '') === 'عدد' ? 'selected' : ''); ?>>عدد
+ unit : '') === 'کیلوگرم' ? 'selected' : ''); ?>>کیلوگرم
+ unit : '') === 'متر' ? 'selected' : ''); ?>>متر
+ unit : '') === 'مترمربع' ? 'selected' : ''); ?>>مترمربع
+ unit : '') === 'لیتر' ? 'selected' : ''); ?>>لیتر
+ unit : '') === 'رول' ? 'selected' : ''); ?>>رول
+ unit : '') === 'بسته' ? 'selected' : ''); ?>>بسته
+ unit : '') === 'دستگاه' ? 'selected' : ''); ?>>دستگاه
+ unit : '') === 'تن' ? 'selected' : ''); ?>>تن
+ unit : '') === 'ساعت' ? 'selected' : ''); ?>>ساعت
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $cat): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+ is_active : true) ? 'checked' : ''); ?>
+
+ class="sr-only peer">
+
+
+
+
+
+
+
+
+ description : '')); ?>
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/d4837b0709b873e59cd24d661c7a1525.php b/storage/framework/views/d4837b0709b873e59cd24d661c7a1525.php
new file mode 100644
index 00000000..0a9f73e9
--- /dev/null
+++ b/storage/framework/views/d4837b0709b873e59cd24d661c7a1525.php
@@ -0,0 +1,148 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+
+
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ project_id ?? '') == $project->id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+ *
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $cat): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ category ?? '') === $cat ? 'selected' : ''); ?>>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+ *
+
+
+
+
+
+
*
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $currency): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ currency_id ?? $baseCurrency?->id ?? '') == $currency->id ? 'selected' : ''); ?>>
+ code); ?> - name); ?>
+
+ id === $baseCurrency?->id): ?> ()
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ *
+
+
+ 'components.date-input','data' => ['name' => 'expense_date','value' => old('expense_date', $expense?->expense_date?->format('Y-m-d')),'required' => 'true']] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'expense_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(old('expense_date', $expense?->expense_date?->format('Y-m-d'))),'required' => 'true']); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ receipt_path): ?>
+
+
+
+
+
+
+
+
+ description ?? '')); ?>
+
+
+
+
+
+
+
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/e3253f4ba065d81f5eeecfaef7fa0887.php b/storage/framework/views/e3253f4ba065d81f5eeecfaef7fa0887.php
new file mode 100644
index 00000000..da4b0104
--- /dev/null
+++ b/storage/framework/views/e3253f4ba065d81f5eeecfaef7fa0887.php
@@ -0,0 +1,159 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+ ['color' => 'gray', 'icon' => 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z'],
+ 'in_progress' => ['color' => 'blue', 'icon' => 'M13 10V3L4 14h7v7l9-11h-7z'],
+ 'review' => ['color' => 'amber', 'icon' => 'M15 12a3 3 0 11-6 0 3 3 0 016 0z'],
+ 'done' => ['color' => 'emerald', 'icon' => 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z']
+ ]; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $status => $config): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $task): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+
+ project): ?>
+
project->name); ?>
+
+
+
+
+ due_date): ?>
+
+
+ due_date)); ?>
+
+
+
+
+
+ assignee): ?>
+
+ assignee->name, 0, 1))); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+startPush('scripts'); ?>
+
+stopPush(); ?>
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/e3358e6b2ca544b1228f9c327b88a6e0.php b/storage/framework/views/e3358e6b2ca544b1228f9c327b88a6e0.php
new file mode 100644
index 00000000..cf600c2f
--- /dev/null
+++ b/storage/framework/views/e3358e6b2ca544b1228f9c327b88a6e0.php
@@ -0,0 +1,125 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $cat): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ >
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+ code ?? '-'); ?>
+ name); ?>
+ category ?? '-'); ?>
+ unit ?? '-'); ?>
+
+ current_stock ?? 0;
+ $minStock = $item->min_stock ?? 0;
+ ?>
+
+
+
+
+ 0): ?>
+
+
+
+
+
+
+
+ is_active ? __('inventory.active') : __('inventory.inactive')); ?>
+
+
+
+
+
+
+
+
+ inventoryTransactions()->count() === 0): ?>
+
+
+
+
+
+
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/eb3da118726a3243dc4a5dd762b87bb8.php b/storage/framework/views/eb3da118726a3243dc4a5dd762b87bb8.php
new file mode 100644
index 00000000..9b9eb19e
--- /dev/null
+++ b/storage/framework/views/eb3da118726a3243dc4a5dd762b87bb8.php
@@ -0,0 +1,528 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $wh): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $t): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ ">
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'from_date','value' => request('from_date')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'from_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(request('from_date'))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+
+
+
+ 'components.date-input','data' => ['name' => 'to_date','value' => request('to_date')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('date-input'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['name' => 'to_date','value' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(request('to_date'))]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $txn): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+
+ type)); ?>
+
+
+
+ item?->name ?? '-'); ?>
+ warehouse?->name ?? '-'); ?>
+
+ type === 'out' ? '-' : '+'); ?>quantity, 2))); ?>
+
+ item?->unit): ?>
+ item->unit); ?>
+
+
+
+ unit_price): ?>
+ unit_price, $txn->currency?->code)); ?>
+
+
+ -
+
+
+
+ total_price): ?>
+ total_price, $txn->currency?->code)); ?>
+
+
+ -
+
+
+ reference_number ?? $txn->reference ?? '-'); ?>
+ created_at ? calendar_date($txn->created_at) : '-'); ?>
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?> code ? '(' . $item->code . ')' : ''); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $wh): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+ *
+
+
+
+
+
+
+ count() > 0): ?>
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $currency): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ is_default ? 'selected' : ''); ?>>name); ?> (code); ?>)
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ :
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?> code ? '(' . $item->code . ')' : ''); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $wh): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+ *
+
+
+
+
+
+
+ count() > 0): ?>
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $currency): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ is_default ? 'selected' : ''); ?>>name); ?> (code); ?>)
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ :
+ -
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?> code ? '(' . $item->code . ')' : ''); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+ *
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $wh): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+ *
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $wh): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+ :
+ -
+
+ getBag($__errorArgs[1] ?? 'default');
+if ($__bag->has($__errorArgs[0])) :
+if (isset($message)) { $__messageOriginal = $message; }
+$message = $__bag->first($__errorArgs[0]); ?>
+
+
+
+
+
+
+
+
+
+
+
+startPush('scripts'); ?>
+
+stopPush(); ?>
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/storage/framework/views/ebeebd8922e18d3f7f95a6b1c67f8bfe.php b/storage/framework/views/ebeebd8922e18d3f7f95a6b1c67f8bfe.php
new file mode 100644
index 00000000..5121b3ba
--- /dev/null
+++ b/storage/framework/views/ebeebd8922e18d3f7f95a6b1c67f8bfe.php
@@ -0,0 +1,117 @@
+hasPages()): ?>
+
+
+ onFirstPage()): ?>
+
+
+
+
+
+
+
+
+
+
+
+ hasMorePages()): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ firstItem()): ?>
+ firstItem()); ?>
+
+
+ lastItem()); ?>
+
+ count()); ?>
+
+
+
+
+ total()); ?>
+
+
+
+
+
+
+
+
+ onFirstPage()): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $element): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $page => $url): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ currentPage()): ?>
+
+
+
+
+
+
+
+
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+ hasMorePages()): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/storage/framework/views/f65f51438d6e40d1a0e9db5fe3643c14.php b/storage/framework/views/f65f51438d6e40d1a0e9db5fe3643c14.php
new file mode 100644
index 00000000..691cc9f7
--- /dev/null
+++ b/storage/framework/views/f65f51438d6e40d1a0e9db5fe3643c14.php
@@ -0,0 +1,158 @@
+startSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $project): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $status): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $priority): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ >
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $user): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ id ? 'selected' : ''); ?>>name); ?>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $task): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
+
+
+
+
+ 'components.priority-badge','data' => ['priority' => $task->priority]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
+withName('priority-badge'); ?>
+shouldRender()): ?>
+startComponent($component->resolveView(), $component->data()); ?>
+
+except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
+
+withAttributes(['priority' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute($task->priority)]); ?>
+renderComponent(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
title); ?>
+
+ project?->name); ?>
+ due_date): ?>
+ •
+ due_date)); ?>
+
+ assignee): ?>
+ •
+ assignee->name); ?>
+
+
+
+
+
+
+ addLoop($__currentLoopData); foreach($__currentLoopData as $s): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
+ status === $s ? 'selected' : ''); ?>>
+ popLoop(); $loop = $__env->getLastLoop(); ?>
+
+
+ popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
+
+
+
+
+
+
+
+ withQueryString()->links()); ?>
+
+
+
+
+startPush('scripts'); ?>
+
+stopPush(); ?>
+stopSection(); ?>
+
+make('layouts.app', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
\ No newline at end of file
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 00000000..e7cd7eb5
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,54 @@
+import defaultTheme from 'tailwindcss/defaultTheme';
+import forms from '@tailwindcss/forms';
+
+export default {
+ content: [
+ './resources/**/*.blade.php',
+ './resources/**/*.js',
+ './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
+ ],
+ darkMode: 'class',
+ theme: {
+ extend: {
+ fontFamily: {
+ sans: ['Vazirmatn', 'Inter', ...defaultTheme.fontFamily.sans],
+ },
+ colors: {
+ brand: {
+ 50: '#fef3e2',
+ 100: '#fde4b9',
+ 200: '#fbc96f',
+ 300: '#f9ae2a',
+ 400: '#e89510',
+ 500: '#cf7a08',
+ 600: '#a95c06',
+ 700: '#834106',
+ 800: '#6e360b',
+ 900: '#5e2f0d',
+ },
+ accent: {
+ 50: '#f0fdfa',
+ 100: '#ccfbf1',
+ 200: '#99f6e4',
+ 300: '#5eead4',
+ 400: '#2dd4bf',
+ 500: '#14b8a6',
+ 600: '#0d9488',
+ 700: '#0f766e',
+ 800: '#115e59',
+ 900: '#134e4a',
+ },
+ sidebar: {
+ DEFAULT: '#1e293b',
+ foreground: '#f8fafc',
+ primary: '#14b8a6',
+ 'primary-foreground': '#ffffff',
+ accent: '#334155',
+ 'accent-foreground': '#f8fafc',
+ border: '#334155',
+ },
+ },
+ },
+ },
+ plugins: [forms],
+};
diff --git a/vite.config.js b/vite.config.js
index 421b5695..6a93a7c4 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -8,4 +8,9 @@ export default defineConfig({
refresh: true,
}),
],
+ resolve: {
+ alias: {
+ '@': '/resources',
+ },
+ },
});