Skip to content

Commit 3d727d7

Browse files
committed
Merge branch 'refs/heads/main' into feat/price_lists
# Conflicts: # database/migrations/2025_07_29_142749_create_price_lists_table.php # src/Models/PriceList.php
2 parents d3ff4ec + a3b5166 commit 3d727d7

92 files changed

Lines changed: 7480 additions & 58 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/linter.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ jobs:
2222
- name: Checkout code
2323
uses: actions/checkout@v4
2424
with:
25-
ref: ${{ github.head_ref }}
25+
# Checkout the actual branch, not a specific commit
26+
ref: ${{ github.head_ref || github.ref_name }}
27+
# Fetch the full history to avoid shallow clone issues
28+
fetch-depth: 0
2629

2730
- name: Run Laravel Pint
2831
uses: aglipanci/laravel-pint-action@latest

.github/workflows/test-runner.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
- name: Checkout code
4242
uses: actions/checkout@v4
4343
with:
44-
ref: ${{ github.head_ref }}
44+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
4545

4646
- name: Validate composer.json and composer.lock
4747
run: composer validate --strict

composer.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@
4848
"bezhansalleh/filament-shield": "^3.3",
4949
"datalinx/php-utils": "^2.5",
5050
"eclipsephp/common": "dev-main",
51+
"eclipsephp/world-plugin": "dev-main",
5152
"filament/filament": "^3.3",
53+
"filament/spatie-laravel-media-library-plugin": "^3.2",
5254
"filament/spatie-laravel-translatable-plugin": "^3.2",
55+
"solution-forest/filament-tree": "^2.1",
5356
"spatie/laravel-package-tools": "^1.19",
54-
"spatie/laravel-translatable": "^6.11"
57+
"spatie/laravel-translatable": "^6.11",
58+
"nben/filament-record-nav": "^1.0"
5559
},
5660
"require-dev": {
5761
"laravel/pint": "^1.21",

config/eclipse-catalogue.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<?php
22

33
return [
4-
//
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Multi-tenancy config
8+
|--------------------------------------------------------------------------
9+
*/
10+
'tenancy' => [
11+
'model' => null,
12+
'foreign_key' => null,
13+
],
514
];
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
namespace Eclipse\Catalogue\Factories;
4+
5+
use Eclipse\Catalogue\Models\Category;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
use Illuminate\Support\Carbon;
8+
use Illuminate\Support\Str;
9+
10+
class CategoryFactory extends Factory
11+
{
12+
protected $model = Category::class;
13+
14+
public function definition(): array
15+
{
16+
$englishName = mb_ucfirst(fake()->words(2, true));
17+
$slovenianName = 'SI: '.$englishName;
18+
19+
$englishShortDesc = fake()->sentence();
20+
$slovenianShortDesc = 'SI: '.$englishShortDesc;
21+
22+
$englishDesc = fake()->paragraphs(3, true);
23+
$slovenianDesc = 'SI: '.$englishDesc;
24+
25+
return [
26+
'name' => [
27+
'en' => $englishName,
28+
'sl' => $slovenianName,
29+
],
30+
'parent_id' => null,
31+
'image' => self::generateCategoryImage($englishName),
32+
'sort' => fake()->randomNumber(),
33+
'is_active' => fake()->boolean(),
34+
'code' => fake()->optional()->bothify('CAT-####'),
35+
'recursive_browsing' => fake()->boolean(),
36+
'sef_key' => [
37+
'en' => Str::slug($englishName),
38+
'sl' => Str::slug($slovenianName),
39+
],
40+
'short_desc' => [
41+
'en' => $englishShortDesc,
42+
'sl' => $slovenianShortDesc,
43+
],
44+
'description' => [
45+
'en' => $englishDesc,
46+
'sl' => $slovenianDesc,
47+
],
48+
'created_at' => Carbon::now(),
49+
'updated_at' => Carbon::now(),
50+
'site_id' => null,
51+
];
52+
}
53+
54+
private static function generateCategoryImage(string $name): ?string
55+
{
56+
$colors = ['3B82F6', '10B981', 'F59E0B', 'EF4444', '8B5CF6', '06B6D4', 'F97316', 'EC4899'];
57+
$backgrounds = ['E0F2FE', 'ECFDF5', 'FFFBEB', 'FEF2F2', 'F3E8FF', 'ECFEFF', 'FFF7ED', 'FDF2F8'];
58+
59+
$color = fake()->randomElement($colors);
60+
$background = fake()->randomElement($backgrounds);
61+
62+
return fake()->optional(0.8)->passthrough(
63+
'https://ui-avatars.com/api/?name='.urlencode($name).
64+
'&size=400&background='.$background.
65+
'&color='.$color.
66+
'&bold=true&format=png'
67+
);
68+
}
69+
70+
public function parent(): static
71+
{
72+
return $this->state(fn (array $attributes): array => [
73+
'parent_id' => null,
74+
]);
75+
}
76+
77+
public function child(?Category $parent = null): static
78+
{
79+
return $this->state(fn (array $attributes): array => [
80+
'parent_id' => $parent?->id ?? Category::factory()->parent()->create()->id,
81+
]);
82+
}
83+
84+
public function active(): static
85+
{
86+
return $this->state(fn (array $attributes): array => [
87+
'is_active' => true,
88+
]);
89+
}
90+
91+
public function inactive(): static
92+
{
93+
return $this->state(fn (array $attributes): array => [
94+
'is_active' => false,
95+
]);
96+
}
97+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
namespace Eclipse\Catalogue\Factories;
4+
5+
use Eclipse\Catalogue\Models\PriceList;
6+
use Eclipse\Catalogue\Models\PriceListData;
7+
use Illuminate\Database\Eloquent\Factories\Factory;
8+
9+
/**
10+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\Eclipse\Catalogue\Models\PriceListData>
11+
*/
12+
class PriceListDataFactory extends Factory
13+
{
14+
protected $model = PriceListData::class;
15+
16+
public function definition(): array
17+
{
18+
$definition = [
19+
'price_list_id' => PriceList::factory(),
20+
'is_active' => $this->faker->boolean(80), // 80% chance of being active
21+
'is_default' => false,
22+
'is_default_purchase' => false,
23+
];
24+
25+
// Add tenant foreign key if configured
26+
if (config('eclipse-catalogue.tenancy.foreign_key')) {
27+
$tenantModel = config('eclipse-catalogue.tenancy.model');
28+
if (class_exists($tenantModel)) {
29+
$definition[config('eclipse-catalogue.tenancy.foreign_key')] = $tenantModel::factory();
30+
}
31+
}
32+
33+
return $definition;
34+
}
35+
36+
public function active(): static
37+
{
38+
return $this->state(fn (array $attributes) => [
39+
'is_active' => true,
40+
]);
41+
}
42+
43+
public function inactive(): static
44+
{
45+
return $this->state(fn (array $attributes) => [
46+
'is_active' => false,
47+
]);
48+
}
49+
50+
public function defaultSelling(): static
51+
{
52+
return $this->state(fn (array $attributes) => [
53+
'is_default' => true,
54+
'is_default_purchase' => false,
55+
]);
56+
}
57+
58+
public function defaultPurchase(): static
59+
{
60+
return $this->state(fn (array $attributes) => [
61+
'is_default' => false,
62+
'is_default_purchase' => true,
63+
]);
64+
}
65+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Eclipse\Catalogue\Factories;
4+
5+
use Eclipse\Catalogue\Models\PriceList;
6+
use Eclipse\World\Models\Currency;
7+
use Illuminate\Database\Eloquent\Factories\Factory;
8+
9+
/**
10+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\Eclipse\Catalogue\Models\PriceList>
11+
*/
12+
class PriceListFactory extends Factory
13+
{
14+
protected $model = PriceList::class;
15+
16+
public function definition(): array
17+
{
18+
// Get existing currencies or create a default one
19+
$currencies = Currency::all();
20+
if ($currencies->isEmpty()) {
21+
Currency::create(['id' => 'EUR', 'name' => 'Euro', 'is_active' => true]);
22+
$currencies = Currency::all();
23+
}
24+
25+
return [
26+
'currency_id' => $currencies->random()->id,
27+
'name' => $this->faker->words(2, true).' Price List',
28+
'code' => strtoupper($this->faker->unique()->lexify('PL???')),
29+
'tax_included' => $this->faker->boolean(),
30+
'notes' => $this->faker->optional()->sentence(),
31+
];
32+
}
33+
34+
public function withTaxIncluded(): static
35+
{
36+
return $this->state(fn (array $attributes) => [
37+
'tax_included' => true,
38+
]);
39+
}
40+
41+
public function withoutTax(): static
42+
{
43+
return $this->state(fn (array $attributes) => [
44+
'tax_included' => false,
45+
]);
46+
}
47+
}

database/factories/ProductFactory.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
namespace Eclipse\Catalogue\Factories;
44

5+
use Eclipse\Catalogue\Models\Category;
56
use Eclipse\Catalogue\Models\Product;
7+
use Exception;
68
use Illuminate\Database\Eloquent\Factories\Factory;
79
use Illuminate\Support\Carbon;
10+
use Illuminate\Support\Facades\Log;
811

912
class ProductFactory extends Factory
1013
{
@@ -32,6 +35,7 @@ public function definition(): array
3235
'en' => $englishName,
3336
'sl' => $slovenianName,
3437
],
38+
'category_id' => Category::inRandomOrder()->first()?->id ?? Category::factory(),
3539
'short_description' => [
3640
'en' => $englishShortDesc,
3741
'sl' => $slovenianShortDesc,
@@ -44,4 +48,23 @@ public function definition(): array
4448
'updated_at' => Carbon::now(),
4549
];
4650
}
51+
52+
public function configure(): static
53+
{
54+
return $this->afterCreating(function (Product $product) {
55+
$imageNumber = rand(1, 15);
56+
$imagePath = storage_path("app/public/sample-products/{$imageNumber}.jpg");
57+
58+
if (file_exists($imagePath)) {
59+
try {
60+
$product->addMedia($imagePath)
61+
->preservingOriginal()
62+
->withCustomProperties(['is_cover' => true])
63+
->toMediaCollection('images');
64+
} catch (Exception $e) {
65+
Log::warning("Failed to attach image to product {$product->id}: ".$e->getMessage());
66+
}
67+
}
68+
});
69+
}
4770
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up(): void
10+
{
11+
Schema::create('catalogue_categories', function (Blueprint $table) {
12+
$table->id();
13+
$table->foreignId('site_id')
14+
->constrained()
15+
->cascadeOnDelete()
16+
->cascadeOnUpdate();
17+
$table->string('name');
18+
$table->integer('parent_id')->default(-1);
19+
$table->string('image')->nullable();
20+
$table->integer('sort')->default(0)->index();
21+
$table->boolean('is_active');
22+
$table->string('code')->nullable();
23+
$table->boolean('recursive_browsing')->nullable();
24+
$table->string('sef_key')->nullable();
25+
$table->text('short_desc')->nullable();
26+
$table->text('description')->nullable();
27+
$table->timestamps();
28+
$table->softDeletes();
29+
});
30+
}
31+
32+
public function down(): void
33+
{
34+
Schema::dropIfExists('catalogue_categories');
35+
}
36+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up(): void
10+
{
11+
Schema::table('catalogue_products', function (Blueprint $table) {
12+
$table->foreignId('category_id')
13+
->nullable()
14+
->after('name')
15+
->constrained('catalogue_categories')
16+
->nullOnDelete()
17+
->cascadeOnUpdate();
18+
});
19+
}
20+
21+
public function down(): void
22+
{
23+
Schema::table('catalogue_products', function (Blueprint $table) {
24+
$table->dropForeign(['category_id']);
25+
$table->dropColumn('category_id');
26+
});
27+
}
28+
};

0 commit comments

Comments
 (0)