Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/build_panel_assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ on:
- "packages/panel/resources/**"
- "packages/panel/vite.config.ts"
- "packages/panel/package.json"
- "packages/bundles/resources/js/**"
- "packages/bundles/vite.config.js"
- "packages/bundles/package.json"
- ".github/workflows/build_panel_assets.yml"

permissions:
Expand Down Expand Up @@ -58,8 +61,11 @@ jobs:
run: npx vite build
working-directory: packages/panel

- name: Build bundles add-on bundle
run: npm run build --workspace @lunarphp/bundles-addon

- name: Commit built assets
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore(panel): rebuild compiled assets"
file_pattern: packages/panel/public/build
file_pattern: packages/panel/public/build packages/bundles/build
1 change: 1 addition & 0 deletions .github/workflows/split_packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
matrix:
package:
- "admin"
- "bundles"
- "core"
- "demo-data"
- "filament"
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
php: ${{ fromJSON(github.event_name == 'pull_request' && '["8.4"]' || '["8.4","8.5"]') }}
laravel: ["12.*", "13.*"]
dependency-version: [prefer-stable]
testsuite: [core, admin, panel, filament, shipping, stripe, paypal, search, upgrade]
testsuite: [core, admin, panel, filament, shipping, stripe, paypal, search, bundles, upgrade]
include:
- laravel: 12.*
testbench: 10.*
Expand Down Expand Up @@ -196,6 +196,9 @@ jobs:
- name: Build example add-on bundle
run: npm run build --workspace @lunarphp/panel-addon-example

- name: Build bundles add-on bundle
run: npm run build --workspace @lunarphp/bundles-addon

# Fails when a publishable npm package's content no longer matches the
# tarball published under its current version — the reminder to bump.
# Relies on the panel build above for fresh @lunarphp/panel types.
Expand Down
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Items tagged _(judgement)_ are genuine line-calls worth revisiting.

## Done

- Product bundles — `lunarphp/bundles`: a variant with components, derived stock, component order lines, panel editor (spec 0085)
- Attribute field-type configuration schema — field types declare their settings once in core; the panel and Filament bridge both render from the descriptors (spec 0054)
- Filament admin & bridge hardening — standalone bridge, dead hooks and config, locale nav groups, guard and asset id (spec 0076)
- First staff account creation in core — `lunar:create-admin` moves out of the Filament admin; panel install offers it (spec 0075)
Expand Down
6 changes: 6 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
"psr-4": {
"Lunar\\Core\\": "packages/core/src",
"Lunar\\Admin\\": "packages/admin/src/",
"Lunar\\Bundles\\": "packages/bundles/src/",
"Lunar\\Bundles\\Database\\Factories\\": "packages/bundles/database/factories",
"Lunar\\Core\\Database\\Factories\\": "packages/core/database/factories",
"Lunar\\Core\\Database\\Seeders\\": "packages/core/database/seeders",
"Lunar\\Core\\Database\\State\\": "packages/core/database/state",
Expand All @@ -85,6 +87,7 @@
"psr-4": {
"Lunar\\Tests\\": "tests",
"Lunar\\Tests\\Admin\\": "tests/admin",
"Lunar\\Tests\\Bundles\\": "tests/bundles",
"Lunar\\Tests\\Core\\": "tests/core",
"Lunar\\Tests\\DemoData\\": "tests/demo-data",
"Lunar\\Tests\\Filament\\": "tests/filament",
Expand All @@ -103,6 +106,7 @@
"name": [
"Table Rate Shipping",
"Search",
"Bundles",
"Meilisearch",
"Paypal Payments",
"Stripe Payments",
Expand All @@ -119,6 +123,7 @@
"Lunar\\Filament\\LunarFilamentServiceProvider",
"Lunar\\Admin\\LunarPanelProvider",
"Lunar\\Shipping\\ShippingServiceProvider",
"Lunar\\Bundles\\BundlesServiceProvider",
"Lunar\\Upgrade\\UpgradeServiceProvider",
"Lunar\\DemoData\\DemoDataServiceProvider",
"Lunar\\Panel\\PanelServiceProvider",
Expand All @@ -129,6 +134,7 @@
"replace": {
"lunarphp/core": "self.version",
"lunarphp/admin": "self.version",
"lunarphp/bundles": "self.version",
"lunarphp/demo-data": "self.version",
"lunarphp/filament": "self.version",
"lunarphp/meilisearch": "self.version",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"workspaces": [
"packages/panel/resources/package",
"packages/panel/resources/panel-package",
"packages/panel-addon-example"
"packages/panel-addon-example",
"packages/bundles"
],
"scripts": {
"check-npm-drift": "node scripts/check-npm-drift.mjs"
Expand Down
13 changes: 13 additions & 0 deletions packages/bundles/.github/workflows/close-pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Close Pull Request

on:
pull_request_target:
types: [opened]

jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: "Thank you for your pull request. However, you have submitted this PR on the Lunar Bundles Repo which is a read-only sub split of `lunarphp/lunar`. Please submit your PR on the https://github.com/lunarphp/lunar repository.<br><br>Thanks!"
1 change: 1 addition & 0 deletions packages/bundles/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
16 changes: 16 additions & 0 deletions packages/bundles/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Lunar Bundles

Sell a set of product variants as one product. A bundle is an ordinary `ProductVariant` that carries a component list: stock is derived from the components, the price is either fixed or derived from the components, and the order records one component line per part so fulfilment and stock commitment work unchanged.

```php
$bundle = app(DefinesBundle::class)->execute($variant, BundlePricing::Components, discountPercentage: 10);

$bundle->syncComponents([
['variant' => $body, 'quantity' => 1],
['variant' => $bag, 'quantity' => 1],
]);

CartSession::add($variant, 1);
```

See spec `0085-product-bundles` in the monorepo for the full design.
1 change: 1 addition & 0 deletions packages/bundles/build/assets/addon-Bd9bHYW_.js

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions packages/bundles/build/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"resources/js/addon.ts": {
"file": "assets/addon-Bd9bHYW_.js",
"name": "addon",
"src": "resources/js/addon.ts",
"isEntry": true
}
}
48 changes: 48 additions & 0 deletions packages/bundles/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "lunarphp/bundles",
"type": "library",
"description": "Product bundles for LunarPHP: sell a set of variants as one product with derived stock, component-derived pricing and component order lines.",
"keywords": [
"lunarphp",
"laravel",
"ecommerce",
"e-commerce",
"headless",
"store",
"shop",
"bundles",
"kits"
],
"license": "MIT",
"authors": [
{
"name": "Lunar",
"homepage": "https://lunarphp.io/"
}
],
"require": {
"php": "^8.4",
"lunarphp/core": "self.version"
},
"suggest": {
"lunarphp/panel": "Adds the bundle editor to the product pages of the admin panel."
},
"autoload": {
"psr-4": {
"Lunar\\Bundles\\": "src/",
"Lunar\\Bundles\\Database\\Factories\\": "database/factories"
}
},
"extra": {
"lunar": {
"name": "Bundles"
},
"laravel": {
"providers": [
"Lunar\\Bundles\\BundlesServiceProvider"
]
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
13 changes: 13 additions & 0 deletions packages/bundles/config/bundles.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

return [
/*
|--------------------------------------------------------------------------
| Maximum components
|--------------------------------------------------------------------------
|
| The most components a single bundle may carry, grouped ones included.
|
*/
'max_components' => 25,
];
25 changes: 25 additions & 0 deletions packages/bundles/database/factories/BundleComponentFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Lunar\Bundles\Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Lunar\Bundles\Models\Bundle;
use Lunar\Bundles\Models\BundleComponent;
use Lunar\Core\Models\ProductVariant;

class BundleComponentFactory extends Factory
{
protected $model = BundleComponent::class;

public function definition(): array
{
return [
'bundle_id' => Bundle::factory(),
'bundle_group_id' => null,
'product_variant_id' => ProductVariant::factory(),
'quantity' => 1,
'default' => false,
'position' => 0,
];
}
}
30 changes: 30 additions & 0 deletions packages/bundles/database/factories/BundleFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Lunar\Bundles\Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Lunar\Bundles\Enums\BundlePricing;
use Lunar\Bundles\Models\Bundle;
use Lunar\Core\Models\ProductVariant;

class BundleFactory extends Factory
{
protected $model = Bundle::class;

public function definition(): array
{
return [
'product_variant_id' => ProductVariant::factory(),
'pricing' => BundlePricing::Fixed,
'discount_percentage' => null,
];
}

public function componentPriced(?float $discountPercentage = null): static
{
return $this->state([
'pricing' => BundlePricing::Components,
'discount_percentage' => $discountPercentage,
]);
}
}
23 changes: 23 additions & 0 deletions packages/bundles/database/factories/BundleGroupFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Lunar\Bundles\Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Lunar\Bundles\Models\Bundle;
use Lunar\Bundles\Models\BundleGroup;

class BundleGroupFactory extends Factory
{
protected $model = BundleGroup::class;

public function definition(): array
{
return [
'bundle_id' => Bundle::factory(),
'name' => ['en' => $this->faker->words(2, true)],
'min_selections' => 1,
'max_selections' => 1,
'position' => 0,
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Lunar\Core\Database\Migration;

return new class extends Migration
{
public function up(): void
{
Schema::create($this->prefix.'bundles', function (Blueprint $table) {
$table->id();
$table->ulid('public_id')->unique();
$table->foreignId('product_variant_id')->unique()->constrained($this->prefix.'product_variants')->cascadeOnDelete();
$table->string('pricing', 16)->default('fixed');
$table->decimal('discount_percentage', 5, 2)->nullable();
$table->timestamps();
});
}

public function down(): void
{
Schema::dropIfExists($this->prefix.'bundles');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Lunar\Core\Database\Migration;

return new class extends Migration
{
public function up(): void
{
Schema::create($this->prefix.'bundle_groups', function (Blueprint $table) {
$table->id();
$table->ulid('public_id')->unique();
$table->foreignId('bundle_id')->constrained($this->prefix.'bundles')->cascadeOnDelete();
$table->json('name');
$table->smallInteger('min_selections')->default(1);
$table->smallInteger('max_selections')->default(1);
$table->smallInteger('position')->default(0);
$table->timestamps();
});
}

public function down(): void
{
Schema::dropIfExists($this->prefix.'bundle_groups');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Lunar\Core\Database\Migration;

return new class extends Migration
{
public function up(): void
{
Schema::create($this->prefix.'bundle_components', function (Blueprint $table) {
$table->id();
$table->ulid('public_id')->unique();
$table->foreignId('bundle_id')->constrained($this->prefix.'bundles')->cascadeOnDelete();
$table->foreignId('bundle_group_id')->nullable()->constrained($this->prefix.'bundle_groups')->cascadeOnDelete();
$table->foreignId('product_variant_id')->constrained($this->prefix.'product_variants')->cascadeOnDelete();
$table->smallInteger('quantity')->default(1);
$table->boolean('default')->default(false);
$table->smallInteger('position')->default(0);
$table->timestamps();

$table->unique(['bundle_id', 'bundle_group_id', 'product_variant_id'], 'bundle_components_unique');
});
}

public function down(): void
{
Schema::dropIfExists($this->prefix.'bundle_components');
}
};
18 changes: 18 additions & 0 deletions packages/bundles/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@lunarphp/bundles-addon",
"private": true,
"type": "module",
"scripts": {
"build": "vite build",
"dev": "vite"
},
"devDependencies": {
"@inertiajs/vue3": "^3.0.0",
"@lunarphp/panel": "^0.1.0",
"@lunarphp/panel-vite-plugin": "^0.1.0",
"@vitejs/plugin-vue": "^5.2.0",
"vite": "^6.0.0",
"vue": "^3.5.13",
"vue-i18n": "^11.4.6"
}
}
Loading
Loading