diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index a4433edf11..bec79b268e 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -2,7 +2,8 @@ name: "\U0001F41B Bug report" about: Report something that's broken. title: '' -labels: bug,unconfirmed +type: bug +projects: ['lunarphp/9'] assignees: '' --- diff --git a/.github/workflows/document-facades.yml b/.github/workflows/document-facades.yml index a5ce7528f1..c3780a2101 100644 --- a/.github/workflows/document-facades.yml +++ b/.github/workflows/document-facades.yml @@ -2,9 +2,9 @@ name: document-facades on: - push: - branches: - - '*.x' +# push: +# branches: +# - '*.x' workflow_dispatch: permissions: diff --git a/.github/workflows/fix-code-style.yml b/.github/workflows/fix-code-style.yml index 517d722242..695a8097e3 100644 --- a/.github/workflows/fix-code-style.yml +++ b/.github/workflows/fix-code-style.yml @@ -1,7 +1,7 @@ name: fix-code-style on: - push: + pull_request: jobs: fix-code-style: diff --git a/.github/workflows/split_packages.yml b/.github/workflows/split_packages.yml index 4bd58029d1..401acbe432 100644 --- a/.github/workflows/split_packages.yml +++ b/.github/workflows/split_packages.yml @@ -1,65 +1,34 @@ -name: Split Packages +name: monorepo-split + on: push: - branches: - - main - - "0.*" - - "1.*" - tags: - - "*" + tags: '*' + jobs: - tag_split_packages: + split-monorepo: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - package: ["admin", "core", "opayo", "paypal", "table-rate-shipping", "stripe", "meilisearch", "search"] + package: + - "admin" + - "core" + - "opayo" + - "paypal" + - "table-rate-shipping" + - "stripe" + - "search" steps: - uses: actions/checkout@v4 - - uses: shivammathur/setup-php@v2 - with: - php-version: 8.2 - coverage: none - - - name: Split ${{ matrix.package }} - uses: "danharrin/monorepo-split-github-action@v2.3.0" - if: "!startsWith(github.ref, 'refs/tags/')" - env: - GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} - with: - package_directory: "packages/${{ matrix.package }}" - repository_organization: "lunarphp" - repository_name: "${{ matrix.package }}" - user_name: "GitHub Action" - user_email: "action@github.com" - branch: ${GITHUB_REF#refs/heads/} - - - name: Set env - if: "startsWith(github.ref, 'refs/tags/')" - run: echo "GITHUB_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - - - name: Split ${{ matrix.package }} - uses: "danharrin/monorepo-split-github-action@v2.3.0" - if: "startsWith(github.ref, 'refs/tags/')" - env: - GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} - with: - package_directory: "packages/${{ matrix.package }}" - repository_organization: "lunarphp" - repository_name: "${{ matrix.package }}" - user_name: "GitHub Action" - user_email: "action@github.com" - branch: ${GITHUB_TAG%.*} - - - name: Tag ${{ matrix.package }} - if: "startsWith(github.ref, 'refs/tags/')" - uses: "danharrin/monorepo-split-github-action@v2.3.0" + - name: Monorepo Split of ${{ matrix.package }} + uses: danharrin/monorepo-split-github-action@v2.4.0 env: GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} with: - tag: ${GITHUB_REF#refs/tags/} package_directory: "packages/${{ matrix.package }}" repository_organization: "lunarphp" repository_name: "${{ matrix.package }}" + branch: 1.x + tag: ${{ github.ref_name }} user_name: "GitHub Action" user_email: "action@github.com" - branch: ${GITHUB_TAG%.*} diff --git a/README.md b/README.md index b3658ef311..b80df30e68 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -

Lunar

+

Lunar

-[Lunar](https://lunarphp.io) is a set of Laravel packages that bring functionality akin to Shopify and other e-commerce platforms to +[Lunar](https://lunarphp.com) is a set of Laravel packages that bring functionality akin to Shopify and other e-commerce platforms to Laravel. You have complete freedom to create your own storefront(s), but we've already done the hard work for you in the backend. @@ -8,7 +8,7 @@ This repository serves as a monorepo for the main packages that make up Lunar. ## Documentation -- [v1.0 documentation](https://docs-v1.lunarphp.io/) +- [v1.0 documentation](https://docs.lunarphp.com/) ## Contribution @@ -18,8 +18,8 @@ This repository serves as a monorepo for the main packages that make up Lunar. ## Community -- [Join our discord server](https://discord.gg/v6qVWaf) and chat to the developers and people using Lunar. -- [We have a roadmap](https://github.com/orgs/lunarphp/projects/8) where we will be detailing which features are next. +- [Join our discord server](https://lunarphp.com/discord) and chat to the developers and people using Lunar. +- [We have a roadmap](https://github.com/orgs/lunarphp/projects/9) where we will be detailing which features are next. ## Packages in this monorepo diff --git a/composer.json b/composer.json index 0e116cd0ba..290346940e 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "awcodes/shout": "^2.0.4", "barryvdh/laravel-dompdf": "^3.0", "cartalyst/converter": "^9.0|^10", - "doctrine/dbal": "^3.6", + "doctrine/dbal": "^3.6|^4.0", "dompdf/dompdf": "^3.1", "ext-bcmath": "*", "ext-exif": "*", diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 5506568240..0000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -.vitepress/cache -.vitepress/dist diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js deleted file mode 100644 index 484bbef2d4..0000000000 --- a/docs/.vitepress/config.js +++ /dev/null @@ -1,195 +0,0 @@ -import {defineConfig} from 'vitepress' - -// https://vitepress.dev/reference/site-config -export default defineConfig({ - title: "Lunar", - description: "Laravel Headless E-Commerce", - head: [ - [ - 'link', - {rel: 'icon', href: '/icon.svg', type: 'image/svg'} - ], - [ - 'link', - {rel: 'preconnect', href: 'https://fonts.googleapis.com'} - ], - [ - 'link', - {rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: ''} - ], - [ - 'link', - { - rel: 'stylesheet', - href: 'https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700;900&display=swap' - } - ], - [ - 'script', - { - src: 'https://cdn.usefathom.com/script.js', - 'data-spa': 'auto', - 'data-site': 'KMZGQTYE', - defer: '' - } - ], - [ - 'script', - { - async: '', - src: 'https://tally.so/widgets/embed.js' - } - ] - // would render: - ], - - appearance: 'dark', - - themeConfig: { - // https://vitepress.dev/reference/default-theme-config - logo: { - light: '/icon.svg', - dark: '/icon-dark.svg', - }, - - nav: [ - { - text: 'Core', - link: '/core/overview', - activeMatch: '/core/' - }, - { - text: 'Admin Panel', - link: '/admin/overview', - activeMatch: '/admin/' - }, - { - text: 'Support', - link: '/support', - activeMatch: '/support' - }, - { - text: 'Resources', - items: [ - {text: 'Add-ons', link: 'https://github.com/lunarphp/awesome'}, - {text: 'Discord', link: 'https://discord.gg/v6qVWaf'}, - {text: 'Discussions', link: 'https://github.com/lunarphp/lunar/discussions'} - ] - }, - { - text: '1.x', - items: [ - {text: 'Changelog', link: '/core/upgrading'}, - {text: 'Contributing', link: '/core/contributing'}, - {text: 'Roadmap', link: 'https://github.com/orgs/lunarphp/projects/8'}, - {text: '0.x Docs', link: 'https://v0.lunarphp.io/'}, - ] - } - ], - - sidebar: { - // This sidebar gets displayed when a user - // is on `guide` directory. - '/core/': [ - { - text: 'Getting Started', - collapsed: false, - items: [ - {text: 'Overview', link: '/core/overview'}, - {text: 'Installation', link: '/core/installation'}, - {text: 'Starter Kits', link: '/core/starter-kits'}, - {text: 'Configuration', link: '/core/configuration'}, - {text: 'Upgrade Guide', link: '/core/upgrading'}, - {text: 'Security', link: '/core/securing-your-site'}, - {text: 'Contributing', link: '/core/contributing'} - ] - }, - { - text: 'Reference', - collapsed: false, - items: [ - {text: 'Activity Log', link: '/core/reference/activity-log'}, - {text: 'Addresses', link: '/core/reference/addresses'}, - {text: 'Associations', link: '/core/reference/associations'}, - {text: 'Attributes', link: '/core/reference/attributes'}, - {text: 'Carts', link: '/core/reference/carts'}, - {text: 'Channels', link: '/core/reference/channels'}, - {text: 'Collections', link: '/core/reference/collections'}, - {text: 'Currencies', link: '/core/reference/currencies'}, - {text: 'Customers', link: '/core/reference/customers'}, - {text: 'Discounts', link: '/core/reference/discounts'}, - {text: 'Languages', link: '/core/reference/languages'}, - {text: 'Media', link: '/core/reference/media'}, - {text: 'Orders', link: '/core/reference/orders'}, - {text: 'Payments', link: '/core/reference/payments'}, - {text: 'Pricing', link: '/core/reference/pricing'}, - {text: 'Products', link: '/core/reference/products'}, - {text: 'Search', link: '/core/reference/search'}, - {text: 'Tags', link: '/core/reference/tags'}, - {text: 'Taxation', link: '/core/reference/taxation'}, - {text: 'URLs', link: '/core/reference/urls'} - ] - }, - { - text: 'Storefront', - collapsed: false, - items: [ - {text: 'Storefront Session', link: '/core/storefront-utils/storefront-session'}, - ] - }, - { - text: 'Extending', - collapsed: false, - items: [ - {text: 'Carts', link: '/core/extending/carts'}, - {text: 'Discounts', link: '/core/extending/discounts'}, - {text: 'Models', link: '/core/extending/models'}, - {text: 'Orders', link: '/core/extending/orders'}, - {text: 'Payments', link: '/core/extending/payments'}, - {text: 'Search', link: '/core/extending/search'}, - {text: 'Shipping', link: '/core/extending/shipping'}, - {text: 'Taxation', link: '/core/extending/taxation'} - ] - } - ], - - '/admin/': [ - { - text: 'Getting Started', - collapsed: false, - items: [ - {text: 'Overview', link: '/admin/overview'}, - ] - }, - { - text: 'Extending', - collapsed: false, - items: [ - {text: 'Overview', link: '/admin/extending/overview'}, - {text: 'Access Control', link: '/admin/extending/access-control'}, - {text: 'Add-ons', link: '/admin/extending/addons'}, - {text: 'Attributes', link: '/admin/extending/attributes'}, - {text: 'Panel', link: '/admin/extending/panel'}, - {text: 'Pages', link: '/admin/extending/pages'}, - {text: 'Resources', link: '/admin/extending/resources'}, - {text: 'Relation Managers', link: '/admin/extending/relation-managers'}, - {text: 'Order Management', link: '/admin/extending/order-management'} - ] - } - ], - - }, - - socialLinks: [ - {icon: 'github', link: 'https://github.com/lunarphp/lunar'}, - {icon: 'twitter', link: 'https://twitter.com/lunarphp'}, - {icon: 'discord', link: 'https://discord.gg/v6qVWaf'}, - ], - - algolia: { - appId: 'ZHX0K72823', - apiKey: '42f3d86ed75f289e5cb75e9d7c6f43f9', - indexName: 'lunarphp' - }, - } -}) diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue deleted file mode 100644 index 350febec01..0000000000 --- a/docs/.vitepress/theme/Layout.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css deleted file mode 100644 index 73aed0ab6d..0000000000 --- a/docs/.vitepress/theme/custom.css +++ /dev/null @@ -1,69 +0,0 @@ -:root { - --vp-c-brand: #22baf7; - --vp-c-brand-light: #08a1dd; - - --vp-c-text-light-1: #122036; - --vp-c-text-light-2: #263957; - --vp-c-text-light-3: #435777; - - --vp-font-family-base: 'Poppins', sans-serif; -} - -.dark { - --vp-c-bg: #122036; - - --vp-c-bg-elv: #0A172D; - --vp-c-bg-elv-up: #313136; - --vp-c-bg-elv-down: #1e1e20; - --vp-c-bg-elv-mute: #313136; - - --vp-c-bg-soft: #0A172D; - --vp-c-bg-soft-up: #0A172D; - --vp-c-bg-soft-down: #0A172D; - --vp-c-bg-soft-mute: #0A172D; - - --vp-c-bg-alt: #0A172D; - - --vp-c-mute: #384B68; -} - -.vp-doc .header-anchor { - margin-left: -1.1em; -} - -.btn { - display: inline-block; - padding: 10px 20px; - background: #22baf7; - color: white; - text-decoration: none; - border-radius: 5px; - font-weight: bold; -} -.btn:hover { - background: #08a1dd; -} - -.banner { - background-color: var(--vp-c-bg-soft); - padding: 15px; - font-size: 14px; - border-radius: 5px; -} - -.banner .banner-title { - font-weight: bold; -} - -.banner .banner-btn { - background-color: var(--vp-c-bg); - padding: 10px; - text-align: center; - font-size: 14px; - border-radius: 5px; - margin-top: 10px; -} - -.banner .banner-btn:hover { - background-color: var(--vp-c-mute); -} diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js deleted file mode 100644 index c191ceedb5..0000000000 --- a/docs/.vitepress/theme/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import DefaultTheme from 'vitepress/theme' -import './custom.css' -import Layout from "./Layout.vue"; - -export default { - extends: DefaultTheme, - // override the Layout with a wrapper component that - // injects the slots - Layout: Layout -} diff --git a/docs/admin/extending/access-control.md b/docs/admin/extending/access-control.md deleted file mode 100644 index bd258b2015..0000000000 --- a/docs/admin/extending/access-control.md +++ /dev/null @@ -1,55 +0,0 @@ -# Staff Members - -Your staff members are essentially users who can log in to the admin panel and have permissions assigned to them. Staff -members are not to be confused with users in the `users` table, the Lunar uses a different table for authenticating -users in the admin panel. This is a design choice to ensure that your customers can never accidentally be given access. - -# Roles and permissions - -In Lunar panel, we are utilizing roles and permission for authorization. This give you the ability to assign multiple -permissions to a role and assign it to the staff without assigning permission one by one to the staff. - -::: tip -The admin panel is using `spatie/laravel-permission` package -::: - -### Roles -Out of the box Lunar provided `admin` and `staff` roles. You can create new role using our Access Control page in Staff -menu. -After installation, the panel will have one admin. You can assign more but non admins cannot assign other admins. - -### Permissions -Permissions can be assigned to roles or directly to staff and this dictates what they can do or see in the panel. If a -user does not have a certain permission to view a page or perform an action they will get an Unauthorized HTTP error. -They will also potentially see a reduced amount of menu items throughout the admin panel. - -To enable permissions on a staff member, simply edit them via the staff page and assign the permissions you want them -to have. - -##### Adding permissions -While the panel provided a page to create role and assign permissions. It's deliberated that permission are not created -from the panel as the authorization are required to be implemented in code. It might change in the future but the -recommended way to create roles and permission would be Lunar migration state or Laravel's migration. So you can deploy -it to other environment easily. - -## Authorisation -First party permission provided by Lunar are used to authorise repective section of the panel. You should still -implement authorisation checking respectively for your new permissions and custom pages. - -Example: -`middleware` 'can:permission-handle' -`in-code` Auth::user()->can('permission-handle') -::: - -### Two-Factor Authentication - -You can choose whether to enforce Two-Factor Authentication or disable it entirely. - -```php - -public function register() -{ - \Lunar\Admin\Support\Facades\LunarPanel::enforceTwoFactorAuth()->register(); - \Lunar\Admin\Support\Facades\LunarPanel::disableTwoFactorAuth()->register(); -} -``` diff --git a/docs/admin/extending/addons.md b/docs/admin/extending/addons.md deleted file mode 100644 index e3458ab659..0000000000 --- a/docs/admin/extending/addons.md +++ /dev/null @@ -1,21 +0,0 @@ -# Developing Add-ons - -When creating add-on packages for Lunar you may wish to add new screens and functionality to the Filament panel. - -To achieve this you will want to create a Filament plugin in your package. With Filament plugins you can add additional -resources, pages and widgets. See https://filamentphp.com/docs/3.x/panels/plugins for more information. - -## Registering Filament plugins - -Add-on packages should not try to register a Filament plugin automatically in the Lunar panel. Instead, installation -instructions should be provided. - -Below is an example of how a plugin should be registered to the Lunar admin panel, typically in your Laravel app -service provider. - -```php -use Lunar\Admin\Support\Facades\LunarPanel; - -LunarPanel::panel(fn($panel) => $panel->plugin(new ReviewsPlugin())) - ->register(); -``` diff --git a/docs/admin/extending/attributes.md b/docs/admin/extending/attributes.md deleted file mode 100644 index b4ed06d02e..0000000000 --- a/docs/admin/extending/attributes.md +++ /dev/null @@ -1,105 +0,0 @@ -# Extending Attributes - -You can add your own attribute field types to Lunar and control how they are rendered in the panel. - -In order to render the form component from the attribute, it needs to be converted into a Filament form component and a suitable Livewire Synthesizer associated so it can be hydrated/dehydrated properly. - -## Create the field - -```php -use Lunar\FieldTypes\Text; - -class CustomField extends Text -{ - // ... -} -``` - -## Create the field type - -```php -use Lunar\Admin\Support\FieldTypes\BaseFieldType; - -class CustomFieldType extends BaseFieldType -{ - protected static string $synthesizer = CustomFieldSynth::class; - - public static function getFilamentComponent(Attribute $attribute): Component - { - return TextInput::make($attribute->handle); - } -} -``` - -## Adding settings - -There may be additional settings you want your field to have, for example the Number field has `min` and `max` settings. -To add these fields, you will need to tell Filament how to render the inputs. - -```php -class CustomFieldType extends BaseFieldType -{ - // ... - - public static function getConfigurationFields(): array - { - return [ - Grid::make(2)->schema([ - \Filament\Forms\Components\TextInput::make('min_length'), - \Filament\Forms\Components\TextInput::make('max_length'), - ]), - ]; - } -} -``` - -These will when be stored in the `configuration` JSON column for the attribute. Which you can then access when you -render the field in the panel. - -```php -use Lunar\Admin\Support\FieldTypes\BaseFieldType; - -class CustomFieldType extends BaseFieldType -{ - // ... - - public static function getFilamentComponent(Attribute $attribute): Component - { - $min = (int) $attribute->configuration->get('min_length'); - $max = (int) $attribute->configuration->get('max_length'); - - return TextInput::make($attribute->handle)->min($min)->max($max); - } -} -``` - -## Create the Livewire Synthesizer - -So Livewire knows how to hydrate/dehydrate the values provided to the field type when editing, we need to add a -Synthesizer. You can read more about Livewire Synthesizers and what they do -here: [https://livewire.laravel.com/docs/synthesizers](https://livewire.laravel.com/docs/synthesizers) - -```php - MyManageOrderExtension::class, -]); -``` - -You then have access to these methods in your class to override area's of the order view screen. - -- `extendInfolistSchema(): array` - -- `extendInfolistAsideSchema(): array` - - `extendCustomerEntry(): Infolists\Components\Component` - - `extendTagsSection(): Infolists\Components\Component` - - `extendAdditionalInfoSection(): Infolists\Components\Component` - - `extendShippingAddressInfolist(): Infolists\Components\Component` - - `extendBillingAddressInfolist(): Infolists\Components\Component` - - `extendAddressEditSchema(): array` - -- `exendOrderSummaryInfolist(): Infolists\Components\Section` -- `extendOrderSummarySchema(): array` - - `extendOrderSummaryNewCustomerEntry(): Infolists\Components\Entry` - - `extendOrderSummaryStatusEntry(): Infolists\Components\Entry` - - `extendOrderSummaryReferenceEntry(): Infolists\Components\Entry` - - `extendOrderSummaryCustomerReferenceEntry(): Infolists\Components\Entry` - - `extendOrderSummaryChannelEntry(): Infolists\Components\Entry` - - `extendOrderSummaryCreatedAtEntry(): Infolists\Components\Entry` - - `extendOrderSummaryPlacedAtEntry(): Infolists\Components\Entry` -- `extendTimelineInfolist(): Infolists\Components\Component` -- `extendOrderTotalsAsideSchema(): array` - - `extendDeliveryInstructionsEntry(): Infolists\Components\TextEntry` - - `extendOrderNotesEntry(): Infolists\Components\TextEntry` -- `extendOrderTotalsInfolist(): Infolists\Components\Section` -- `extendOrderTotalsSchema(): array` - - `extendSubTotalEntry(): Infolists\Components\TextEntry` - - `extendDiscountTotalEntry(): Infolists\Components\TextEntry` - - `extendShippingBreakdownGroup(): Infolists\Components\Group` - - `extendTaxBreakdownGroup(): Infolists\Components\Group` - - `extendTotalEntry(): Infolists\Components\TextEntry` - - `extendPaidEntry(): Infolists\Components\TextEntry` - - `extendRefundEntry(): Infolists\Components\TextEntry` - -- `extendShippingInfolist(): Infolists\Components\Section` -- `extendTransactionsInfolist(): Infolists\Components\Component` -- `extendTransactionsRepeatableEntry(): Infolists\Components\RepeatableEntry` - -## Extending `OrderItemsTable` - -```php -\Lunar\Facades\LunarPanel::extensions([ - \Lunar\Admin\Filament\Resources\OrderResource\Pages\Components\OrderItemsTable::class => OrderItemsTableExtension::class -]); -``` -### Available Methods - -- `extendOrderLinesTableColumns(): array` -- `extendTable(): Table` diff --git a/docs/admin/extending/overview.md b/docs/admin/extending/overview.md deleted file mode 100644 index 311dbe899f..0000000000 --- a/docs/admin/extending/overview.md +++ /dev/null @@ -1,82 +0,0 @@ - -# Overview - -The Lunar Panel is highly customizable, you can add and change the behaviour of existing Filament resources. This might be useful if you wish to add a button for -additional custom functionality. - -## Extending Pages - -To extend a page you need to create and register an extension. - -### Extending edit resource - -For example, the code below will register a custom extension called `MyEditExtension` for the `EditProduct` Filament page. - -```php -use Lunar\Admin\Support\Facades\LunarPanel; -use Lunar\Panel\Filament\Resources\ProductResource\Pages\EditProduct; -use App\Admin\Filament\Resources\Pages\MyEditExtension; - -LunarPanel::extensions([ - EditProduct::class => MyEditExtension::class, -]); -``` - -### Extending list resource - -For example, the code below will register a custom extension called `MyListExtension` for the `ListProduct` Filament page. - -```php -use Lunar\Admin\Support\Facades\LunarPanel; -use Lunar\Panel\Filament\Resources\ProductResource\Pages\ListProduct; -use App\Admin\Filament\Resources\Pages\MyEditExtension; - -LunarPanel::extensions([ - ListProduct::class => MyListExtension::class, -]); - -``` - -## Extending Resources -Much like extending pages, to extend a resource you need to create and register an extension. - -For example, the code below will register a custom extension called `MyProductResourceExtension` for the `ProductResource` Filament resource. - -```php -use Lunar\Admin\Support\Facades\LunarPanel; -use Lunar\Panel\Filament\Resources\ProductResource; -use App\Admin\Filament\Resources\MyProductResourceExtension; - -LunarPanel::extensions([ - ProductResource::class => MyProductResourceExtension::class, -]); - -``` - -## Extendable resources - -All Lunar panel resources are extendable. This means you can now add your own functionality or change out existing behaviour. - -```php -use Lunar\Panel\Filament\Resources\ActivityResource; -use Lunar\Panel\Filament\Resources\AttributeGroupResource; -use Lunar\Panel\Filament\Resources\BrandResource; -use Lunar\Panel\Filament\Resources\ChannelResource; -use Lunar\Panel\Filament\Resources\CollectionGroupResource; -use Lunar\Panel\Filament\Resources\CollectionResource; -use Lunar\Panel\Filament\Resources\CurrencyResource; -use Lunar\Panel\Filament\Resources\CustomerGroupResource; -use Lunar\Panel\Filament\Resources\CustomerResource; -use Lunar\Panel\Filament\Resources\DiscountResource; -use Lunar\Panel\Filament\Resources\LanguageReousrce; -use Lunar\Panel\Filament\Resources\OrderResource; -use Lunar\Panel\Filament\Resources\ProductOptionrResource; -use Lunar\Panel\Filament\Resources\ProductResource; -use Lunar\Panel\Filament\Resources\ProductResource; -use Lunar\Panel\Filament\Resources\ProductTypeResource; -use Lunar\Panel\Filament\Resources\ProductVariantResource; -use Lunar\Panel\Filament\Resources\StaffResource; -use Lunar\Panel\Filament\Resources\TagResource; -use Lunar\Panel\Filament\Resources\TaxClassResource; -use Lunar\Panel\Filament\Resources\TaxZoneResource; -``` diff --git a/docs/admin/extending/pages.md b/docs/admin/extending/pages.md deleted file mode 100644 index 8ba953278f..0000000000 --- a/docs/admin/extending/pages.md +++ /dev/null @@ -1,401 +0,0 @@ -# Extending Pages - -## Writing Extensions - -There are three extension types Lunar provides, these are for Create, Edit and Listing pages. - -You will want to place the extension class in your application. A sensible location might be `App\Lunar\MyCreateExtension`. - -Once created you will need to register the extension, typically in your app service provider. - - -## CreatePageExtension - -An example of extending a create page. - -```php -use Filament\Actions; -use Lunar\Admin\Support\Extending\CreatePageExtension; -use Lunar\Admin\Filament\Widgets; - -class MyCreateExtension extends CreatePageExtension -{ - public function heading($title): string - { - return $title . ' - Example'; - } - - public function subheading($title): string - { - return $title . ' - Example'; - } - - public function getTabs(array $tabs): array - { - return [ - ...$tabs, - 'review' => Tab::make('Review') - ->modifyQueryUsing(fn (Builder $query) => $query->where('status', 'review')), - ]; - } - - public function headerWidgets(array $widgets): array - { - $widgets = [ - ...$widgets, - Widgets\Dashboard\Orders\OrderStatsOverview::make(), - ]; - - return $widgets; - } - - public function headerActions(array $actions): array - { - $actions = [ - ...$actions, - Actions\Action::make('Cancel'), - ]; - - return $actions; - } - - public function formActions(array $actions): array - { - $actions = [ - ...$actions, - Actions\Action::make('Create and Edit'), - ]; - - return $actions; - } - - public function footerWidgets(array $widgets): array - { - $widgets = [ - ...$widgets, - Widgets\Dashboard\Orders\LatestOrdersTable::make(), - ]; - - return $widgets; - } - - public function beforeCreate(array $data): array - { - $data['model_code'] .= 'ABC'; - - return $data; - } - - public function beforeCreation(array $data): array - { - return $data; - } - - public function afterCreation(Model $record, array $data): Model - { - return $record; - } -} - -// Typically placed in your AppServiceProvider file... -LunarPanel::extensions([ - \Lunar\Admin\Filament\Resources\CustomerGroupResource\Pages\CreateCustomerGroup::class => MyCreateExtension::class, -]); -``` - -## EditPageExtension - -An example of extending an edit page. - -```php -use Filament\Actions; -use Lunar\Admin\Support\Extending\EditPageExtension; -use Lunar\Admin\Filament\Widgets; - -class MyEditExtension extends EditPageExtension -{ - public function heading($title): string - { - return $title . ' - Example'; - } - - public function subheading($title): string - { - return $title . ' - Example'; - } - - public function headerWidgets(array $widgets): array - { - $widgets = [ - ...$widgets, - Widgets\Dashboard\Orders\OrderStatsOverview::make(), - ]; - - return $widgets; - } - - public function headerActions(array $actions): array - { - $actions = [ - ...$actions, - Actions\ActionGroup::make([ - Actions\Action::make('View on Storefront'), - Actions\Action::make('Copy Link'), - Actions\Action::make('Duplicate'), - ]) - ]; - - return $actions; - } - - public function formActions(array $actions): array - { - $actions = [ - ...$actions, - Actions\Action::make('Update and Edit'), - ]; - - return $actions; - } - - public function footerWidgets(array $widgets): array - { - $widgets = [ - ...$widgets, - Widgets\Dashboard\Orders\LatestOrdersTable::make(), - ]; - - return $widgets; - } - - public function beforeFill(array $data): array - { - $data['model_code'] .= 'ABC'; - - return $data; - } - - public function beforeSave(array $data): array - { - return $data; - } - - public function beforeUpdate(array $data, Model $record): array - { - return $data; - } - - public function afterUpdate(Model $record, array $data): Model - { - return $record; - } - - public function relationManagers(array $managers): array - { - return $managers; - } -} - -// Typically placed in your AppServiceProvider file... -LunarPanel::extensions([ - \Lunar\Admin\Filament\Resources\ProductResource\Pages\EditProduct::class => MyEditExtension::class, -]); -``` - -## ListPageExtension - -An example of extending a list page. - -```php -use Filament\Actions; -use Illuminate\Database\Eloquent\Builder; -use Illuminate\Contracts\Pagination\Paginator; -use Lunar\Admin\Support\Extending\ListPageExtension; -use Lunar\Admin\Filament\Widgets; - -class MyListExtension extends ListPageExtension -{ - public function heading($title): string - { - return $title . ' - Example'; - } - - public function subheading($title): string - { - return $title . ' - Example'; - } - - public function headerWidgets(array $widgets): array - { - $widgets = [ - ...$widgets, - Widgets\Dashboard\Orders\OrderStatsOverview::make(), - ]; - - return $widgets; - } - - public function headerActions(array $actions): array - { - $actions = [ - ...$actions, - Actions\ActionGroup::make([ - Actions\Action::make('View on Storefront'), - Actions\Action::make('Copy Link'), - Actions\Action::make('Duplicate'), - ]), - ]; - - return $actions; - } - - public function paginateTableQuery(Builder $query, int $perPage = 25): Paginator - { - return $query->paginate($perPage); - } - - public function footerWidgets(array $widgets): array - { - $widgets = [ - ...$widgets, - Widgets\Dashboard\Orders\LatestOrdersTable::make(), - ]; - - return $widgets; - } -} - -// Typically placed in your AppServiceProvider file... -LunarPanel::extensions([ - \Lunar\Admin\Filament\Resources\ProductResource\Pages\ListProducts::class => MyListExtension::class, -]); -``` - -## ViewPageExtension - -An example of extending a view page. - -```php -use Filament\Actions; - -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Table; -use Filament\Infolists\Infolist; -use Filament\Infolists\Components\TextEntry; -use Lunar\Admin\Support\Extending\ViewPageExtension; -use Lunar\Admin\Filament\Widgets; - -class MyViewExtension extends ViewPageExtension -{ - public function headerWidgets(array $widgets): array - { - $widgets = [ - ...$widgets, - Widgets\Dashboard\Orders\OrderStatsOverview::make(), - ]; - - return $widgets; - } - - public function heading($title): string - { - return $title . ' - Example'; - } - - public function subheading($title): string - { - return $title . ' - Example'; - } - - public function headerActions(array $actions): array - { - $actions = [ - ...$actions, - Actions\ActionGroup::make([ - Actions\Action::make('Download PDF') - ]) - ]; - - return $actions; - } - - public function extendsInfolist(Infolist $infolist): Infolist - { - return $infolist->schema([ - ...$infolist->getComponents(true), - TextEntry::make('custom_title'), - ]); - } - - public function footerWidgets(array $widgets): array - { - $widgets = [ - ...$widgets, - Widgets\Dashboard\Orders\LatestOrdersTable::make(), - ]; - - return $widgets; - } -} - -// Typically placed in your AppServiceProvider file... -LunarPanel::extensions([ - \Lunar\Admin\Filament\Resources\OrderResource\Pages\ManageOrder::class => MyViewExtension::class, -]); -``` - -## RelationPageExtension - -An example of extending a relation page. - -```php -use Filament\Actions; -use Lunar\Admin\Support\Extending\RelationPageExtension; - -class MyRelationExtension extends RelationPageExtension -{ - public function heading($title): string - { - return $title . ' - Example'; - } - - public function subheading($title): string - { - return $title . ' - Example'; - } - - public function headerActions(array $actions): array - { - $actions = [ - ...$actions, - Actions\ActionGroup::make([ - Actions\Action::make('Download PDF') - ]) - ]; - - return $actions; - } -} - -// Typically placed in your AppServiceProvider file... -LunarPanel::extensions([ - \Lunar\Admin\Filament\Resources\ProductResource\Pages\ManageProductMedia::class => MyRelationExtension::class, -]); -``` - -## Extending Pages In Addons - -If you are building an addon for Lunar, you may need to take a slightly different approach when modifying forms, etc. - -For example, you cannot assume the contents of a form, so you may need to take an approach such as this... - -```php - public function extendForm(Form $form): Form - { - $form->schema([ - ...$form->getComponents(true), // Gets the currently registered components - TextInput::make('model_code'), - ]); - return $form; - } -``` diff --git a/docs/admin/extending/panel.md b/docs/admin/extending/panel.md deleted file mode 100644 index 000004edc4..0000000000 --- a/docs/admin/extending/panel.md +++ /dev/null @@ -1,57 +0,0 @@ -# Extending The Panel - -You may customise the Filament panel when registering it in your app service provider. - -We provide a handy function which gives you direct access to the panel to change its properties. - -For example, the following would change the panel's URL to `/admin` rather than the default `/lunar`. - -```php -use Lunar\Admin\Support\Facades\LunarPanel; - -LunarPanel::panel(fn($panel) => $panel->path('admin')) - ->extensions([ - // ... - ]) - ->register(); -``` - -## Adding to the panel - -The [Filament](https://filamentphp.com/) panel allows you to add further screens in the form of Pages or Resources, and -indeed customise any available panel option. - -Below is an example of how you can use the panel object. - -```php -LunarPanel::panel(function ($panel) { - return $panel - ->pages([ - // Register standalone Filament Pages - SalesReport::class, - RevenueReport::class, - ]) - ->resources([ - // Register new Filament Resources - OpeningTimeResource::class, - BannerResource::class, - ]) - ->livewireComponents([ - // Register Livewire components - OrdersSalesChart::class, - ])->plugin( - // Register a Filament plugin - new ShippingPlugin(), - ) - ->navigationGroups([ - // Set the navigation groups - 'Catalog', - 'Sales', - 'CMS', - 'Reports', - 'Shipping', - 'Settings', - ]); -})->register(); -``` -For further information please consult the [Filament documentation](https://filamentphp.com/docs). diff --git a/docs/admin/extending/relation-managers.md b/docs/admin/extending/relation-managers.md deleted file mode 100644 index 98675f8414..0000000000 --- a/docs/admin/extending/relation-managers.md +++ /dev/null @@ -1,32 +0,0 @@ -# Extending Relation Managers - -## MyCustomerGroupPricingRelationManagerExtension - -An example of extending the CustomerGroupPricingRelationManager - -```php -class MyCustomerGroupPricingRelationManagerExtension extends \Lunar\Admin\Support\Extending\RelationManagerExtension -{ - public function extendForm(\Filament\Forms\Form $form): \Filament\Forms\Form - { - return $form->schema([ - ...$form->getComponents(withHidden: true), - - \Filament\Forms\Components\TextInput::make('custom_column') - ]); - } - - public function extendTable(\Filament\Tables\Table $table): \Filament\Tables\Table - { - return $table->columns([ - ...$table->getColumns(), - \Filament\Tables\Columns\TextColumn::make('product_code') - ]); - } -} - -// Typically placed in your AppServiceProvider file... -LunarPanel::extensions([ - \Lunar\Admin\Filament\Resources\ProductResource\RelationManagers\CustomerGroupPricingRelationManager::class => MyCustomerGroupPricingRelationManagerExtension::class, -]); -``` diff --git a/docs/admin/extending/resources.md b/docs/admin/extending/resources.md deleted file mode 100644 index 417bd6f69f..0000000000 --- a/docs/admin/extending/resources.md +++ /dev/null @@ -1,62 +0,0 @@ -# Extending Resources - -## MyProductResourceExtension - -An example of extending the ProductResource - -```php -class MyProductResourceExtension extends \Lunar\Admin\Support\Extending\ResourceExtension -{ - public function extendForm(\Filament\Forms\Form $form): \Filament\Forms\Form - { - return $form->schema([ - ...$form->getComponents(withHidden: true), - - \Filament\Forms\Components\TextInput::make('custom_column') - ]); - } - - public function extendTable(\Filament\Tables\Table $table): \Filament\Tables\Table - { - return $table->columns([ - ...$table->getColumns(), - \Filament\Tables\Columns\TextColumn::make('product_code') - ]); - } - - public function getRelations(array $managers) : array - { - return [ - ...$managers, - // This is just a standard Filament relation manager. - // see https://filamentphp.com/docs/3.x/panels/resources/relation-managers#creating-a-relation-manager - MyCustomProductRelationManager::class, - ]; - } - - public function extendPages(array $pages) : array - { - return [ - ...$pages, - // This is just a standard Filament page - // see https://filamentphp.com/docs/3.x/panels/pages#creating-a-page - 'my-page-route-name' => MyPage::route('/{record}/my-page'), - ]; - } - - public function extendSubNavigation(array $nav) : array - { - return [ - ...$nav, - // This is just a standard Filament page - // see https://filamentphp.com/docs/3.x/panels/pages#creating-a-page - MyPage::class, - ]; - } -} - -// Typically placed in your AppServiceProvider file... -LunarPanel::extensions([ - \Lunar\Admin\Filament\Resources\ProductResource::class => MyProductResourceExtension::class, -]); -``` diff --git a/docs/admin/overview.md b/docs/admin/overview.md deleted file mode 100644 index c8a5bf4ef5..0000000000 --- a/docs/admin/overview.md +++ /dev/null @@ -1,31 +0,0 @@ -# Introduction - -Lunar's admin panel is powered by **Filament v3**. It allows you to easily extend the admin panel to suit your project. - -With the panel you can administer your products, collections, orders, customers, discounts, settings and much more. - -## Registering - -If you followed the core installation instructions or have installed a starter kit, you will likely already have this in place. - -```php -use Lunar\Admin\Support\Facades\LunarPanel; - -class AppServiceProvider extends ServiceProvider -{ - public function register(): void - { - LunarPanel::register(); - } -``` - -## Contributing - -If you wish to contribute to the project, please review the roadmap at https://github.com/orgs/lunarphp/projects/8/views/8 - -You can request to contribute on an issue in the backlog, or you can propose a new issue. - -::: tip -Here's a guide on how to set-up your development environment ready for contributing to Lunar. -[Setting Up Lunar For Local Development](/core/local-development) -::: diff --git a/docs/core/configuration.md b/docs/core/configuration.md deleted file mode 100644 index a843ddcc47..0000000000 --- a/docs/core/configuration.md +++ /dev/null @@ -1,117 +0,0 @@ -# Configuration - -## Overview - -Configuration for Lunar is separated into individual files under `config/lunar`. -You can either override the different config options adhoc or you can publish all the configuration options and tweak -as you see fit. - -```bash -php artisan vendor:publish --tag=lunar -``` - -### Database Table Prefix - -`lunar/database.php` - -So that Lunar tables do not conflict with your existing application database tables, you can specify a prefix to use. If you change this after installation, you are on your own - happy renaming! - -```php - 'table_prefix' => 'lunar_', -``` - -### Database Connection - -`lunar/database.php` - - By default, the package uses the default database connection defined in Laravel. Here specify a custom database connection for Lunar. - -```php -'connection' => 'some_custom_connection', -``` - -If you are using a custom database connection that is not the default connection in your Laravel configuration, you need to specify it in the .env file as well. - -``` -ACTIVITY_LOGGER_DB_CONNECTION=some_custom_connection -``` - -In our package, we utilize Spatie's [laravel-activitylog](https://spatie.be/docs/laravel-activitylog) for logging. The mentioned configuration allows the activity logger to use a different database connection instead of the default database connection. - -### Orders - -`lunar/orders.php` - -Here you can set up the statuses you wish to use for your orders. - -```php -'draft_status' => 'awaiting-payment', -'statuses' => [ - 'awaiting-payment' => [ - 'label' => 'Awaiting Payment', - 'color' => '#848a8c', - ], - 'payment-received' => [ - 'label' => 'Payment Received', - 'color' => '#6a67ce', - ], -], -``` - -### Media - -`lunar/media.php` - -Transformations for all uploaded images. - -```php -'transformations' => [ - 'zoom' => [ - 'width' => 500, - 'height' => 500, - ], - 'large' => [ - // ... - ], - 'medium' => [ - // ... - ], - 'small' => [ - // ... - ], -], -``` - -### Products - -`lunar-hub/products.php` - -```php -'disable_variants' => false, -'sku' => [ - 'required' => true, - 'unique' => true, -], -'gtin' => [ - 'required' => false, - 'unique' => false, -], -'mpn' => [ - 'required' => false, - 'unique' => false, -], -'ean' => [ - 'required' => false, - 'unique' => false, -], -``` - -### Pricing - -`lunar/pricing.php` - -If you want to store pricing inclusive of tax then set this config value to `true`. - -```php -'stored_inclusive_of_tax' => false, -``` diff --git a/docs/core/contributing.md b/docs/core/contributing.md deleted file mode 100644 index ce5a7c5b1c..0000000000 --- a/docs/core/contributing.md +++ /dev/null @@ -1,95 +0,0 @@ -# Contributing - -## Overview - -Lunar is an open source project, and so by its very nature, welcomes contributions. - -You can contribute to the project in many different ways. Reporting bugs, fixing bugs, helping with the documentation, -making suggestions and submitting improvements to the software. - -## Monorepo - -Lunar uses a monorepo [lunarphp/lunar](https://github.com/lunarphp/lunar) approach to maintaining its codebase. [Monorepos](https://en.wikipedia.org/wiki/Monorepo) are quite common, -but may not be familiar to some. The monorepo helps us to organise the code for ease of development. - -## Contributing Code - -The basic process for contributing to Lunar is as follows... - -1. Fork the monorepo -2. Clone your fork locally -3. Make your changes -4. Ensure the tests run and complete successfully -5. Submit a pull request - -However, if you're not used to working with monorepo's and setting them up inside a test Laravel application, no problem! - -::: tip -Here's a guide on how to set-up your development environment ready for contributing to Lunar. -[Setting Up Lunar For Local Development](/core/local-development) -::: - -## Found a Bug? - -If you find a bug in the software please raise a GitHub Issue on the -[lunarphp/lunar](https://github.com/lunarphp/lunar/issues) repository. Please ensure that your issue includes the -following: - -**Minimum** - -- Clear title and description of the issue -- Steps on how to reproduce the issue - -**Ideal** - -- An accompanying Pull Request with a test to demonstrate the issue. - -Lunar is an open source project and as such we want contribution to be as accessible as possible and to enable -contributors to actively collaborate on features and issues. By making sure you provide as much information as -possible you are giving your issue the best chance to get the attention it needs. - -:::info -Be aware that creating an issue does not mean it will get activity straight away, please be patient and understand we -will do our best to look into it as soon as possible. - -Open source code belongs to all of us, and it's all of our responsibility to push it forward. -::: - -## Proposing a Feature - -Before you start coding away on the next awesome feature, we highly recommend starting a -[discussion](https://github.com/lunarphp/lunar/discussions) to check that your contribution will be welcomed. We would -hate for you to spend valuable time on something that won't be merged into Lunar. - -However, you're more than welcome to code away on your idea if you think it will help the discussion. - -## Making a Pull Request - -When making a [pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request), -there should be a suitable template for you to follow to ensure the bug or feature can be reviewed in a timely manner. -If the pull request is missing information or is unclear about what it offers or solves, it could delay progress or be -closed. -A PR should be able to include the following: - -- The title should be relevant and quickly explain what to expect inside -- A clear description of the feature or fix -- References to any issues the PR resolves -- Label as either a `bug`, `enhancement`, `feature` or `documentation` -- Any relevant documentation updates -- Automated tests with adequate code coverage - -## Documentation Updates - -If you would like to contribute to the documentation you can do easily by following these instructions... - -1. Fork the monorepo `lunarphp/lunar` -2. Clone your fork locally -3. In your terminal change to the `/docs` directory -4. Run `npm install` -5. Run `npm run docs:dev` to preview the documentation locally -6. Make your changes -7. Submit a pull request - -Lunar uses [VitePress](https://vitepress.dev/) for our documentation site which uses -[Markdown](https://www.markdownguide.org/basic-syntax/) files to store the content. You'll find these Markdown files in -the `/docs` directory. diff --git a/docs/core/extending/carts.md b/docs/core/extending/carts.md deleted file mode 100644 index 4c4a6491fd..0000000000 --- a/docs/core/extending/carts.md +++ /dev/null @@ -1,134 +0,0 @@ -# Cart Extending - -## Overview - -Carts are a central part of any E-Commerce storefront. We have designed Carts to be easily extended, so you can add any logic you need for your storefront throughout its lifetime. - -## Pipelines - -### Adding a Cart Pipeline - -All pipelines are defined in `config/lunar/cart.php` - -```php -'pipelines' => [ - /* - |-------------------------------------------------------------------------- - | Run these pipelines when the cart is calculating. - |-------------------------------------------------------------------------- - */ - 'cart' => [ - \Lunar\Pipelines\Cart\CalculateLines::class, - \Lunar\Pipelines\Cart\ApplyShipping::class, - \Lunar\Pipelines\Cart\Calculate::class, - ], - /* - |-------------------------------------------------------------------------- - | Run these pipelines when the cart lines are being calculated. - |-------------------------------------------------------------------------- - */ - 'cart_lines' => [ - \Lunar\Pipelines\CartLine\GetUnitPrice::class, - ], -], -``` - -You can add your own pipelines to the configuration, they might look something like: - -```php - [ - // ... - App\Pipelines\Cart\CustomCartPipeline::class, -], -``` - -::: tip -Pipelines will run from top to bottom -::: - -## Actions - -During the lifecycle of a Cart, various actions are taken. While generally what Lunar provides will be fine for most storefronts, there are times where you may want something done slightly differently. For this reason we have made all actions configurable, so you can swap them out as you see fit. - -Actions are defined in `config/lunar/carts` and if you need to replace an action, check the class of the action you want to change to see what it is expecting. - -## Action validation - -You may wish to provide some validation against actions before they run. Your own validation may look something like: - - -```php -parameters['quantity'] ?? 0; - - // ... - - if (!$condition) { - return $this->fail('cart', 'Something went wrong'); - } - - - return $this->pass(); - } -} - -``` - -You can then register this class against the corresponding action in `config/lunar/cart.php`: - -```php -'validators' => [ - 'add_to_cart' => [ - // ... - \App\Validation\CartLine\CartLineQuantity::class, - ], - // ... -], -``` - -If validation fails, a `Lunar\Exceptions\CartException` will be thrown. You will be able to access errors like you can on Laravel's own validation responses. - -```php -try { - $cart->setShippingOption($option); -} catch (CartException $e) { - $e->errors()->all(); -} -``` diff --git a/docs/core/extending/discounts.md b/docs/core/extending/discounts.md deleted file mode 100644 index 3e98b70239..0000000000 --- a/docs/core/extending/discounts.md +++ /dev/null @@ -1,96 +0,0 @@ - -# Discounts - -## Overview - -If you want to add additional functionality to Discounts, you can register your own custom discount types. - -## Registering a discount type. - -```php -use Lunar\Facades\Discounts; - -Discounts::addType(MyCustomDiscountType::class); -``` - - -```php -label('My label') - ->required(), - ]; - } - - /** - * Mutate the model data before displaying it in the admin form. - */ - public function lunarPanelOnFill(array $data): array - { - // optionally do something with $data - return $data; - } - - /** - * Mutate the form data before saving it to the discount model. - */ - public function lunarPanelOnSave(array $data): array - { - // optionally do something with $data - return $data; - } -} -``` diff --git a/docs/core/extending/models.md b/docs/core/extending/models.md deleted file mode 100644 index 8ce9220ef6..0000000000 --- a/docs/core/extending/models.md +++ /dev/null @@ -1,147 +0,0 @@ -# Models - -## Overview - -Lunar provides a number of Eloquent Models and quite often in custom applications you will want to add your own relationships and functionality to these models. - -::: warning -We highly suggest using your own Eloquent Models to add additional data, rather than trying to change fields on the core Lunar models. -::: - -## Replaceable Models -All Lunar models are replaceable, this means you can instruct Lunar to use your own custom model, throughout the ecosystem, using dependency injection. - - -### Registration -We recommend registering your own models for your application within the boot method of your Service Provider. - -When registering your models, you will need to set the Lunar model's contract as the first argument then your own model implementation for the second. - - -```php -/** - * Bootstrap any application services. - * - * @return void - */ -public function boot() -{ - \Lunar\Facades\ModelManifest::replace( - \Lunar\Models\Contracts\Product::class, - \App\Model\Product::class, - ); -} -``` - -#### Registering multiple Lunar models. - -If you have multiple models you want to replace, instead of manually replacing them one by one, you can specify a directory for Lunar to look in for Lunar models to use. -This assumes that each model extends its counterpart model i.e. `App\Models\Product` extends `Lunar\Models\Product`. - -```php -/** - * Bootstrap any application services. - * - * @return void - */ -public function boot() -{ - \Lunar\Facades\ModelManifest::addDirectory( - __DIR__.'/../Models' - ); -} -``` - -### Route binding - -Route binding is supported for your own routes and simply requires the relevant contract class to be injected. - -```php -Route::get('products/{id}', function (\Lunar\Models\Contracts\Product $product) { - $product; // App\Models\Product -}); -``` - -### Relationship support - -If you replace a model which is used in a relationship, you can easily get your own model back via relationship methods. Assuming we want to use our own instance of `App\Models\ProductVariant`. - -```php -// In our service provider. -public function boot() -{ - \Lunar\Facades\ModelManifest::replace( - \Lunar\Models\Contracts\ProductVariant::class, - \App\Model\ProductVariant::class, - ); -} - -// Somewhere else in your code... - -$product = \Lunar\Models\Product::first(); -$product->variants->first(); // App\Models\ProductVariant -``` - -### Static call forwarding - -If you have custom methods in your own model, you can call those functions directly from the Lunar model instance. - -Assuming we want to provide a new function to a product variant model. - -```php -belongsTo(Ticket::class, 'ticket_id'); -}); -``` - -See [https://laravel.com/docs/eloquent-relationships#dynamic-relationships](https://laravel.com/docs/eloquent-relationships#dynamic-relationships) for more information. diff --git a/docs/core/extending/orders.md b/docs/core/extending/orders.md deleted file mode 100644 index 0709a9ad9c..0000000000 --- a/docs/core/extending/orders.md +++ /dev/null @@ -1,63 +0,0 @@ -# Order Extending - -## Overview - -If you want to add additional functionality to the Order creation process, you can do so using pipelines. - -## Pipelines - -### Adding an Order Pipeline - -All pipelines are defined in `config/lunar/orders.php` - -```php -'pipelines' => [ - 'creation' => [ - Lunar\Pipelines\Order\Creation\FillOrderFromCart::class, - Lunar\Pipelines\Order\Creation\CreateOrderLines::class, - Lunar\Pipelines\Order\Creation\CreateOrderAddresses::class, - Lunar\Pipelines\Order\Creation\CreateShippingLine::class, - Lunar\Pipelines\Order\Creation\CleanUpOrderLines::class, - Lunar\Pipelines\Order\Creation\MapDiscountBreakdown::class, - // ... - ], -], -``` - -You can add your own pipelines to the configuration, they might look something like: - -```php - [ - 'creation' => [ - // ... - App\Pipelines\Orders\CustomOrderPipeline::class, - ], -], -``` - -::: tip -Pipelines will run from top to bottom -::: \ No newline at end of file diff --git a/docs/core/extending/payments.md b/docs/core/extending/payments.md deleted file mode 100644 index e3792b382b..0000000000 --- a/docs/core/extending/payments.md +++ /dev/null @@ -1,278 +0,0 @@ -# Payments - -## Overview - -Lunar provides an easy way for you to add your own payment drivers, by default, there is a basic `OfflinePayment` driver -that ships with Lunar, additional providers should be added to your Storefront via addons. - -Below is a list of available payment drivers. - -## Available drivers - -### First party - -- [Stripe](https://github.com/lunarphp/stripe) - -### Community - -> Made your own driver you want listing here? Get in touch on our discord channel and we'll get it added. - -## Building your own - -A payment driver should take into account 2 fundamentals: - -* Capturing a payment (whether straight away, or at a later date) -* Refunding an existing payment - -### Registering your driver - -```php -use Lunar\Facades\Payments; - -Payments::extend('custom', function ($app) { - return $app->make(CustomPayment::class); -}); -``` - -### The payment driver class - -First, we'll show you the complete class and then break it down to see what's going on. - -```php -order) { - if (!$this->order = $this->cart->order) { - $this->order = $this->cart->createOrder(); - } - } - - // ... - - $response = new PaymentAuthorize( - success: true, - message: 'The payment was successful', - orderId: $this->order->id, - paymentType: 'custom-type' - ); - - PaymentAttemptEvent::dispatch($response) - - return $response; - } - - /** - * {@inheritDoc} - */ - public function refund(Transaction $transaction, int $amount = 0, $notes = null): PaymentRefund - { - // ... - return new PaymentRefund(true); - } - - /** - * {@inheritDoc} - */ - public function capture(Transaction $transaction, $amount = 0): PaymentCapture - { - // ... - return new PaymentCapture(true); - } -} -``` - -This is the most basic implementation of a driver, you can see we are extending an `AbstractPayment`. This is a class -which is provided by Lunar and contains some useful helpers you can utilise in your own driver. - -[See available methods](#abstract-class-methods) - -#### Releasing payments - -```php -public function authorize(); -``` - -This is where you'd check the payment details which have been passed in, create any transactions for the order and -return the response. - -If you're not taking payment straight away, you should set any transactions to the type of `intent`. When you then later -capture the payment, we would recommend creating another transaction that is related to that via -the `parent_transaction_id`. - -#### Capturing payments - -```php -public function capture(Transaction $transaction, $amount = 0): PaymentCapture -``` - -When you have a transaction that has a type of `intent` the Staff member who is logged into the hub can then decide to -capture it so the card used gets charged the amount that has been authorised. - -You can pass an optional amount, but be cautious as you generally cannot capture an amount that exceeds the original -amount on the `intent` transaction. If you capture an amount less, services like Stripe will treat that as a partial -refund and no further captures can take place on that order. - -Here you should create an additional transaction against the order to show how much has been captured. - -#### Refunding payments - -```php -public function refund(Transaction $transaction, int $amount = 0, $notes = null): PaymentRefund -``` - -When refunding a transaction, you can only do so to one that's been captured. If you need to refund an order that hasn't -been captured you should instead capture an amount less to what's been authorised. - -You should only refund transactions with the type `capture`. - - - -## The AbstractPayment class - -### Available methods - -- [`cart`](#cart) -- [`order`](#order) -- [`withData`](#withData) -- [`setConfig`](#setconfig) - -#### `cart` - -```php -public function cart(Cart $cart): self -``` - -Sets the `$cart` property on the payment driver. When using the `release` method we recommend expecting a `$cart` -instance and checking for the existence of an order. - -#### `order` - -```php -public function order(Order $order): self -``` - -Sets the `$order` property on the payment driver. - -#### `withData` - -```php -public function withData(array $data): self -``` - -This method allows you to add any additional data to the payment driver, this can be anything that the payment driver -needs to function, for example. - -```php -Payments::driver('stripe')->withData([ - 'payment_intent' => $paymentIntentId -])->authorize(); -``` - -#### `setConfig` - -```php -public function setConfig(array $config): self -``` - -Here you can set up any additional config for this payment driver. By default, this will be called when you register -your payment driver and will take any values which are set in `config/lunar/payments.php` for that type. - -## Creating transactions - -Depending on how your driver works, you're likely going to need to create some transactions depending on different -scenarios. - -### Database Schema - -``` -Lunar\Models\Transaction -``` - -| Field | Description | Example | -|:----------------------|:------------------------------------------------|:--------------------------------------------------------------------------------------------| -| id | | -| parent_transaction_id | The ID of the related transaction, nullable | -| order_id | The ID of the order this transaction relates to | -| success | Whether or not the transaction was successful | 1 -| type | Whether `intent`,`capture` or `refund` | `intent` -| driver | The driver used i.e. `stripe` | `stripe` -| amount | The amount for the transaction in cents | `10000` -| reference | The reference for the driver to use | `STRIPE_123456` -| status | Usually populated from the payment provider | `success` -| notes | Any additional notes for the transaction | -| card_type | The card type | `visa` -| last_four | The last four digits of the card used | `1234` -| captured_at | The DateTime the transaction was captured | -| meta | Any additional meta info for the transaction | `{"cvc_check": "pass", "address_line1_check": "pass", "address_postal_code_check": "pass"}` -| created_at | | -| updated_at | | - -### Best Practices - -#### Releasing - -When releasing a payment, if you're not charging the card straight away, you should create a transaction with -type `intent`. This tells Lunar you intend to charge the card at a later date. - -```php -Transaction::create([ - //... - 'type' => 'intent', -]); -``` - -If you are charging the card straight away, set the type to `capture`. - -```php -Transaction::create([ - //... - 'type' => 'capture', -]); -``` - -#### Capturing - -:::tip -If you're already charging the card, you can skip this as you already have payment. 🥳 -::: - -When capturing a transaction, you should create an additional transaction with the amount that's been captured. Even if -this is the same amount as the `intent` transaction. - -```php -$intent = Transaction::whereType('intent')->first(); - -Transaction::create([ - //... - 'parent_transaction_id' => $intent->id, - 'type' => 'capture', - 'amount' => 2000, -]); -``` - -#### Refunding - -```php -$capture = Transaction::whereType('capture')->first(); - -Transaction::create([ - //... - 'parent_transaction_id' => $capture->id, - 'type' => 'refund', -]); -``` diff --git a/docs/core/extending/search.md b/docs/core/extending/search.md deleted file mode 100644 index 0ed3a5be93..0000000000 --- a/docs/core/extending/search.md +++ /dev/null @@ -1,139 +0,0 @@ -# Search - -## Overview - -Good search is the backbone of any storefront so Lunar aims to make this as extensible as possible so you can index what -you need for your front-end, without compromising on what we require our side in the hub. - -There are three things to consider when you want to extend the search: - -- Searchable fields -- Sortable fields -- Filterable fields - -## Default index values - -Eloquent models which use the `Lunar\Base\Traits\Searchable` trait will use an indexer class to tell Scout how each it -should be indexed, if an indexer isn't mapped in the config the default `EloquentIndexer` (provided by Lunar) will be -used. - -This class will map a basic set of fields to the search index: - -- The ID of the model -- Any `searchable` attributes. - -Some models require a bit more information to be indexed, such as SKU's, prices etc. For these scenarios, dedicated -indexers have been created and are mapped in the config already. - -#### `Lunar\Search\ProductIndexer` - -Fields which are indexed: - -- The ID of the model -- Any `searchable` attributes. -- The product `status` -- The product `product_type` -- The `brand` (if applicable) -- The ProductVariant `skus` related to the product. -- The `created_at` timestamp - -## Mapping custom indexers - -All indexers are mapped in `config/search.php` under `indexers`, if a model isn't mapped here then it will -simply use the default `ELoquentIndexer`. To change how each model is indexed, simply map it like so: - -```php -return [ - // ... - 'indexers' => [ - Lunar\Models\Product::class => App\Search\CustomProductIndexer::class, - ], -], -``` - -## Creating a custom indexer - -To create your own indexer, simply create a custom class like so: - -```php -with([ - 'thumbnail', - 'variants', - 'productType', - 'brand', - ]); - } - - // Scout method to get the ID used for indexing - public function getScoutKey(Model $model): mixed - { - return $model->getKey(); - } - - // Scout method to get the column used for the ID. - public function getScoutKeyName(Model $model): mixed - { - return $model->getKeyName(); - } - - // Simple array of any sortable fields. - public function getSortableFields(): array - { - return [ - 'created_at', - 'updated_at', - ]; - } - - // Simple array of any filterable fields. - public function getFilterableFields(): array - { - return [ - '__soft_deleted', - ]; - } - - // Return an array representing what should be sent to the search service i.e. Algolia - public function toSearchableArray(Model $model, string $engine): array - { - return array_merge([], $this->mapSearchableAttributes($model)); - } -} -``` - -The `EloquentIndexer` class implements the `Lunar\Search\Interfaces\ModelIndexerInterface` so if your class doesn't -extend the Eloquent one, you must implement this interface. - -There are some methods which are available just on the `EloquentIndexer` but not defined on the interface are: - -#### mapSearchableAttributes - -```php -mapSearchableAttributes(Model $model): array -``` - -This method will take all `searchable` attributes for the model attribute type and map them into the index, -this means when you add searchable attributes in the hub they will automatically be added to the index. \ No newline at end of file diff --git a/docs/core/extending/shipping.md b/docs/core/extending/shipping.md deleted file mode 100644 index a9a5a74172..0000000000 --- a/docs/core/extending/shipping.md +++ /dev/null @@ -1,84 +0,0 @@ -# Shipping - -## Overview - -On your checkout, if your customer has added an item that needs shipping, you're likely going to want to display some shipping options. Currently the best way to do this is to implement your own by adding a `ShippingModifier` and adding using that to determine what shipping options you want to make available and add them to the `ShippingManifest` class. - -## Adding a Shipping Modifier - -Create your own custom shipping provider: - -```php -namespace App\Modifiers; - -use Lunar\Base\ShippingModifier; -use Lunar\DataTypes\Price; -use Lunar\DataTypes\ShippingOption; -use Lunar\Facades\ShippingManifest; -use Lunar\Models\Cart; -use Lunar\Models\Currency; -use Lunar\Models\TaxClass; - -class CustomShippingModifier extends ShippingModifier -{ - public function handle(Cart $cart, \Closure $next) - { - // Get the tax class - $taxClass = TaxClass::first(); - - ShippingManifest::addOption( - new ShippingOption( - name: 'Basic Delivery', - description: 'A basic delivery option', - identifier: 'BASDEL', - price: new Price(500, $cart->currency, 1), - taxClass: $taxClass - ) - ); - - ShippingManifest::addOption( - new ShippingOption( - name: 'Pick up in store', - description: 'Pick your order up in store', - identifier: 'PICKUP', - price: new Price(0, $cart->currency, 1), - taxClass: $taxClass, - // This is for your reference, so you can check if a collection option has been selected. - collect: true - ) - ); - - // Or add multiple options, it's your responsibility to ensure the identifiers are unique - ShippingManifest::addOptions(collect([ - new ShippingOption( - name: 'Basic Delivery', - description: 'A basic delivery option', - identifier: 'BASDEL', - price: new Price(500, $cart->currency, 1), - taxClass: $taxClass - ), - new ShippingOption( - name: 'Express Delivery', - description: 'Express delivery option', - identifier: 'EXDEL', - price: new Price(1000, $cart->currency, 1), - taxClass: $taxClass - ) - ])); - - return $next($cart); - } -} - -``` - -In your service provider: - -```php -public function boot(\Lunar\Base\ShippingModifiers $shippingModifiers) -{ - $shippingModifiers->add( - CustomShippingModifier::class - ); -} -``` diff --git a/docs/core/extending/taxation.md b/docs/core/extending/taxation.md deleted file mode 100644 index eaa1bfb4df..0000000000 --- a/docs/core/extending/taxation.md +++ /dev/null @@ -1,103 +0,0 @@ -# Taxation - -## Overview - -Taxation is a tricky business and sometimes what Lunar offers simply won't be enough, and we completely understand. This why Taxation is driver based, so you can add your own logic if you need to. - -By default we have a `SystemTaxManager` which will use Lunar's internal models and database as outlined above. If you need to write our own implementation, or if you're creating an add on for Tax, you can change the driver in the `config/taxes.php` config file. - -```php - 'system', -]; -``` - -## Writing Your Own Driver - -To write your own driver you need to add a class which implements the `Lunar\Base\TaxManager` interface and has the following methods: - -```php -make(TaxJar::class); - }) -} -``` - -You can then set this as the driver in the taxes config. diff --git a/docs/core/installation.md b/docs/core/installation.md deleted file mode 100644 index a212b3a44f..0000000000 --- a/docs/core/installation.md +++ /dev/null @@ -1,101 +0,0 @@ -# Installation - -## Requirements - -- PHP >= 8.2 -- Laravel 11, 12 -- MySQL 8.0+ / PostgreSQL 9.4+ -- exif PHP extension (on most systems it will be installed by default) -- intl PHP extension (on most systems it will be installed by default) -- bcmath PHP extension (on most systems it will be installed by default) -- GD PHP extension (used for image manipulation) - -## Install Lunar - -### Composer Require Package - -```sh -composer require lunarphp/lunar:"^1.0" -W -``` - -### Add the LunarUser Trait - -Some parts of the core rely on the User model having certain relationships set up. We have bundled these into a trait -and an interface, which you must add to any models that represent users in your database. - -```php -use Lunar\Base\Traits\LunarUser; -use Lunar\Base\LunarUser as LunarUserInterface; -// ... - -class User extends Authenticatable implements LunarUserInterface -{ - use LunarUser; - // ... -} -``` - -### Publish Configuration -Before you run the Lunar installer command, you may wish to customise some of the set-up. - - -```sh -php artisan vendor:publish --tag=lunar -``` - -## Register the admin panel - -The admin panel needs registering in your app service provider before you can use it. - -```php -use Lunar\Admin\Support\Facades\LunarPanel; - -class AppServiceProvider extends ServiceProvider -{ - public function register(): void - { - LunarPanel::register(); - } -``` - -## Run the Artisan Installer - -```sh -php artisan lunar:install -``` - -This will take you through a set of questions to configure your Lunar install. The process includes... - -- Creating a default admin user (if required) -- Seeding initial data -- Inviting you to star our repo on GitHub ⭐ - -You should now be able to access the panel at `https:///lunar`. - -## Advanced Installation Options - -### Telemetry insights - -Lunar will phone home to send anonymous usage insights, the data we capture does not identify your store in any way, it -exists to help us gain an understanding of how Lunar is used. You can easily opt out of this by adding the following to -your service provider's boot method: - -```php -\Lunar\Facades\Telemetry::optOut(); -``` - -### Table Prefix - -Lunar uses table prefixes to avoid conflicts with your app's tables. You can change this in the [configuration](/core/configuration.html). - -### User ID Field Type - -Lunar assumes your User ID field is a "BIGINT". If you are using an "INT" or "UUID", you will want to update the configuration in `config/lunar/database.php` to set the correct field type before running the migrations. - -### Publish Migrations - -You can optionally publish Lunar's migrations so they're added to your Laravel app. - -```sh -php artisan vendor:publish --tag=lunar.migrations -``` diff --git a/docs/core/local-development.md b/docs/core/local-development.md deleted file mode 100644 index 658b528f27..0000000000 --- a/docs/core/local-development.md +++ /dev/null @@ -1,60 +0,0 @@ -# Setting Up Lunar For Local Development - -## Overview - -This guide is here to help you set-up Lunar locally so you can contribute to the core and admin hub. - -## Before your start - -You will need a Laravel application to run Lunar in. - -## Set-Up - -In the root folder of your Laravel application, create a "packages" folder. - -```sh -mkdir packages && cd packages -```` - -Add the "packages" folder to your `.gitignore` file so the folder is not committed to your Git repository. - -``` -... -/.idea -/.vscode -/packages -``` - -Fork and then clone the [monorepo](https://github.com/lunarphp/lunar) to the `packages` folder, e.g. `/packages/lunar/`. - -```sh -git clone https://github.com/YOUR-USERNAME/lunar -```` - -Update your `composer.json` file similar to the following. - -```json - "repositories": [{ - "type": "path", - "url": "packages/*", - "symlink": true - }], - - "require": { - "lunarphp/lunarmono": "*", - } -```` - -Ensure minimum stability is set for development -```json - "minimum-stability": "dev", -```` - -Run `composer update` from your Laravel application's root directory and fingers crossed you're all up and running,. - -```sh -composer update -```` - -## Done -You can now follow the Lunar installation process and start contributing. diff --git a/docs/core/overview.md b/docs/core/overview.md deleted file mode 100644 index 84d2db678b..0000000000 --- a/docs/core/overview.md +++ /dev/null @@ -1,28 +0,0 @@ -# Welcome to Lunar! - -We are delighted you are considering Lunar for your project. We've spent a lot of time developing this project to bring -headless e-commerce functionality to Laravel. - -## What is Lunar? - -Lunar is a [Laravel e-commerce package](https://lunarphp.io/) which brings functionality akin to Shopify and other e-commerce platforms -to Laravel. You have complete freedom to create your own storefront(s), but we've already done the hard work for you in -the backend. - -## Tech Stack - -Lunar is comprised of two packages; `lunarphp/core` which provides the e-commerce functionality and `lunarphp/lunar` -which provides an admin panel built on Filament. - -:::info -Although the admin panel uses Laravel Livewire, there is no requirement for your app to use Livewire itself. -::: - -## What are the future plans? - -Lunar as a company has grand plans to continue developing new functionality to help you build awesome e-commerce -websites. We want Lunar to be a true alternative to the likes of Magento, Shopify, WooCommerce, etc. - -## Get started - -Enough waffle! Let's help you [install Lunar...](/core/installation) diff --git a/docs/core/reference/activity-log.md b/docs/core/reference/activity-log.md deleted file mode 100644 index 666e017d0c..0000000000 --- a/docs/core/reference/activity-log.md +++ /dev/null @@ -1,13 +0,0 @@ -# Activity Log - -## Overview - -We've made a design choice to have activity logging throughout Lunar when it comes to changes happening on Eloquent models. We believe it's important to keep track of what updates are happening and who is making them. It allows us to provide you with an invaluable insight into what's happening in your store. - -## How it works - -For the actual logging, we have opted to use the incredible package by Spatie, [laravel-activitylog](https://spatie.be/docs/laravel-activitylog). This allows Lunar to keep track changes throughout the system so you can have a full history of what's going on. - -## Enabling on your own models - -If you want to enable logging on your own models you can simply [follow the guides on their website](https://spatie.be/docs/laravel-activitylog) diff --git a/docs/core/reference/addresses.md b/docs/core/reference/addresses.md deleted file mode 100644 index c35908b696..0000000000 --- a/docs/core/reference/addresses.md +++ /dev/null @@ -1,84 +0,0 @@ -# Addresses - -## Overview - -Customers may save addresses to make checking-out easier and quicker. - -## Addresses - -```php -Lunar\Models\Address -``` - -|Field|Description| -|:-|:-| -|`id`|| -|`customer_id`|| -|`title`|nullable| -|`first_name`|| -|`last_name`|| -|`company_name`|nullable| -|`tax_identifier`|nullable| -|`line_one`|| -|`line_two`|nullable| -|`line_three`|nullable| -|`city`|| -|`state`|nullable| -|`postcode`|nullable| -|`country_id`|| -|`delivery_instructions`|| -|`contact_email`|| -|`contact_phone`|| -|`last_used_at`|Timestamp for when the address was last used in an order.| -|`meta`|JSON| -|`shipping_default`|Boolean| -|`billing_default`|Boolean| -|`created_at`|| -|`updated_at`|| - -## Countries - -```php -Lunar\Models\Country -``` - -|Field|Description| -|:-|:-| -|`id`|| -|`name`|| -|`iso3`|| -|`iso2`|| -|`phonecode`|| -|`capital`|| -|`currency`|| -|`native`|| -|`emoji`|Flag| -|`emoji_u`|Flag| -|`created_at`|| -|`updated_at`|| - - -## States - -```php -Lunar\Models\State -``` - -|Field|Description| -|:-|:-| -|`id`|| -|`country_id`|| -|`name`|| -|`code`|| -|`created_at`|| -|`updated_at`|| - -## Address Data - -Data for Countries and States is provided by https://github.com/dr5hn/countries-states-cities-database. - -You can use the following command to import countries and states. - -```sh -php artisan lunar:import:address-data -``` diff --git a/docs/core/reference/associations.md b/docs/core/reference/associations.md deleted file mode 100644 index a4debeee7a..0000000000 --- a/docs/core/reference/associations.md +++ /dev/null @@ -1,154 +0,0 @@ -# Associations - -## Overview - -Associations allow you to relate products to each other. There are a few different ways you can associate two products and this type of relationship would define how they are presented on your storefront and also how Lunar sees them. - -## Loading associations - -```php -$product->associations -``` - -This will return a Laravel collection of `Lunar\Models\ProductAssociation` models. On each model you will have access to the following: - -```php -// Lunar\Models\ProductAssociation -$association->parent; // The owning product who has the associations -$association->target // The associated (cross-sell, up-sell, alternate) product. -$association->type // Whether it's cross-sell, up-sell or alternate. -``` - -## Types of association - -### Cross Sell - -Cross selling is the process of encouraging customers to purchase products or services in addition to the original items they intended to purchase. Oftentimes the cross-sold items are complementary to one another so customers have more of a reason to purchase both of them. - -For example, if you're selling a Phone on your store, you may want to present some headphones or a case that works with the phone which the customer may be interested in. - -**Adding a cross-sell association** - -```php -$product->associate( - \Lunar\Models\Product $crossSellProduct, - \Lunar\Models\ProductAssociation::CROSS_SELL -); - -$product->associate( - [$productA, $productB], - \Lunar\Models\ProductAssociation::CROSS_SELL -); -``` - -**Fetching cross-sell products** - -```php -// Via a relationship scope -$product->associations()->crossSell()->get(); - -// Via the type scope -$product->associations()->type(ProductAssociation::CROSS_SELL); -``` - -### Up Sell - -Upselling is the process of encouraging customers to upgrade or include add-ons to the product or service they’re buying. The product or service being promoted is typically a more expensive product or add ons which can increase the overall order value. - -Using the phone example from above, lets consider we have two phones. - -- Phone 16gb 5" Screen -- Phone 32gb 6" Screen - -When editing the 16gb Phone we would add the 32gb version as an up-sell association and could present this when a user is viewing the 16gb version. - -```php -$product->associate( - \Lunar\Models\Product $upSellProduct, - \Lunar\Models\ProductAssociation::UP_SELL -); - -$product->associate( - [$productA, $productB], - \Lunar\Models\ProductAssociation::UP_SELL -); -``` - -**Fetching up-sell products** - -```php -// Via a relationship scope -$product->associations()->upSell()->get(); - -// Via the type scope -$product->associations()->type(ProductAssociation::UP_SELL); -``` - -### Alternate - -Alternate products are what you could present the user as an alternative to the current product. This is helpful in situations where the product might be out of stock or not quite fit for purpose and you could show these. - -```php -$product->associate( - \Lunar\Models\Product $alternateProduct, - \Lunar\Models\ProductAssociation::ALTERNATE -); - -$product->associate( - [$productA, $productB], - \Lunar\Models\ProductAssociation::ALTERNATE -); -``` - -**Fetching alternate products** - -```php -// Via a relationship scope -$product->associations()->alternate()->get(); - -// Via the type scope -$product->associations()->type(ProductAssociation::ALTERNATE); -``` - -### Custom types - -Although Lunar comes preloaded with the associations above, you are free to add your own custom association types. - -```php -$product->associate( - \Lunar\Models\Product $alternateProduct, - 'my-custom-type' -); -``` - -You can then fetch all associated products like so: - -```php -$product->associations()->type('my-custom-type')->get(); -``` - -## Removing associations - -You can dissociate products with one simple method. If you only pass through the related models, or an array of models, all associations will be removed. If you wish to only remove associations for a certain type you can pass the type through as the second parameter. - -```php -// Remove this associated product from however many different association types it might have. -$product->dissociate($associatedProduct); - -// Will also accept an array or collection of products. -$product->dissociate([/* ... */]) - -// Only remove this products association if it has been associated as a cross-sell. -$product->dissociate($associatedProduct, Product::CROSS_SELL); -``` - -## Database Schema - -|Field|Description| -|:-|:-| -|`id`|| -|`product_id`|| -|`product_association_id`|| -|`type`|(cross-sell, up-sell, alternate)| -|`created_at`|| -|`updated_at`|| diff --git a/docs/core/reference/attributes.md b/docs/core/reference/attributes.md deleted file mode 100644 index 883a0aa53f..0000000000 --- a/docs/core/reference/attributes.md +++ /dev/null @@ -1,213 +0,0 @@ -# Attributes - -## Overview - -Attributes can be associated to Eloquent models to allow custom data to be stored. Typically, these will be used the most with Products where different information is needed to be stored and presented to visitors. - -For example, a television might have the following attributes assigned... - -* Screen Size -* Screen Technology -* Tuner -* Resolution - -## Attributes - -```php -Lunar\Models\Attribute -``` - -|Field| Description | -|:-|:----------------------------------------------------------------------------------| -|`attribute_type`| Morph map of the model type that can use attribute, e.g. `product` | -|`attribute_group_id`| The associated group | -|`position`| An integer used to define the sorting order of attributes within attribute groups | -|`name`| Laravel Collection of translations `{'en': 'Screen Size'}` | -|`handle`| Kebab-cased reference, e.g. `screen-size` | -|`section`| An optional name to define where an attribute should be used. | -|`type`| The field type to be used, e.g. `Lunar\FieldTypes\Number` | -|`required`| Boolean | -|`default_value`| | -|`configuration`| Meta data stored as a Laravel Collection | -|`system`| If set to true, indicates it should not be deleted | - -### Field Types - - -|Type|Config| -|:-|:-| -|`Lunar\FieldTypes\Number`|Integer or Decimal| -|`Lunar\FieldTypes\Text`|Single-line, Multi-line, Rich Text| -|`Lunar\FieldTypes\TranslatedText`|Single-line, Multi-line, Rich Text| -|`Lunar\FieldTypes\ListField`|An re-orderable list of text values| - -::: tip INFO -More field types will be coming soon. -::: - -### Models that use Attributes - -* Lunar\Models\Product -* Lunar\Models\ProductVariant -* Lunar\Models\Collection - -### Saving Attribute Data - -```php -$product->attribute_data = collect([ - 'meta_title' => new \Lunar\FieldTypes\Text('The best screwdriver you will ever buy!'), - 'pack_qty' => new \Lunar\FieldTypes\Number(2), - 'description' => new \Lunar\FieldTypes\TranslatedText(collect([ - 'en' => new \Lunar\FieldTypes\Text('Blue'), - 'fr' => new \Lunar\FieldTypes\Text('Bleu'), - ])), -]); -``` - - -### Adding attributes to your own model - -```php -use Lunar\Base\Casts\AsAttributeData; -use Lunar\Base\Traits\HasAttributes; - -class Collection extends Model -{ - use HasAttributes; - - /** - * Define which attributes should be cast. - * - * @var array - */ - protected $casts = [ - 'attribute_data' => AsAttributeData::class, - ]; - - //... -} - -``` - -Then ensure you have a [JSON field](https://laravel.com/docs/8.x/migrations#column-method-json) on your model's table called `attribute_data`. - - -::: tip -When loading models it is advised you eager load the attribute data required. -::: - -### Accessing Attribute Data. - -There will come times where you need to be able to retrieve the attribute data you have stored against a model. When you target the `attribute_data` property it will be cast as a collection and resolved into it's corresponding field type. - -```php -dump($product->attribute_data); - -Illuminate\Support\Collection {#1522 ▼ - #items: array:2 [▼ - "name" => Lunar\FieldTypes\TranslatedText {#1533 ▼ - #value: Illuminate\Support\Collection {#1505 ▼ - #items: array:3 [▼ - "de" => Lunar\FieldTypes\Text {#1506 ▼ - #value: "Leren laarzen" - } - "en" => Lunar\FieldTypes\Text {#1514 ▼ - #value: "Leather boots" - } - "fr" => Lunar\FieldTypes\Text {#1502 ▼ - #value: "Bottes en cuires" - } - ] - } - } - "description" => Lunar\FieldTypes\Text {#1537 ▼ - #value: "

I'm a description!

" - } - ] -} -``` - -If you need to just get the value for one field, you can use the `translateAttribute` method on the model: - -```php -// Leather boots -$product->translateAttribute('name'); - -// Bootes en cuires -$product->translateAttribute('name', 'fr'); - -// Leather boots -$product->translateAttribute('name', 'FOO'); -// We will default here to either the current system locale or the first value available. -``` - -### Advanced usage - -```php -use Lunar\Base\Traits\HasAttributes; - -class ProductType extends Model -{ - use HasAttributes; - - //... -} - -``` - -```php - -use Lunar\Base\Casts\AsAttributeData; - -class Product extends Model -{ - /** - * Define which attributes should be cast. - * - * @var array - */ - protected $casts = [ - 'attribute_data' => AsAttributeData::class, - ]; - - //... -} - -``` - -```php - -use Lunar\Base\Casts\AsAttributeData; - -class ProductVariant extends Model -{ - /** - * Define which attributes should be cast. - * - * @var array - */ - protected $casts = [ - 'attribute_data' => AsAttributeData::class, - ]; - - //... -} - -``` - - -## Attribute Groups - -Attribute Groups form a collection of attributes that are logically grouped together for display purposes. - -A good example might be an "SEO" attribute group which has attributes for "Meta Title" and "Meta Description". - -```php -Lunar\Models\AttributeGroup -``` - -|Field|Description| -|:-|:-| -|`name`|Laravel Collection of translations `{'en': 'SEO'}`| -|`handle`|Kebab-cased reference, e.g. `seo`| -|`position`|An integer used to define the sorting order of groups| diff --git a/docs/core/reference/carts.md b/docs/core/reference/carts.md deleted file mode 100644 index 8db0f047db..0000000000 --- a/docs/core/reference/carts.md +++ /dev/null @@ -1,521 +0,0 @@ -# Carts - -## Overview - -Carts are a collection of products (or other custom purchasable types) that you -would like to order. Carts belong to Users (which relate to Customers). - -::: tip -Cart prices are dynamically calculated and are not stored (unlike Orders). -::: - -## Carts - -```php -Lunar\Models\Cart -``` - -| Field | Description | -|:------------|:--------------------------------------------------------------------------------| -| id | Unique ID for the cart. | -| user_id | Can be `null` for guest users. | -| customer_id | Can be `null`. | -| merged_id | If a cart was merged with another cart, it defines the cart it was merged into. | -| currency_id | Carts can only be for a single currency. | -| channel_id | | -| coupon_code | Can be `null`, stores a promotional coupon code, e.g. `SALE20`. | -| created_at | | -| updated_at | When an order was created from the basket, via a checkout. | -| meta | JSON data for saving any custom information. | - -### Creating a cart - -```php -$cart = \Lunar\Models\Cart::create([ - 'currency_id' => 1, - 'channel_id' => 2, -]); -``` - -## Cart Lines - -```php -Lunar\Models\CartLine -``` - -| Field | Description | -|:-----------------|:---------------------------------------------| -| id | | -| cart_id | | -| purchasable_type | e.g. `product_variant` | -| purchasable_id | | -| quantity | | -| created_at | | -| updated_at | | -| meta | JSON data for saving any custom information. | - -```php -$purchasable = \Lunar\Models\ProductVariant::create([/** ... */]); -$cartLine = new \Lunar\Models\CartLine([ - 'cart_id' => 1, - 'purchasable_type' => $purchasable->getMorphClass(), - 'purchasable_id' => $purchasable->id, - 'quantity' => 2, - 'meta' => [ - 'personalization' => 'Love you mum xxx', - ] -]); - -// Or you can use the relationship on the cart. -$cart->lines()->create([/* .. */]); -``` - -### Validation - -When adding items to a cart there are a series of validation actions which are run, which are defined in the `config/lunar/cart.php` config file. - -These actions will throw a `Lunar\Exceptions\Carts\CartException`. - -```php -try { - $cart->add($purchasable, 500); -} catch (\Lunar\Exceptions\Carts\CartException $e) { - $error = $e->getMessage(); -} -``` - -Now you have a basic Cart up and running, it's time to show you how you would -use the cart to get all the calculated totals and tax. - -We've also tried to make Carts extendable as much as possible so, depending on -what your stores requirements are, you are free to chop and change things as -much as you need to. - -## Hydrating the cart totals - -```php -$cart->calculate(); -``` - -This will create a "hydrated" version of your cart with the following: - -::: tip -All values will return a `Lunar\Datatypes\Price` object. So you have -access to the following: `value`, `formatted`, `decimal` -::: - -```php -$cart->total; // The total price value for the cart -$cart->subTotal; // The cart sub total, excluding tax -$cart->subTotalDiscounted; // The cart sub total, minus the discount amount. -$cart->shippingTotal; // The monetary value for the shipping total. (if applicable) -$cart->taxTotal; // The monetary value for the amount of tax applied. -$cart->taxBreakdown; // This is a collection of all taxes applied across all lines. -$cart->discountTotal; // The monetary value for the discount total. -$cart->discountBreakdown; // This is a collection of how discounts were calculated -$cart->shippingSubTotal; // The shipping total, excluding tax. -$cart->shippingTotal; // The shipping total including tax. -$cart->shippingBreakdown; // This is a collection of the shipping breakdown for the cart. - -foreach ($cart->taxBreakdown as $taxRate) { - $taxRate->name - $taxRate->total->value -} - -foreach ($cart->shippingBreakdown->items as $shippingBreakdown) { - $shippingBreakdown->name; - $shippingBreakdown->identifier; - $shippingBreakdown->price->formatted(); -} - - -foreach ($cart->discountBreakdown as $discountBreakdown) { - $discountBreakdown->discount_id - foreach ($discountBreakdown->lines as $discountLine) { - $discountLine->quantity - $discountLine->line - } - $discountBreakdown->total->value -} - -foreach ($cart->lines as $cartLine) { - $cartLine->unitPrice; // The monetary value for a single item. - $cartLine->unitPriceInclTax; // The monetary value for a single item, including tax amount. - $cartLine->total; // The total price value for the cart - $cartLine->subTotal; // The sub total, excluding tax - $cartLine->subTotalDiscounted; // The sub total, minus the discount amount. - $cartLine->taxAmount; // The monetary value for the amount of tax applied. - $cartLine->taxBreakdown; // This is a collection of all taxes applied across all lines. - $cartLine->discountTotal; // The monetary value for the discount total. -} - -``` - -## Modifying Carts - -If you need to programmatically change the Cart values, e.g. custom discounts or -prices, you will want to extend the Cart. - -You can find out more in the Extending Lunar section for -[Cart Extending](/core/extending/carts). - -## Calculating Tax - -During the cart's lifetime, it's unlikely you will have access to any address -information, which can be a pain when you want to accurately display the amount -of tax applied to the basket. Moreover, some countries don't even show tax until -they reach the checkout. We've tried to make this as easy and extendable as -possible for you as the developer to build your store. - -When you calculate the cart totals, you will be able to set the billing and/or -shipping address on the cart, which will then be used when we calculate which -tax breakdowns should be applied. - -```php -$shippingAddress = [ - 'country_id' => null, - 'title' => null, - 'first_name' => null, - 'last_name' => null, - 'company_name' => null, - 'tax_identifier' => null, - 'line_one' => null, - 'line_two' => null, - 'line_three' => null, - 'city' => null, - 'state' => null, - 'postcode' => 'H0H 0H0', - 'delivery_instructions' => null, - 'contact_email' => null, - 'contact_phone' => null, - 'meta' => null, -]; - -$billingAddress = /** .. */; - -$cart->setShippingAddress($shippingAddress); -$cart->setBillingAddress($billingAddress); -``` - -You can also pass through a `\Lunar\Models\Address` model, or even another -`\Lunar\Models\CartAddress` - -```php -$shippingAddress = \Lunar\Models\Address::first(); - -$cart->setShippingAddress($shippingAddress); - -$cart->setBillingAddress( - $cart->shippingAddress -); -``` - -## Cart Session Manager - -::: tip -The cart session manager is useful if you're building a traditional -Laravel storefront which makes use of sessions. -::: - -When building a store, you're going to want an easy way to fetch the cart for -the current user (or guest user) by retrieving it from their current session. -Lunar provides an easy to use class to make this easier for you, so you don't -have to keep reinventing the wheel. - -### Available config - -Configuration for your cart is handled in `lunar/cart.php` - -| Field | Description | Default | -|:--------------|:---------------------------------------------------------------------------------------|:-------------| -| `auth_policy` | When a user logs in, how should we handle merging of the basket? | `merge` | -| `eager_load` | Which relationships should be eager loaded by default when calculating the cart totals | - -There is additional, separate, config specifically for when using the `CartSession` located in `lunar/cart_session.php`. - -| Field | Description | Default | -|:---------------------------------|:-------------------------------------------------------------------|:-------------| -| `session_key` | What key to use when storing the cart id in the session | `lunar_cart` | -| `auto_create` | If no current basket exists, should we create one in the database? | `false` | -| `allow_multiple_orders_per_cart` | Whether carts can have multiple orders associated to them. | `false` | - -### Getting the cart session instance - -You can either use the facade or inject the `CartSession` into your code. - -```php -$cart = \Lunar\Facades\CartSession::current(); - -public function __construct( - protected \Lunar\Base\CartSessionInterface $cartSession -) { - // ... -} -``` - -### Fetching the current cart - -```php -$cart = \Lunar\Facades\CartSession::current(); -``` - -When you call current, you have two options, you either return `null` if they -don't have a cart, or you want to create one straight away. By default, we do -not create them initially as this could lead to a ton of cart models being -created for no good reason. If you want to enable this functionality, you can -adjust the config in `lunar/cart_session.php` - -### Forgetting the cart -Forgetting the cart will remove it from the user session and also soft-delete -the cart in the database. - -```php -CartSession::forget(); -``` - -If you don't want to delete the cart, you can pass the following parameter. - -```php -CartSession::forget(delete: false); -``` - - -### Using a specific cart - -You may want to manually specify which cart should be used for the session. - -```php -$cart = \Lunar\Models\Cart::first(); -CartSessionManager::use($cart); -``` - -The other available methods are as follows: - -### Add a cart line - -```php -CartSession::add($purchasable, $quantity); -``` - -### Add multiple lines - -```php -CartSession::addLines([ - [ - 'purchasable' => \Lunar\Models\ProductVariant::find(123), - 'quantity' => 25, - 'meta' => ['foo' => 'bar'], - ], - // ... -]); -``` - -_Accepts a `collection` or an `array`_ - -### Update a single line - -```php -CartSession::updateLine($cartLineId, $quantity, $meta); -``` - -### Update multiple lines - -```php -CartSession::updateLines(collect([ - [ - 'id' => 1, - 'quantity' => 25, - 'meta' => ['foo' => 'bar'], - ], - // ... -])); -``` - -### Remove a line - -```php -CartSession::remove($cartLineId); -``` - -### Clear a cart - -This will remove all lines from the cart. - -```php -CartSession::clear(); -``` - -### Associating a cart to a user - -You can easily associate a cart to a user. - -```php -CartSession::associate($user, 'merge'); -``` - -### Associating a cart to a customer - -You can easily associate a cart to a customer. - -```php -CartSession::setCustomer($customer); -``` - - -## Adding shipping/billing address - -As outlined above, you can add shipping / billing addresses to the cart using -the following methods: - -```php -$cart->setShippingAddress([ - 'first_name' => null, - 'last_name' => null, - 'line_one' => null, - 'line_two' => null, - 'line_three' => null, - 'city' => null, - 'state' => null, - 'postcode' => null, - 'country_id' => null, -]); -$cart->setBillingAddress([ - 'first_name' => null, - 'last_name' => null, - 'line_one' => null, - 'line_two' => null, - 'line_three' => null, - 'city' => null, - 'state' => null, - 'postcode' => null, - 'country_id' => null, -]); -``` - -You can easily retrieve these addresses by accessing the appropriate property: - -```php -$cart->shippingAddress; -$cart->billingAddress; -``` - -### ShippingOption override - -In some cases you might want to present an estimated shipping cost without users having to fill out a full shipping address, this is where the `ShippingOptionOverride` comes in, if set on the cart it can be used to calculate shipping for a single request. - -```php -$shippingOption = $cart->getEstimatedShipping([ - 'postcode' => '123456', - 'state' => 'Essex', - 'country' => Country::first(), -]); -```` - -This will return an estimated (cheapest) shipping option for the cart, based on it's current totals. By default this will not be taken into account when calculating shipping in the cart pipelines, in order to enable that we need to pass an extra parameter. - -```php -$shippingOption = $cart->getEstimatedShipping([ - 'postcode' => '123456', - 'state' => 'Essex', - 'country' => Country::first(), -], setOverride: true); -```` - -Now when the pipelines are run, the option which was returned by `getEstimatedShipping` will be used when calculating shipping totals, bypassing any other logic, note this will only happen for that one request. - -If you are using the `CartSession` manager, you can easily set the parameters you want to estimate shipping so you don't need to pass them each time: - -```php -CartSession::estimateShippingUsing([ - 'postcode' => '123456', - 'state' => 'Essex', - 'country' => Country::first(), -]); -``` - -You can also manually set the shipping method override directly on the cart. - -```php -$cart->shippingOptionOverride = new \Lunar\DataTypes\ShippingOption(/* .. */); -``` - -Calling `CartSession::current()` by itself won't trigger the shipping override, but you can pass the `estimateShipping` parameter to enable it: - -```php -// Will not use the shipping override, default behaviour. -CartSession::current(); - -// Will use the shipping override, based on what is set using `estimateShippingUsing` -CartSession::current(estimateShipping: true); -``` - -## Handling User Login - -When a user logs in, you will likely want to check if they have a cart -associated to their account and use that, or if they have started a cart as a -guest and logged in, you will likely want to be able to handle this. Lunar takes -the pain out of this by listening to the authentication events and responding -automatically by associating any previous guest cart they may have had and, -depending on your `auth_policy` merge or override the basket on their account. - -## Determining cart changes - -Carts by nature are dynamic, which means anything can change at any moment. This means it can be quite challenging to -determine whether a cart has changed from the one currently loaded, for example, if the user goes to check out and -changes their cart on another tab, how does the checkout know there has been a change? - -To help this, a cart will have a fingerprint generated which you can check to determine whether there has been -any changes and if so, refresh the cart. - -```php -$cart->fingerprint(); - -try { - $cart->checkFingerprint('4dfAW33awd'); -} catch (\Lunar\Exceptions\FingerprintMismatchException $e) { - //... Refresh the cart. -} -``` - -### Changing the underlying class. - -The class which generates the fingerprint is referenced in `config/lunar/cart.php`. - -```php -return [ - // ... - 'fingerprint_generator' => Lunar\Actions\Carts\GenerateFingerprint::class, -]; -``` - -In most cases you won't need to change this. - -## Pruning cart data - -Over time you will experience a build up of carts in your database that you may want to regularly remove. - -You can enable automatic removal of these carts using the `lunar.carts.prune_tables.enabled` config. By setting this to `true` any carts without an order associated will be removed after 90 days. - -You can change the number of days carts are retained for using the `lunar.carts.prune_tables. prune_interval` config. - -If you have specific needs around pruning you can also change the `lunar.carts.prune_tables.pipelines` array to determine what carts should be removed. - - - -```php -return [ - // ... - 'prune_tables' => [ - - 'enabled' => false, - - 'pipelines' => [ - Lunar\Pipelines\CartPrune\PruneAfter::class, - Lunar\Pipelines\CartPrune\WithoutOrders::class, - ], - - 'prune_interval' => 90, // days - - ], -]; -``` diff --git a/docs/core/reference/channels.md b/docs/core/reference/channels.md deleted file mode 100644 index 827b58d8cc..0000000000 --- a/docs/core/reference/channels.md +++ /dev/null @@ -1,55 +0,0 @@ -# Channels - -## Overview - -Default `webstore`. - -## Assigning channels to models. - -You can assign Eloquent models to different channels and specify whether they are enabled permanently or whether they should be scheduled to be enabled. - -In order to add this kind of functionality to your model, you need to add the `HasChannels` trait. - -```php -scheduleChannel($channel, now()->addDays(14), now()->addDays(24)); - -// Schedule the product to be enabled straight away -$product->scheduleChannel($channel); - -// The schedule method will accept and array or collection of channels. -$product->scheduleChannel(Channel::get()); -``` - -There is also a channel scope available to models which use this trait: - -```php -// Limit to a single channel -Product::channel($channel)->get(); - -// Limit to multiple channels -Product::channel([$channelA, $channelB])->get(); - -// Limit to a channel available the next day -Product::channel($channelA, now()->addDay())->get(); - -// Limit to a channel within a date range. -Product::channel($channelA, now()->addDay(), now()->addDays(2))->get(); -``` diff --git a/docs/core/reference/collections.md b/docs/core/reference/collections.md deleted file mode 100644 index 940898254a..0000000000 --- a/docs/core/reference/collections.md +++ /dev/null @@ -1,92 +0,0 @@ -# Collections - -## Overview - -Collections, although not strictly the same, are akin to Categories. They serve to allow you to add products, either explicitly or via certain criteria, for use on your store. - -For example, you may have a Collection called "Red T-Shirts" and within that collection specify that any product which has the tag "Red" and "T-Shirt" to be included. - -A collection can also have other collections underneath it, forming a nested set hierarchy. - -A collection must also belong to a collection group, this allows you to have greater flexibility when building out things like Menus and Landing pages. - - -## Collection Groups - -Create a collection group - -```php -$group = \Lunar\Models\CollectionGroup::create([ - 'name' => 'Main Catalogue', - 'handle' => 'main-catalogue' // Will auto generate if omitted. -]); -``` - - -## Collections - -Collections are a hierarchy of models that have products associated to them, you can think of them as "Categories". Once you have added products you can then determine how they are sorted on display. - -### Create a collection - -```php -$collection = \Lunar\Models\Collection::create([ - 'attribute_data' => [ - 'name' => new \Lunar\FieldTypes\Text('Clearance'), - ], - 'collection_group_id' => $group->id, -]); -``` - - -### Add a child collection - -```php -$child = new \Lunar\Models\Collection::create([/*..*/]); - -$collection->appendNode($child); -``` - -This results in the following - -```bash -- Clearance - - Child -``` - -Lunar uses the [Laravel Nested Set](https://github.com/lazychaser/laravel-nestedset) package, so feel free to take a look at it to see what's possible. - -### Adding products - -Products are related using a `BelongsToMany` relationship with a pivot column for `position`. - -```php -$products = [ - 1 => [ - 'position' => 1, - ], - 2 => [ - 'position' => 2, - ] -]; - -$collection->products()->sync($products); -``` - -::: tip -The key in the `$products` array is the product id -::: - -### Sorting products - -Lunar comes with a handful of criteria out the box for sorting products in a collection: - -|Name|Description| -|:-|:-| -|`min_price:asc`|Sorts using the base price ascending| -|`min_price:desc`|Sorts using the base price descending| -|`sku:asc`|Sorts using the sku ascending| -|`sku:desc`|Sorts using the sku descending| -|`custom`|This will allow you to specify the order of each product manually| - -Depending on what you have as the sort time on the collection, Lunar will automatically sort the products for you when you update the products. diff --git a/docs/core/reference/currencies.md b/docs/core/reference/currencies.md deleted file mode 100644 index 0100c3aaf5..0000000000 --- a/docs/core/reference/currencies.md +++ /dev/null @@ -1,47 +0,0 @@ -# Currencies - -## Overview - -Currencies allow you to charge different amounts relative to the currency you're targeting. - -## Creating a currency - -```php -\Lunar\Models\Currency::create([ - 'code' => 'GBP', - 'name' => 'British Pound', - 'exchange_rate' => 1.0000, - 'decimal_places' => 2, - 'enabled' => 1, - 'default' => 1, -]); -``` - -|Field|Description| -|:-|:-| -|`code`|This should be the `ISO 4217` currency code. | -|`name`|A given name for the currency.| -|`exchange_rate`|This should be the exchange rate relative to the default currency (see below)| -|`decimal_places`|Specify the decimal places, e.g. 2| -|`enabled`|Whether the currency is enabled| -|`default`|Whether the currency is the default| - -## Exchange rates -These are relative to the default currency. For example assuming we have the following: - -```php -\Lunar\Models\Currency::create([ - 'code' => 'GBP', - 'name' => 'British Pound', - 'exchange_rate' => 1.0000, - 'decimal_places' => 2, - 'enabled' => 1, - 'default' => 1, -]); -``` - -We set the model to be the default and we set the exchange rate to be `1` as we're defining this as our base (default) currency. - -Now, say we wanted to add EUR (Euros). Currently the exchange rate from GBP to EUR is `1.17`. But we want this to be relative to our default record. So 1 / 1.17 = 0.8547. - -It's entirely up to you what you want to set the exchange rates as, it is also worth mentioning that this is independent of product pricing in the sense that you can specify the price per currency. The exchange rate serves as a helper when working with prices so you have something to go by. diff --git a/docs/core/reference/customers.md b/docs/core/reference/customers.md deleted file mode 100644 index 97885f9e60..0000000000 --- a/docs/core/reference/customers.md +++ /dev/null @@ -1,271 +0,0 @@ -# Customers - -## Overview - -We use Customers in Lunar to store the customer details, rather than Users. We do this for a few reasons. One, so that we leave your User models well alone and two, because it provides flexibility. - -## Customers - -```php -Lunar\Models\Customer -``` - -|Field|Description| -|:-|:-| -|`id`|| -|`title`|Mr, Mrs, Miss, etc| -|`first_name`|| -|`last_name`|| -|`company_name`|nullable| -|`tax_identifier`|nullable| -|`account_ref`|nullable| -|`attribute_data`|JSON| -|`meta`|JSON| -|`created_at`|| -|`updated_at`|| - -### Creating a customer - -```php -Lunar\Models\Customer::create([ - 'title' => 'Mr.', - 'first_name' => 'Tony', - 'last_name' => 'Stark', - 'company_name' => 'Stark Enterprises', - 'tax_identifier' => null, - 'meta' => [ - 'account_no' => 'TNYSTRK1234' - ], -]) -``` - -### Relationships - -- Customer Groups `customer_customer_group` -- Users - `customer_user` - -## Users - -Customers will typically be associated with a user, so they can place orders. But it is also possible to have multiple users associated with a customer. This can be useful on B2B e-commerce where a customer may have multiple buyers. - -### Attaching users to a customer - -```php -$customer = \Lunar\Models\Customer::create([/* ... */]); - -$customer->users()->attach($user); - -$customer->users()->sync([1,2,3]); -``` - -## Attaching a customer to a customer group - -```php -$customer = \Lunar\Models\Customer::create([/* ... */]); - -$customer->customerGroups()->attach($customerGroup); - -$customer->customerGroups()->sync([4,5,6]); -``` - -## Impersonating users - -When a customer needs help with their account, it's useful to be able to log in as that user so you can help diagnose the issue they're having. -Lunar allows you to specify your own method of how you want to impersonate users, usually this is in the form of a signed URL an admin can go to in order to log in as the user. - -### Creating the impersonate class - -```php -addMinutes(5), [ - 'user' => $authenticatable->getAuthIdentifier(), - ]); - } -} -``` - -Then you need to register this in `config/lunar-hub/customers.php`. - -```php -return [ - 'impersonate' => App\Auth\Impersonate::class, - // ... -]; -``` - -Once added you will see an option to impersonate the user when viewing a customer. This will then go to the URL specified in your class where you will be able to handle the impersonation logic. - -## Customer Groups - -Default `retail` - -Customer groups allow you to group your customers into logical segments which enables you to define different criteria on models based on what customer belongs to that group. - -These criteria include things like: - -### Pricing - -Specify different pricing per customer group, for example you may have certain prices for customers that are in the `trade` customer group. - -### Product Availability - -You can turn product visibility off depending on the customer group, this would mean only certain products would show depending on the group they belong to. This will also include scheduling availability so you can release products earlier or later to different groups. - ---- -You must have at least one customer group in your store and when you install Lunar you will be given a default one to get you started named `retail`. - -## Creating a customer group - -```php -$customerGroup = Lunar\Models\CustomerGroup::create([ - 'name' => 'Retail', - 'handle' => 'retail', // Must be unique - 'default' => false, -]); -``` - -::: tip -You can only have one default at a time, if you create a customer group and pass default to true, then the existing default will be set to `false`. -::: - -## Scheduling availability - -If you would like to add customer group availability to your own models, you can use the `HasCustomerGroups` trait. - -```php - -// ... -use Lunar\Base\Traits\HasCustomerGroups; - -class MyModel extends Model -{ - use HasCustomerGroups; -} -``` - -You will need to define the relationship for customer groups so that Lunar knows how to handle it. - -```php -public function customerGroups() -{ - return $this->hasMany(\Lunar\Models\CustomerGroup::class)->withTimestamps()->withPivot([/* .. */]); -} -``` -You will then have access to the following methods: - -### Scheduling customer groups - -```php -// Will schedule for this product to be enabled in 14 days for this customer group. -$myModel->scheduleCustomerGroup( - $customerGroup, - $startDate, - $endData, - $pivotData -); - -// Schedule the product to be enabled straight away -$myModel->scheduleCustomerGroup($customerGroup); - -// The schedule method will accept an array or collection of customer groups. -$myModel->scheduleCustomerGroup(CustomerGroup::get()); -``` - -### Unscheduling customer groups - -If you do not want a model to be enabled for a customer group, you can unschedule it. This will keep any previous `start` and `end` dates but will toggle the `enabled` column. - -```php -$myModel->unscheduleCustomerGroup($customerGroup, $pivotData); -``` - -### Parameters - -|Field|Description|Type|Default| -|:-|:-|:-|:-| -|`customerGroup`|A collection of CustomerGroup models or id's.|mixed| -|`startDate`|The date the customer group will be active from.|`DateTime`| -|`endDate`|The date the customer group will be active until.|`DateTime`| -|`pivotData`|Any additional pivot data you may have on your link table. (not including scheduling defaults)|`array`| - - -**Pivot Data** - -By default the following values are used for `$pivotData` - -- `enabled` - Whether the customer group is enabled, defaults to `true` when scheduling and `false` when unscheduling. - -You can override any of these yourself as they are merged behind the scenes. - -### Retrieving the relationship - -The `HasCustomerGroup` trait adds a `customerGroup` scope to the model. This lets you query based on availability for a specific or multiple customer groups. - -The scope will accept either a single ID or instance of `CustomerGroup` and will accept an array. - -```php -$results = MyModel::customerGroup(1, $startDate, $endDate)->paginate(); - -$results = MyModel::customerGroup([ - $groupA, - $groupB, -])->paginate(50); -``` - -The start and end dates should be `DateTime` objects with will query for the existence of a customer group association with the start and end dates between those given. These are optional and the following happens in certain situations: - -**Pass neither `startDate` or `endDate`** - -Will query for customer groups which are enabled and the `startDate` is after `now()` - -**Pass only `startDate`** - -Will query for customer groups which are enabled, the start date is after the given date and the end date is either null or before `now()` - -**Pass both `startDate` and `endDate`** - -Will query for customer groups which are enabled, the start date is after the given date and the end date is before the given date. - -**Pass `endDate` without `startDate`** - -Will query for customer groups which are enabled, the start date is after the `now()` and the end date is before the given date. - -If you omit the second parameter the scope will take the current date and time. - -::: tip -A model will only be returned if the `enabled` column is positive, regardless of whether the start and end dates match. -::: - -### Limit by customer group - -Eloquent models which use the `HasCustomerGroups` trait have a useful scope available: - -```php -// Limit products available to a single customer group -Product::customerGroup($customerGroup)->get(); - -// Limit products available to multiple customer groups -Product::customerGroup([$groupA, $groupB])->get(); - -// Limit to products which are available the next day -Product::customerGroup($groupA, now()->addDay())->get(); - -// Limit to products which are available within a date range. -Product::customerGroup($groupA, now()->addDay(), now()->addDays(2))->get(); -``` diff --git a/docs/core/reference/discounts.md b/docs/core/reference/discounts.md deleted file mode 100644 index ab2756891f..0000000000 --- a/docs/core/reference/discounts.md +++ /dev/null @@ -1,143 +0,0 @@ -# Discounts - -## Overview - -// ... - -## Discounts - -```php -Lunar\Models\Discount -``` - -| Field | Description | Example | -|:-------------|:-------------------------------------------------------------|:--------------------------------------| -| `id` | | | -| `name` | The given name for the discount | | -| `handle` | The unique handle for the discount | | -| `type` | The type of discount | `Lunar\DiscountTypes\BuyXGetY` | -| `data` | JSON | Any data to be used by the type class | -| `starts_at` | The datetime the discount starts (required) | | -| `ends_at` | The datetime the discount expires, if `NULL` it won't expire | | -| `uses` | How many uses the discount has had | | -| `max_uses` | The maximum times this discount can be applied storewide | | -| `priority` | The order of priority | | -| `stop` | Whether this discount will stop others after propagating | | -| `created_at` | | | -| `updated_at` | | | - -### Creating a discount - -```php -Lunar\Models\Discount::create([ - 'name' => '20% Coupon', - 'handle' => '20_coupon', - 'type' => 'Lunar\DiscountTypes\Coupon', - 'data' => [ - 'coupon' => '20OFF', - 'min_prices' => [ - 'USD' => 2000 // $20 - ], - ], - 'starts_at' => '2022-06-17 13:30:55', - 'ends_at' => null, - 'max_uses' => null, -]) -``` - -### Fetching a discount - -The following scopes are available: - -```php -/** -* Query for discounts using the `start_at` and `end_at` dates. - */ -Discount::active()->get(); - -/** -* Query for discounts where the `uses` column is less than the `max_uses` column or `max_uses` is null. - */ -Discount::usable()->get(); - -/** -* Query for discounts where the associated products are of a certain type, based on given product ids. - */ -Discount::products($productIds, $type = 'condition'); -``` - -### Resetting the discount cache - -For performance reasons the applicable discounts are cached per request. If you need to reset this cache (for example after adding a discount code) you should call `resetDiscounts()`: - -```php -Discount::resetDiscounts(); -``` - -## Discountables - -You can relate a purchasable, collection or variant to a discount via this model. Each has a type for whether it's a `condition` or `reward`. - -- `condition` - If your discount requires these models to be in the cart to activate -- `reward` - Once the conditions are met, discount one of more of these models. - -```php -Lunar\Models\Discountable -``` - -| Field | Description | Example | -|:--------------------|:------------------------|:------------------------------| -| `id` | | | -| `discount_id` | | | -| `discountable_type` | | `product_variant` | -| `discountable_id` | | | -| `type` | `condition` or `reward` | | -| `created_at` | | | -| `updated_at` | | | - -### Relationships - -- Discountable `discountables` -- Users - `customer_user` - -### Adding your own Discount type - -```php -addMedia($request->file('image'))->toMediaCollection('images'); -``` - -For more information on what's available, see [Associating files](https://spatie.be/docs/laravel-medialibrary/v9/basic-usage/associating-files) - -## Fetching images - -```php -$product = \Lunar\Models\Product::find(123); - -$product->getMedia('images'); -``` -For more information on what's available, see [Retrieving media](https://spatie.be/docs/laravel-medialibrary/v9/basic-usage/retrieving-media) - -## Fallback images -If your model does not contain any images, calling getFirstMediaUrl or getFirstMediaPath will return null. You can -provide a fallback path/url in the config `lunar/media` or `.env`. -```php -'fallback' => [ - 'url' => env('FALLBACK_IMAGE_URL', null), - 'path' => env('FALLBACK_IMAGE_PATH', null) -] -``` - -## Media Collections & Conversions - -Lunar provides a way to customise the media collections and conversions for each model that implements the `HasMedia` -trait. - -You will find the default settings in the config `lunar/media`. Here you can switch out the class that handles the -registration of the media definitions. - -### Custom Media Definitions - -To create custom media definitions you will want to create your own implementation along the lines of the code below. - -When registering media definitions you can define not only the name but many other options. -See [Spatie Media Library](https://spatie.be/docs/laravel-medialibrary/v10/working-with-media-collections/defining-media-collections) for more information. - -```php -use Lunar\Base\MediaDefinitionsInterface; -use Spatie\Image\Enums\Fit; -use Spatie\MediaLibrary\HasMedia; -use Spatie\MediaLibrary\MediaCollections\MediaCollection; -use Spatie\MediaLibrary\MediaCollections\Models\Media; - -class CustomMediaDefinitions implements MediaDefinitionsInterface -{ - public function registerMediaConversions(HasMedia $model, Media $media = null): void - { - // Add a conversion for the admin panel to use - $model->addMediaConversion('small') - ->fit(Fit::Fill, 300, 300) - ->sharpen(10) - ->keepOriginalImageFormat(); - } - - public function registerMediaCollections(HasMedia $model): void - { - $fallbackUrl = config('lunar.media.fallback.url'); - $fallbackPath = config('lunar.media.fallback.path'); - - // Reset to avoid duplication - $model->mediaCollections = []; - - $collection = $model->addMediaCollection('images'); - - if ($fallbackUrl) { - $collection = $collection->useFallbackUrl($fallbackUrl); - } - - if ($fallbackPath) { - $collection = $collection->useFallbackPath($fallbackPath); - } - - $this->registerCollectionConversions($collection, $model); - } - - protected function registerCollectionConversions(MediaCollection $collection, HasMedia $model): void - { - $conversions = [ - 'zoom' => [ - 'width' => 500, - 'height' => 500, - ], - 'large' => [ - 'width' => 800, - 'height' => 800, - ], - 'medium' => [ - 'width' => 500, - 'height' => 500, - ], - ]; - - $collection->registerMediaConversions(function (Media $media) use ($model, $conversions) { - foreach ($conversions as $key => $conversion) { - $model->addMediaConversion($key) - ->fit( - Fit::Fill, - $conversion['width'], - $conversion['height'] - )->keepOriginalImageFormat(); - } - }); - } - - public function getMediaCollectionTitles(): array - { - return [ - 'images' => 'Images', - ]; - } - - public function getMediaCollectionDescriptions(): array - { - return [ - 'images' => '', - ]; - } -} -``` -Then register your new class against the model(s) you wish to use it. - -```php -return [ - - 'definitions' => [ - Lunar\Models\Product::class => CustomMediaDefinitions::class, - //.. - ], - - //.. -``` - -#### Generate Media Conversions - -To regenerate conversions, e.g. if you have changed the configuration, you can run the following command. - -```sh -php artisan media-library:regenerate -``` - -This will create queue jobs for each media entry to be re-processed. More information can be found on the -[medialibrary website](https://spatie.be/docs/laravel-medialibrary/v9/converting-images/regenerating-images) - - -## Extending - -You can extend your own models to use media, either by using our implementation or by implementing medialibrary -directly. It's totally up to you and your requirements. If you want to use medialibrary directly, -[just follow their guides](https://spatie.be/docs/laravel-medialibrary/v9/basic-usage/preparing-your-model) and you'll be all set. - -::: warning -If you decide to use medialibrary directly, you will not have access to our transformations or any other Lunar features -we add. -::: - -### Extending with Lunar - -To enable image transformations on your models within Lunar, simply add the `HasMedia` trait. - -```php -createOrder( - allowMultipleOrders: false, - orderIdToUpdate: null, -); -``` - -- `allowMultipleOrders` - Generally carts will only have one draft order associated, however if you want to allow carts to - have multiple, you can pass `true` here. -- `orderIdToUpdate` - You can optionally pass the ID of an order to update instead of attempting to create a new order, this must be a draft order i.e. a null `placed_at` and related to the cart. - -The underlying class for creating an order is `Lunar\Actions\Carts\CreateOrder`, you are free to override this in the -config file `config/cart.php` - -```php -return [ - // ... - 'actions' => [ - // ... - 'order_create' => CustomCreateOrder::class - ] -] -``` - -At minimum your class should look like the following: - -```php -final class CreateOrder extends Lunar\Actions\AbstractAction -{ - /** - * Execute the action. - */ - public function execute( - Cart $cart, - bool $allowMultipleOrders = false, - int $orderIdToUpdate = null - ): self { - return $this; - } -} -``` - -### Validating a cart before creation. - -If you also want to check before you attempt this if the cart is ready to create an order, you can call the helper -method: - -```php -$cart->canCreateOrder(); -``` - -Under the hood this will use the `ValidateCartForOrderCreation` class which lunar provides and throw any validation -exceptions with helpful messages if the cart isn't ready to create an order. - -You can specify your own class to handle this in `config/cart.php`. - -```php -return [ - // ... - 'validators' => [ - 'order_create' => MyCustomValidator::class, - ] -] -``` - -Which may look something like: - -```php -parameters['cart']; - - if ($somethingWentWrong) { - return $this->fail('There was an issue'); - } - - return $this->pass(); - } -} -``` - -## Order Reference Generation - -By default Lunar will generate a new order reference for you when you create an order from a cart. The format for this -is: - -``` -{prefix?}{0..0}{orderId} -``` - -`{0..0}` indicates the order id will be padded until the length is 8 digits (not including the prefix) -The prefix is optional and defined in the `lunar/orders.php` config file, which gives access to a number of settings you can change. - -### Custom Generators - -If your store has a specific requirement for how references are generated, you can easily swap out the Lunar one for -your own: - -`config/lunar/orders.php` - -```php -return [ - 'reference_generator' => App\Generators\MyCustomGenerator::class, -]; -``` - -Or, if you don't want references at all (not recommended) you can simply set it to `null` - -Here's the underlying class for the custom generator: - -```php -namespace App\Generators; - -use Lunar\Models\Order; - -class MyCustomGenerator implements OrderReferenceGeneratorInterface -{ - /** - * {@inheritDoc} - */ - public function generate(Order $order): string - { - // ... - return 'my-custom-reference'; - } -} -``` - -## Modifying Orders - -If you need to programmatically change the Order values or add in new behaviour, you will want to extend the Order -system. - -You can find out more in the Extending Lunar section for [Order Modifiers](/core/extending/orders). - -## Order Lines - -```php -Lunar\Models\OrderLine -``` - -| Field | Description | -|:-----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------| -| id | | -| order_id | | -| purchasable_type | Morph reference for the purchasable item e.g. `product_variant` | -| purchasable_id | -| type | Whether `digital`,`physical` etc -| description | A description of the line item -| option | If this was a variant, the option info is here -| identifier | Something to identify the purchasable item, usually an `sku` -| unit_price | The unit price of the line -| unit_quantity | The line unit quantity, usually this is 1 -| quantity | The amount of this item purchased -| sub_total | The sub total minus any discounts, excl. tax -| discount_total | Any discount amount excl. tax -| tax_breakdown | A json field for the tax breakdown e.g. `[{"description": "VAT", "identifier" : "vat", "value": 123, "percentage": 20, "currency_code": "GBP"}]` -| tax_total | The total amount of tax applied -| total | The grand total with tax -| notes | Any additional order notes -| meta | Any additional meta info you wish to store -| created_at | | -| updated_at | | - - -### Create an order line - -::: tip -If you are using the `createOrder` method on a cart, this is all handled for you automatically. -::: - -```php -\Lunar\Models\OrderLine::create([ - // ... -]); -``` - -Or via the relationship - -```php -$order->lines()->create([ - // ... -]); -``` - -## Order Addresses - -An order can have many addresses, typically you would just have one for billing and one for shipping. - -::: tip -If you are using the `createOrder` method on a cart, this is all handled for you automatically. -::: - -```php -\Lunar\Models\OrderAddress::create([ - 'order_id' => 1, - 'country_id' => 1, - 'title' => null, - 'first_name' => 'Jacob', - 'last_name' => null, - 'company_name' => null, - 'tax_identifier' => null, - 'line_one' => '123 Foo Street', - 'line_two' => null, - 'line_three' => null, - 'city' => 'London', - 'state' => null, - 'postcode' => 'NW1 1WN', - 'delivery_instructions' => null, - 'contact_email' => null, - 'contact_phone' => null, - 'type' => 'shipping', // billing/shipping - 'shipping_option' => null, // A unique code for you to identify shipping -]); - -// Or via the relationship. -$order->addresses()->create([ - // ... -]); -``` - -You can then use some relationship helpers to fetch the address you need: - -```php -$order->shippingAddress; -$order->billingAddress; -``` - -## Shipping Options - -::: tip -A Shipping Tables addon is planned to make setting up shipping in the admin hub easy for most scenarios. -::: - -To add Shipping Options you will need to [extend Lunar](/core/extending/shipping) to add in your own logic. - -Then in your checkout, or where ever you want, you can fetch these options: - -```php -\Lunar\Facades\ShippingManifest::getOptions(\Lunar\Models\Cart $cart); -``` - -This will return a collection of `Lunar\DataTypes\ShippingOption` objects. - -### Adding the shipping option to the cart - -Once the user has selected the shipping option they want, you will need to add this to the cart so it can calculate the -new totals. - -```php -$cart->setShippingOption(\Lunar\DataTypes\ShippingOption $option); -``` - -## Transactions - -```php -Lunar\Models\Transaction -``` - -| Field | Description | -|:-----------|:----------------------------------------------------------------------------------------------| -| id | | -| success | Whether the transaction was successful | -| refund | `true` if this was a refund | -| driver | The payment driver used e.g. `stripe` | -| amount | An integer amount | -| reference | The reference returned from the payment Provider. Used to identify the transaction with them. -| status | A string representation of the status, unlinked to Lunar e.g. `settled` | -| notes | Any relevant notes for the transaction -| card_type | e.g. `visa` -| last_four | Last 4 digits of the card -| meta | Any additional meta info you wish to store -| created_at | | -| updated_at | | - -### Create a transaction - -::: tip -Just because an order has a transaction does not mean it has been placed. Lunar determines whether an order is -considered placed when the `placed_at` column has a datetime, regardless if any transactions exist or not. -::: - -Most stores will likely want to store a transaction against the order, this helps determining how much has been paid, -how it was paid and give a clue on the best way to issue a refund if needed. - -```php -\Lunar\Models\Transaction::create([ - //... -]); - -// Or via the order -$order->transactions()->create([ - //.. -]); -``` - -These can then be returned via the relationship. - -```php -$order->transactions; // Get all transactions. - -$order->charges; // Get all transactions that are charges. - -$order->refunds; // Get all transactions that are refunds. -``` - -## Payments - -We will be looking to add support for the most popular payment providers, so keep an eye out here as we will list them -all out. - -In the meantime, you can absolutely still get a storefront working, at the end of the day Lunar doesn't really mind what payment provider you use or plan to use. - -In terms of an order, all it's worried about is whether or not the `placed_at` column is populated on the orders table, -the rest is completely up to you how you want to handle that. We have some helper utilities to make such things easier -for you as laid out above. - -And as always, if you have any questions you can reach out on our Discord! - -## Order Status - -The `placed_at` field determines whether an Order is considered draft or placed. The Order model has two helper methods -to determine the status of an Order. - -```php -$order->isDraft(); -$order->isPlaced(); -``` - -## Order Notifications - -Lunar allows you to specify what Laravel mailers/notifications should be available for sending when you update an -order's status. These are configured in the `lunar/orders` config file and are defined like so: - -```php -'statuses' => [ - 'awaiting-payment' => [ - 'label' => 'Awaiting Payment', - 'color' => '#848a8c', - 'mailers' => [ - App\Mail\MyMailer::class, - App\Mail\MyOtherMailer::class, - ], - 'notifications' => [], - ], - // ... -], -``` - -Now when you update an order's status in the hub, you will have these mailers available if the new status -is `awaiting-payment`. You can then choose the email addresses which the email should be sent to and also add an -additional email address if required. - -Once updated, Lunar will keep a render of the email sent out in the activity log so you have a clear history of what's -been sent out. - -:::tip -These email notifications do not get sent out automatically if you update the status outside of the hub. -::: - -### Mailer template - -When building out the template for your mailer, you should assume you have access to the `$order` model. When the status -is updated this is passed through to the view data for the mailer, along with any additional content entered. -Since you may not always have additional content when sending out the mailer, you should check the existence first. - -Here's an example of what the template could look like: - -```blade -

It's on the way!

- -

Your order with reference {{ $order->reference }} has been dispatched!

- -

{{ $order->total->formatted() }}

- -@if($content ?? null) -

Additional notes

-

{{ $content }}

-@endif - -@foreach($order->lines as $line) - -@endforeach -``` - -## Order Invoice PDF - -By default when you click "Download PDF" in the hub when viewing an order, you will get a basic PDF generated for you to -download. You can publish the view that powers this to create your own PDF template. - -```bash -php artisan vendor:publish --tag=lunar.hub.views -``` - -This will create a view called `resources/vendor/lunarpanel/pdf/order.blade.php`, where you will be able to freely -customise the PDF you want displayed on download. diff --git a/docs/core/reference/payments.md b/docs/core/reference/payments.md deleted file mode 100644 index 5141d98fcf..0000000000 --- a/docs/core/reference/payments.md +++ /dev/null @@ -1,356 +0,0 @@ -# Payments - -:::tip -If you're looking for a guide on how to create your own Payment Driver, or for a more in-depth look at how they work, -head over to the [extending section](/core/extending/payments). -::: - -## Overview - -Lunar takes a driver based approach with Payments, meaning you are free to use either add ons to support the provider -you wish to use, or you can create your own to meet your exact needs. - -## Configuration - -All configuration for payments is located in `config/lunar/payments.php`. Here you can specify different types of -payments and the driver each one should use. - -```php - env('PAYMENTS_TYPE', 'offline'), - - 'types' => [ - 'cash-in-hand' => [ - 'driver' => 'offline', - 'released' => 'payment-offline', - ], - 'card' => [ - 'driver' => 'stripe', - 'released' => 'payment-received', - ], - ], -]; -``` - -## Usage - -To use a payment driver, you need to pass the type of payment you wish to use, this will then return an instance of the -driver. - -```php -$driver = \Lunar\Facades\Payments::driver('card'); -``` - -We can then set the cart. - -```php -$driver->cart(\Lunar\Models\Cart $cart); -``` - -Set any additional data that the driver may need. - -```php -$driver->withData([ - 'payment_token' => $token, -]); -``` - -Finally, we can authorize the payment. - -```php -$driver->authorize(); -``` - - diff --git a/docs/core/reference/pricing.md b/docs/core/reference/pricing.md deleted file mode 100644 index db07dfb176..0000000000 --- a/docs/core/reference/pricing.md +++ /dev/null @@ -1,223 +0,0 @@ -# Pricing - -## Overview - -When you display prices on your storefront, you want to be sure the customer is seeing the correct format relative to -the currency they are purchasing in. - -Every storefront is different. We understand as a developer you might want to do this your own way or have very specific -requirements, so we have made price formatting easy to swap out with your own implementation, but also we provide a -suitable default that will suit most use cases. - -## Price formatting - -The class which handles price formatting is referenced in the `config/pricing.php` file: - -```php -return [ - // ... - 'formatter' => \Lunar\Pricing\DefaultPriceFormatter::class, -]; -``` - -When you retrieve a `Lunar\Models\Price` model, you will have access to the `->price` attribute which will return -a `Lunar\DataTypes\Price` object. This is what we will use to get our formatted values. - -The `Lunar\DataTypes\Price` class is not limited to database columns and can be found throughout the Lunar core when -dealing with prices, other examples include: - -### `Lunar\Models\Order` - -- `subTotal` -- `total` -- `taxTotal` -- `discount_total` -- `shipping_total` - -### `Lunar\Models\OrderLine` - -- `unit_price` -- `sub_total` -- `tax_total` -- `discount_total` -- `total` - -### `Lunar\Models\Transaction` - -- `amount` - -### `DefaultPriceFormatter` - -The default price formatter ships with Lunar and will handle most use cases for formatting a price, lets go through -them, first we'll create a standard price model. - -```php -$priceModel = \Lunar\Models\Price::create([ - // ... - 'price' => 1000, // Price is an int and should be in the lowest common denominator - 'min_quantity' => 1, -]); - -// Lunar\DataTypes\Price -$priceDataType = $priceModel->price; -``` - -Return the raw value, as it's stored in the database. - -```php -echo $priceDataType->value; // 1000 -``` - -Return the decimal representation for the price.The decimal value takes into account how many decimal places you have -set for the currency. So in this example if the -decimal places was 3 you would get 10.000 - -```php -echo $priceDataType->decimal(rounding: true); // 10.00 -echo $priceDataType->unitDecimal(rounding: true); // 10.00 -``` - -You may have noticed these two values are the same, so what's happening? Well the unit decimal will take into account -the unit quantity of the purchasable we have the price for. Let's show another example: - -```php -$productVariant = \Lunar\Models\ProductVariant::create([ - // ... - 'unit_quantity' => 10, -]); -``` - -By setting `unit_quantity` to 10 we're telling Lunar that 10 individual units make up this product at this price point, -this is useful if you're selling something that by itself would be under 1 cent i.e. 0.001EUR, which isn't a valid -price. - -```php -$priceModel = $productVariant->prices()->create([ - 'price' => 10, // 0.10 EUR -]); - -// Lunar\DataTypes\Price -$priceDataType = $priceModel->price; -``` - -Now lets try again: - -```php -echo $priceDataType->decimal(rounding: true); // 0.10 -echo $priceDataType->unitDecimal(rounding: true); // 0.01 -``` - -You can see the `unitDecimal` method has taken into account that `10` units make up the price so this gives a unit cost -of `0.01`. - -##### Formatting to a currency string - -The formatted price uses the native PHP [NumberFormatter](https://www.php.net/manual/en/class.numberformatter.php). If -you wish to specify a locale or formatting style you can, see the examples below. - -```php -$priceDataType->price->formatted('fr') // 10,00 £GB -$priceDataType->price->formatted('en-gb', \NumberFormatter::SPELLOUT) // ten point zero zero. -$priceDataType->price->formattedUnit('en-gb') // £10.00 -``` - -## Full reference for `DefaultPriceFormatter` - -```php -$priceDataType->decimal( - rounding: false -); - -$priceDataType->decimalUnit( - rounding: false -); - -$priceDataType->formatted( - locale: null, - formatterStyle: NumberFormatter::CURRENCY, - decimalPlaces: null, - trimTrailingZeros: true -); - -$priceDataType->unitFormatted( - locale: null, - formatterStyle: NumberFormatter::CURRENCY, - decimalPlaces: null, - trimTrailingZeros: true -); -``` - -## Creating a custom formatter - -Your formatter should implement the `PriceFormatterInterface` and have a constructor was accepts and sets -the `$value`, `$currency` and `$unitQty` properties. - -```php -currency) { - $this->currency = Currency::getDefault(); - } - } - - public function decimal(): float - { - // ... - } - - public function unitDecimal(): float - { - // ... - } - - public function formatted(): mixed - { - // ... - } - - public function unitFormatted(): mixed - { - // ... - } -} -``` - -The methods you implement can accept any number of arguments you want to support, you are not bound to what -the `DefaultPriceFormatter` accepts. - -Once you have implemented the required methods, simply swap it out in `config/lunar/pricing.php`: - -```php -return [ - // ... - 'formatter' => \App\Pricing\CustomPriceFormatter::class, -]; -``` - -## Model Casting - -If you have your own models which you want to use price formatting for, Lunar has a cast class you can use. The only -requirement is the column returns an `integer`. - -```php -class MyModel extends Model -{ - protected $casts = [ - //... - 'price' => \Lunar\Base\Casts\Price::class - ]; -} -``` \ No newline at end of file diff --git a/docs/core/reference/products.md b/docs/core/reference/products.md deleted file mode 100644 index 4fc39c7e58..0000000000 --- a/docs/core/reference/products.md +++ /dev/null @@ -1,832 +0,0 @@ -# Products - -## Overview - -Products are generally what you will be selling in your store. You define all your attributes against the product and -products can also have variations. In Lunar a product will always have at least one variation. From a UX point of view, -it will just look like you're editing a single product, but behind the scenes you'll be editing one variant. - -Products also belong to a `ProductType` and aside from the attributes, which you are free to define yourself, will have -a base SKU and a brand name. - -## Creating a product - -```php -Lunar\Models\Product::create([ - 'product_type_id' => $productTypeId, - 'status' => 'published', - 'brand_id' => $brandId, - 'attribute_data' => [ - 'name' => new TranslatedText(collect([ - 'en' => new Text('FooBar'), - ])), - 'description' => new Text('This is a Foobar product.'), - ], -]); -``` - -## Customer Groups - -You can assign customer groups to a product, this allows you to either always have that product enabled for the customer -group, or you can state which dates they should be active for (as long as the customer group is enabled). - -### Attaching customer groups - -```php - -// Will schedule for this product to be enabled in 14 days for this customer group. -$product->scheduleCustomerGroup($customerGroup, now()->addDays(14)); - -// Schedule the product to be enabled straight away -$product->scheduleCustomerGroup($customerGroup); - -// The schedule method will accept an array or collection of customer groups. -$product->scheduleCustomerGroup(CustomerGroup::get()); -``` - -### Retrieving products for a customer group - -If you want to get all products related to a customer group, you can use the `customerGroup` scope. This scope can -either take a single customer group (or customer group id) or a collection/array of ids/customer group models. - -```php -// Can be an array of ids -$products = \Lunar\Models\Product::customerGroup(1)->paginate(50); - -$products = \Lunar\Models\Product::customerGroup([ - $groupA, - $groupB, -])->paginate(50); -``` - -## Product Types - -e.g. Television, T-Shirt, Book, Phone, etc. - -Assigns the appropriate attributes for the product type. - -### Creating a product type - -```php -Lunar\Models\ProductType::create([ - 'name' => 'Boots', -]); -``` - -Product Types also have [Attributes](/core/reference/attributes) associated to them. These associated attributes will -determine what fields are available to products when editing. For example if you had an attribute of `Screen Type` -associated to a `TVs` product type, any products with that product type would have access to that attribute when -editing. - -You can associate attributes to a product type like so (it's just a straight -forward [Polymorphic relationship](https://laravel.com/docs/8.x/eloquent-relationships#many-to-many-polymorphic-relations)). - -```php -$productType->mappedAttributes()->attach([ /* attribute ids ... */ ]); -``` - -You can associate both `Product` and `ProductVariant` attributes to a product type which will then display on either the -product or product variant when editing. - -::: warning -If you decide to delete an attribute, this will cause the association to be dropped and you could lose data. -::: - -### Retrieving the product type relationship - -If you have a product, you can fetch its product type like so: - -```php -$product->productType; - -$product->load(['productType']); -``` - -## Product Identifiers - -You can choose to add product identifiers to each product variant. These are fields which, as the name suggests, allow -you to identify a product and its variants for use in your internal systems. You can choose whether these are required -and unique in the hub whilst editing. - -### Available fields - -**SKU** - -SKU stands for “stock keeping unit” and — as the name suggests — it is a number (usually eight alphanumeric digits) that -you can assign to products to keep track of stock levels internally. If a product has different colors and sizes, each -variation may use a unique SKU number. - -**GTIN** - -A Global Trade Item Number (GTIN) is a unique and internationally recognized identifier for a product. These usually -accompany a barcode and are useful when using services such as Google to help them classify the product. - -**MPN** - -MPN (Manufacturer Part Number) is the product identifier used to differentiate a product among other (similar) products -from the same brand/manufacturer, making it easier for customers to find and buy your products and protecting them from -counterfeit. - -**EAN** - -European Article numbering code (EAN) is a series of letters and numbers in a unique order that helps identify specific -products within your own inventory. - -### Validation - -Depending on your storefront needs, you might not need any of these fields to be required or unique. For this reason you -can change this behaviour at a validation level. - -`config/lunar-hub/products.php` - -```php - 'sku' => [ - 'required' => true, - 'unique' => false, - ], - 'gtin' => [ - 'required' => false, - 'unique' => false, - ], - 'mpn' => [ - 'required' => false, - 'unique' => false, - ], - 'ean' => [ - 'required' => false, - 'unique' => false, - ], -``` - -## Product Options - -These are what you use to define the different options a product has available to it. These are directly related to the -different variants a product might have. Each `ProductOption` will have a set of `ProductOptionValue` models related to -it. For example: - -You could have a `ProductOption` called "Colour" and then multiple `ProductionOptionValue` models for each colour you -would like to offer. - -::: tip -Product options and Product option values are defined at a system level and are translatable. -::: - -### Creating a `ProductOption` - -```php -$option = Lunar\Models\ProductOption::create([ - 'name' => [ - 'en' => 'Colour', - 'fr' => 'Couleur', - ], - 'label' => [ - 'en' => 'Colour', - 'fr' => 'Couleur', - ], -]); -``` - -We can then create values for this option: - -```php -// Lunar\Models\ProductOptionValue -$option->values()->createMany([ - [ - 'name' => [ - 'en' => 'Blue', - 'fr' => 'Bleu', - ], - ], - [ - 'name' => [ - 'en' => 'Red', - 'fr' => 'Rouge', - ], - ], -]); -``` - -This Product option and it's values are now ready to be used to with Product Variants. - -# Product Shipping - -By default Lunar will mark all product variants as `shippable`. If you don't need to ship a certain variant then you can -simply set this to false. - -```php -$variant->update([ - 'shippable' => false, -]); -``` - -## Product dimensions - -Product's can have different dimensions assigned to them, the values we have available are - -- Length -- Width -- Height -- Weight -- Volume - -For handling conversions, we use the [Cartalyst Converter](https://github.com/cartalyst/converter/tree/master) package. -This supports a wide range of UOM and can convert those values to different measurements. - -Each UOM has a corresponding `_value` and `_unit` column in the database. For example: - -```php -length_value -length_unit -width_value -width_unit -``` - -### Configuring measurements - -You can configure the available UOM's in the `lunar/shipping.php` config file. Here is an example of what Lunar provides -by default: - -**Length** - -- m -- mm -- cm -- ft -- in - -**Weight** - -- kg -- g -- lbs - -**Volume** - -- l -- ml -- gal -- floz - -### Getting and converting measurement values - -You are free to access to the `*_value` and `*_unit` values for the variant and use them in their raw form, however we -do offer an accessor for each unit that you can use: - -```php -$variant->length->to('length.ft')->convert(); -``` - -#### Volume calculation - -Volume is calculated different to the rest of the measurements. You can either have it automatically figure out the -volume or manually set it yourself: - -```php -$variant->update([ - 'length_value' => 50, - 'length_unit' => 'mm', - 'height_value' => 50, - 'height_unit' => 'mm', - 'width_value' => 50, - 'width_unit' => 'mm', -]) - -// By default will return ml -$variant->volume->getValue(); // 125 - -// Use any UOM you want for volume -$variant->volume->to('volume.l')->convert()->getValue(); // 12.5 - -// Set the volume manually and it'll always use that. -$variant->update([ - 'volume_unit' => 'floz', - 'volume_value' => 100 -]); - -$variant->volume->getValue(); // 100 - -$variant->volume->to('volume.l')->convert()->getValue(); // 2.95735... -``` - -**Get a formatted value** - -If you want to display a formatted value, you can! - -```php -$variant->length->format(); // 50cm -``` - -## Variants - -Variants allow you to specify different variations of a product. Think things like Small Blue T-shirt, or Size 8 and -Size 9 Leather Boots. Your product is the main parent and your variants are based off that product to create multiple -permutations. - -Variants are also responsible for storing data such as Pricing, Inventory/Stock information, Shipping information etc. -For that reason a product will always have at least one variant. - -When you decide you want to offer more than one variant for a product, upon generation, Lunar will take the first -variant and use that as a base for all other variants in terms of pricing, inventory etc. So you won't lose any data you -may have already saved against the existing product. - -## Creating variants - -### New Product - -If you have a new product, you would create your variants in bulk based on an array of `ProductOptionValue` IDs. - -You will need to determine what variants you need to create and assign the correct option values to that variant. - -For example, lets say we have an option "Colour" and we want to create "Blue" and "Red" variants. - -::: tip -A product variant will require a product, currency and a tax class. -If you do not have these entities created, you will need to do so before continuing. -::: - -If needed, Create the product and tax class. - -```php -$product = Product::create([...]); -$taxClass = TaxClass::create([...]); -$currency = Currency::create([...]); -``` - -Otherwise, fetch them. - -```php -$product = Product::where(...)->first(); -$taxClass = TaxClass::where(...)->first(); -$currency = Currency::where(...)->first(); -``` - -You can also specify a status to limit results to using the provided scope. - -```php -Product::status('published')->get(); -``` - -Then we need to create our base option and it's values. - -```php -$option = \Lunar\Models\ProductOption::create([ - 'name' => [ - 'en' => 'Colour', - ]; - 'label' => [ - 'en' => 'Colour', - ]; -]); - -$blueOption = $option->values()->create([ - 'name' => [ - 'en' => 'Blue', - ], -]); - -$redOption = $option->values()->create([ - 'name' => [ - 'en' => 'Red', - ], -]); -``` - -From here we create our variant, associate it with a product and a tax class, then attach the option id. - -```php -$blueVariant = ProductVariant::create([ - 'sku' => 'blue-product', -]); - -$blueVariant->values()->attach($blueOption); - -$redVariant = ProductVariant::create([ - 'product_id' => $product->id, - 'tax_class_id' => $taxClass->id, - 'sku' => 'red-product', -]); - -$redVariant->values()->attach($redOption); -``` - -Now, we need to create a price for our variant. This is where we use our *currency* created or fetched earlier. - -```php -$variant->prices()->create([ - 'price' => 199, - 'currency_id' => $currency->id, -]); -``` - -### Exceptions - -When creating variants there are some exceptions that will be thrown if certain conditions are met. - -| Exception | Conditions | -|:-------------------------------------------------|:---------------------------------------------------------------------------------------| -| `Illuminate\Validation\ValidationException` | Thrown if validation fails on the value options array. | - -## Pricing - -### Overview - -Prices are stored in the database as integers. When retrieving a `Price` model the `price` and `compare_price` -attributes are cast to a `Price` datatype. This casting gives you some useful helpers when dealing with prices on your -front end. - -| Field | Description | Default | Required | -|:--------------------|:-------------------------------------------------------------------------------------|:--------|:---------| -| `price` | A integer value for the price | `null` | yes | -| `compare_price` | For display purposes, allows you to show a comparison price, e.g. RRP. | `null` | no | -| `currency_id` | The ID of the related currency | `null` | yes | -| `min_quantity` | The minimum quantity required to get this price. | `1` | no | -| `customer_group_id` | The customer group this price relates to, leaving as `null` means any customer group | `null` | no | -| `priceable_type` | This is the class reference to the related model which owns the price | `null` | yes | -| `priceable_id` | This is the id of the related model which owns the price | `null` | yes | - -```php -$priceable = \Lunar\Models\ProductVariant::create([/** ... */]); -$price = \Lunar\Models\Price::create([ - 'price' => 199, - 'compare_price' => 299, - 'currency_id' => 1, - 'min_quantity' => 1, - 'customer_group_id' => null, - 'priceable_type' => $priceable->getMorphClass(), - 'priceable_id' => $priceable->id, -]); -``` - -## Price formatting - -For the full reference on how to format prices for your storefront see the [Pricing Reference](/core/reference/pricing) - -::: tip -The same methods apply to the compare_price attribute -::: - -### Base Pricing - -Pricing is defined on a variant level, meaning you will have a different price for each variant and also for each -currency in the system. In order to add pricing to a variant, you can either create the model directly or use the -relationship method. - -```php -$priceable = \Lunar\Models\ProductVariant::create([/** ... */]); -\Lunar\Models\Price::create([ - 'price' => 199, - 'compare_price' => 299, - 'currency_id' => 1, - 'min_quantity' => 1, - 'customer_group_id' => null, - 'priceable_type' => $priceable->getMorphClass(), - 'priceable_id' => $priceable->id, -]); -``` - -```php -$variant->prices()->create([/* .. */]); -``` - -### Customer group pricing - -You can specify which customer group the price applies to by setting the `customer_group_id` column. If left as `null` -the price will apply to all customer groups. This is useful if you want to have different pricing for certain customer -groups and also different price quantity breaks per customer group. - -### Price Break Pricing - -Price Break pricing is a concept in which when you buy in bulk, the cost per item will change (usually go down). With Pricing -on Lunar, this is determined by the `min_quantity` column when creating prices. For example: - -```php -Price::create([ - // ... - 'price' => 199, - 'compare_price' => 399, - 'min_quantity' => 1, -]); - -Price::create([ - // ... - 'price' => 150, - 'compare_price' => 399, - 'min_quantity' => 10, -]); -``` - -In the above example if you order between 1 and 9 items you will pay `1.99` per item. But if you order at least 10 you -will pay `1.50` per item. - -## Fetching the price - -Once you've got your pricing all set up, you're likely going to want to display it on your storefront. We've created -a `PricingManager` which is available via a facade to make this process as painless as possible. - -To get the pricing for a product you can simply use the following helpers: - -#### Minimum example - -A quantity of 1 is implied when not passed. - -```php -$pricing = \Lunar\Facades\Pricing::for($variant)->get(); -``` - -#### With Quantities - -```php -$pricing = \Lunar\Facades\Pricing::qty(5)->for($variant)->get(); -``` - -#### With Customer Groups - -If you don't pass in a customer group, Lunar will use the default, including any pricing that isn't specific to a -customer group. - -```php -$pricing = \Lunar\Facades\Pricing::customerGroups($groups)->for($variant)->get(); - -// Or a single customer group -$pricing = \Lunar\Facades\Pricing::customerGroup($group)->for($variant)->get(); -``` - -#### Specific to a user - -The PricingManager assumes you want the price for the current authenticated user. - -If you want to always return the guest price, you may use... - -```php -$pricing = \Lunar\Facades\Pricing::guest()->for($variant)->get(); -``` - -Or to specify a different user... - -```php -$pricing = \Lunar\Facades\Pricing::user($user)->for($variant)->get(); -``` - -#### With a specific currency - -If you don't pass in a currency, the default is implied. - -```php -$pricing = \Lunar\Facades\Pricing::currency($currency)->for($variant)->get(); -``` - -#### For a model - -Assuming you have a model that implements the `hasPrices` trait, such as a `ProductVariant`, you can use the following -to retrieve pricing. - -```php -$pricing = $variant->pricing()->qty(5)->get(); -``` - -::: danger Be aware -If you try and fetch a price for a currency that doesn't exist, a ` Lunar\Exceptions\MissingCurrencyPriceException` -exception will be thrown. -::: - ---- - -This will return a `PricingResponse` object which you can interact with to display the correct prices. Unless it's a -collection, each property will return a `Lunar\Models\Price` object. - -```php -/** - * The price that was matched given the criteria. - */ -$pricing->matched; - -/** - * The base price associated to the variant. - */ -$pricing->base; - -/** - * A collection of all the price quantity breaks available for the given criteria. - */ -$pricing->priceBreaks; - -/** - * All customer group pricing available for the given criteria. - */ -$pricing->customerGroupPrices; -``` - -Sometimes you might want to simply get all prices a product has from the variants, instead of loading up all a products -variants fetching the prices that way, you can use the `prices` relationship on the product. - -```php -$product->prices -``` - -This will return a collection of `Price` models. - -### Storing Prices Inclusive of Tax - -Lunar allows you to store pricing inclusive of tax if you need to. This is helpful if you need to show charm pricing, at -$9.99 for example, which may not be possible if pricing is stored exclusive of tax due to rounding. - -To start you will need to set the `stored_inclusive_of_tax` config value in `lunar/pricing` to `true`. Then you will -need to ensure your default Tax Zone is set up correctly with the correct tax rates. - -Once set, the cart will automatically calculate the tax for you. - -If you need to show both ex. and inc. tax pricing on your product pages, you can use the following methods which are -available on the `Lunar\Models\Price` model. - -```php -$price->priceIncTax(); -$price->priceExTax(); -$price->comparePriceIncTax(); // to retrieve the tax inclusive compare price -``` - -### Customising Prices with Pipelines - -All pipelines are defined in `config/lunar/pricing.php` - -```php -'pipelines' => [ - //, -], -``` - -You can add your own pipelines to the configuration, they might look something like: - -```php -pricing->matched; - - $matchedPrice->price->value = 200; - - $pricingManager->pricing->matched = $matchedPrice; - - return $next($pricingManager); - } -} -``` - -```php -'pipelines' => [ - // ... - App\Pipelines\Pricing\CustomPricingPipeline::class, -], -``` - -::: tip -Pipelines will run from top to bottom -::: - -## Full Example - -For this example, we're going to be creating some Dr. Martens boots. Below is a screenshot of what we're aiming for: - -![](/images/products/dr-martens.png) - -Here are the steps we're going to take: - -- Create our product type -- Create the initial product -- Create the product options and their values -- Generate the variants based on those values - -### Set up the product type. - -```php -$productType = Lunar\Models\ProductType::create([ - 'name' => 'Boots', -]); -``` - -::: tip Note -This example assumes we already have Attributes set up for name and description and that they're assigned to the product -type. -::: - -### Create the initial product - -```php -Lunar\Models\Product::create([ - 'product_type_id' => $productType->id, - 'status' => 'published', - 'brand_id' => $brandId, - 'sku' => 'DRBOOT', - 'attribute_data' => [ - 'name' => new TranslatedText(collect([ - 'en' => new Text('1460 PATENT LEATHER BOOTS'), - ])), - 'description' => new Text('Even more shades from our archive...'), - ], -]); -``` - -### Product Options - -Based on the example above we're going to need 2 options, Size and Colour. - -```php -$colour = Lunar\Models\ProductOption::create([ - 'name' => [ - 'en' => 'Colour', - ], - 'label' => [ - 'en' => 'Colour', - ], -]); - -$size = Lunar\Models\ProductOption::create([ - 'name' => [ - 'en' => 'Size', - ], - 'label' => [ - 'en' => 'Size', - ], -]); -``` - -### Product Option Values - -From here we now need to create our option values like so: - -```php -$colour->values()->createMany([ - [ - 'name' => [ - 'en' => 'Black', - ], - ], - [ - 'name' => [ - 'en' => 'White', - ], - ], - [ - 'name' => [ - 'en' => 'Pale Pink', - ], - ], - [ - 'name' => [ - 'en' => 'Mid Blue', - ], - ], -]); - -// We won't do all the sizes here, just enough to get the idea... -$size->values()->createMany([ - [ - 'name' => [ - 'en' => '3', - ], - ], - [ - 'name' => [ - 'en' => '6', - ], - ], -]); -``` - -### Generate the variants - -First we just need to grab the values we want to use to generate the variants. Since we're generating them for -everything, we just grab all of them. - -```php -$optionValueIds = $size->values->merge($colour->values)->pluck('id'); - -\Lunar\Hub\Jobs\Products\GenerateVariants::dispatch($product, $optionValueIds); -``` - -::: tip -When generating variants, the sku will be derived from the Product's base SKU, in this case `DRBOOT` and will be -suffixed with `-{count}`. -::: - -The resulting generation is as follows: - -| SKU | Colour | Size | -|:---------|:----------|:-----| -| DRBOOT-1 | Black | 3 | -| DRBOOT-2 | Black | 6 | -| DRBOOT-3 | White | 3 | -| DRBOOT-4 | White | 6 | -| DRBOOT-5 | Pale Pink | 3 | -| DRBOOT-6 | Pale Pink | 6 | -| DRBOOT-7 | Mid Blue | 3 | -| DRBOOT-8 | Mid Blue | 6 | - -You are then free to change the SKU's as you see fit, update the pricing for each variant etc before publishing. diff --git a/docs/core/reference/search.md b/docs/core/reference/search.md deleted file mode 100644 index 705ccf870e..0000000000 --- a/docs/core/reference/search.md +++ /dev/null @@ -1,63 +0,0 @@ -# Search - -## Overview - -Search is configured using the [Laravel Scout](https://laravel.com/docs/scout) package. - -Using Scout allows us to provide search out the box but also make it easy for you as the developer to customise and tailor searching to your needs. - -## Initial set up - -The database driver provides basic search to get you up and running but you will likely find you want to implement something with a bit more power, such as [Meilisearch](https://www.meilisearch.com/) or [Algolia](https://www.algolia.com/). - -## Configuration - -By default, scout has the setting `soft_delete` set to `false`. You need to make sure this is set to `true` otherwise you will see soft deleted models appear in your search results. - -If you want to use other models or your own models in the search engine, you can add the reference for them on the config file. - -```php -'models' => [ - // These models are required by the system, do not change them. - \Lunar\Models\Collection::class, - \Lunar\Models\Product::class, - \Lunar\Models\ProductOption::class, - \Lunar\Models\Order::class, - \Lunar\Models\Customer::class, - // Below you can add your own models for indexing - ] -``` - -## Index records - -If you installed the Lunar package in an existing project and you would like to use the database records with the search engine, or you just need to do some maintenance on the indexes, you can use the index command. - -```sh -php artisan lunar:search:index -``` - -The command will import the records of the models listed in the `lunar/search.php` configuration file. Type `--help` to see the available options. - -## Meilisearch - -If you used the Meilisearch package you would like to use the command to create filterable and searchable attributes on Meilisearch indexes. - -```sh -php artisan lunar:meilisearch:setup -``` - -## Engine Mapping - -By default, Scout will use the driver defined in your .env file as `SCOUT_DRIVER`. So if that's set to `meilisearch`, all your models will be indexed via the Meilisearch driver. This can present some issues, if you wanted to use a service like Algolia for Products, you wouldn't want all your Orders being indexed there since it will ramp up the record count and the cost. - -In Lunar we've made it possible to define what driver you would like to use per model. It's all defined in the `config/lunar/search.php` config file and looks like this: - -```php -'engine_map' => [ - \Lunar\Models\Product::class => 'algolia', - \Lunar\Models\Order::class => 'meilisearch', - \Lunar\Models\Collection::class => 'meilisearch', -], -``` - -It's quite self explanatory, if a model class isn't added to the config, it will take on the Scout default. diff --git a/docs/core/reference/tags.md b/docs/core/reference/tags.md deleted file mode 100644 index bcdc1dd22c..0000000000 --- a/docs/core/reference/tags.md +++ /dev/null @@ -1,48 +0,0 @@ -# Tags - -## Overview - -Tags serve a simple function in Lunar, you can add tags to models. This is useful for relating otherwise unrelated models in the system. They will also impact other parts of the system such as Dynamic collections. - -For example, you could have two products "Blue T-Shirt" and "Blue Shoes", which in their nature are unrelated, but you could add a `BLUE` tag to each product and then create a Dynamic Collection to include any products with a `BLUE` tag and they will be returned. - -::: tip -Heads up! Tags are converted uppercase as they are saved. -::: - -## Enabling tags - -In order to enable tagging on a model, simply add the `HasTags` trait. - -```php -syncTags($tags); - -$model->tags; - -// ['TAG ONE', 'TAG TWO', 'TAG THREE'] -``` - -If a tag exists already by name it will use it, otherwise they will be created. The process runs via a job so will run in the background if you have that set up. diff --git a/docs/core/reference/taxation.md b/docs/core/reference/taxation.md deleted file mode 100644 index e93596149c..0000000000 --- a/docs/core/reference/taxation.md +++ /dev/null @@ -1,161 +0,0 @@ -# Taxation - -## Overview - -No one likes taxes! But we have to deal with them... Lunar provides manual tax rules to implement the correct sales tax for each order. For complex taxation (e.g. US States) we suggest integrating with a service such as [TaxJar](https://www.taxjar.com/). - - -## Tax Classes - -Tax Classes are assigned to Products and allow us to classify products to certain taxable groups that may have differing tax rates. - -```php -Lunar\Models\TaxClass -``` - -|Field|Description| -|:-|:-| -|id|| -|name|e.g. `Clothing`| -|created_at|| -|updated_at|| - -```php -$taxClass = TaxClass::create([ - 'name' => 'Clothing', -]); -``` - -## Tax Zones - -These specify a geographic zone for tax rates to be applied. Tax Zones can be based upon countries, states or zip/post codes. - -```php -Lunar\Models\TaxZone -``` - -|Field| Description | -|:-|:------------------------------------| -|id| | -|name| e.g. `UK` | -|zone_type| `country`, `states`, or `postcodes` | -|price_display| `tax_inclusive` or `tax_exclusive` | -|active| true/false | -|default| true/false | -|created_at| | -|updated_at| | - -```php -$taxZone = TaxZone::create([ - 'name' => 'UK', - 'zone_type' => 'country', - 'price_display' => 'tax_inclusive', - 'active' => true, - 'default' => true, -]); -``` - -```php -Lunar\Models\TaxZoneCountry -``` - -|Field|Description| -|:-|:-| -|id|| -|tax_zone_id|| -|country_id|| -|created_at|| -|updated_at|| - - -```php -$taxZone->countries()->create([ - 'country_id' => \Lunar\Models\Country::first()->id, -]); -``` - -```php -Lunar\Models\TaxZoneState -``` - -|Field|Description| -|:-|:-| -|id|| -|tax_zone_id|| -|state_id|| -|created_at|| -|updated_at|| - -```php -$taxZone->states()->create([ - 'state_id' => \Lunar\Models\State::first()->id, -]); -``` - -```php -Lunar\Models\TaxZonePostcode -``` - -|Field|Description| -|:-|:-| -|id|| -|tax_zone_id|| -|country_id|| -|postcode|wildcard, e.g. `9021*`| -|created_at|| -|updated_at|| - -```php -Lunar\Models\TaxZoneCustomerGroup -``` - -|Field|Description| -|:-|:-| -|id|| -|tax_zone_id|| -|customer_group_id|| -|created_at|| -|updated_at|| - - -## Tax Rates - -Tax Zones have one or many tax rates. E.g. you might have a tax rate for the State and also the City, which would collectively make up the total tax amount. - -```php -Lunar\Models\TaxRate -``` - -|Field|Description| -|:-|:-| -|id|| -|tax_zone_id|| -|name|e.g. `UK`| -|created_at|| -|updated_at|| - -```php -Lunar\Models\TaxRateAmount -``` - -|Field|Description| -|:-|:-| -|id|| -|tax_rate_id|| -|tax_class_id|| -|percentage|e.g. `6` for 6%| -|created_at|| -|updated_at|| - - -## Settings -- Shipping and other specific costs are assigned to tax classes in the settings. -- Calculate tax based upon Shipping or Billing address? -- Default Tax Zone - -## Extending - -Sometimes the standard tax calculations aren't enough, and you may want to implement your own logic, perhaps connecting to a Tax service such as TaxJar. - -Lunar allows you to implement your own tax driver, check the [Extending Lunar](/core/extending/taxation) section for more information. - diff --git a/docs/core/reference/urls.md b/docs/core/reference/urls.md deleted file mode 100644 index dd51447382..0000000000 --- a/docs/core/reference/urls.md +++ /dev/null @@ -1,157 +0,0 @@ -# URLs - -## Overview - -URLs are not to be confused with Routes in Laravel. You can create routes for a number of resources, but most commonly they would be created for a Product. - -They allow you to add a way to identify/query for a resource without having to use the ID of that resource. Notably these would be useful for vanity URLs so instead of something like: - -```bash -/products/1 -``` - -On your storefront, you could have: - -```bash -/products/apple-iphone -``` - -`apple-iphone` is the slug for a URL which would correspond to a product and would allow you to fetch it easily without having to expose IDs or do any weird round trips to your API. - -::: tip -A URL cannot share the same `slug` and `language_id` columns. You can also only have one `default` URL per language for that resource. -::: - -## Creating a URL - -```php -\Lunar\Models\Url::create([ - 'slug' => 'apple-iphone', - 'language_id' => $language->id, - 'default' => true, -]); -``` - -::: tip -If you add a new default URL for a language which already has one, the new URL will override and become the new default. -::: - -```php -$urlA = \Lunar\Models\Url::create([ - 'slug' => 'apple-iphone', - 'language_id' => 1, - 'default' => true, -]); - -$urlA->default // true - -$urlB = \Lunar\Models\Url::create([ - 'slug' => 'apple-iphone-26', - 'language_id' => 1, - 'default' => true, -]); - -$urlA->default // false -$urlB->default // true - -/** - * Since this is a different language, no other URLs will be changed. - **/ -$urlC = \Lunar\Models\Url::create([ - 'slug' => 'apple-iphone-french', - 'language_id' => 2, - 'default' => true, -]); - - -$urlA->default // false -$urlB->default // true -$urlC->default // true -``` - -## Deleting a URL - -When you delete a URL, if it was the default then Lunar will look for a non default of the same language and assign that instead. - - -```php -$urlA = \Lunar\Models\Url::create([ - 'slug' => 'apple-iphone', - 'language_id' => 1, - 'default' => true, -]); - -$urlB = \Lunar\Models\Url::create([ - 'slug' => 'apple-iphone-26', - 'language_id' => 1, - 'default' => false, -]); - -$urlB->default // false - -$urlA->delete(); - -$urlB->default // true -``` - - -## Adding URL support to Models - -Out the box Lunar has a few pre-configured models which have URLs - -- Products -- Collections - -You are free to add URLs to your own models. - -```php -urls; // Collection -``` - -## Automatically generating URLs - -You can tell Lunar to generate URLs for models that use the `HasUrls` trait automatically by setting the `generator` config option in `config/lunar/urls.php`. - -By default this is set to `Lunar\Generators\UrlGenerator::class` which means URLs will be generated. To disable this, set the config like below: - -```php -return [ - 'generator' => null -]; -``` - -By default this will use the default language and take the `name` attribute as the slug, you are of course free to use your own class for this. You just need to make sure there is a `handle` method which accepts a `Model`. - -```php -= 8.2 -- MySQL 8.0+ / PostgreSQL 9.2+ -- exif PHP extension (on most systems it will be installed by default) -- intl PHP extension (on most systems it will be installed by default) -- bcmath PHP extension (on most systems it will be installed by default) -- GD PHP extension (used for image manipulation) - -## Installation - -::: tip -We assume you have a suitable local development environment in which to run Lunar. We would highly suggest [Laravel Herd](https://herd.laravel.com) for this purpose. -::: - -### Create a New Project - -```bash -composer create-project --stability dev lunarphp/livewire-starter-kit my-store - -cd my-store -``` - -or using the latest Laravel Installer you can do... - -```bash -laravel new my-store --using=lunarphp/livewire-starter-kit - -cd my-store -``` - - -### Configure the Laravel app - -Copy the `.env.example` file to `.env` and make sure the details match to your install. - -```bash -cp .env.example .env -``` - -All the relevant configuration files should be present in the repo. - -### Migrate and Seed - -Install Lunar - -```bash -php artisan lunar:install -``` - -Seed the demo data. - -```bash -php artisan db:seed -``` - -Link the storage directory - -```bash -php artisan storage:link -``` - -## Finished 🚀 - -You are now installed! - -- You can access the storefront at `http://` -- You can access the admin hub at `http:///lunar` - -You can review the source code at the GitHub Repository: [https://github.com/lunarphp/livewire-starter-kit](https://github.com/lunarphp/livewire-starter-kit). diff --git a/docs/core/storefront-utils/storefront-session.md b/docs/core/storefront-utils/storefront-session.md deleted file mode 100644 index 4b06b2e463..0000000000 --- a/docs/core/storefront-utils/storefront-session.md +++ /dev/null @@ -1,122 +0,0 @@ -# Storefront Session - -## Overview - -The storefront session facade is a provided to help keep certain resources your storefront needs set, such as channel, customer group etc. - -```php -use Lunar\Facades\StorefrontSession; -``` - -## Channels - -### Initialise the Channel - -This will set the Channel based on what's been previously set, otherwise it will use the default. - -```php -StorefrontSession::initChannel(); -``` - -:::tip This is automatically called when using the facade. -::: - -### Set the Channel - -```php -$channel = new Channel([ - 'name' => 'Webstore', - 'handle' => 'webstore', -]); - -StorefrontSession::setChannel($channel); -StorefrontSession::setChannel('webstore'); -``` - -### Get the Channel - -```php -StorefrontSession::getChannel(); -``` - -## Customer Groups - -### Initialise the Customer Groups - -This will set the Customer Groups based on what's been previously set (from the session), otherwise it will use the default record. - -```php -StorefrontSession::initCustomerGroups(); -``` - -:::tip This is automatically called when using the facade. -::: - -### Set the Customer Groups - -```php -$customerGroup = new CustomerGroup([ - 'name' => 'Retail', - 'handle' => 'retail', -]); - -// Set multiple customer groups -StorefrontSession::setCustomerGroups(collect($customerGroup)); - -// Set a single customer group, under the hood this will just call `setCustomerGroups`. -StorefrontSession::setCustomerGroup($customerGroup); -``` - -### Get the Customer Groups - -```php -StorefrontSession::getCustomerGroups(); -``` - -## Customer - -### Initialise the Customer - -This will set the Customer based on what's been previously set (from the session), otherwise it will retrieve the latest customer attached with the logged in user. - -```php -StorefrontSession::initCustomer(); -``` - -:::tip This is automatically called when using the facade. -::: - -### Set the Customer - -```php -$customer = /** your store logic to determine current customer */ ; - -StorefrontSession::setCustomer($customer); -``` - -### Get the Customer - -```php -StorefrontSession::getCustomer(); -``` - -## Currencies - -### Set the Currency - -```php -$currency = new Currency([ - 'name' => 'US Dollars', - 'code' => 'USD', - // ... -]); - -StorefrontSession::setCurrency($currency); -StorefrontSession::setCurrency('USD'); -``` - -### Get the Currency - -```php -StorefrontSession::getCurrency(); -``` diff --git a/docs/core/upgrading.md b/docs/core/upgrading.md deleted file mode 100644 index 8b933ae173..0000000000 --- a/docs/core/upgrading.md +++ /dev/null @@ -1,408 +0,0 @@ -# Upgrading - -## General Upgrade Instructions - -Update the package - -```sh -composer update lunarphp/lunar -``` - -Run any migrations - -```sh -php artisan migrate -``` - -## 1.0.0 (stable) - -### Medium Impact - -#### Scout no longer enabled by default for admin panel -With the recent panel search improvements and the fact most users initially will not be using Scout, the default -configuration for the panel is now to *not* enable Scout for search. - -If you are using Scout, you may need to update your `panel.php` config. - -```php -'scout_enabled' => true, -``` - - -### Low Impact - -#### Telemetry -This release introduces anonymous usage insights, which are sent via a deferred API call to Lunar. The reason for this addition -is to allow us to have an idea of how Lunar is being used and at what capacity. We do not send or use any identifying information whatsoever. - -This is completely optional, however, it is turned on by default. To opt-out add the following to your service provider's boot method: - -```php -\Lunar\Facades\Telemetry::optOut(); -``` - -## 1.0.0-beta.24 - -### Medium Impact - -#### Customer `vat_no` field renamed -The field on the `customers` table has been renamed to `tax_identifier`. This is to align with the new field of the same -name on `addresses`, `cart_addresses` and `order_addresses`. - -### Low Impact - -#### Buy X Get Y Discount conditions and rewards - -Buy X Get Y discounts can now use collections and variants as conditions, and variants as rewards. As part of this change the `discount_purchasables` table has been renamed `discountables` and has its own `Discountable` model. If you have been using `discount_purchasables` directly, or the `purchasables` relation on the discount model, you will need to update your code. - -## 1.0.0-beta.22 - -### High Impact - -#### Removed Laravel 10 Support - -Laravel 10 support has been removed as it was becoming harder to support. You will want to upgrade your projects to -Laravel 11+ for this release. You may consider [Laravel Shift](https://laravelshift.com/) to assist you. - -#### Lunar Panel Discount Interface - -The `LunarPanelDiscountInterface` now requires a `lunarPanelRelationManagers` method that returns an array of relation managers you want to show in the admin panel when the discount type is used. You will need to update any custom discount types you have created to include this method. - -## 1.0.0-beta.21 - -### High Impact - -#### Order reference generation changes - -The current order reference generator uses the format `YYYY-MM-{X}` which has been implemented since the early days of when Lunar was called GetCandy. - -This approach to formatting is not great for order references and can lead to anomalies when attempting to determine the next reference in the sequence. - -The new format uses the Order ID and adds leading zeros and an optional prefix i.e. - -Assuming order ID is 1965 -``` -// Old -2025-04-00250 -// New -00001965 -``` -The length of the reference, plus the prefix can now be defined in the `lunar/orders.php` config file: - -```php - 'reference_format' => [ - /** - * Optional prefix for the order reference - */ - 'prefix' => null, - - /** - * STR_PAD_LEFT: 00001965 - * STR_PAD_RIGHT: 19650000 - * STR_PAD_BOTH: 00196500 - */ - 'padding_direction' => STR_PAD_LEFT, - - /** - * 00001965 - * AAAA1965 - */ - 'padding_character' => '0', - - /** - * If the length specified below is smaller than the length - * of the Order ID, then no padding will take place. - */ - 'length' => 8, - ], -``` - -If you wish to keep using the current action used to generate references, you can [copy the existing class](https://github.com/lunarphp/lunar/blob/1.0.0-beta20/packages/core/src/Base/OrderReferenceGenerator.php) into your app and update the `reference_generator` path in config. - -### Medium Impact - -#### Two-Factor Authentication has been added - -To continue improving security for the Lunar panel, Staff members now have the ability to set up Two-Factor Authentication. Currently this is opt-in, however you can enforce all Staff members to set up 2FA: - -```php -public function register() -{ - \Lunar\Admin\Support\Facades\LunarPanel::enforceTwoFactorAuth()->register(); -} -``` - -If you do not wish to use Two-Factor Authentication at all, you can disable it and the option to set it up won't show. - -```php -public function register() -{ - \Lunar\Admin\Support\Facades\LunarPanel::disableTwoFactorAuth()->register(); -} -``` - -## 1.0.0-beta.1 - -### High Impact - -#### Model Extending - -Model extending has been completely rewritten and will require changes to your Laravel app if you have previously extended Lunar models. - -The biggest difference is now Lunar models implement a contract (interface) and support dependency injection across your storefront and the Lunar panel. - -You will need to update how you register models in Lunar. - -##### Old -```php - ModelManifest::register(collect([ - Product::class => \App\Models\Product::class, - // ... - ])); -``` - -##### New -```php -ModelManifest::replace( - \Lunar\Models\Contracts\Product::class, - \App\Models\Product::class -); -``` - -::: tip -If your models are not extending their Lunar counterpart, you must implement the relevant contract in `Lunar\Models\Contracts` -::: - -Look at the [model extending](/core/extending/models) section for all available functionality. - -#### Polymorphic relationships - -In order to support model extending better, all polymorphic relationships now use an alias instead of the fully qualified class name, this allows relationships to resolve to your custom model when interacting with Eloquent. - -There is an additional config setting in `config/lunar/database.php`, where you can set whether these polymorph mappings should be prefixed in Lunar's context. - -```php -'morph_prefix' => null, -``` - -By default, this is set as `null` so the mapping for a product would just be `product`. - -There is a migration which will handle this change over for Lunar tables and some third party tables, however you may need to add your own migrations to other tables or to switch any custom models you may have. - -#### Shipping methods availability - -Shipping methods are now associated to Customer Groups. If you are using the shipping addon then you should ensure that all your shipping methods are associated to the correct customer groups. - -#### Stripe Addon - -The Stripe addon will now attempt to update an order's billing and shipping address based on what has been stored against the Payment Intent. This is due to Stripe not always returning this information during their express checkout flows. This can be disabled by setting the `lunar.stripe.sync_addresses` config value to `false`. - -##### PaymentIntent storage and reference to carts/orders -Currently, PaymentIntent information is stored in the Cart model's meta, which is then transferred to the order when created. - -Whilst this works okay it causes for limitations and also means that if the carts meta is ever updated elsewhere, or the intent information is removed, then it will cause unrecoverable loss. - -We have now looked to move away from the payment_intent key in the meta to a StripePaymentIntent model, this allows us more flexibility in how payment intents are handled and reacted on. A StripePaymentIntent will be associated to both a cart and an order. - -The information we store is now: - -- `intent_id` - This is the PaymentIntent ID which is provided by Stripe -- `status` - The PaymentIntent status -- `event_id` - If the PaymentIntent was placed via the webhook, this will be populated with the ID of that event -- `processing_at` - When a request to place the order is made, this is populated -- `processed_at` - Once the order is placed, this will be populated with the current timestamp - -##### Preventing overlap -Currently, we delay sending the job to place the order to the queue by 20 seconds, this is less than ideal, now the payment type will check whether we are already processing this order and if so, not do anything further. This should prevent overlaps regardless of how they are triggered. - -## 1.0.0-alpha.34 - -### Medium Impact - -#### Stripe Addon - -The Stripe driver will now check whether an order has a value for `placed_at` against an order and if so, no further processing will take place. - -Additionally, the logic in the webhook has been moved to the job queue, which is dispatched with a delay of 20 seconds, this is to allow storefronts to manually process a payment intent, in addition to the webhook, without having to worry about overlap. - -The Stripe webhook ENV entry has been changed from `STRIPE_WEBHOOK_PAYMENT_INTENT` to `LUNAR_STRIPE_WEBHOOK_SECRET`. - -The stripe config Lunar looks for in `config/services.php` has changed and should now look like: - -```php -'stripe' => [ - 'key' => env('STRIPE_SECRET'), - 'public_key' => env('STRIPE_PK'), - 'webhooks' => [ - 'lunar' => env('LUNAR_STRIPE_WEBHOOK_SECRET'), - ], -], -``` - -## 1.0.0-alpha.32 - -### High Impact - -There is now a new `LunarUser` interface you will need to implement on your `User` model. - -```php -// ... -class User extends Authenticatable implements \Lunar\Base\LunarUser -{ - use \Lunar\Base\Traits\LunarUser; -} -``` - -## 1.0.0-alpha.31 - -### High Impact - -Certain parts of `config/cart.php` which are more specific to when you are interacting with carts via the session have been relocated to a new `config/cart_session.php` file. - -```php -// Move to config/cart_session.php -'session_key' => 'lunar_cart', -'auto_create' => false, -``` - -You should also check this file for any new config values you may need to add. - -## 1.0.0-alpha.29 - -### High Impact - -#### Cart calculate function will no longer recalculate - -If you have been using the `$cart->calculate()` function it has previously always run the calculations regardless of -whether the cart has already been calculated. Now the calculate function will only run if we don't have cart totals. -To allow for recalculation we have now introduced `$cart->recalculate()` to force the cart to recalculate. - -#### Unique index for Collection Group handle - -Collection Group now have unique index on the column `handle`. -If you are creating Collection Group from the admin panel, there is no changes required. - -### Medium Impact - -#### Update custom shipping modifiers signature - -The `\Lunar\Base\ShippingModifier` `handle` method now correctly passes a closure as the second parameter. You will need to update any custom shipping modifiers that extend this as follows: - -```php -public function handle(\Lunar\Models\Cart $cart, \Closure $next) -{ - //.. - - return $next($cart); -} -``` - -## 1.0.0-alpha.26 - -### Medium Impact - -If you are using your own classes that implement the `Purchasable` interface, you will need to add the following additional methods: - -```php -public function canBeFulfilledAtQuantity(int $quantity): bool; -public function getTotalInventory(): int; -``` - -If you are checking the `ProductVariant` `purchasable` attribute in your code, you should update the following check: - -```php -// Old -$variant->purchasable == 'backorder'; -// New -$variant->purchasable == 'in_stock_or_on_backorder'; - -``` - -## 1.0.0-alpha.22 - -### Medium Impact - -Carts now use soft deletes and a cart will be deleted when `CartSession::forget()` is called. -If you don't want to delete the cart when you call `forget` you can pass `delete: false` as a parameter: - -```php -\Lunar\Facades\CartSession::forget(delete: false); -``` - -## 1.0.0-alpha.20 - -### High Impact - -#### Stripe addon facade change - -If you are using the Stripe addon, you need to update the facade as the name has changed. - -```php -// Old -\Lunar\Stripe\Facades\StripeFacade; - -// New -\Lunar\Stripe\Facades\Stripe; -``` - -## 1.0.0-alpha - -:::warning -If upgrading from `0.x` please ensure you are on `0.8` before upgrading to `1.x`. -::: - -### High Impact - -#### Change to Staff model namespace - -The Staff model has changed location from `Lunar\Hub\Models\Staff` to `Lunar\Admin\Models\Staff` so this will need to be updated within -your codebase and any polymorphic relations. - -#### Spatie Media Library -This package has been upgrade to version 11, which introduces some breaking changes. -See here for more information https://github.com/spatie/laravel-medialibrary/blob/main/UPGRADING.md - -#### Media Conversions -The `lunar.media.conversions` configuration has been removed, in favour of registering custom media definitionss instead. -Media definition classes allow you to register media collections, conversions and much more. See [Media Collections](/core/reference/media.html#media-collections) -for further information. - -#### Product Options -The `position` field has been removed from the `product_options` table and is now found on the `product_product_option` -pivot table. Any position data will be automatically adjusted when running migrations. - -#### Tiers renamed to Price Breaks - -The `tier` column on pricing has been renamed to `min_quantity`, any references in code to `tiers` needs to be updated. - -##### Price Model - -```php -// Old -$priceModel->tier -// New -$priceModel->min_quantity - -// Old -$priceModel->tiers -// New -$priceModel->priceBreaks -``` - -##### Lunar\Base\DataTransferObjects\PricingResponse - -```php -// Old -public Collection $tiered, -// New -public Collection $priceBreaks, -``` - -##### Lunar\Base\DataTransferObjects\PaymentAuthorize - -Two new properties have been added to the constructor for this DTO. - -```php -public ?int $orderId = null, -public ?string $paymentType = null -``` diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index fff488175e..0000000000 --- a/docs/index.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "Lunar Documentation" -description: "Learn to create your next online store in Laravel with Lunar E-Commerce." - -# https://vitepress.dev/reference/default-theme-home-page -layout: home - -hero: - name: "Documentation" - text: "Lunar Headless E‑Commerce" - tagline: Explore our guides and examples to integrate Lunar into your Laravel application. - actions: - - theme: brand - text: Get Started - link: /core/overview - - theme: alt - text: Join us on Discord - link: https://discord.gg/v6qVWaf - -features: - - title: E-Commerce Core - details: Based around Eloquent models, quickly build your online store in any Laravel stack. - link: /core/overview - - title: Admin Panel - details: Filament-powered admin panel to administer your product catalog, customers, orders and much more. - link: /admin/overview - - title: Support - details: Get community help or expert support—whatever you need for your Lunar-powered project. - link: /support ---- diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 9306c527d3..0000000000 --- a/docs/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "devDependencies": { - "vitepress": "^1.6.3" - }, - "scripts": { - "docs:dev": "vitepress dev", - "docs:build": "vitepress build", - "docs:preview": "vitepress preview" - }, - "type": "module" -} diff --git a/docs/public/icon-dark.svg b/docs/public/icon-dark.svg deleted file mode 100644 index f6a5308509..0000000000 --- a/docs/public/icon-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/public/icon.svg b/docs/public/icon.svg deleted file mode 100644 index a856d3109f..0000000000 --- a/docs/public/icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/public/images/products/dr-martens.png b/docs/public/images/products/dr-martens.png deleted file mode 100644 index a0ddb4546c..0000000000 Binary files a/docs/public/images/products/dr-martens.png and /dev/null differ diff --git a/docs/public/logo.svg b/docs/public/logo.svg deleted file mode 100644 index b0615ef642..0000000000 --- a/docs/public/logo.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs/support.md b/docs/support.md deleted file mode 100644 index 274b518fe2..0000000000 --- a/docs/support.md +++ /dev/null @@ -1,92 +0,0 @@ -# Support - -We’re here to help you get the most out of Lunar. Whether you need quick answers from the community or dedicated -assistance from our team, we offer multiple ways to get support for your Lunar-powered project. - -## Community Support - -Lunar is an open-source project, and we have an active community of developers who are happy to help. If you’re looking for general advice or troubleshooting tips, try the following: - -🗣️ [Discord Community](https://discord.gg/v6qVWaf) – Join the discussion, ask questions, and collaborate with other Lunar users. - -🔍 [GitHub Discussions](https://github.com/lunarphp/lunar/discussions) – Browse existing topics or start a new discussion about features and issues. - -🐛 [Bug Reports](https://github.com/lunarphp/lunar/issues) – Found a bug? Report it on our GitHub repository so we can improve Lunar. - -Community support is a great way to get help, but response times may vary. If you need priority support, consider our Premium Support options. - -## Premium Support - -Need expert guidance while integrating or extending Lunar? Our Premium Support plan provides direct access to the Lunar -team, ensuring you get the help you need to build, scale, and optimise your e-commerce platform with confidence. - -### Why Choose Premium Support? -- Direct Access to the Lunar Team – Get support from the developers behind Lunar. -- Fast Response Times – Prioritised assistance to keep your project moving. -- Private Support Channels – Engage with us via a dedicated support portal. -- Best Practices & Guidance – Ensure your implementation follows recommended patterns. -- Extension & Customisation Help – Assistance with extending Lunar to fit your needs. - -### What’s Included? - -Our support plan is designed to help your team navigate the complexities of e-commerce development with Lunar. - -- ✅ Technical Q&A – Get answers to your Lunar-related development questions. -- ✅ Troubleshooting Assistance – Help diagnosing issues with your implementation. -- ✅ Performance & Scalability Advice – Best practices for optimising your Lunar setup. -- ✅ Upgrade Guidance – Assistance with upgrading to newer versions of Lunar. -- ✅ Best Practices & Development Guidance – Advice on structuring your code and custom features. - -### What’s Not Included? - -- 🚫 Custom Development – We’ll guide you, but we won’t write custom code. -- 🚫 Full codebase reviews – We can review snippets of code, but not an entire application. -- 🚫 Bug Fixes Outside Core Lunar – We’ll help identify issues, but fixing bugs in your custom implementation is your responsibility. -- 🚫 General Laravel Support – Our focus is Lunar. For Laravel-specific questions, we recommend the official Laravel support channels. - -### Get Started - -Ready to ensure smooth development with expert-backed support? Our standard plan is $750 per month and we can also -design custom support plans to suit your needs. - - - -## Custom Development - -Need a custom solution built on Lunar? Whether you’re looking for bespoke add-ons, integrations, or a complete -e-commerce storefront, our team can help. As the creators of Lunar, we have the expertise to bring your vision to life, -ensuring a seamless, scalable, and high-performance solution tailored to your business needs. - -How We Can Help... - -#### 🔌 Custom Add-ons & Extensions - -Extend Lunar with bespoke functionality, from advanced product configurations to custom checkout experiences. - -#### 🔄 Integrations - -Seamlessly connect Lunar with third-party services, including payment gateways, ERP systems, and fulfilment providers. - -#### 🛍️ Full Storefront Builds - -Need a complete e-commerce solution? We design and develop fully customised storefronts, optimised for performance, SEO, -and conversions. - -#### 📦 Migration & Optimisation - -Moving from another platform? We can help migrate your data and optimise your store for speed and scalability. - -### Why Work With Us? - -- ✅ Built by the Lunar Team – Get direct access to the people who know Lunar best. -- ✅ Tailored Solutions – Custom development to match your exact business requirements. -- ✅ Scalable & Future-Proof – Solutions built with performance and long-term growth in mind. -- ✅ High-Quality Code – Following best practices to ensure maintainability and efficiency. - -### Let’s Build Together - -Tell us about your project, and we’ll help you create a custom Lunar-powered solution that fits your needs. - - diff --git a/docs/vercel.json b/docs/vercel.json deleted file mode 100644 index 88986d9d0e..0000000000 --- a/docs/vercel.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "redirects": [ - { - "source": "/lunar/:path(.*)", - "destination": "/core/reference/:path", - "statusCode": 301 - }, - { - "source": "/extending/:path(.*)", - "destination": "/core/extending/:path", - "statusCode": 301 - } - ] -} diff --git a/packages/admin/LICENSE.md b/packages/admin/LICENSE.md deleted file mode 100644 index a5dec88053..0000000000 --- a/packages/admin/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) LunarPHP Ltd. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/packages/admin/README.md b/packages/admin/README.md deleted file mode 100644 index c37f05c46d..0000000000 --- a/packages/admin/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Lunar Admin Panel - -See the docs at https://docs.lunarphp.io/ diff --git a/packages/admin/phpunit.xml b/packages/admin/phpunit.xml deleted file mode 100644 index 60aba9af1b..0000000000 --- a/packages/admin/phpunit.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - ./tests - - - - - ./src - - - - - - - - diff --git a/packages/admin/resources/dist/lunar-panel.css b/packages/admin/resources/dist/lunar-panel.css index 2753bbbe4b..e6255d20ba 100644 --- a/packages/admin/resources/dist/lunar-panel.css +++ b/packages/admin/resources/dist/lunar-panel.css @@ -1 +1 @@ -/*! tailwindcss v3.4.0 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:rgba(var(--gray-200),1)}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family),ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:rgba(var(--gray-400),1)}input::placeholder,textarea::placeholder{opacity:1;color:rgba(var(--gray-400),1)}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}input::placeholder,textarea::placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='rgba(var(--gray-500), var(--tw-stroke-opacity, 1))' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:#0000;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=checkbox]:indeterminate,[type=radio]:checked:focus,[type=radio]:checked:hover{border-color:#0000;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media (forced-colors:active){[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:#0000;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}:root.dark{color-scheme:dark}[data-field-wrapper]{scroll-margin-top:8rem}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.-left-\[calc\(0\.5rem_-_1px\)\]{left:calc(-.5rem - -1px)}.-left-\[calc\(0\.75rem_-_1px\)\]{left:calc(-.75rem - -1px)}.left-0{left:0}.left-5{left:1.25rem}.right-0{right:0}.top-\[2px\]{top:2px}.-my-8{margin-top:-2rem;margin-bottom:-2rem}.-ml-\[5px\]{margin-left:-5px}.-mt-3\.5{margin-top:-.875rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.ml-5{margin-left:1.25rem}.ml-7{margin-left:1.75rem}.ml-8{margin-left:2rem}.mt-4{margin-top:1rem}.flow-root{display:flow-root}.w-1\/3{width:33.333333%}.w-\[2px\]{width:2px}.min-w-\[50vw\]{min-width:50vw}.min-w-full{min-width:100%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-y-px{--tw-translate-y:-1px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\!cursor-default{cursor:default!important}.cursor-grab{cursor:grab}.scroll-mt-32{scroll-margin-top:8rem}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.place-content-center{place-content:center}.gap-0{gap:0}.gap-0\.5{gap:.125rem}.gap-2\.5{gap:.625rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.divide-y-2>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(2px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(2px*var(--tw-divide-y-reverse))}.divide-gray-950\/10>:not([hidden])~:not([hidden]){border-color:rgba(var(--gray-950),.1)}.\!overflow-auto{overflow:auto!important}.\!rounded-full{border-radius:9999px!important}.rounded-b-lg{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.\!border-red-300{--tw-border-opacity:1!important;border-color:rgb(252 165 165/var(--tw-border-opacity))!important}.\!border-red-500{--tw-border-opacity:1!important;border-color:rgb(239 68 68/var(--tw-border-opacity))!important}.border-green-300{--tw-border-opacity:1;border-color:rgb(134 239 172/var(--tw-border-opacity))}.border-orange-300{--tw-border-opacity:1;border-color:rgb(253 186 116/var(--tw-border-opacity))}.border-sky-300{--tw-border-opacity:1;border-color:rgb(125 211 252/var(--tw-border-opacity))}.border-white\/10{border-color:#ffffff1a}.\!bg-red-50{--tw-bg-opacity:1!important;background-color:rgb(254 242 242/var(--tw-bg-opacity))!important}.bg-gray-300\/20{background-color:rgba(var(--gray-300),.2)}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity))}.bg-purple-500{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity))}.bg-sky-500{--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity))}.bg-teal-500{--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity))}.bg-white\/70{background-color:#ffffffb3}.\!p-0{padding:0!important}.\!p-3{padding:.75rem!important}.\!ps-6{padding-inline-start:1.5rem!important}.pl-2{padding-left:.5rem}.pl-8{padding-left:2rem}.pt-8{padding-top:2rem}.pt-\[1px\]{padding-top:1px}.pt-\[5px\]{padding-top:5px}.uppercase{text-transform:uppercase}.\!text-red-400\/60{color:#f8717199!important}.\!text-red-400\/80{color:#f87171cc!important}.\!text-red-600{--tw-text-opacity:1!important;color:rgb(220 38 38/var(--tw-text-opacity))!important}.text-gray-900{--tw-text-opacity:1;color:rgba(var(--gray-900),var(--tw-text-opacity))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity))}.text-orange-600{--tw-text-opacity:1;color:rgb(234 88 12/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.text-sky-600{--tw-text-opacity:1;color:rgb(2 132 199/var(--tw-text-opacity))}.shadow,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.ring-1,.ring-4{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-gray-100{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--gray-100),var(--tw-ring-opacity))}.ring-purple-100{--tw-ring-opacity:1;--tw-ring-color:rgb(243 232 255/var(--tw-ring-opacity))}.ring-sky-100{--tw-ring-opacity:1;--tw-ring-color:rgb(224 242 254/var(--tw-ring-opacity))}.ring-teal-100{--tw-ring-opacity:1;--tw-ring-color:rgb(204 251 241/var(--tw-ring-opacity))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:cursor-pointer:hover{cursor:pointer}.hover\:bg-danger-100\/80:hover{background-color:rgba(var(--danger-100),.8)}.hover\:bg-primary-50:hover{--tw-bg-opacity:1;background-color:rgba(var(--primary-50),var(--tw-bg-opacity))}.hover\:bg-primary-50\/50:hover{background-color:rgba(var(--primary-50),.5)}.hover\:underline:hover{text-decoration-line:underline}.group:hover .group-hover\:flex{display:flex}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/button:hover .group-hover\/button\:text-gray-500{--tw-text-opacity:1;color:rgba(var(--gray-500),var(--tw-text-opacity))}.group:hover .group-hover\:text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity))}.group:hover .group-hover\:text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}@media (min-width:768px){.md\:min-w-\[32rem\]{min-width:32rem}}:is(:where([dir=ltr]) .ltr\:rotate-90){--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is(:where([dir=rtl]) .rtl\:\!rotate-90){--tw-rotate:90deg!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}:is(:where([dir=rtl]) .rtl\:rotate-180){--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is(:where([dir=rtl]) .rtl\:space-x-reverse)>:not([hidden])~:not([hidden]){--tw-space-x-reverse:1}:is(:where([dir=rtl]) .rtl\:text-right){text-align:right}:is(:where(.dark) .dark\:divide-gray-600)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(var(--gray-600),var(--tw-divide-opacity))}:is(:where(.dark) .dark\:divide-white\/10)>:not([hidden])~:not([hidden]){border-color:#ffffff1a}:is(:where(.dark) .dark\:divide-white\/5)>:not([hidden])~:not([hidden]){border-color:#ffffff0d}:is(:where(.dark) .dark\:border-b){border-bottom-width:1px}:is(:where(.dark) .dark\:border-gray-600){--tw-border-opacity:1;border-color:rgba(var(--gray-600),var(--tw-border-opacity))}:is(:where(.dark) .dark\:border-white\/10){border-color:#ffffff1a}:is(:where(.dark) .dark\:border-t-white\/10){border-top-color:#ffffff1a}:is(:where(.dark) .dark\:bg-gray-600){--tw-bg-opacity:1;background-color:rgba(var(--gray-600),var(--tw-bg-opacity))}:is(:where(.dark) .dark\:bg-gray-800){--tw-bg-opacity:1;background-color:rgba(var(--gray-800),var(--tw-bg-opacity))}:is(:where(.dark) .dark\:bg-gray-900){--tw-bg-opacity:1;background-color:rgba(var(--gray-900),var(--tw-bg-opacity))}:is(:where(.dark) .dark\:bg-green-400\/10){background-color:#4ade801a}:is(:where(.dark) .dark\:bg-orange-400\/10){background-color:#fb923c1a}:is(:where(.dark) .dark\:bg-sky-400\/10){background-color:#38bdf81a}:is(:where(.dark) .dark\:bg-white\/10){background-color:#ffffff1a}:is(:where(.dark) .dark\:bg-white\/5){background-color:#ffffff0d}:is(:where(.dark) .dark\:\!text-red-400\/60){color:#f8717199!important}:is(:where(.dark) .dark\:text-gray-100){--tw-text-opacity:1;color:rgba(var(--gray-100),var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-gray-200){--tw-text-opacity:1;color:rgba(var(--gray-200),var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-gray-300){--tw-text-opacity:1;color:rgba(var(--gray-300),var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-gray-400){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-gray-500){--tw-text-opacity:1;color:rgba(var(--gray-500),var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-green-400){--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-green-400\/80){color:#4ade80cc}:is(:where(.dark) .dark\:text-orange-400){--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-primary-400){--tw-text-opacity:1;color:rgba(var(--primary-400),var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-primary-400\/80){color:rgba(var(--primary-400),.8)}:is(:where(.dark) .dark\:text-red-400\/80){color:#f87171cc}:is(:where(.dark) .dark\:text-sky-400){--tw-text-opacity:1;color:rgb(56 189 248/var(--tw-text-opacity))}:is(:where(.dark) .dark\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(:where(.dark) .dark\:ring-gray-600){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--gray-600),var(--tw-ring-opacity))}:is(:where(.dark) .dark\:ring-gray-700){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--gray-700),var(--tw-ring-opacity))}:is(:where(.dark) .dark\:ring-purple-800){--tw-ring-opacity:1;--tw-ring-color:rgb(107 33 168/var(--tw-ring-opacity))}:is(:where(.dark) .dark\:ring-sky-800){--tw-ring-opacity:1;--tw-ring-color:rgb(7 89 133/var(--tw-ring-opacity))}:is(:where(.dark) .dark\:ring-teal-800){--tw-ring-opacity:1;--tw-ring-color:rgb(17 94 89/var(--tw-ring-opacity))}:is(:where(.dark) .dark\:ring-white\/10){--tw-ring-color:#ffffff1a}:is(:where(.dark) .dark\:ring-white\/20){--tw-ring-color:#fff3}:is(:where(.dark) .dark\:hover\:bg-danger-300\/20:hover){background-color:rgba(var(--danger-300),.2)}:is(:where(.dark) .dark\:hover\:bg-white\/5:hover){background-color:#ffffff0d}:is(:where(.dark) .dark\:focus-visible\:ring-primary-500:focus-visible){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--primary-500),var(--tw-ring-opacity))}:is(:where(.dark) .group\/button:hover .dark\:group-hover\/button\:text-gray-400){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(:where(.dark) .group:hover .dark\:group-hover\:text-green-400){--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}:is(:where(.dark) .group:hover .dark\:group-hover\:text-red-400){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}.\[\&_table\]\:h-\[1px\] table{height:1px} \ No newline at end of file +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.18 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:rgba(var(--gray-200),1)}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family),ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:rgba(var(--gray-400),1)}input::placeholder,textarea::placeholder{opacity:1;color:rgba(var(--gray-400),1)}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}input::placeholder,textarea::placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media (forced-colors:active){[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}:root.dark{color-scheme:dark}[data-field-wrapper]{scroll-margin-top:8rem}.-left-\[calc\(0\.5rem_-_1px\)\]{left:calc(-.5rem - -1px)}.-left-\[calc\(0\.75rem_-_1px\)\]{left:calc(-.75rem - -1px)}.left-0{left:0}.left-5{left:1.25rem}.right-0{right:0}.top-\[2px\]{top:2px}.-my-8{margin-top:-2rem;margin-bottom:-2rem}.-ml-\[5px\]{margin-left:-5px}.-mt-3\.5{margin-top:-.875rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.ml-5{margin-left:1.25rem}.ml-7{margin-left:1.75rem}.ml-8{margin-left:2rem}.mt-4{margin-top:1rem}.flow-root{display:flow-root}.w-1\/3{width:33.333333%}.w-\[2px\]{width:2px}.min-w-\[50vw\]{min-width:50vw}.min-w-full{min-width:100%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-y-px{--tw-translate-y:-1px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\!cursor-default{cursor:default!important}.cursor-grab{cursor:grab}.scroll-mt-32{scroll-margin-top:8rem}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.place-content-center{place-content:center}.gap-0\.5{gap:.125rem}.gap-2\.5{gap:.625rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.divide-y-2>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(2px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(2px*var(--tw-divide-y-reverse))}.divide-gray-950\/10>:not([hidden])~:not([hidden]){border-color:rgba(var(--gray-950),.1)}.\!overflow-auto{overflow:auto!important}.\!rounded-full{border-radius:9999px!important}.rounded-b-lg{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.\!border-red-300{--tw-border-opacity:1!important;border-color:rgb(252 165 165/var(--tw-border-opacity,1))!important}.\!border-red-500{--tw-border-opacity:1!important;border-color:rgb(239 68 68/var(--tw-border-opacity,1))!important}.border-green-300{--tw-border-opacity:1;border-color:rgb(134 239 172/var(--tw-border-opacity,1))}.border-orange-300{--tw-border-opacity:1;border-color:rgb(253 186 116/var(--tw-border-opacity,1))}.border-sky-300{--tw-border-opacity:1;border-color:rgb(125 211 252/var(--tw-border-opacity,1))}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.\!bg-red-50{--tw-bg-opacity:1!important;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))!important}.bg-gray-300\/20{background-color:rgba(var(--gray-300),.2)}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity,1))}.bg-purple-500{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity,1))}.bg-sky-500{--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity,1))}.bg-teal-500{--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity,1))}.bg-white\/70{background-color:hsla(0,0%,100%,.7)}.\!p-0{padding:0!important}.\!p-3{padding:.75rem!important}.\!ps-6{padding-inline-start:1.5rem!important}.pl-2{padding-left:.5rem}.pl-8{padding-left:2rem}.pt-8{padding-top:2rem}.pt-\[1px\]{padding-top:1px}.pt-\[5px\]{padding-top:5px}.uppercase{text-transform:uppercase}.\!text-red-400\/60{color:hsla(0,91%,71%,.6)!important}.\!text-red-400\/80{color:hsla(0,91%,71%,.8)!important}.\!text-red-600{--tw-text-opacity:1!important;color:rgb(220 38 38/var(--tw-text-opacity,1))!important}.text-gray-900{--tw-text-opacity:1;color:rgba(var(--gray-900),var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-orange-600{--tw-text-opacity:1;color:rgb(234 88 12/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-sky-600{--tw-text-opacity:1;color:rgb(2 132 199/var(--tw-text-opacity,1))}.shadow,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.ring-1,.ring-4{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-gray-100{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--gray-100),var(--tw-ring-opacity,1))}.ring-purple-100{--tw-ring-opacity:1;--tw-ring-color:rgb(243 232 255/var(--tw-ring-opacity,1))}.ring-sky-100{--tw-ring-opacity:1;--tw-ring-color:rgb(224 242 254/var(--tw-ring-opacity,1))}.ring-teal-100{--tw-ring-opacity:1;--tw-ring-color:rgb(204 251 241/var(--tw-ring-opacity,1))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:cursor-pointer:hover{cursor:pointer}.hover\:bg-danger-100\/80:hover{background-color:rgba(var(--danger-100),.8)}.hover\:bg-primary-50:hover{--tw-bg-opacity:1;background-color:rgba(var(--primary-50),var(--tw-bg-opacity,1))}.hover\:bg-primary-50\/50:hover{background-color:rgba(var(--primary-50),.5)}.hover\:underline:hover{text-decoration-line:underline}.group:hover .group-hover\:flex{display:flex}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/button:hover .group-hover\/button\:text-gray-500{--tw-text-opacity:1;color:rgba(var(--gray-500),var(--tw-text-opacity,1))}.group:hover .group-hover\:text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.dark\:divide-gray-600:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgba(var(--gray-600),var(--tw-divide-opacity,1))}.dark\:border-b:is(.dark *){border-bottom-width:1px}.dark\:bg-green-400\/10:is(.dark *){background-color:rgba(74,222,128,.1)}.dark\:bg-orange-400\/10:is(.dark *){background-color:rgba(251,146,60,.1)}.dark\:bg-sky-400\/10:is(.dark *){background-color:rgba(56,189,248,.1)}.dark\:\!text-red-400\/60:is(.dark *){color:hsla(0,91%,71%,.6)!important}.dark\:text-gray-100:is(.dark *){--tw-text-opacity:1;color:rgba(var(--gray-100),var(--tw-text-opacity,1))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity:1;color:rgba(var(--gray-300),var(--tw-text-opacity,1))}.dark\:text-green-400:is(.dark *){--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.dark\:text-green-400\/80:is(.dark *){color:rgba(74,222,128,.8)}.dark\:text-orange-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.dark\:text-primary-400\/80:is(.dark *){color:rgba(var(--primary-400),.8)}.dark\:text-red-400\/80:is(.dark *){color:hsla(0,91%,71%,.8)}.dark\:text-sky-400:is(.dark *){--tw-text-opacity:1;color:rgb(56 189 248/var(--tw-text-opacity,1))}.dark\:ring-gray-600:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgba(var(--gray-600),var(--tw-ring-opacity,1))}.dark\:ring-purple-800:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(107 33 168/var(--tw-ring-opacity,1))}.dark\:ring-sky-800:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(7 89 133/var(--tw-ring-opacity,1))}.dark\:ring-teal-800:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(17 94 89/var(--tw-ring-opacity,1))}.dark\:hover\:bg-danger-300\/20:hover:is(.dark *){background-color:rgba(var(--danger-300),.2)}.group:hover .dark\:group-hover\:text-green-400:is(.dark *){--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.group:hover .dark\:group-hover\:text-red-400:is(.dark *){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}@media (min-width:768px){.md\:min-w-\[32rem\]{min-width:32rem}}.ltr\:rotate-90:where([dir=ltr],[dir=ltr] *){--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rtl\:\!rotate-90:where([dir=rtl],[dir=rtl] *){--tw-rotate:90deg!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.rtl\:space-x-reverse:where([dir=rtl],[dir=rtl] *)>:not([hidden])~:not([hidden]){--tw-space-x-reverse:1}.rtl\:text-right:where([dir=rtl],[dir=rtl] *){text-align:right}.\[\&_table\]\:h-\[1px\] table{height:1px} \ No newline at end of file diff --git a/packages/admin/resources/lang/de/relationmanagers.php b/packages/admin/resources/lang/de/relationmanagers.php index daca16dbfe..8ea3a15502 100644 --- a/packages/admin/resources/lang/de/relationmanagers.php +++ b/packages/admin/resources/lang/de/relationmanagers.php @@ -212,4 +212,20 @@ ], ], ], + 'values' => [ + 'title' => 'Werte', + 'form' => [ + 'name' => [ + 'label' => 'Name', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + 'position' => [ + 'label' => 'Position', + ], + ], + ], ]; diff --git a/packages/admin/resources/lang/de/tax.php b/packages/admin/resources/lang/de/tax.php new file mode 100644 index 0000000000..3de9113804 --- /dev/null +++ b/packages/admin/resources/lang/de/tax.php @@ -0,0 +1,9 @@ + 'Steuer', + + 'plural_label' => 'Steuern', + +]; diff --git a/packages/admin/resources/lang/de/taxrate.php b/packages/admin/resources/lang/de/taxrate.php new file mode 100644 index 0000000000..9363eaf449 --- /dev/null +++ b/packages/admin/resources/lang/de/taxrate.php @@ -0,0 +1,33 @@ + 'Steuersatz', + + 'plural_label' => 'Steuersätze', + + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + 'tax_zone' => [ + 'label' => 'Steuerzone', + ], + 'priority' => [ + 'label' => 'Priorität', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Name', + ], + 'priority' => [ + 'label' => 'Priorität', + ], + 'tax_zone_id' => [ + 'label' => 'Steuerzone', + ], + ], + +]; diff --git a/packages/admin/resources/lang/en/discount.php b/packages/admin/resources/lang/en/discount.php index 57ec0aff8a..3791acf136 100644 --- a/packages/admin/resources/lang/en/discount.php +++ b/packages/admin/resources/lang/en/discount.php @@ -283,8 +283,8 @@ ], ], 'conditions' => [ - 'title' => 'Conditions', - 'description' => 'Select the conditions required for the discount to apply.', + 'title' => 'Product and Variant Conditions', + 'description' => 'Select the product or variant conditions required for the discount to apply.', 'actions' => [ 'attach' => [ 'label' => 'Add Condition', @@ -317,6 +317,20 @@ ], ], ], + 'collection_conditions' => [ + 'title' => 'Collection Conditions', + 'description' => 'Select the collection conditions required for the discount to apply.', + 'actions' => [ + 'attach' => [ + 'label' => 'Add Condition', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + ], + ], 'productvariants' => [ 'title' => 'Product Variants', 'description' => 'Select which product variants this discount should be limited to.', diff --git a/packages/admin/resources/lang/en/product.php b/packages/admin/resources/lang/en/product.php index 6f02bc16e1..53bc4c9096 100644 --- a/packages/admin/resources/lang/en/product.php +++ b/packages/admin/resources/lang/en/product.php @@ -8,6 +8,8 @@ 'tabs' => [ 'all' => 'All', + 'published' => 'Published', + 'draft' => 'Draft', ], 'status' => [ diff --git a/packages/admin/resources/lang/en/relationmanagers.php b/packages/admin/resources/lang/en/relationmanagers.php index bbaa0b3270..e9101309f5 100644 --- a/packages/admin/resources/lang/en/relationmanagers.php +++ b/packages/admin/resources/lang/en/relationmanagers.php @@ -272,6 +272,11 @@ ], 'values' => [ 'title' => 'Values', + 'form' => [ + 'name' => [ + 'label' => 'Name', + ], + ], 'table' => [ 'name' => [ 'label' => 'Name', diff --git a/packages/admin/resources/lang/es/relationmanagers.php b/packages/admin/resources/lang/es/relationmanagers.php index a470c8364b..991f3911ad 100644 --- a/packages/admin/resources/lang/es/relationmanagers.php +++ b/packages/admin/resources/lang/es/relationmanagers.php @@ -263,6 +263,11 @@ ], 'values' => [ 'title' => 'Valores', + 'form' => [ + 'name' => [ + 'label' => 'Nombre', + ], + ], 'table' => [ 'name' => [ 'label' => 'Nombre', diff --git a/packages/admin/resources/lang/es/tax.php b/packages/admin/resources/lang/es/tax.php new file mode 100644 index 0000000000..fe230731ca --- /dev/null +++ b/packages/admin/resources/lang/es/tax.php @@ -0,0 +1,9 @@ + 'Impuesto', + + 'plural_label' => 'Impuestos', + +]; diff --git a/packages/admin/resources/lang/fr/relationmanagers.php b/packages/admin/resources/lang/fr/relationmanagers.php index 93ce6df69f..419dbedb19 100644 --- a/packages/admin/resources/lang/fr/relationmanagers.php +++ b/packages/admin/resources/lang/fr/relationmanagers.php @@ -263,6 +263,11 @@ ], 'values' => [ 'title' => 'Valeurs', + 'form' => [ + 'name' => [ + 'label' => 'Nom', + ], + ], 'table' => [ 'name' => [ 'label' => 'Nom', diff --git a/packages/admin/resources/lang/fr/tax.php b/packages/admin/resources/lang/fr/tax.php new file mode 100644 index 0000000000..cc480a4776 --- /dev/null +++ b/packages/admin/resources/lang/fr/tax.php @@ -0,0 +1,9 @@ + 'Taxe', + + 'plural_label' => 'Taxes', + +]; diff --git a/packages/admin/resources/lang/hu/actions.php b/packages/admin/resources/lang/hu/actions.php new file mode 100644 index 0000000000..167c67bc18 --- /dev/null +++ b/packages/admin/resources/lang/hu/actions.php @@ -0,0 +1,52 @@ + [ + 'create_root' => [ + 'label' => 'Gyökérgyűjtemény létrehozása', + ], + 'create_child' => [ + 'label' => 'Algyűjtemény létrehozása', + ], + 'move' => [ + 'label' => 'Gyűjtemény áthelyezése', + ], + 'delete' => [ + 'label' => 'Törlés', + 'notifications' => [ + 'cannot_delete' => [ + 'title' => 'Nem törölhető', + 'body' => 'Ennek a gyűjteménynek vannak algyűjteményei, ezért nem törölhető.', + ], + ], + ], + ], + 'orders' => [ + 'update_status' => [ + 'label' => 'Állapot frissítése', + 'wizard' => [ + 'step_one' => [ + 'label' => 'Állapot', + ], + 'step_two' => [ + 'label' => 'E-mailek és értesítések', + 'no_mailers' => 'Ehhez az állapothoz nincs elérhető e-mail sablon.', + ], + 'step_three' => [ + 'label' => 'Előnézet és mentés', + 'no_mailers' => 'Nincs kiválasztva e-mail előnézethez.', + ], + ], + 'notification' => [ + 'label' => 'A rendelés állapota frissítve', + ], + 'billing_email' => [ + 'label' => 'Számlázási e-mail', + ], + 'shipping_email' => [ + 'label' => 'Szállítási e-mail', + ], + ], + + ], +]; diff --git a/packages/admin/resources/lang/hu/activity.php b/packages/admin/resources/lang/hu/activity.php new file mode 100644 index 0000000000..d05ee82432 --- /dev/null +++ b/packages/admin/resources/lang/hu/activity.php @@ -0,0 +1,29 @@ + 'Tevékenység', + + 'plural_label' => 'Tevékenységek', + + 'table' => [ + 'subject' => 'Tárgy', + 'description' => 'Leírás', + 'log' => 'Napló', + 'logged_at' => 'Rögzítés ideje', + 'event' => 'Esemény', + 'logged_from' => 'Naplózva innen', + 'logged_until' => 'Naplózva eddig', + ], + + 'form' => [ + 'causer_type' => 'Okozó típusa', + 'causer_id' => 'Okozó azonosító', + 'subject_type' => 'Tárgy típusa', + 'subject_id' => 'Tárgy azonosító', + 'description' => 'Leírás', + 'attributes' => 'Attribútumok', + 'old' => 'Régi', + ], + +]; diff --git a/packages/admin/resources/lang/hu/address.php b/packages/admin/resources/lang/hu/address.php new file mode 100644 index 0000000000..a6b2814730 --- /dev/null +++ b/packages/admin/resources/lang/hu/address.php @@ -0,0 +1,99 @@ + 'Cím', + + 'plural_label' => 'Címek', + + 'table' => [ + 'title' => [ + 'label' => 'Cím', + ], + 'first_name' => [ + 'label' => 'Keresztnév', + ], + 'last_name' => [ + 'label' => 'Vezetéknév', + ], + 'company_name' => [ + 'label' => 'Cégnév', + ], + 'tax_identifier' => [ + 'label' => 'Adóazonosító', + ], + 'line_one' => [ + 'label' => 'Utca, házszám', + ], + 'line_two' => [ + 'label' => 'Emelet, ajtó', + ], + 'line_three' => [ + 'label' => 'Egyéb címadat', + ], + 'city' => [ + 'label' => 'Város', + ], + 'country_id' => [ + 'label' => 'Ország', + ], + 'state' => [ + 'label' => 'Megye', + ], + 'postcode' => [ + 'label' => 'Irányítószám', + ], + 'contact_email' => [ + 'label' => 'Kapcsolattartó e-mail', + ], + 'contact_phone' => [ + 'label' => 'Kapcsolattartó telefonszám', + ], + ], + + 'form' => [ + 'title' => [ + 'label' => 'Cím', + ], + 'first_name' => [ + 'label' => 'Keresztnév', + ], + 'last_name' => [ + 'label' => 'Vezetéknév', + ], + 'company_name' => [ + 'label' => 'Cégnév', + ], + 'tax_identifier' => [ + 'label' => 'Adóazonosító', + ], + 'line_one' => [ + 'label' => 'Utca, házszám', + ], + 'line_two' => [ + 'label' => 'Emelet, ajtó', + ], + 'line_three' => [ + 'label' => 'Egyéb címadat', + ], + 'city' => [ + 'label' => 'Város', + ], + 'country_id' => [ + 'label' => 'Ország', + ], + 'state' => [ + 'label' => 'Megye', + ], + 'postcode' => [ + 'label' => 'Irányítószám', + ], + 'contact_email' => [ + 'label' => 'Kapcsolattartó e-mail', + ], + 'contact_phone' => [ + 'label' => 'Kapcsolattartó telefonszám', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/attribute.php b/packages/admin/resources/lang/hu/attribute.php new file mode 100644 index 0000000000..7d9c496f2a --- /dev/null +++ b/packages/admin/resources/lang/hu/attribute.php @@ -0,0 +1,55 @@ + 'Attribútum', + + 'plural_label' => 'Attribútumok', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'description' => [ + 'label' => 'Leírás', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'type' => [ + 'label' => 'Típus', + ], + ], + + 'form' => [ + 'attributable_type' => [ + 'label' => 'Típus', + ], + 'name' => [ + 'label' => 'Név', + ], + 'description' => [ + 'label' => 'Leírás', + 'helper' => 'Használja a bejegyzés alatt a súgó szöveg megjelenítéséhez.', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'searchable' => [ + 'label' => 'Kereshető', + ], + 'filterable' => [ + 'label' => 'Szűrhető', + ], + 'required' => [ + 'label' => 'Kötelező', + ], + 'type' => [ + 'label' => 'Típus', + ], + 'validation_rules' => [ + 'label' => 'Érvényesítési szabályok', + 'helper' => 'Szabályok az attribútum mezőhöz, pl.: min:1|max:10|...', + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/attributegroup.php b/packages/admin/resources/lang/hu/attributegroup.php new file mode 100644 index 0000000000..110d543b6c --- /dev/null +++ b/packages/admin/resources/lang/hu/attributegroup.php @@ -0,0 +1,46 @@ + 'Attribútumcsoport', + + 'plural_label' => 'Attribútumcsoportok', + + 'table' => [ + 'attributable_type' => [ + 'label' => 'Típus', + ], + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'position' => [ + 'label' => 'Pozíció', + ], + ], + + 'form' => [ + 'attributable_type' => [ + 'label' => 'Típus', + ], + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'position' => [ + 'label' => 'Pozíció', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Ez az attribútumcsoport nem törölhető, mivel vannak hozzá kapcsolódó attribútumok.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/auth.php b/packages/admin/resources/lang/hu/auth.php new file mode 100644 index 0000000000..47a96206bb --- /dev/null +++ b/packages/admin/resources/lang/hu/auth.php @@ -0,0 +1,32 @@ + 'Admin', + 'roles.admin.description' => 'Teljes hozzáféréssel rendelkező admin', + 'roles.staff.label' => 'Munkatárs', + 'roles.staff.description' => 'Alapvető jogosultságokkal rendelkező munkatárs', + /** + * Permissions. + */ + 'permissions.settings.label' => 'Beállítások', + 'permissions.settings.description' => 'Hozzáférést ad a Hub beállítási területéhez', + 'permissions.settings:core.label' => 'Alapbeállítások', + 'permissions.settings:core.description' => 'Hozzáférés az alapvető áruházbeállításokhoz (csatornák, nyelvek, pénznemek stb.)', + 'permissions.settings:manage-staff.label' => 'Munkatársak kezelése', + 'permissions.settings:manage-staff.description' => 'Engedélyezi más munkatársak adatainak szerkesztését', + 'permissions.settings:manage-attributes.label' => 'Attribútumok kezelése', + 'permissions.settings:manage-attributes.description' => 'Engedélyezi további attribútumok létrehozását és szerkesztését', + 'permissions.catalog:manage-products.label' => 'Termékek kezelése', + 'permissions.catalog:manage-products.description' => 'Engedélyezi a termékek, terméktípusok és márkák szerkesztését', + 'permissions.catalog:manage-collections.label' => 'Kollekciók kezelése', + 'permissions.catalog:manage-collections.description' => 'Engedélyezi a kollekciók és azok csoportjainak szerkesztését', + 'permissions.sales:manage-orders.label' => 'Rendelések kezelése', + 'permissions.sales:manage-orders.description' => 'Engedélyezi a rendelések kezelését', + 'permissions.sales:manage-customers.label' => 'Vásárlók kezelése', + 'permissions.sales:manage-customers.description' => 'Engedélyezi a vásárlók kezelését', + 'permissions.sales:manage-discounts.label' => 'Kedvezmények kezelése', + 'permissions.sales:manage-discounts.description' => 'Engedélyezi a kedvezmények kezelését', +]; diff --git a/packages/admin/resources/lang/hu/brand.php b/packages/admin/resources/lang/hu/brand.php new file mode 100644 index 0000000000..529f213ce6 --- /dev/null +++ b/packages/admin/resources/lang/hu/brand.php @@ -0,0 +1,75 @@ + 'Márka', + + 'plural_label' => 'Márkák', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'products_count' => [ + 'label' => 'Termékek száma', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'A márka nem törölhető, mert termékek vannak hozzárendelve.', + ], + ], + ], + 'pages' => [ + 'edit' => [ + 'title' => 'Alapvető információk', + ], + 'products' => [ + 'label' => 'Termékek', + 'actions' => [ + 'attach' => [ + 'label' => 'Termék hozzáadása', + 'form' => [ + 'record_id' => [ + 'label' => 'Termék', + ], + ], + 'notification' => [ + 'success' => 'Termék hozzárendelve a márkához', + ], + ], + 'detach' => [ + 'notification' => [ + 'success' => 'Termék leválasztva.', + ], + ], + ], + ], + 'collections' => [ + 'label' => 'Gyűjtemények', + 'table' => [ + 'header_actions' => [ + 'attach' => [ + 'record_select' => [ + 'placeholder' => 'Válassz gyűjteményt', + ], + ], + ], + ], + 'actions' => [ + 'attach' => [ + 'label' => 'Gyűjtemény hozzárendelése', + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/channel.php b/packages/admin/resources/lang/hu/channel.php new file mode 100644 index 0000000000..7d5ae05f65 --- /dev/null +++ b/packages/admin/resources/lang/hu/channel.php @@ -0,0 +1,39 @@ + 'Csatorna', + + 'plural_label' => 'Csatornák', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'url' => [ + 'label' => 'URL', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'url' => [ + 'label' => 'URL', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/collection.php b/packages/admin/resources/lang/hu/collection.php new file mode 100644 index 0000000000..05f845d7c0 --- /dev/null +++ b/packages/admin/resources/lang/hu/collection.php @@ -0,0 +1,45 @@ + 'Gyűjtemény', + + 'plural_label' => 'Gyűjtemények', + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + ], + + 'pages' => [ + 'children' => [ + 'label' => 'Algyűjtemények', + 'actions' => [ + 'create_child' => [ + 'label' => 'Algyűjtemény létrehozása', + ], + ], + 'table' => [ + 'children_count' => [ + 'label' => 'Gyermekek száma', + ], + 'name' => [ + 'label' => 'Név', + ], + ], + ], + 'edit' => [ + 'label' => 'Alapvető információk', + ], + 'products' => [ + 'label' => 'Termékek', + 'actions' => [ + 'attach' => [ + 'label' => 'Termék csatolása', + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/collectiongroup.php b/packages/admin/resources/lang/hu/collectiongroup.php new file mode 100644 index 0000000000..3f9c625647 --- /dev/null +++ b/packages/admin/resources/lang/hu/collectiongroup.php @@ -0,0 +1,37 @@ + 'Gyűjteménycsoport', + + 'plural_label' => 'Gyűjteménycsoportok', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'collections_count' => [ + 'label' => 'Gyűjtemények száma', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Ez a gyűjteménycsoport nem törölhető, mert gyűjtemények kapcsolódnak hozzá.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/components.php b/packages/admin/resources/lang/hu/components.php new file mode 100644 index 0000000000..cf52e142a2 --- /dev/null +++ b/packages/admin/resources/lang/hu/components.php @@ -0,0 +1,111 @@ + [ + 'notification' => [ + 'updated' => 'Címkék frissítve', + ], + ], + + 'activity-log' => [ + + 'input' => [ + 'placeholder' => 'Megjegyzés hozzáadása', + ], + + 'action' => [ + 'add-comment' => 'Megjegyzés hozzáadása', + ], + + 'system' => 'Rendszer', + + 'partials' => [ + 'orders' => [ + 'order_created' => 'Rendelés létrehozva', + + 'status_change' => 'Státusz frissítve', + + 'capture' => 'Fizetés: :amount, kártya utolsó négy számjegye: :last_four', + + 'authorized' => 'Engedélyezve: :amount, kártya utolsó négy számjegye: :last_four', + + 'refund' => 'Visszatérítés: :amount, kártya utolsó négy számjegye: :last_four', + + 'address' => ':type frissítve', + + 'billingAddress' => 'Számlázási cím', + + 'shippingAddress' => 'Szállítási cím', + ], + + 'update' => [ + 'updated' => ':model frissítve', + ], + + 'create' => [ + 'created' => ':model létrehozva', + ], + + 'tags' => [ + 'updated' => 'Címkék frissítve', + 'added' => 'Hozzáadva', + 'removed' => 'Eltávolítva', + ], + ], + + 'notification' => [ + 'comment_added' => 'Megjegyzés hozzáadva', + ], + + ], + + 'forms' => [ + 'youtube' => [ + 'helperText' => 'Add meg a YouTube videó azonosítóját. pl.: dQw4w9WgXcQ', + ], + ], + + 'collection-tree-view' => [ + 'actions' => [ + 'move' => [ + 'form' => [ + 'target_id' => [ + 'label' => 'Szülő gyűjtemény', + ], + ], + ], + ], + 'notifications' => [ + 'collections-reordered' => [ + 'success' => 'Gyűjtemények át-rendezve', + ], + 'node-expanded' => [ + 'danger' => 'Nem lehet betölteni a gyűjteményeket', + ], + 'delete' => [ + 'danger' => 'Nem lehet törölni a gyűjteményt', + ], + ], + ], + + 'product-options-list' => [ + 'add-option' => [ + 'label' => 'Opció hozzáadása', + ], + 'delete-option' => [ + 'label' => 'Opció törlése', + ], + 'remove-shared-option' => [ + 'label' => 'Megosztott opció eltávolítása', + ], + 'add-value' => [ + 'label' => 'Új érték hozzáadása', + ], + 'name' => [ + 'label' => 'Név', + ], + 'values' => [ + 'label' => 'Értékek', + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/currency.php b/packages/admin/resources/lang/hu/currency.php new file mode 100644 index 0000000000..757aa1b268 --- /dev/null +++ b/packages/admin/resources/lang/hu/currency.php @@ -0,0 +1,58 @@ + 'Pénznem', + + 'plural_label' => 'Pénznemek', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'code' => [ + 'label' => 'Kód', + ], + 'exchange_rate' => [ + 'label' => 'Átváltási árfolyam', + ], + 'decimal_places' => [ + 'label' => 'Tizedesjegyek', + ], + 'enabled' => [ + 'label' => 'Engedélyezve', + ], + 'sync_prices' => [ + 'label' => 'Árak szinkronizálása', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'code' => [ + 'label' => 'Kód', + ], + 'exchange_rate' => [ + 'label' => 'Átváltási árfolyam', + ], + 'decimal_places' => [ + 'label' => 'Tizedesjegyek', + ], + 'enabled' => [ + 'label' => 'Engedélyezve', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + 'sync_prices' => [ + 'label' => 'Árak szinkronizálása', + 'helper_text' => 'Tartsa ennek a pénznemnek az árait szinkronban az alapértelmezett pénznem áraival.', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/customer.php b/packages/admin/resources/lang/hu/customer.php new file mode 100644 index 0000000000..7577e8fa4a --- /dev/null +++ b/packages/admin/resources/lang/hu/customer.php @@ -0,0 +1,63 @@ + 'Vásárló', + + 'plural_label' => 'Vásárlók', + + 'table' => [ + 'full_name' => [ + 'label' => 'Név', + ], + 'first_name' => [ + 'label' => 'Keresztnév', + ], + 'last_name' => [ + 'label' => 'Vezetéknév', + ], + 'title' => [ + 'label' => 'Cím', + ], + 'company_name' => [ + 'label' => 'Cégnév', + ], + 'tax_identifier' => [ + 'label' => 'Adóazonosító', + ], + 'account_reference' => [ + 'label' => 'Ügyfélszám', + ], + 'new' => [ + 'label' => 'Új', + ], + 'returning' => [ + 'label' => 'Visszatérő', + ], + ], + + 'form' => [ + 'title' => [ + 'label' => 'Cím', + ], + 'first_name' => [ + 'label' => 'Keresztnév', + ], + 'last_name' => [ + 'label' => 'Vezetéknév', + ], + 'company_name' => [ + 'label' => 'Cégnév', + ], + 'account_ref' => [ + 'label' => 'Ügyfélszám', + ], + 'tax_identifier' => [ + 'label' => 'Adóazonosító', + ], + 'customer_groups' => [ + 'label' => 'Vásárlói csoportok', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/customergroup.php b/packages/admin/resources/lang/hu/customergroup.php new file mode 100644 index 0000000000..ea1d9c7999 --- /dev/null +++ b/packages/admin/resources/lang/hu/customergroup.php @@ -0,0 +1,40 @@ + 'Vásárlói csoport', + + 'plural_label' => 'Vásárlói csoportok', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Ez a vásárlói csoport nem törölhető, mert vásárlók kapcsolódnak hozzá.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/discount.php b/packages/admin/resources/lang/hu/discount.php new file mode 100644 index 0000000000..7dbd27e7a2 --- /dev/null +++ b/packages/admin/resources/lang/hu/discount.php @@ -0,0 +1,353 @@ + 'Kedvezmények', + 'label' => 'Kedvezmény', + 'form' => [ + 'conditions' => [ + 'heading' => 'Feltételek', + ], + 'buy_x_get_y' => [ + 'heading' => 'Vásárolj X-et, kapj Y-t', + ], + 'amount_off' => [ + 'heading' => 'Összeg alapú kedvezmény', + ], + 'name' => [ + 'label' => 'Név', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'starts_at' => [ + 'label' => 'Kezdés dátuma', + ], + 'ends_at' => [ + 'label' => 'Befejezés dátuma', + ], + 'priority' => [ + 'label' => 'Prioritás', + 'helper_text' => 'A magasabb prioritású kedvezmények kerülnek először alkalmazásra.', + 'options' => [ + 'low' => [ + 'label' => 'Alacsony', + ], + 'medium' => [ + 'label' => 'Közepes', + ], + 'high' => [ + 'label' => 'Magas', + ], + ], + ], + 'stop' => [ + 'label' => 'Más kedvezmények alkalmazásának megakadályozása ez után', + ], + 'coupon' => [ + 'label' => 'Kupon', + 'helper_text' => 'Add meg a kuponkódot, amely szükséges a kedvezmény alkalmazásához; üresen hagyva automatikusan alkalmazódik.', + ], + 'max_uses' => [ + 'label' => 'Max felhasználás', + 'helper_text' => 'Hagyd üresen a korlátlan felhasználáshoz.', + ], + 'max_uses_per_user' => [ + 'label' => 'Max felhasználás felhasználónként', + 'helper_text' => 'Hagyd üresen a korlátlan felhasználáshoz.', + ], + 'minimum_cart_amount' => [ + 'label' => 'Minimum kosárérték', + ], + 'min_qty' => [ + 'label' => 'Termék mennyisége', + 'helper_text' => 'Add meg, hány jogosult termék szükséges a kedvezmény érvényesítéséhez.', + ], + 'reward_qty' => [ + 'label' => 'Ingyenes tételek száma', + 'helper_text' => 'Termékenként hány darab jár ingyen.', + ], + 'max_reward_qty' => [ + 'label' => 'Maximális jutalom mennyiség', + 'helper_text' => 'A maximális termékmennyiség, amely kedvezményben részesíthető, függetlenül a feltételektől.', + ], + 'automatic_rewards' => [ + 'label' => 'Jutalmak automatikus hozzáadása', + 'helper_text' => 'Kapcsolja be, hogy a jutalomtermékek automatikusan hozzáadódjanak, ha nincsenek a kosárban.', + ], + 'fixed_value' => [ + 'label' => 'Rögzített érték', + ], + 'percentage' => [ + 'label' => 'Százalék', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'status' => [ + 'label' => 'Státusz', + \Lunar\Models\Discount::ACTIVE => [ + 'label' => 'Aktív', + ], + \Lunar\Models\Discount::PENDING => [ + 'label' => 'Függőben', + ], + \Lunar\Models\Discount::EXPIRED => [ + 'label' => 'Lejárt', + ], + \Lunar\Models\Discount::SCHEDULED => [ + 'label' => 'Ütemezett', + ], + ], + 'type' => [ + 'label' => 'Típus', + ], + 'starts_at' => [ + 'label' => 'Kezdés dátuma', + ], + 'ends_at' => [ + 'label' => 'Befejezés dátuma', + ], + 'created_at' => [ + 'label' => 'Létrehozva', + ], + 'coupon' => [ + 'label' => 'Kupon', + ], + ], + 'pages' => [ + 'availability' => [ + 'label' => 'Elérhetőség', + ], + 'edit' => [ + 'title' => 'Alapinformációk', + ], + 'limitations' => [ + 'label' => 'Korlátozások', + ], + ], + 'relationmanagers' => [ + 'collections' => [ + 'title' => 'Gyűjtemények', + 'description' => 'Válaszd ki, mely gyűjteményekre legyen érvényes ez a kedvezmény.', + 'actions' => [ + 'attach' => [ + 'label' => 'Gyűjtemény csatolása', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'type' => [ + 'label' => 'Típus', + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + ], + ], + 'customers' => [ + 'title' => 'Vásárlók', + 'description' => 'Válaszd ki, mely vásárlókra legyen érvényes ez a kedvezmény.', + 'actions' => [ + 'attach' => [ + 'label' => 'Vásárló csatolása', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + ], + ], + 'brands' => [ + 'title' => 'Márkák', + 'description' => 'Válaszd ki, mely márkákra legyen érvényes ez a kedvezmény.', + 'actions' => [ + 'attach' => [ + 'label' => 'Márka csatolása', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'type' => [ + 'label' => 'Típus', + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + ], + ], + 'products' => [ + 'title' => 'Termékek', + 'description' => 'Válaszd ki, mely termékekre legyen érvényes ez a kedvezmény.', + 'actions' => [ + 'attach' => [ + 'label' => 'Termék hozzáadása', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'type' => [ + 'label' => 'Típus', + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + ], + ], + 'rewards' => [ + 'title' => 'Jutalmak', + 'description' => 'Válaszd ki, mely termékek kapnak kedvezményt, ha a kosárban megtalálhatók és a feltételek teljesülnek.', + 'actions' => [ + 'attach' => [ + 'label' => 'Jutalom hozzáadása', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'type' => [ + 'label' => 'Típus', + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + ], + ], + 'conditions' => [ + 'title' => 'Feltételek', + 'description' => 'Válaszd ki azokat a feltételeket, amelyek szükségesek a kedvezmény alkalmazásához.', + 'actions' => [ + 'attach' => [ + 'label' => 'Feltétel hozzáadása', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'type' => [ + 'label' => 'Típus', + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + ], + ], + 'productvariants' => [ + 'title' => 'Termékváltozatok', + 'description' => 'Válaszd ki, mely termékváltozatokra legyen érvényes ez a kedvezmény.', + 'actions' => [ + 'attach' => [ + 'label' => 'Termékváltozat hozzáadása', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'sku' => [ + 'label' => 'Cikkszám (SKU)', + ], + 'values' => [ + 'label' => 'Opció(k)', + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Korlátozás', + ], + 'exclusion' => [ + 'label' => 'Kizárás', + ], + ], + ], + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/fieldtypes.php b/packages/admin/resources/lang/hu/fieldtypes.php new file mode 100644 index 0000000000..0d9fde633b --- /dev/null +++ b/packages/admin/resources/lang/hu/fieldtypes.php @@ -0,0 +1,72 @@ + [ + 'label' => 'Lenyíló lista', + 'form' => [ + 'lookups' => [ + 'label' => 'Értékkészlet', + 'key_label' => 'Címke', + 'value_label' => 'Érték', + ], + ], + ], + 'listfield' => [ + 'label' => 'Lista mező', + ], + 'text' => [ + 'label' => 'Szöveg', + 'form' => [ + 'richtext' => [ + 'label' => 'Formázott szöveg', + ], + ], + ], + 'translatedtext' => [ + 'label' => 'Többnyelvű szöveg', + 'form' => [ + 'richtext' => [ + 'label' => 'Formázott szöveg', + ], + 'locales' => 'Nyelvek', + ], + ], + 'toggle' => [ + 'label' => 'Kapcsoló', + ], + 'youtube' => [ + 'label' => 'YouTube', + ], + 'vimeo' => [ + 'label' => 'Vimeo', + ], + 'number' => [ + 'label' => 'Szám', + 'form' => [ + 'min' => [ + 'label' => 'Min.', + ], + 'max' => [ + 'label' => 'Max.', + ], + ], + ], + 'file' => [ + 'label' => 'Fájl', + 'form' => [ + 'file_types' => [ + 'label' => 'Engedélyezett fájltípusok', + 'placeholder' => 'Új MIME', + ], + 'multiple' => [ + 'label' => 'Több fájl engedélyezése', + ], + 'min_files' => [ + 'label' => 'Min. fájlok száma', + ], + 'max_files' => [ + 'label' => 'Max. fájlok száma', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/global.php b/packages/admin/resources/lang/hu/global.php new file mode 100644 index 0000000000..a0bd50bdd0 --- /dev/null +++ b/packages/admin/resources/lang/hu/global.php @@ -0,0 +1,12 @@ + [ + 'catalog' => 'Katalógus', + 'sales' => 'Eladások', + 'reports' => 'Jelentések', + 'settings' => 'Beállítások', + ], + +]; diff --git a/packages/admin/resources/lang/hu/language.php b/packages/admin/resources/lang/hu/language.php new file mode 100644 index 0000000000..857df00487 --- /dev/null +++ b/packages/admin/resources/lang/hu/language.php @@ -0,0 +1,33 @@ + 'Nyelv', + + 'plural_label' => 'Nyelvek', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'code' => [ + 'label' => 'Kód', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'code' => [ + 'label' => 'Kód', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/order.php b/packages/admin/resources/lang/hu/order.php new file mode 100644 index 0000000000..6a5eae9dfc --- /dev/null +++ b/packages/admin/resources/lang/hu/order.php @@ -0,0 +1,305 @@ + 'Rendelés', + + 'plural_label' => 'Rendelések', + + 'breadcrumb' => [ + 'manage' => 'Kezelés', + ], + + 'tabs' => [ + 'all' => 'Mind', + ], + + 'transactions' => [ + 'capture' => 'Lekönyvelve', + 'intent' => 'Fizetési szándék', + 'refund' => 'Visszatérítve', + 'failed' => 'Sikertelen', + ], + + 'table' => [ + 'status' => [ + 'label' => 'Státusz', + ], + 'reference' => [ + 'label' => 'Hivatkozás', + ], + 'customer_reference' => [ + 'label' => 'Vásárlói azonosító', + ], + 'customer' => [ + 'label' => 'Vásárló', + ], + 'tags' => [ + 'label' => 'Címkék', + ], + 'postcode' => [ + 'label' => 'Irányítószám', + ], + 'email' => [ + 'label' => 'E-mail', + 'copy_message' => 'E-mail cím másolva', + ], + 'phone' => [ + 'label' => 'Telefon', + ], + 'total' => [ + 'label' => 'Végösszeg', + ], + 'date' => [ + 'label' => 'Dátum', + ], + 'new_customer' => [ + 'label' => 'Vásárló típusa', + ], + 'placed_after' => [ + 'label' => 'Rendelés ideje után', + ], + 'placed_before' => [ + 'label' => 'Rendelés ideje előtt', + ], + ], + + 'form' => [ + 'address' => [ + 'first_name' => [ + 'label' => 'Keresztnév', + ], + 'last_name' => [ + 'label' => 'Vezetéknév', + ], + 'line_one' => [ + 'label' => 'Utca, házszám', + ], + 'line_two' => [ + 'label' => 'Emelet, ajtó', + ], + 'line_three' => [ + 'label' => 'Egyéb címadat', + ], + 'company_name' => [ + 'label' => 'Cégnév', + ], + 'tax_identifier' => [ + 'label' => 'Adóazonosító', + ], + 'contact_phone' => [ + 'label' => 'Telefon', + ], + 'contact_email' => [ + 'label' => 'E-mail cím', + ], + 'city' => [ + 'label' => 'Város', + ], + 'state' => [ + 'label' => 'Megye / Tartomány', + ], + 'postcode' => [ + 'label' => 'Irányítószám', + ], + 'country_id' => [ + 'label' => 'Ország', + ], + ], + + 'reference' => [ + 'label' => 'Hivatkozás', + ], + 'status' => [ + 'label' => 'Státusz', + ], + 'transaction' => [ + 'label' => 'Tranzakció', + ], + 'amount' => [ + 'label' => 'Összeg', + + 'hint' => [ + 'less_than_total' => 'Ön kevesebb összeget készül lekönyvelni, mint a teljes tranzakció értéke', + ], + ], + + 'notes' => [ + 'label' => 'Megjegyzések', + ], + 'confirm' => [ + 'label' => 'Megerősítés', + + 'alert' => 'Megerősítés szükséges', + + 'hint' => [ + 'capture' => 'Kérjük, erősítse meg, hogy le akarja könyvelni ezt a fizetést', + 'refund' => 'Kérjük, erősítse meg, hogy vissza kívánja téríteni ezt az összeget.', + ], + ], + ], + + 'infolist' => [ + 'notes' => [ + 'label' => 'Megjegyzések', + 'placeholder' => 'Nincsenek megjegyzések ehhez a rendeléshez', + ], + 'delivery_instructions' => [ + 'label' => 'Szállítási utasítások', + ], + 'shipping_total' => [ + 'label' => 'Szállítási díj', + ], + 'paid' => [ + 'label' => 'Kifizetve', + ], + 'refund' => [ + 'label' => 'Visszatérítés', + ], + 'unit_price' => [ + 'label' => 'Egységár', + ], + 'quantity' => [ + 'label' => 'Mennyiség', + ], + 'sub_total' => [ + 'label' => 'Részösszeg', + ], + 'discount_total' => [ + 'label' => 'Kedvezmény összege', + ], + 'total' => [ + 'label' => 'Végösszeg', + ], + 'current_stock_level' => [ + 'message' => 'Jelenlegi készletszint: :count', + ], + 'purchase_stock_level' => [ + 'message' => 'rendelés idején: :count', + ], + 'status' => [ + 'label' => 'Státusz', + ], + 'reference' => [ + 'label' => 'Hivatkozás', + ], + 'customer_reference' => [ + 'label' => 'Vásárlói azonosító', + ], + 'channel' => [ + 'label' => 'Csatorna', + ], + 'date_created' => [ + 'label' => 'Létrehozva', + ], + 'date_placed' => [ + 'label' => 'Rendelés dátuma', + ], + 'new_returning' => [ + 'label' => 'Új / Visszatérő', + ], + 'new_customer' => [ + 'label' => 'Új vásárló', + ], + 'returning_customer' => [ + 'label' => 'Visszatérő vásárló', + ], + 'shipping_address' => [ + 'label' => 'Szállítási cím', + ], + 'billing_address' => [ + 'label' => 'Számlázási cím', + ], + 'address_not_set' => [ + 'label' => 'Nincs cím megadva', + ], + 'billing_matches_shipping' => [ + 'label' => 'Ugyanaz, mint a szállítási cím', + ], + 'additional_info' => [ + 'label' => 'További információ', + ], + 'no_additional_info' => [ + 'label' => 'Nincs további információ', + ], + 'tags' => [ + 'label' => 'Címkék', + ], + 'timeline' => [ + 'label' => 'Idővonal', + ], + 'transactions' => [ + 'label' => 'Tranzakciók', + 'placeholder' => 'Nincsenek tranzakciók', + ], + 'alert' => [ + 'requires_capture' => 'Ennél a rendelésnél még fizetést kell lekönyvelni.', + 'partially_refunded' => 'Ennél a rendelésnél részleges visszatérítés történt.', + 'refunded' => 'Ennél a rendelésnél visszatérítés történt.', + ], + ], + + 'action' => [ + 'bulk_update_status' => [ + 'label' => 'Státusz frissítése', + 'notification' => 'Rendelések státusza frissítve', + ], + 'update_status' => [ + 'new_status' => [ + 'label' => 'Új státusz', + ], + 'additional_content' => [ + 'label' => 'További tartalom', + ], + 'additional_email_recipient' => [ + 'label' => 'További e-mail címzett', + 'placeholder' => 'opcionális', + ], + ], + 'download_order_pdf' => [ + 'label' => 'PDF letöltése', + 'notification' => 'Rendelés PDF letöltése', + ], + 'edit_address' => [ + 'label' => 'Szerkesztés', + + 'notification' => [ + 'error' => 'Hiba', + + 'billing_address' => [ + 'saved' => 'Számlázási cím mentve', + ], + + 'shipping_address' => [ + 'saved' => 'Szállítási cím mentve', + ], + ], + ], + 'edit_tags' => [ + 'label' => 'Szerkesztés', + 'form' => [ + 'tags' => [ + 'label' => 'Címkék', + 'helper_text' => 'Címkék elválasztása Enterrel, Tab-bal vagy vesszővel (,)', + ], + ], + ], + 'capture_payment' => [ + 'label' => 'Fizetés lekönyvelése', + + 'notification' => [ + 'error' => 'Hiba történt a lekönyvelés során', + 'success' => 'Lekönyvelés sikeres', + ], + ], + 'refund_payment' => [ + 'label' => 'Visszatérítés', + + 'notification' => [ + 'error' => 'Hiba történt a visszatérítés során', + 'success' => 'Visszatérítés sikeres', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/product.php b/packages/admin/resources/lang/hu/product.php new file mode 100644 index 0000000000..06c3fbdcd1 --- /dev/null +++ b/packages/admin/resources/lang/hu/product.php @@ -0,0 +1,131 @@ + 'Termék', + + 'plural_label' => 'Termékek', + + 'tabs' => [ + 'all' => 'Mind', + ], + + 'status' => [ + 'unpublished' => [ + 'content' => 'Jelenleg vázlat státuszban van, ez a termék minden csatornán és vásárlói csoportban rejtve van.', + ], + 'availability' => [ + 'customer_groups' => 'Ez a termék jelenleg nem elérhető egyik vásárlói csoport számára sem.', + 'channels' => 'Ez a termék jelenleg nem elérhető egyik csatornán sem.', + ], + ], + + 'table' => [ + 'status' => [ + 'label' => 'Státusz', + 'states' => [ + 'deleted' => 'Törölve', + 'draft' => 'Vázlat', + 'published' => 'Közzétéve', + ], + ], + 'name' => [ + 'label' => 'Név', + ], + 'brand' => [ + 'label' => 'Márka', + ], + 'sku' => [ + 'label' => 'Cikkszám (SKU)', + ], + 'stock' => [ + 'label' => 'Készlet', + ], + 'producttype' => [ + 'label' => 'Terméktípus', + ], + ], + + 'actions' => [ + 'edit_status' => [ + 'label' => 'Státusz frissítése', + 'heading' => 'Státusz frissítése', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'brand' => [ + 'label' => 'Márka', + ], + 'sku' => [ + 'label' => 'Cikkszám (SKU)', + ], + 'producttype' => [ + 'label' => 'Terméktípus', + ], + 'status' => [ + 'label' => 'Státusz', + 'options' => [ + 'published' => [ + 'label' => 'Közzétéve', + 'description' => 'Ez a termék elérhető lesz minden engedélyezett vásárlói csoportban és csatornán', + ], + 'draft' => [ + 'label' => 'Vázlat', + 'description' => 'Ez a termék rejtett lesz minden csatornán és vásárlói csoportban', + ], + ], + ], + 'tags' => [ + 'label' => 'Címkék', + 'helper_text' => 'Címkék elválasztása Enterrel, Tab-bal vagy vesszővel (,)', + ], + 'collections' => [ + 'label' => 'Gyűjtemények', + 'select_collection' => 'Válassz gyűjteményt', + ], + ], + + 'pages' => [ + 'availability' => [ + 'label' => 'Elérhetőség', + ], + 'edit' => [ + 'title' => 'Alapvető információk', + ], + 'identifiers' => [ + 'label' => 'Termékazonosítók', + ], + 'inventory' => [ + 'label' => 'Készlet', + ], + 'pricing' => [ + 'form' => [ + 'tax_class_id' => [ + 'label' => 'Adóosztály', + ], + 'tax_ref' => [ + 'label' => 'Adóreferencia', + 'helper_text' => 'Opcionális, külső rendszerekkel való integrációhoz.', + ], + ], + ], + 'shipping' => [ + 'label' => 'Szállítás', + ], + 'variants' => [ + 'label' => 'Változatok', + ], + 'collections' => [ + 'label' => 'Gyűjtemények', + 'select_collection' => 'Válassz gyűjteményt', + ], + 'associations' => [ + 'label' => 'Termékasszociációk', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/productoption.php b/packages/admin/resources/lang/hu/productoption.php new file mode 100644 index 0000000000..bb278d6240 --- /dev/null +++ b/packages/admin/resources/lang/hu/productoption.php @@ -0,0 +1,127 @@ + 'Termék opció', + + 'plural_label' => 'Termék opciók', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'label' => [ + 'label' => 'Címke', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + 'shared' => [ + 'label' => 'Megosztott', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'label' => [ + 'label' => 'Címke', + ], + 'handle' => [ + 'label' => 'Azonosító', + ], + ], + + 'widgets' => [ + 'product-options' => [ + 'notifications' => [ + 'save-variants' => [ + 'success' => [ + 'title' => 'Termékváltozatok mentve', + ], + ], + ], + 'actions' => [ + 'cancel' => [ + 'label' => 'Mégse', + ], + 'save-options' => [ + 'label' => 'Opciók mentése', + ], + 'add-shared-option' => [ + 'label' => 'Megosztott opció hozzáadása', + 'form' => [ + 'product_option' => [ + 'label' => 'Termék opció', + ], + 'no_shared_components' => [ + 'label' => 'Nincsenek elérhető megosztott opciók.', + ], + 'preselect' => [ + 'label' => 'Minden érték előzetes kiválasztása alapértelmezés szerint.', + ], + ], + ], + 'add-restricted-option' => [ + 'label' => 'Opció hozzáadása', + ], + ], + 'options-list' => [ + 'empty' => [ + 'heading' => 'Nincsenek konfigurált termék opciók', + 'description' => 'Adj hozzá egy megosztott vagy korlátozott termék opciót a változatok generálásának megkezdéséhez.', + ], + ], + 'options-table' => [ + 'title' => 'Termék opciók', + 'configure-options' => [ + 'label' => 'Opciók konfigurálása', + ], + 'table' => [ + 'option' => [ + 'label' => 'Opció', + ], + 'values' => [ + 'label' => 'Értékek', + ], + ], + ], + 'variants-table' => [ + 'title' => 'Termékváltozatok', + 'actions' => [ + 'create' => [ + 'label' => 'Termékváltozat létrehozása', + ], + 'edit' => [ + 'label' => 'Szerkesztés', + ], + 'delete' => [ + 'label' => 'Törlés', + ], + ], + 'empty' => [ + 'heading' => 'Nincsenek konfigurált változatok', + ], + 'table' => [ + 'new' => [ + 'label' => 'Új', + ], + 'option' => [ + 'label' => 'Opció', + ], + 'sku' => [ + 'label' => 'Cikkszám (SKU)', + ], + 'price' => [ + 'label' => 'Ár', + ], + 'stock' => [ + 'label' => 'Készlet', + ], + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/producttype.php b/packages/admin/resources/lang/hu/producttype.php new file mode 100644 index 0000000000..4e683404b2 --- /dev/null +++ b/packages/admin/resources/lang/hu/producttype.php @@ -0,0 +1,52 @@ + 'Terméktípus', + + 'plural_label' => 'Terméktípusok', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'products_count' => [ + 'label' => 'Termékek száma', + ], + 'product_attributes_count' => [ + 'label' => 'Termék attribútumok', + ], + 'variant_attributes_count' => [ + 'label' => 'Termékváltozat attribútumok', + ], + ], + + 'tabs' => [ + 'product_attributes' => [ + 'label' => 'Termék attribútumok', + ], + 'variant_attributes' => [ + 'label' => 'Termékváltozat attribútumok', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + ], + + 'attributes' => [ + 'no_groups' => 'Nincsenek elérhető attribútumcsoportok.', + 'no_attributes' => 'Nincsenek elérhető attribútumok.', + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'A terméktípus nem törölhető, mert vannak hozzá rendelve termékek.', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/productvariant.php b/packages/admin/resources/lang/hu/productvariant.php new file mode 100644 index 0000000000..f1f171e80b --- /dev/null +++ b/packages/admin/resources/lang/hu/productvariant.php @@ -0,0 +1,105 @@ + 'Termékváltozat', + 'plural_label' => 'Termékváltozatok', + 'pages' => [ + 'edit' => [ + 'title' => 'Alapinformációk', + ], + 'media' => [ + 'title' => 'Média', + 'form' => [ + 'no_selection' => [ + 'label' => 'Ehhez a termékváltozathoz jelenleg nincs kép kiválasztva.', + ], + 'no_media_available' => [ + 'label' => 'Jelenleg nincs elérhető média ehhez a termékhez.', + ], + 'images' => [ + 'label' => 'Elsődleges kép', + 'helper_text' => 'Válaszd ki azt a termékképet, amely ezt a termékváltozatot képviseli.', + ], + ], + ], + 'identifiers' => [ + 'title' => 'Azonosítók', + ], + 'inventory' => [ + 'title' => 'Készlet', + ], + 'shipping' => [ + 'title' => 'Szállítás', + ], + ], + 'form' => [ + 'sku' => [ + 'label' => 'Cikkszám (SKU)', + ], + 'gtin' => [ + 'label' => 'Globális kereskedelmi cikkszám (GTIN)', + ], + 'mpn' => [ + 'label' => 'Gyártói cikkszám (MPN)', + ], + 'ean' => [ + 'label' => 'UPC/EAN', + ], + 'stock' => [ + 'label' => 'Raktáron', + ], + 'backorder' => [ + 'label' => 'Utánrendelhető', + ], + 'purchasable' => [ + 'label' => 'Vásárolhatóság', + 'options' => [ + 'always' => 'Mindig', + 'in_stock' => 'Raktáron', + 'in_stock_or_on_backorder' => 'Raktáron vagy utánrendelhető', + ], + ], + 'unit_quantity' => [ + 'label' => 'Egység mennyiség', + 'helper_text' => 'Hány egyedi darabból áll egy egység.', + ], + 'min_quantity' => [ + 'label' => 'Minimális mennyiség', + 'helper_text' => 'A termékváltozat egy vásárlás során megvásárolható minimális mennyisége.', + ], + 'quantity_increment' => [ + 'label' => 'Mennyiség növelése', + 'helper_text' => 'A termékváltozat csak e mennyiség többszörösében vásárolható meg.', + ], + 'tax_class_id' => [ + 'label' => 'Adóosztály', + ], + 'shippable' => [ + 'label' => 'Szállítható', + ], + 'length_value' => [ + 'label' => 'Hossz', + ], + 'length_unit' => [ + 'label' => 'Hossz egység', + ], + 'width_value' => [ + 'label' => 'Szélesség', + ], + 'width_unit' => [ + 'label' => 'Szélesség egység', + ], + 'height_value' => [ + 'label' => 'Magasság', + ], + 'height_unit' => [ + 'label' => 'Magasság egység', + ], + 'weight_value' => [ + 'label' => 'Súly', + ], + 'weight_unit' => [ + 'label' => 'Súly egység', + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/relationmanagers.php b/packages/admin/resources/lang/hu/relationmanagers.php new file mode 100644 index 0000000000..1d6418ee91 --- /dev/null +++ b/packages/admin/resources/lang/hu/relationmanagers.php @@ -0,0 +1,285 @@ + [ + 'title' => 'Vásárlói csoportok', + 'actions' => [ + 'attach' => [ + 'label' => 'Vásárlói csoport csatolása', + ], + ], + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'enabled' => [ + 'label' => 'Engedélyezve', + ], + 'starts_at' => [ + 'label' => 'Kezdés dátuma', + ], + 'ends_at' => [ + 'label' => 'Befejezés dátuma', + ], + 'visible' => [ + 'label' => 'Látható', + ], + 'purchasable' => [ + 'label' => 'Megvásárolható', + ], + ], + 'table' => [ + 'description' => 'Kapcsoljon vásárlói csoportokat a(z) :type-hoz az elérhetőség meghatározásához.', + 'name' => [ + 'label' => 'Név', + ], + 'enabled' => [ + 'label' => 'Engedélyezve', + ], + 'starts_at' => [ + 'label' => 'Kezdés dátuma', + ], + 'ends_at' => [ + 'label' => 'Befejezés dátuma', + ], + 'visible' => [ + 'label' => 'Látható', + ], + 'purchasable' => [ + 'label' => 'Megvásárolható', + ], + ], + ], + 'channels' => [ + 'title' => 'Csatornák', + 'actions' => [ + 'attach' => [ + 'label' => 'Csatorna ütemezése', + ], + ], + 'form' => [ + 'enabled' => [ + 'label' => 'Engedélyezve', + 'helper_text_false' => 'A csatorna nem lesz engedélyezve, még ha van kezdő dátum sem.', + ], + 'starts_at' => [ + 'label' => 'Kezdés dátuma', + 'helper_text' => 'Hagyd üresen, ha bármely dátumtól elérhető legyen.', + ], + 'ends_at' => [ + 'label' => 'Befejezés dátuma', + 'helper_text' => 'Hagyd üresen, ha határozatlan ideig elérhető legyen.', + ], + ], + 'table' => [ + 'description' => 'Állítsd be, mely csatornák engedélyezettek és ütemezd az elérhetőséget.', + 'name' => [ + 'label' => 'Név', + ], + 'enabled' => [ + 'label' => 'Engedélyezve', + ], + 'starts_at' => [ + 'label' => 'Kezdés dátuma', + ], + 'ends_at' => [ + 'label' => 'Befejezés dátuma', + ], + ], + ], + 'medias' => [ + 'title' => 'Média', + 'title_plural' => 'Médiák', + 'actions' => [ + 'attach' => [ + 'label' => 'Média csatolása', + ], + 'create' => [ + 'label' => 'Média létrehozása', + ], + 'detach' => [ + 'label' => 'Leválasztás', + ], + 'view' => [ + 'label' => 'Megtekintés', + ], + ], + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'media' => [ + 'label' => 'Kép', + ], + 'primary' => [ + 'label' => 'Elsődleges', + ], + ], + 'table' => [ + 'image' => [ + 'label' => 'Kép', + ], + 'file' => [ + 'label' => 'Fájl', + ], + 'name' => [ + 'label' => 'Név', + ], + 'primary' => [ + 'label' => 'Elsődleges', + ], + ], + 'all_media_attached' => 'Nincsenek elérhető termékképek csatoláshoz', + 'variant_description' => 'Csatoljon termékképeket ehhez a változathoz', + ], + 'urls' => [ + 'title' => 'URL', + 'title_plural' => 'URL-ek', + 'actions' => [ + 'create' => [ + 'label' => 'URL létrehozása', + ], + ], + 'filters' => [ + 'language_id' => [ + 'label' => 'Nyelv', + ], + ], + 'form' => [ + 'slug' => [ + 'label' => 'URL-azonosító (slug)', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + 'language' => [ + 'label' => 'Nyelv', + ], + ], + 'table' => [ + 'slug' => [ + 'label' => 'URL-azonosító (slug)', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + 'language' => [ + 'label' => 'Nyelv', + ], + ], + ], + 'customer_group_pricing' => [ + 'title' => 'Vásárlói csoport árképzés', + 'title_plural' => 'Vásárlói csoport árképzés', + 'table' => [ + 'heading' => 'Vásárlói csoport árképzés', + 'description' => 'Ár társítása vásárlói csoportokhoz a termék árának meghatározásához.', + 'empty_state' => [ + 'label' => 'Nincs vásárlói csoport ár.', + 'description' => 'Hozz létre vásárlói csoport árat a kezdéshez.', + ], + 'actions' => [ + 'create' => [ + 'label' => 'Vásárlói csoport ár hozzáadása', + 'modal' => [ + 'heading' => 'Vásárlói csoport ár létrehozása', + ], + ], + ], + ], + ], + 'pricing' => [ + 'title' => 'Árképzés', + 'title_plural' => 'Árképzés', + 'tab_name' => 'Ár lépcsők', + 'table' => [ + 'heading' => 'Ár lépcsők', + 'description' => 'Csökkentsd az árat, ha a vásárló nagyobb mennyiséget vásárol.', + 'empty_state' => [ + 'label' => 'Nincsenek ár lépcsők.', + ], + 'actions' => [ + 'create' => [ + 'label' => 'Ár lépcső hozzáadása', + ], + ], + 'price' => [ + 'label' => 'Ár', + ], + 'customer_group' => [ + 'label' => 'Vásárlói csoport', + 'placeholder' => 'Minden vásárlói csoport', + ], + 'min_quantity' => [ + 'label' => 'Minimum mennyiség', + ], + 'currency' => [ + 'label' => 'Pénznem', + ], + ], + 'form' => [ + 'price' => [ + 'label' => 'Ár', + 'helper_text' => 'A vásárlási ár, kedvezmények előtt.', + ], + 'customer_group_id' => [ + 'label' => 'Vásárlói csoport', + 'placeholder' => 'Minden vásárlói csoport', + 'helper_text' => 'Válaszd ki, melyik vásárlói csoportra alkalmazd ezt az árat.', + ], + 'min_quantity' => [ + 'label' => 'Minimum mennyiség', + 'helper_text' => 'Válaszd ki a minimális mennyiséget, amelyre ez az ár érvényes lesz.', + 'validation' => [ + 'unique' => 'A vásárlói csoport és a minimum mennyiség egyedinek kell lennie.', + ], + ], + 'currency_id' => [ + 'label' => 'Pénznem', + 'helper_text' => 'Válaszd ki az ár pénznemét.', + ], + 'compare_price' => [ + 'label' => 'Összehasonlító ár', + 'helper_text' => 'Az eredeti ár vagy ajánlott fogyasztói ár, az összehasonlításhoz.', + ], + 'basePrices' => [ + 'title' => 'Árak', + 'form' => [ + 'price' => [ + 'label' => 'Ár', + 'helper_text' => 'A vásárlási ár, kedvezmények előtt.', + 'sync_price' => 'Az ár szinkronizálva van az alapértelmezett pénznemmel.', + ], + 'compare_price' => [ + 'label' => 'Összehasonlító ár', + 'helper_text' => 'Az eredeti ár vagy ajánlott fogyasztói ár, az összehasonlításhoz.', + ], + ], + 'tooltip' => 'Automatikusan generálva a pénznemek közötti árfolyamok alapján.', + ], + ], + ], + 'tax_rate_amounts' => [ + 'table' => [ + 'description' => '', + 'percentage' => [ + 'label' => 'Százalék', + ], + 'tax_class' => [ + 'label' => 'Adóosztály', + ], + ], + ], + 'values' => [ + 'title' => 'Értékek', + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'position' => [ + 'label' => 'Pozíció', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/staff.php b/packages/admin/resources/lang/hu/staff.php new file mode 100644 index 0000000000..3397e73b36 --- /dev/null +++ b/packages/admin/resources/lang/hu/staff.php @@ -0,0 +1,81 @@ + 'Munkatárs', + + 'plural_label' => 'Munkatársak', + + 'table' => [ + 'first_name' => [ + 'label' => 'Keresztnév', + ], + 'last_name' => [ + 'label' => 'Vezetéknév', + ], + 'email' => [ + 'label' => 'E-mail', + ], + 'admin' => [ + 'badge' => 'Főadminisztrátor', + ], + ], + + 'form' => [ + 'first_name' => [ + 'label' => 'Keresztnév', + ], + 'last_name' => [ + 'label' => 'Vezetéknév', + ], + 'email' => [ + 'label' => 'E-mail', + ], + 'password' => [ + 'label' => 'Jelszó', + 'hint' => 'Jelszó visszaállítása', + ], + 'admin' => [ + 'label' => 'Főadminisztrátor', + 'helper' => 'A főadminisztrátor szerepkörök nem változtathatók a hubban.', + ], + 'roles' => [ + 'label' => 'Szerepkörök', + 'helper' => ':roles teljes hozzáféréssel rendelkeznek', + ], + 'permissions' => [ + 'label' => 'Jogosultságok', + ], + 'role' => [ + 'label' => 'Szerepkör neve', + ], + ], + + 'action' => [ + 'acl' => [ + 'label' => 'Hozzáférés-vezérlés', + ], + 'add-role' => [ + 'label' => 'Szerepkör hozzáadása', + ], + 'delete-role' => [ + 'label' => 'Szerepkör törlése', + 'heading' => 'Szerepkör törlése: :role', + ], + ], + + 'acl' => [ + 'title' => 'Hozzáférés-vezérlés', + 'tooltip' => [ + 'roles-included' => 'A jogosultság a következő szerepkörökben szerepel', + ], + 'notification' => [ + 'updated' => 'Frissítve', + 'error' => 'Hiba', + 'no-role' => 'A szerepkör nincs regisztrálva a Lunarban', + 'no-permission' => 'A jogosultság nincs regisztrálva a Lunarban', + 'no-role-permission' => 'Szerepkör és jogosultság nincs regisztrálva a Lunarban', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/tag.php b/packages/admin/resources/lang/hu/tag.php new file mode 100644 index 0000000000..ed0e133f66 --- /dev/null +++ b/packages/admin/resources/lang/hu/tag.php @@ -0,0 +1,21 @@ + 'Címke', + + 'plural_label' => 'Címkék', + + 'table' => [ + 'value' => [ + 'label' => 'Érték', + ], + ], + + 'form' => [ + 'value' => [ + 'label' => 'Érték', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/tax.php b/packages/admin/resources/lang/hu/tax.php new file mode 100644 index 0000000000..06072c126e --- /dev/null +++ b/packages/admin/resources/lang/hu/tax.php @@ -0,0 +1,9 @@ + 'Adó', + + 'plural_label' => 'Adók', + +]; diff --git a/packages/admin/resources/lang/hu/taxclass.php b/packages/admin/resources/lang/hu/taxclass.php new file mode 100644 index 0000000000..203fce8da2 --- /dev/null +++ b/packages/admin/resources/lang/hu/taxclass.php @@ -0,0 +1,32 @@ + 'Adóosztály', + + 'plural_label' => 'Adóosztályok', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + 'delete' => [ + 'error' => [ + 'title' => 'Nem törölhető az adóosztály', + 'body' => 'Ennek az adóosztálynak vannak hozzá rendelt termékváltozatai, ezért nem törölhető.', + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/taxrate.php b/packages/admin/resources/lang/hu/taxrate.php new file mode 100644 index 0000000000..7515ea9552 --- /dev/null +++ b/packages/admin/resources/lang/hu/taxrate.php @@ -0,0 +1,33 @@ + 'Adókulcs', + + 'plural_label' => 'Adókulcsok', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'tax_zone' => [ + 'label' => 'Adózóna', + ], + 'priority' => [ + 'label' => 'Prioritás', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'priority' => [ + 'label' => 'Prioritás', + ], + 'tax_zone_id' => [ + 'label' => 'Adózóna', + ], + ], + +]; diff --git a/packages/admin/resources/lang/hu/taxzone.php b/packages/admin/resources/lang/hu/taxzone.php new file mode 100644 index 0000000000..398838dde5 --- /dev/null +++ b/packages/admin/resources/lang/hu/taxzone.php @@ -0,0 +1,69 @@ + 'Adózóna', + + 'plural_label' => 'Adózónák', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'zone_type' => [ + 'label' => 'Zóna típusa', + ], + 'active' => [ + 'label' => 'Aktív', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'zone_type' => [ + 'label' => 'Zóna típusa', + 'options' => [ + 'country' => 'Csak országokra korlátozva', + 'states' => 'Csak államokra korlátozva', + 'postcodes' => 'Csak irányítószámokra korlátozva', + ], + ], + 'price_display' => [ + 'label' => 'Ár megjelenítése', + 'options' => [ + 'include_tax' => 'Adót is tartalmaz', + 'exclude_tax' => 'Adót nem tartalmaz', + ], + ], + 'active' => [ + 'label' => 'Aktív', + ], + 'default' => [ + 'label' => 'Alapértelmezett', + ], + + 'zone_countries' => [ + 'label' => 'Országok', + ], + + 'zone_country' => [ + 'label' => 'Ország', + ], + + 'zone_states' => [ + 'label' => 'Államok', + ], + + 'zone_postcodes' => [ + 'label' => 'Irányítószámok', + 'helper' => 'Listázd az egyes irányítószámokat új sorban. Támogatja a helyettesítő karaktereket, mint például NW*', + ], + + ], + +]; diff --git a/packages/admin/resources/lang/hu/user.php b/packages/admin/resources/lang/hu/user.php new file mode 100644 index 0000000000..e4722a26c7 --- /dev/null +++ b/packages/admin/resources/lang/hu/user.php @@ -0,0 +1,29 @@ + 'Felhasználó', + + 'plural_label' => 'Felhasználók', + + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'email' => [ + 'label' => 'E-mail', + ], + ], + + 'form' => [ + 'email' => [ + 'label' => 'E-mail', + ], + 'password' => [ + 'label' => 'Új jelszó', + ], + 'password_confirmation' => [ + 'label' => 'Új jelszó megerősítése', + ], + ], +]; diff --git a/packages/admin/resources/lang/hu/widgets.php b/packages/admin/resources/lang/hu/widgets.php new file mode 100644 index 0000000000..040ff463dd --- /dev/null +++ b/packages/admin/resources/lang/hu/widgets.php @@ -0,0 +1,118 @@ + [ + 'orders' => [ + 'order_stats_overview' => [ + 'stat_one' => [ + 'label' => 'Rendelések ma', + 'increase' => ':percentage% növekedés a tegnapi (:count) értékhez képest', + 'decrease' => ':percentage% csökkenés a tegnapi (:count) értékhez képest', + 'neutral' => 'Nincs változás tegnaphoz képest', + ], + 'stat_two' => [ + 'label' => 'Rendelések az elmúlt 7 napban', + 'increase' => ':percentage% növekedés a :count előző időszakhoz képest', + 'decrease' => ':percentage% csökkenés a :count előző időszakhoz képest', + 'neutral' => 'Nincs változás az előző időszakhoz képest', + ], + 'stat_three' => [ + 'label' => 'Rendelések az elmúlt 30 napban', + 'increase' => ':percentage% növekedés a :count előző időszakhoz képest', + 'decrease' => ':percentage% csökkenés a :count előző időszakhoz képest', + 'neutral' => 'Nincs változás az előző időszakhoz képest', + ], + 'stat_four' => [ + 'label' => 'Rendelések ma', + 'increase' => ':percentage% növekedés a :total tegnapi értékhez képest', + 'decrease' => ':percentage% csökkenés a :total tegnapi értékhez képest', + 'neutral' => 'Nincs változás tegnaphoz képest', + ], + 'stat_five' => [ + 'label' => 'Rendelések az elmúlt 7 napban', + 'increase' => ':percentage% növekedés a :total előző időszakhoz képest', + 'decrease' => ':percentage% csökkenés a :total előző időszakhoz képest', + 'neutral' => 'Nincs változás az előző időszakhoz képest', + ], + 'stat_six' => [ + 'label' => 'Rendelések az elmúlt 30 napban', + 'increase' => ':percentage% növekedés a :total előző időszakhoz képest', + 'decrease' => ':percentage% csökkenés a :total előző időszakhoz képest', + 'neutral' => 'Nincs változás az előző időszakhoz képest', + ], + ], + 'order_totals_chart' => [ + 'heading' => 'Rendelési összesítők az elmúlt évben', + 'series_one' => [ + 'label' => 'Ez az időszak', + ], + 'series_two' => [ + 'label' => 'Előző időszak', + ], + 'yaxis' => [ + 'label' => 'Forgalom :currency', + ], + ], + 'order_sales_chart' => [ + 'heading' => 'Rendelések / Értékesítési Jelentés', + 'series_one' => [ + 'label' => 'Rendelések', + ], + 'series_two' => [ + 'label' => 'Bevétel', + ], + 'yaxis' => [ + 'series_one' => [ + 'label' => '# Rendelések', + ], + 'series_two' => [ + 'label' => 'Összérték', + ], + ], + ], + 'average_order_value' => [ + 'heading' => 'Átlagos rendelési érték', + ], + 'new_returning_customers' => [ + 'heading' => 'Új vs Visszatérő Vásárlók', + 'series_one' => [ + 'label' => 'Új Vásárlók', + ], + 'series_two' => [ + 'label' => 'Visszatérő Vásárlók', + ], + ], + 'popular_products' => [ + 'heading' => 'Legnépszerűbb termékek (utolsó 12 hónap)', + 'description' => 'Ezek a számok arra épülnek, hányszor szerepel egy termék egy rendelésben, nem a megrendelt mennyiségre.', + ], + 'latest_orders' => [ + 'heading' => 'Legutóbbi rendelések', + ], + ], + ], + 'customer' => [ + 'stats_overview' => [ + 'total_orders' => [ + 'label' => 'Összes rendelés', + ], + 'avg_spend' => [ + 'label' => 'Átlagos költés', + ], + 'total_spend' => [ + 'label' => 'Összes költés', + ], + ], + ], + 'variant_switcher' => [ + 'label' => 'Variáns váltása', + 'table' => [ + 'sku' => [ + 'label' => 'Cikkszám (SKU)', + ], + 'values' => [ + 'label' => 'Értékek', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/nl/relationmanagers.php b/packages/admin/resources/lang/nl/relationmanagers.php index dc3e77a9c3..6d15eeeb91 100644 --- a/packages/admin/resources/lang/nl/relationmanagers.php +++ b/packages/admin/resources/lang/nl/relationmanagers.php @@ -263,6 +263,11 @@ ], 'values' => [ 'title' => 'Waarden', + 'form' => [ + 'name' => [ + 'label' => 'Naam', + ], + ], 'table' => [ 'name' => [ 'label' => 'Naam', diff --git a/packages/admin/resources/lang/nl/tax.php b/packages/admin/resources/lang/nl/tax.php new file mode 100644 index 0000000000..72bee0f347 --- /dev/null +++ b/packages/admin/resources/lang/nl/tax.php @@ -0,0 +1,9 @@ + 'Belasting', + + 'plural_label' => 'Belastingen', + +]; diff --git a/packages/admin/resources/lang/pl/relationmanagers.php b/packages/admin/resources/lang/pl/relationmanagers.php index 54b3189b4b..1ef2bd82eb 100644 --- a/packages/admin/resources/lang/pl/relationmanagers.php +++ b/packages/admin/resources/lang/pl/relationmanagers.php @@ -226,6 +226,11 @@ ], 'values' => [ 'title' => 'Wartości', + 'form' => [ + 'name' => [ + 'label' => 'Nazwa', + ], + ], 'table' => [ 'name' => [ 'label' => 'Nazwa', diff --git a/packages/admin/resources/lang/pl/tax.php b/packages/admin/resources/lang/pl/tax.php new file mode 100644 index 0000000000..0000b987e9 --- /dev/null +++ b/packages/admin/resources/lang/pl/tax.php @@ -0,0 +1,9 @@ + 'Podatek', + + 'plural_label' => 'Podatki', + +]; diff --git a/packages/admin/resources/lang/pt_BR/actions.php b/packages/admin/resources/lang/pt_BR/actions.php new file mode 100644 index 0000000000..3fdc1e1f68 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/actions.php @@ -0,0 +1,52 @@ + [ + 'create_root' => [ + 'label' => 'Criar coleção raiz', + ], + 'create_child' => [ + 'label' => 'Criar coleção filha', + ], + 'move' => [ + 'label' => 'Mover coleção', + ], + 'delete' => [ + 'label' => 'Excluir', + 'notifications' => [ + 'cannot_delete' => [ + 'title' => 'Não é possível excluir', + 'body' => 'Esta coleção possui coleções filhas e não pode ser excluída.', + ], + ], + ], + ], + 'orders' => [ + 'update_status' => [ + 'label' => 'Atualizar status', + 'wizard' => [ + 'step_one' => [ + 'label' => 'Status', + ], + 'step_two' => [ + 'label' => 'E-mails e notificações', + 'no_mailers' => 'Não há e-mails disponíveis para este status.', + ], + 'step_three' => [ + 'label' => 'Pré-visualizar e salvar', + 'no_mailers' => 'Nenhum e-mail foi escolhido para pré-visualização.', + ], + ], + 'notification' => [ + 'label' => 'Status do pedido atualizado', + ], + 'billing_email' => [ + 'label' => 'E-mail de cobrança', + ], + 'shipping_email' => [ + 'label' => 'E-mail de entrega', + ], + ], + + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/activity.php b/packages/admin/resources/lang/pt_BR/activity.php new file mode 100644 index 0000000000..1abbbbed2e --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/activity.php @@ -0,0 +1,29 @@ + 'Atividade', + + 'plural_label' => 'Atividades', + + 'table' => [ + 'subject' => 'Assunto', + 'description' => 'Descrição', + 'log' => 'Registro', + 'logged_at' => 'Registrado em', + 'event' => 'Evento', + 'logged_from' => 'Registrado de', + 'logged_until' => 'Registrado até', + ], + + 'form' => [ + 'causer_type' => 'Tipo do agente', + 'causer_id' => 'ID do agente', + 'subject_type' => 'Tipo do assunto', + 'subject_id' => 'ID do assunto', + 'description' => 'Descrição', + 'attributes' => 'Atributos', + 'old' => 'Antigo', + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/address.php b/packages/admin/resources/lang/pt_BR/address.php new file mode 100644 index 0000000000..c5d17d1982 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/address.php @@ -0,0 +1,99 @@ + 'Endereço', + + 'plural_label' => 'Endereços', + + 'table' => [ + 'title' => [ + 'label' => 'Título', + ], + 'first_name' => [ + 'label' => 'Nome', + ], + 'last_name' => [ + 'label' => 'Sobrenome', + ], + 'company_name' => [ + 'label' => 'Nome da empresa', + ], + 'tax_identifier' => [ + 'label' => 'Identificador fiscal', + ], + 'line_one' => [ + 'label' => 'Endereço', + ], + 'line_two' => [ + 'label' => 'Linha dois', + ], + 'line_three' => [ + 'label' => 'Linha três', + ], + 'city' => [ + 'label' => 'Cidade', + ], + 'country_id' => [ + 'label' => 'País', + ], + 'state' => [ + 'label' => 'Estado', + ], + 'postcode' => [ + 'label' => 'CEP', + ], + 'contact_email' => [ + 'label' => 'E-mail de contato', + ], + 'contact_phone' => [ + 'label' => 'Telefone de contato', + ], + ], + + 'form' => [ + 'title' => [ + 'label' => 'Título', + ], + 'first_name' => [ + 'label' => 'Nome', + ], + 'last_name' => [ + 'label' => 'Sobrenome', + ], + 'company_name' => [ + 'label' => 'Nome da empresa', + ], + 'tax_identifier' => [ + 'label' => 'Identificador fiscal', + ], + 'line_one' => [ + 'label' => 'Linha um', + ], + 'line_two' => [ + 'label' => 'Linha dois', + ], + 'line_three' => [ + 'label' => 'Linha três', + ], + 'city' => [ + 'label' => 'Cidade', + ], + 'country_id' => [ + 'label' => 'País', + ], + 'state' => [ + 'label' => 'Estado', + ], + 'postcode' => [ + 'label' => 'CEP', + ], + 'contact_email' => [ + 'label' => 'E-mail de contato', + ], + 'contact_phone' => [ + 'label' => 'Telefone de contato', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/attribute.php b/packages/admin/resources/lang/pt_BR/attribute.php new file mode 100644 index 0000000000..0ed27a0822 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/attribute.php @@ -0,0 +1,55 @@ + 'Atributo', + + 'plural_label' => 'Atributos', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'description' => [ + 'label' => 'Descrição', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'type' => [ + 'label' => 'Tipo', + ], + ], + + 'form' => [ + 'attributable_type' => [ + 'label' => 'Tipo', + ], + 'name' => [ + 'label' => 'Nome', + ], + 'description' => [ + 'label' => 'Descrição', + 'helper' => 'Use para exibir um texto de ajuda abaixo do campo', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'searchable' => [ + 'label' => 'Pesquisável', + ], + 'filterable' => [ + 'label' => 'Filtrável', + ], + 'required' => [ + 'label' => 'Obrigatório', + ], + 'type' => [ + 'label' => 'Tipo', + ], + 'validation_rules' => [ + 'label' => 'Regras de validação', + 'helper' => 'Regras para o campo do atributo, exemplo: min:1|max:10|...', + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/attributegroup.php b/packages/admin/resources/lang/pt_BR/attributegroup.php new file mode 100644 index 0000000000..97121b9027 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/attributegroup.php @@ -0,0 +1,46 @@ + 'Grupo de atributos', + + 'plural_label' => 'Grupos de atributos', + + 'table' => [ + 'attributable_type' => [ + 'label' => 'Tipo', + ], + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'position' => [ + 'label' => 'Posição', + ], + ], + + 'form' => [ + 'attributable_type' => [ + 'label' => 'Tipo', + ], + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'position' => [ + 'label' => 'Posição', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Este grupo de atributos não pode ser excluído pois há atributos associados.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/auth.php b/packages/admin/resources/lang/pt_BR/auth.php new file mode 100644 index 0000000000..60335acdb5 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/auth.php @@ -0,0 +1,32 @@ + 'Admin', + 'roles.admin.description' => 'Administrador com acesso total', + 'roles.staff.label' => 'Equipe', + 'roles.staff.description' => 'Equipe com acesso fundamental', + /** + * Permissions. + */ + 'permissions.settings.label' => 'Configurações', + 'permissions.settings.description' => 'Concede acesso à área de configurações do hub', + 'permissions.settings:core.label' => 'Configurações básicas', + 'permissions.settings:core.description' => 'Acessa configurações fundamentais da loja, como canais, idiomas, moedas etc.', + 'permissions.settings:manage-staff.label' => 'Gerenciar equipe', + 'permissions.settings:manage-staff.description' => 'Permite que o membro da equipe edite outros membros', + 'permissions.settings:manage-attributes.label' => 'Gerenciar atributos', + 'permissions.settings:manage-attributes.description' => 'Permite que o membro da equipe edite e crie atributos adicionais', + 'permissions.catalog:manage-products.label' => 'Gerenciar produtos', + 'permissions.catalog:manage-products.description' => 'Permite que o membro da equipe edite produtos, tipos de produto e marcas', + 'permissions.catalog:manage-collections.label' => 'Gerenciar coleções', + 'permissions.catalog:manage-collections.description' => 'Permite que o membro da equipe edite coleções e seus grupos', + 'permissions.sales:manage-orders.label' => 'Gerenciar pedidos', + 'permissions.sales:manage-orders.description' => 'Permite que o membro da equipe gerencie pedidos', + 'permissions.sales:manage-customers.label' => 'Gerenciar clientes', + 'permissions.sales:manage-customers.description' => 'Permite que o membro da equipe gerencie clientes', + 'permissions.sales:manage-discounts.label' => 'Gerenciar descontos', + 'permissions.sales:manage-discounts.description' => 'Permite que o membro da equipe gerencie descontos', +]; diff --git a/packages/admin/resources/lang/pt_BR/brand.php b/packages/admin/resources/lang/pt_BR/brand.php new file mode 100644 index 0000000000..11da6797f2 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/brand.php @@ -0,0 +1,75 @@ + 'Marca', + + 'plural_label' => 'Marcas', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'products_count' => [ + 'label' => 'Qtd. de produtos', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Esta marca não pode ser excluída pois há produtos associados.', + ], + ], + ], + 'pages' => [ + 'edit' => [ + 'title' => 'Informações básicas', + ], + 'products' => [ + 'label' => 'Produtos', + 'actions' => [ + 'attach' => [ + 'label' => 'Associar um produto', + 'form' => [ + 'record_id' => [ + 'label' => 'Produto', + ], + ], + 'notification' => [ + 'success' => 'Produto associado à marca', + ], + ], + 'detach' => [ + 'notification' => [ + 'success' => 'Produto desassociado.', + ], + ], + ], + ], + 'collections' => [ + 'label' => 'Coleções', + 'table' => [ + 'header_actions' => [ + 'attach' => [ + 'record_select' => [ + 'placeholder' => 'Selecione uma coleção', + ], + ], + ], + ], + 'actions' => [ + 'attach' => [ + 'label' => 'Associar uma coleção', + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/channel.php b/packages/admin/resources/lang/pt_BR/channel.php new file mode 100644 index 0000000000..5ad7d06e6d --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/channel.php @@ -0,0 +1,39 @@ + 'Canal', + + 'plural_label' => 'Canais', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'url' => [ + 'label' => 'URL', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'url' => [ + 'label' => 'URL', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/collection.php b/packages/admin/resources/lang/pt_BR/collection.php new file mode 100644 index 0000000000..509c221559 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/collection.php @@ -0,0 +1,45 @@ + 'Coleção', + + 'plural_label' => 'Coleções', + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + ], + + 'pages' => [ + 'children' => [ + 'label' => 'Coleções filhas', + 'actions' => [ + 'create_child' => [ + 'label' => 'Criar coleção filha', + ], + ], + 'table' => [ + 'children_count' => [ + 'label' => 'Qtd. de filhas', + ], + 'name' => [ + 'label' => 'Nome', + ], + ], + ], + 'edit' => [ + 'label' => 'Informações básicas', + ], + 'products' => [ + 'label' => 'Produtos', + 'actions' => [ + 'attach' => [ + 'label' => 'Anexar produto', + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/collectiongroup.php b/packages/admin/resources/lang/pt_BR/collectiongroup.php new file mode 100644 index 0000000000..65eae3be84 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/collectiongroup.php @@ -0,0 +1,37 @@ + 'Grupo de coleções', + + 'plural_label' => 'Grupos de coleções', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'collections_count' => [ + 'label' => 'Qtd. de coleções', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Este grupo de coleções não pode ser excluído pois há coleções associadas.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/components.php b/packages/admin/resources/lang/pt_BR/components.php new file mode 100644 index 0000000000..d87e62c40a --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/components.php @@ -0,0 +1,117 @@ + [ + 'notification' => [ + + 'updated' => 'Tags atualizadas', + + ], + ], + + 'activity-log' => [ + + 'input' => [ + + 'placeholder' => 'Adicionar um comentário', + + ], + + 'action' => [ + + 'add-comment' => 'Adicionar comentário', + + ], + + 'system' => 'Sistema', + + 'partials' => [ + 'orders' => [ + 'order_created' => 'Pedido criado', + + 'status_change' => 'Status atualizado', + + 'capture' => 'Pagamento de :amount no cartão com final :last_four', + + 'authorized' => 'Autorizado o valor de :amount no cartão com final :last_four', + + 'refund' => 'Reembolso de :amount no cartão com final :last_four', + + 'address' => ':type atualizado', + + 'billingAddress' => 'Endereço de cobrança', + + 'shippingAddress' => 'Endereço de entrega', + ], + + 'update' => [ + 'updated' => ':model atualizado', + ], + + 'create' => [ + 'created' => ':model criado', + ], + + 'tags' => [ + 'updated' => 'Tags atualizadas', + 'added' => 'Adicionado', + 'removed' => 'Removido', + ], + ], + + 'notification' => [ + 'comment_added' => 'Comentário adicionado', + ], + + ], + + 'forms' => [ + 'youtube' => [ + 'helperText' => 'Informe o ID do vídeo do YouTube. Ex.: dQw4w9WgXcQ', + ], + ], + + 'collection-tree-view' => [ + 'actions' => [ + 'move' => [ + 'form' => [ + 'target_id' => [ + 'label' => 'Coleção pai', + ], + ], + ], + ], + 'notifications' => [ + 'collections-reordered' => [ + 'success' => 'Coleções reordenadas', + ], + 'node-expanded' => [ + 'danger' => 'Não foi possível carregar as coleções', + ], + 'delete' => [ + 'danger' => 'Não foi possível excluir a coleção', + ], + ], + ], + + 'product-options-list' => [ + 'add-option' => [ + 'label' => 'Adicionar opção', + ], + 'delete-option' => [ + 'label' => 'Excluir opção', + ], + 'remove-shared-option' => [ + 'label' => 'Remover opção compartilhada', + ], + 'add-value' => [ + 'label' => 'Adicionar outro valor', + ], + 'name' => [ + 'label' => 'Nome', + ], + 'values' => [ + 'label' => 'Valores', + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/currency.php b/packages/admin/resources/lang/pt_BR/currency.php new file mode 100644 index 0000000000..06ddba0eeb --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/currency.php @@ -0,0 +1,58 @@ + 'Moeda', + + 'plural_label' => 'Moedas', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'code' => [ + 'label' => 'Código', + ], + 'exchange_rate' => [ + 'label' => 'Taxa de câmbio', + ], + 'decimal_places' => [ + 'label' => 'Casas decimais', + ], + 'enabled' => [ + 'label' => 'Ativada', + ], + 'sync_prices' => [ + 'label' => 'Sincronizar preços', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'code' => [ + 'label' => 'Código', + ], + 'exchange_rate' => [ + 'label' => 'Taxa de câmbio', + ], + 'decimal_places' => [ + 'label' => 'Casas decimais', + ], + 'enabled' => [ + 'label' => 'Ativada', + ], + 'default' => [ + 'label' => 'Padrão', + ], + 'sync_prices' => [ + 'label' => 'Sincronizar preços', + 'helper_text' => 'Manter preços nesta moeda sincronizados com a moeda padrão.', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/customer.php b/packages/admin/resources/lang/pt_BR/customer.php new file mode 100644 index 0000000000..35eab5dc20 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/customer.php @@ -0,0 +1,63 @@ + 'Cliente', + + 'plural_label' => 'Clientes', + + 'table' => [ + 'full_name' => [ + 'label' => 'Nome', + ], + 'first_name' => [ + 'label' => 'Nome', + ], + 'last_name' => [ + 'label' => 'Sobrenome', + ], + 'title' => [ + 'label' => 'Título', + ], + 'company_name' => [ + 'label' => 'Nome da empresa', + ], + 'tax_identifier' => [ + 'label' => 'Identificador fiscal', + ], + 'account_reference' => [ + 'label' => 'Referência da conta', + ], + 'new' => [ + 'label' => 'Novo', + ], + 'returning' => [ + 'label' => 'Recorrente', + ], + ], + + 'form' => [ + 'title' => [ + 'label' => 'Título', + ], + 'first_name' => [ + 'label' => 'Nome', + ], + 'last_name' => [ + 'label' => 'Sobrenome', + ], + 'company_name' => [ + 'label' => 'Nome da empresa', + ], + 'account_ref' => [ + 'label' => 'Referência da conta', + ], + 'tax_identifier' => [ + 'label' => 'Identificador fiscal', + ], + 'customer_groups' => [ + 'label' => 'Grupos de clientes', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/customergroup.php b/packages/admin/resources/lang/pt_BR/customergroup.php new file mode 100644 index 0000000000..b4e4968b93 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/customergroup.php @@ -0,0 +1,40 @@ + 'Grupo de clientes', + + 'plural_label' => 'Grupos de clientes', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Este grupo de clientes não pode ser excluído pois há clientes associados.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/discount.php b/packages/admin/resources/lang/pt_BR/discount.php new file mode 100644 index 0000000000..a81dd3c074 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/discount.php @@ -0,0 +1,353 @@ + 'Descontos', + 'label' => 'Desconto', + 'form' => [ + 'conditions' => [ + 'heading' => 'Condições', + ], + 'buy_x_get_y' => [ + 'heading' => 'Compre X, leve Y', + ], + 'amount_off' => [ + 'heading' => 'Valor de desconto', + ], + 'name' => [ + 'label' => 'Nome', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'starts_at' => [ + 'label' => 'Data de início', + ], + 'ends_at' => [ + 'label' => 'Data de término', + ], + 'priority' => [ + 'label' => 'Prioridade', + 'helper_text' => 'Descontos com maior prioridade serão aplicados primeiro.', + 'options' => [ + 'low' => [ + 'label' => 'Baixa', + ], + 'medium' => [ + 'label' => 'Média', + ], + 'high' => [ + 'label' => 'Alta', + ], + ], + ], + 'stop' => [ + 'label' => 'Interromper outros descontos após este', + ], + 'coupon' => [ + 'label' => 'Cupom', + 'helper_text' => 'Informe o cupom necessário para aplicar o desconto; se deixado em branco, será aplicado automaticamente.', + ], + 'max_uses' => [ + 'label' => 'Máximo de usos', + 'helper_text' => 'Deixe em branco para usos ilimitados.', + ], + 'max_uses_per_user' => [ + 'label' => 'Máximo de usos por usuário', + 'helper_text' => 'Deixe em branco para usos ilimitados.', + ], + 'minimum_cart_amount' => [ + 'label' => 'Valor mínimo do carrinho', + ], + 'min_qty' => [ + 'label' => 'Quantidade de produtos', + 'helper_text' => 'Defina quantos produtos qualificados são necessários para aplicar o desconto.', + ], + 'reward_qty' => [ + 'label' => 'Qtd. de itens grátis', + 'helper_text' => 'Quantos de cada item serão descontados.', + ], + 'max_reward_qty' => [ + 'label' => 'Quantidade máxima de recompensa', + 'helper_text' => 'Quantidade máxima de produtos que podem ser descontados, independentemente do critério.', + ], + 'automatic_rewards' => [ + 'label' => 'Adicionar recompensas automaticamente', + 'helper_text' => 'Ative para adicionar produtos de recompensa quando não estiverem no carrinho.', + ], + 'fixed_value' => [ + 'label' => 'Valor fixo', + ], + 'percentage' => [ + 'label' => 'Percentual', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'status' => [ + 'label' => 'Status', + \Lunar\Models\Discount::ACTIVE => [ + 'label' => 'Ativo', + ], + \Lunar\Models\Discount::PENDING => [ + 'label' => 'Pendente', + ], + \Lunar\Models\Discount::EXPIRED => [ + 'label' => 'Expirado', + ], + \Lunar\Models\Discount::SCHEDULED => [ + 'label' => 'Agendado', + ], + ], + 'type' => [ + 'label' => 'Tipo', + ], + 'starts_at' => [ + 'label' => 'Data de início', + ], + 'ends_at' => [ + 'label' => 'Data de término', + ], + 'created_at' => [ + 'label' => 'Criado em', + ], + 'coupon' => [ + 'label' => 'Cupom', + ], + ], + 'pages' => [ + 'availability' => [ + 'label' => 'Disponibilidade', + ], + 'edit' => [ + 'title' => 'Informações básicas', + ], + 'limitations' => [ + 'label' => 'Limitações', + ], + ], + 'relationmanagers' => [ + 'collections' => [ + 'title' => 'Coleções', + 'description' => 'Selecione a quais coleções este desconto deve se limitar.', + 'actions' => [ + 'attach' => [ + 'label' => 'Anexar coleção', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'type' => [ + 'label' => 'Tipo', + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + ], + ], + 'customers' => [ + 'title' => 'Clientes', + 'description' => 'Selecione a quais clientes este desconto deve se limitar.', + 'actions' => [ + 'attach' => [ + 'label' => 'Anexar cliente', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + ], + ], + 'brands' => [ + 'title' => 'Marcas', + 'description' => 'Selecione a quais marcas este desconto deve se limitar.', + 'actions' => [ + 'attach' => [ + 'label' => 'Anexar marca', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'type' => [ + 'label' => 'Tipo', + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + ], + ], + 'products' => [ + 'title' => 'Produtos', + 'description' => 'Selecione a quais produtos este desconto deve se limitar.', + 'actions' => [ + 'attach' => [ + 'label' => 'Adicionar produto', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'type' => [ + 'label' => 'Tipo', + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + ], + ], + 'rewards' => [ + 'title' => 'Recompensas', + 'description' => 'Selecione quais produtos serão descontados se estiverem no carrinho e as condições acima forem atendidas.', + 'actions' => [ + 'attach' => [ + 'label' => 'Adicionar recompensa', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'type' => [ + 'label' => 'Tipo', + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + ], + ], + 'conditions' => [ + 'title' => 'Condições', + 'description' => 'Selecione as condições necessárias para aplicar o desconto.', + 'actions' => [ + 'attach' => [ + 'label' => 'Adicionar condição', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'type' => [ + 'label' => 'Tipo', + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + ], + ], + 'productvariants' => [ + 'title' => 'Variações de produto', + 'description' => 'Selecione a quais variações de produto este desconto deve se limitar.', + 'actions' => [ + 'attach' => [ + 'label' => 'Adicionar variação de produto', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'sku' => [ + 'label' => 'SKU', + ], + 'values' => [ + 'label' => 'Opção(ões)', + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitação', + ], + 'exclusion' => [ + 'label' => 'Exclusão', + ], + ], + ], + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/fieldtypes.php b/packages/admin/resources/lang/pt_BR/fieldtypes.php new file mode 100644 index 0000000000..4dadbe7690 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/fieldtypes.php @@ -0,0 +1,72 @@ + [ + 'label' => 'Dropdown', + 'form' => [ + 'lookups' => [ + 'label' => 'Consultas', + 'key_label' => 'Rótulo', + 'value_label' => 'Valor', + ], + ], + ], + 'listfield' => [ + 'label' => 'Campo de lista', + ], + 'text' => [ + 'label' => 'Texto', + 'form' => [ + 'richtext' => [ + 'label' => 'Rich Text', + ], + ], + ], + 'translatedtext' => [ + 'label' => 'Texto traduzido', + 'form' => [ + 'richtext' => [ + 'label' => 'Rich Text', + ], + 'locales' => 'Idiomas', + ], + ], + 'toggle' => [ + 'label' => 'Alternar', + ], + 'youtube' => [ + 'label' => 'YouTube', + ], + 'vimeo' => [ + 'label' => 'Vimeo', + ], + 'number' => [ + 'label' => 'Número', + 'form' => [ + 'min' => [ + 'label' => 'Mín.', + ], + 'max' => [ + 'label' => 'Máx.', + ], + ], + ], + 'file' => [ + 'label' => 'Arquivo', + 'form' => [ + 'file_types' => [ + 'label' => 'Tipos de arquivo permitidos', + 'placeholder' => 'Novo MIME', + ], + 'multiple' => [ + 'label' => 'Permitir múltiplos arquivos', + ], + 'min_files' => [ + 'label' => 'Mín. de arquivos', + ], + 'max_files' => [ + 'label' => 'Máx. de arquivos', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/global.php b/packages/admin/resources/lang/pt_BR/global.php new file mode 100644 index 0000000000..2bfc6203f0 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/global.php @@ -0,0 +1,12 @@ + [ + 'catalog' => 'Catálogo', + 'sales' => 'Vendas', + 'reports' => 'Relatórios', + 'settings' => 'Configurações', + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/language.php b/packages/admin/resources/lang/pt_BR/language.php new file mode 100644 index 0000000000..8c97548422 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/language.php @@ -0,0 +1,33 @@ + 'Idioma', + + 'plural_label' => 'Idiomas', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'code' => [ + 'label' => 'Código', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'code' => [ + 'label' => 'Código', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/order.php b/packages/admin/resources/lang/pt_BR/order.php new file mode 100644 index 0000000000..bffdfa9897 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/order.php @@ -0,0 +1,305 @@ + 'Pedido', + + 'plural_label' => 'Pedidos', + + 'breadcrumb' => [ + 'manage' => 'Gerenciar', + ], + + 'tabs' => [ + 'all' => 'Todos', + ], + + 'transactions' => [ + 'capture' => 'Capturado', + 'intent' => 'Intenção', + 'refund' => 'Reembolsado', + 'failed' => 'Falhou', + ], + + 'table' => [ + 'status' => [ + 'label' => 'Status', + ], + 'reference' => [ + 'label' => 'Referência', + ], + 'customer_reference' => [ + 'label' => 'Referência do cliente', + ], + 'customer' => [ + 'label' => 'Cliente', + ], + 'tags' => [ + 'label' => 'Tags', + ], + 'postcode' => [ + 'label' => 'CEP', + ], + 'email' => [ + 'label' => 'E-mail', + 'copy_message' => 'Endereço de e-mail copiado', + ], + 'phone' => [ + 'label' => 'Telefone', + ], + 'total' => [ + 'label' => 'Total', + ], + 'date' => [ + 'label' => 'Data', + ], + 'new_customer' => [ + 'label' => 'Tipo de cliente', + ], + 'placed_after' => [ + 'label' => 'Realizado após', + ], + 'placed_before' => [ + 'label' => 'Realizado antes', + ], + ], + + 'form' => [ + 'address' => [ + 'first_name' => [ + 'label' => 'Nome', + ], + 'last_name' => [ + 'label' => 'Sobrenome', + ], + 'line_one' => [ + 'label' => 'Endereço - linha 1', + ], + 'line_two' => [ + 'label' => 'Endereço - linha 2', + ], + 'line_three' => [ + 'label' => 'Endereço - linha 3', + ], + 'company_name' => [ + 'label' => 'Nome da empresa', + ], + 'tax_identifier' => [ + 'label' => 'Identificador fiscal', + ], + 'contact_phone' => [ + 'label' => 'Telefone', + ], + 'contact_email' => [ + 'label' => 'Endereço de e-mail', + ], + 'city' => [ + 'label' => 'Cidade', + ], + 'state' => [ + 'label' => 'Estado / Província', + ], + 'postcode' => [ + 'label' => 'CEP', + ], + 'country_id' => [ + 'label' => 'País', + ], + ], + + 'reference' => [ + 'label' => 'Referência', + ], + 'status' => [ + 'label' => 'Status', + ], + 'transaction' => [ + 'label' => 'Transação', + ], + 'amount' => [ + 'label' => 'Valor', + + 'hint' => [ + 'less_than_total' => 'Você está prestes a capturar um valor menor que o total da transação', + ], + ], + + 'notes' => [ + 'label' => 'Notas', + ], + 'confirm' => [ + 'label' => 'Confirmar', + + 'alert' => 'Confirmação necessária', + + 'hint' => [ + 'capture' => 'Confirme que deseja capturar este pagamento', + 'refund' => 'Confirme que deseja reembolsar este valor.', + ], + ], + ], + + 'infolist' => [ + 'notes' => [ + 'label' => 'Notas', + 'placeholder' => 'Sem notas neste pedido', + ], + 'delivery_instructions' => [ + 'label' => 'Instruções de entrega', + ], + 'shipping_total' => [ + 'label' => 'Total de frete', + ], + 'paid' => [ + 'label' => 'Pago', + ], + 'refund' => [ + 'label' => 'Reembolso', + ], + 'unit_price' => [ + 'label' => 'Preço unitário', + ], + 'quantity' => [ + 'label' => 'Quantidade', + ], + 'sub_total' => [ + 'label' => 'Subtotal', + ], + 'discount_total' => [ + 'label' => 'Total de desconto', + ], + 'total' => [ + 'label' => 'Total', + ], + 'current_stock_level' => [ + 'message' => 'Nível de estoque atual: :count', + ], + 'purchase_stock_level' => [ + 'message' => 'no momento do pedido: :count', + ], + 'status' => [ + 'label' => 'Status', + ], + 'reference' => [ + 'label' => 'Referência', + ], + 'customer_reference' => [ + 'label' => 'Referência do cliente', + ], + 'channel' => [ + 'label' => 'Canal', + ], + 'date_created' => [ + 'label' => 'Data de criação', + ], + 'date_placed' => [ + 'label' => 'Data do pedido', + ], + 'new_returning' => [ + 'label' => 'Novo / Recorrente', + ], + 'new_customer' => [ + 'label' => 'Cliente novo', + ], + 'returning_customer' => [ + 'label' => 'Cliente recorrente', + ], + 'shipping_address' => [ + 'label' => 'Endereço de entrega', + ], + 'billing_address' => [ + 'label' => 'Endereço de cobrança', + ], + 'address_not_set' => [ + 'label' => 'Nenhum endereço definido', + ], + 'billing_matches_shipping' => [ + 'label' => 'Igual ao endereço de entrega', + ], + 'additional_info' => [ + 'label' => 'Informações adicionais', + ], + 'no_additional_info' => [ + 'label' => 'Sem informações adicionais', + ], + 'tags' => [ + 'label' => 'Tags', + ], + 'timeline' => [ + 'label' => 'Linha do tempo', + ], + 'transactions' => [ + 'label' => 'Transações', + 'placeholder' => 'Sem transações', + ], + 'alert' => [ + 'requires_capture' => 'Este pedido ainda requer captura do pagamento.', + 'partially_refunded' => 'Este pedido foi parcialmente reembolsado.', + 'refunded' => 'Este pedido foi reembolsado.', + ], + ], + + 'action' => [ + 'bulk_update_status' => [ + 'label' => 'Atualizar status', + 'notification' => 'Status dos pedidos atualizado', + ], + 'update_status' => [ + 'new_status' => [ + 'label' => 'Novo status', + ], + 'additional_content' => [ + 'label' => 'Conteúdo adicional', + ], + 'additional_email_recipient' => [ + 'label' => 'Destinatário de e-mail adicional', + 'placeholder' => 'opcional', + ], + ], + 'download_order_pdf' => [ + 'label' => 'Baixar PDF', + 'notification' => 'Baixando PDF do pedido', + ], + 'edit_address' => [ + 'label' => 'Editar', + + 'notification' => [ + 'error' => 'Erro', + + 'billing_address' => [ + 'saved' => 'Endereço de cobrança salvo', + ], + + 'shipping_address' => [ + 'saved' => 'Endereço de entrega salvo', + ], + ], + ], + 'edit_tags' => [ + 'label' => 'Editar', + 'form' => [ + 'tags' => [ + 'label' => 'Tags', + 'helper_text' => 'Separe tags pressionando Enter, Tab ou vírgula (,)', + ], + ], + ], + 'capture_payment' => [ + 'label' => 'Capturar pagamento', + + 'notification' => [ + 'error' => 'Houve um problema na captura', + 'success' => 'Captura realizada com sucesso', + ], + ], + 'refund_payment' => [ + 'label' => 'Reembolsar', + + 'notification' => [ + 'error' => 'Houve um problema no reembolso', + 'success' => 'Reembolso realizado com sucesso', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/product.php b/packages/admin/resources/lang/pt_BR/product.php new file mode 100644 index 0000000000..f2407d31e1 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/product.php @@ -0,0 +1,131 @@ + 'Produto', + + 'plural_label' => 'Produtos', + + 'tabs' => [ + 'all' => 'Todos', + ], + + 'status' => [ + 'unpublished' => [ + 'content' => 'Atualmente em rascunho, este produto está oculto em todos os canais e grupos de clientes.', + ], + 'availability' => [ + 'customer_groups' => 'Este produto está indisponível para todos os grupos de clientes.', + 'channels' => 'Este produto está indisponível para todos os canais.', + ], + ], + + 'table' => [ + 'status' => [ + 'label' => 'Status', + 'states' => [ + 'deleted' => 'Excluído', + 'draft' => 'Rascunho', + 'published' => 'Publicado', + ], + ], + 'name' => [ + 'label' => 'Nome', + ], + 'brand' => [ + 'label' => 'Marca', + ], + 'sku' => [ + 'label' => 'SKU', + ], + 'stock' => [ + 'label' => 'Estoque', + ], + 'producttype' => [ + 'label' => 'Tipo de produto', + ], + ], + + 'actions' => [ + 'edit_status' => [ + 'label' => 'Atualizar status', + 'heading' => 'Atualizar status', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'brand' => [ + 'label' => 'Marca', + ], + 'sku' => [ + 'label' => 'SKU', + ], + 'producttype' => [ + 'label' => 'Tipo de produto', + ], + 'status' => [ + 'label' => 'Status', + 'options' => [ + 'published' => [ + 'label' => 'Publicado', + 'description' => 'Este produto ficará disponível em todos os canais e grupos de clientes habilitados', + ], + 'draft' => [ + 'label' => 'Rascunho', + 'description' => 'Este produto ficará oculto em todos os canais e grupos de clientes', + ], + ], + ], + 'tags' => [ + 'label' => 'Tags', + 'helper_text' => 'Separe tags pressionando Enter, Tab ou vírgula (,)', + ], + 'collections' => [ + 'label' => 'Coleções', + 'select_collection' => 'Selecione uma coleção', + ], + ], + + 'pages' => [ + 'availability' => [ + 'label' => 'Disponibilidade', + ], + 'edit' => [ + 'title' => 'Informações básicas', + ], + 'identifiers' => [ + 'label' => 'Identificadores do produto', + ], + 'inventory' => [ + 'label' => 'Estoque', + ], + 'pricing' => [ + 'form' => [ + 'tax_class_id' => [ + 'label' => 'Classe de imposto', + ], + 'tax_ref' => [ + 'label' => 'Referência de imposto', + 'helper_text' => 'Opcional, para integração com sistemas de terceiros.', + ], + ], + ], + 'shipping' => [ + 'label' => 'Envio', + ], + 'variants' => [ + 'label' => 'Variações', + ], + 'collections' => [ + 'label' => 'Coleções', + 'select_collection' => 'Selecione uma coleção', + ], + 'associations' => [ + 'label' => 'Associações de produto', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/productoption.php b/packages/admin/resources/lang/pt_BR/productoption.php new file mode 100644 index 0000000000..65cdf1db50 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/productoption.php @@ -0,0 +1,127 @@ + 'Opção de produto', + + 'plural_label' => 'Opções de produto', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'label' => [ + 'label' => 'Rótulo', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + 'shared' => [ + 'label' => 'Compartilhada', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'label' => [ + 'label' => 'Rótulo', + ], + 'handle' => [ + 'label' => 'Identificador', + ], + ], + + 'widgets' => [ + 'product-options' => [ + 'notifications' => [ + 'save-variants' => [ + 'success' => [ + 'title' => 'Variações de produto salvas', + ], + ], + ], + 'actions' => [ + 'cancel' => [ + 'label' => 'Cancelar', + ], + 'save-options' => [ + 'label' => 'Salvar opções', + ], + 'add-shared-option' => [ + 'label' => 'Adicionar opção compartilhada', + 'form' => [ + 'product_option' => [ + 'label' => 'Opção de produto', + ], + 'no_shared_components' => [ + 'label' => 'Não há opções compartilhadas disponíveis.', + ], + 'preselect' => [ + 'label' => 'Pré-selecionar todos os valores por padrão.', + ], + ], + ], + 'add-restricted-option' => [ + 'label' => 'Adicionar opção', + ], + ], + 'options-list' => [ + 'empty' => [ + 'heading' => 'Não há opções de produto configuradas', + 'description' => 'Adicione uma opção de produto compartilhada ou restrita para começar a gerar variações.', + ], + ], + 'options-table' => [ + 'title' => 'Opções de produto', + 'configure-options' => [ + 'label' => 'Configurar opções', + ], + 'table' => [ + 'option' => [ + 'label' => 'Opção', + ], + 'values' => [ + 'label' => 'Valores', + ], + ], + ], + 'variants-table' => [ + 'title' => 'Variações de produto', + 'actions' => [ + 'create' => [ + 'label' => 'Criar variação', + ], + 'edit' => [ + 'label' => 'Editar', + ], + 'delete' => [ + 'label' => 'Excluir', + ], + ], + 'empty' => [ + 'heading' => 'Nenhuma variação configurada', + ], + 'table' => [ + 'new' => [ + 'label' => 'NOVO', + ], + 'option' => [ + 'label' => 'Opção', + ], + 'sku' => [ + 'label' => 'SKU', + ], + 'price' => [ + 'label' => 'Preço', + ], + 'stock' => [ + 'label' => 'Estoque', + ], + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/producttype.php b/packages/admin/resources/lang/pt_BR/producttype.php new file mode 100644 index 0000000000..fd61067ef3 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/producttype.php @@ -0,0 +1,52 @@ + 'Tipo de produto', + + 'plural_label' => 'Tipos de produto', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'products_count' => [ + 'label' => 'Qtd. de produtos', + ], + 'product_attributes_count' => [ + 'label' => 'Atributos do produto', + ], + 'variant_attributes_count' => [ + 'label' => 'Atributos da variação', + ], + ], + + 'tabs' => [ + 'product_attributes' => [ + 'label' => 'Atributos do produto', + ], + 'variant_attributes' => [ + 'label' => 'Atributos da variação', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + ], + + 'attributes' => [ + 'no_groups' => 'Não há grupos de atributos disponíveis.', + 'no_attributes' => 'Não há atributos disponíveis.', + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Este tipo de produto não pode ser excluído pois há produtos associados.', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/productvariant.php b/packages/admin/resources/lang/pt_BR/productvariant.php new file mode 100644 index 0000000000..905f41fd1c --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/productvariant.php @@ -0,0 +1,105 @@ + 'Variação de produto', + 'plural_label' => 'Variações de produto', + 'pages' => [ + 'edit' => [ + 'title' => 'Informações básicas', + ], + 'media' => [ + 'title' => 'Mídia', + 'form' => [ + 'no_selection' => [ + 'label' => 'Você não possui uma imagem selecionada para esta variação.', + ], + 'no_media_available' => [ + 'label' => 'Não há mídia disponível neste produto.', + ], + 'images' => [ + 'label' => 'Imagem principal', + 'helper_text' => 'Selecione a imagem do produto que representa esta variação.', + ], + ], + ], + 'identifiers' => [ + 'title' => 'Identificadores', + ], + 'inventory' => [ + 'title' => 'Estoque', + ], + 'shipping' => [ + 'title' => 'Envio', + ], + ], + 'form' => [ + 'sku' => [ + 'label' => 'SKU', + ], + 'gtin' => [ + 'label' => 'GTIN (Global Trade Item Number)', + ], + 'mpn' => [ + 'label' => 'MPN (Manufacturer Part Number)', + ], + 'ean' => [ + 'label' => 'UPC/EAN', + ], + 'stock' => [ + 'label' => 'Em estoque', + ], + 'backorder' => [ + 'label' => 'Sob encomenda', + ], + 'purchasable' => [ + 'label' => 'Disponibilidade de compra', + 'options' => [ + 'always' => 'Sempre', + 'in_stock' => 'Em estoque', + 'in_stock_or_on_backorder' => 'Em estoque ou sob encomenda', + ], + ], + 'unit_quantity' => [ + 'label' => 'Quantidade por unidade', + 'helper_text' => 'Quantos itens individuais formam 1 unidade.', + ], + 'min_quantity' => [ + 'label' => 'Quantidade mínima', + 'helper_text' => 'Quantidade mínima da variação que pode ser comprada em uma única compra.', + ], + 'quantity_increment' => [ + 'label' => 'Incremento de quantidade', + 'helper_text' => 'A variação deve ser comprada em múltiplos desta quantidade.', + ], + 'tax_class_id' => [ + 'label' => 'Classe de imposto', + ], + 'shippable' => [ + 'label' => 'Enviável', + ], + 'length_value' => [ + 'label' => 'Comprimento', + ], + 'length_unit' => [ + 'label' => 'Unidade de comprimento', + ], + 'width_value' => [ + 'label' => 'Largura', + ], + 'width_unit' => [ + 'label' => 'Unidade de largura', + ], + 'height_value' => [ + 'label' => 'Altura', + ], + 'height_unit' => [ + 'label' => 'Unidade de altura', + ], + 'weight_value' => [ + 'label' => 'Peso', + ], + 'weight_unit' => [ + 'label' => 'Unidade de peso', + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/relationmanagers.php b/packages/admin/resources/lang/pt_BR/relationmanagers.php new file mode 100644 index 0000000000..078f9b6c2b --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/relationmanagers.php @@ -0,0 +1,285 @@ + [ + 'title' => 'Grupos de clientes', + 'actions' => [ + 'attach' => [ + 'label' => 'Anexar grupo de clientes', + ], + ], + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'enabled' => [ + 'label' => 'Ativado', + ], + 'starts_at' => [ + 'label' => 'Data de início', + ], + 'ends_at' => [ + 'label' => 'Data de término', + ], + 'visible' => [ + 'label' => 'Visível', + ], + 'purchasable' => [ + 'label' => 'Disponível para compra', + ], + ], + 'table' => [ + 'description' => 'Associe grupos de clientes a este :type para determinar sua disponibilidade.', + 'name' => [ + 'label' => 'Nome', + ], + 'enabled' => [ + 'label' => 'Ativado', + ], + 'starts_at' => [ + 'label' => 'Data de início', + ], + 'ends_at' => [ + 'label' => 'Data de término', + ], + 'visible' => [ + 'label' => 'Visível', + ], + 'purchasable' => [ + 'label' => 'Disponível para compra', + ], + ], + ], + 'channels' => [ + 'title' => 'Canais', + 'actions' => [ + 'attach' => [ + 'label' => 'Agendar outro canal', + ], + ], + 'form' => [ + 'enabled' => [ + 'label' => 'Ativado', + 'helper_text_false' => 'Este canal não será ativado mesmo que uma data de início esteja definida.', + ], + 'starts_at' => [ + 'label' => 'Data de início', + 'helper_text' => 'Deixe em branco para ficar disponível a partir de qualquer data.', + ], + 'ends_at' => [ + 'label' => 'Data de término', + 'helper_text' => 'Deixe em branco para ficar disponível indefinidamente.', + ], + ], + 'table' => [ + 'description' => 'Defina quais canais estão ativados e agende a disponibilidade.', + 'name' => [ + 'label' => 'Nome', + ], + 'enabled' => [ + 'label' => 'Ativado', + ], + 'starts_at' => [ + 'label' => 'Data de início', + ], + 'ends_at' => [ + 'label' => 'Data de término', + ], + ], + ], + 'medias' => [ + 'title' => 'Mídia', + 'title_plural' => 'Mídia', + 'actions' => [ + 'attach' => [ + 'label' => 'Anexar mídia', + ], + 'create' => [ + 'label' => 'Criar mídia', + ], + 'detach' => [ + 'label' => 'Desanexar', + ], + 'view' => [ + 'label' => 'Visualizar', + ], + ], + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'media' => [ + 'label' => 'Imagem', + ], + 'primary' => [ + 'label' => 'Principal', + ], + ], + 'table' => [ + 'image' => [ + 'label' => 'Imagem', + ], + 'file' => [ + 'label' => 'Arquivo', + ], + 'name' => [ + 'label' => 'Nome', + ], + 'primary' => [ + 'label' => 'Principal', + ], + ], + 'all_media_attached' => 'Não há imagens de produtos disponíveis para anexar', + 'variant_description' => 'Anexe imagens de produto a esta variação', + ], + 'urls' => [ + 'title' => 'URL', + 'title_plural' => 'URLs', + 'actions' => [ + 'create' => [ + 'label' => 'Criar URL', + ], + ], + 'filters' => [ + 'language_id' => [ + 'label' => 'Idioma', + ], + ], + 'form' => [ + 'slug' => [ + 'label' => 'Slug', + ], + 'default' => [ + 'label' => 'Padrão', + ], + 'language' => [ + 'label' => 'Idioma', + ], + ], + 'table' => [ + 'slug' => [ + 'label' => 'Slug', + ], + 'default' => [ + 'label' => 'Padrão', + ], + 'language' => [ + 'label' => 'Idioma', + ], + ], + ], + 'customer_group_pricing' => [ + 'title' => 'Preços por grupo de clientes', + 'title_plural' => 'Preços por grupo de clientes', + 'table' => [ + 'heading' => 'Preços por grupo de clientes', + 'description' => 'Associe preços a grupos de clientes para determinar o preço do produto.', + 'empty_state' => [ + 'label' => 'Não existem preços por grupo de clientes.', + 'description' => 'Crie um preço para grupo de clientes para começar.', + ], + 'actions' => [ + 'create' => [ + 'label' => 'Adicionar preço por grupo de clientes', + 'modal' => [ + 'heading' => 'Criar preço por grupo de clientes', + ], + ], + ], + ], + ], + 'pricing' => [ + 'title' => 'Preço', + 'title_plural' => 'Preços', + 'tab_name' => 'Faixas de preço', + 'table' => [ + 'heading' => 'Faixas de preço', + 'description' => 'Reduza o preço quando o cliente comprar em maiores quantidades.', + 'empty_state' => [ + 'label' => 'Não existem faixas de preço.', + ], + 'actions' => [ + 'create' => [ + 'label' => 'Adicionar faixa de preço', + ], + ], + 'price' => [ + 'label' => 'Preço', + ], + 'customer_group' => [ + 'label' => 'Grupo de clientes', + 'placeholder' => 'Todos os grupos de clientes', + ], + 'min_quantity' => [ + 'label' => 'Quantidade mínima', + ], + 'currency' => [ + 'label' => 'Moeda', + ], + ], + 'form' => [ + 'price' => [ + 'label' => 'Preço', + 'helper_text' => 'Preço de compra, antes de descontos.', + ], + 'customer_group_id' => [ + 'label' => 'Grupo de clientes', + 'placeholder' => 'Todos os grupos de clientes', + 'helper_text' => 'Selecione a qual grupo de clientes aplicar este preço.', + ], + 'min_quantity' => [ + 'label' => 'Quantidade mínima', + 'helper_text' => 'Selecione a quantidade mínima para a qual este preço estará disponível.', + 'validation' => [ + 'unique' => 'Grupo de clientes e quantidade mínima devem ser únicos.', + ], + ], + 'currency_id' => [ + 'label' => 'Moeda', + 'helper_text' => 'Selecione a moeda para este preço.', + ], + 'compare_price' => [ + 'label' => 'Preço de comparação', + 'helper_text' => 'O preço original (ou preço sugerido) para comparação com o preço de compra.', + ], + 'basePrices' => [ + 'title' => 'Preços', + 'form' => [ + 'price' => [ + 'label' => 'Preço', + 'helper_text' => 'Preço de compra, antes de descontos.', + 'sync_price' => 'Preço sincronizado com a moeda padrão.', + ], + 'compare_price' => [ + 'label' => 'Preço de comparação', + 'helper_text' => 'O preço original (ou preço sugerido) para comparação com o preço de compra.', + ], + ], + 'tooltip' => 'Gerado automaticamente com base nas taxas de câmbio.', + ], + ], + ], + 'tax_rate_amounts' => [ + 'table' => [ + 'description' => '', + 'percentage' => [ + 'label' => 'Percentual', + ], + 'tax_class' => [ + 'label' => 'Classe de imposto', + ], + ], + ], + 'values' => [ + 'title' => 'Valores', + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'position' => [ + 'label' => 'Posição', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/staff.php b/packages/admin/resources/lang/pt_BR/staff.php new file mode 100644 index 0000000000..3ddda69733 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/staff.php @@ -0,0 +1,81 @@ + 'Equipe', + + 'plural_label' => 'Equipe', + + 'table' => [ + 'first_name' => [ + 'label' => 'Nome', + ], + 'last_name' => [ + 'label' => 'Sobrenome', + ], + 'email' => [ + 'label' => 'E-mail', + ], + 'admin' => [ + 'badge' => 'Super Admin', + ], + ], + + 'form' => [ + 'first_name' => [ + 'label' => 'Nome', + ], + 'last_name' => [ + 'label' => 'Sobrenome', + ], + 'email' => [ + 'label' => 'E-mail', + ], + 'password' => [ + 'label' => 'Senha', + 'hint' => 'Redefinir senha', + ], + 'admin' => [ + 'label' => 'Super Admin', + 'helper' => 'Funções de super admin não podem ser alteradas no hub.', + ], + 'roles' => [ + 'label' => 'Funções', + 'helper' => ':roles têm acesso total', + ], + 'permissions' => [ + 'label' => 'Permissões', + ], + 'role' => [ + 'label' => 'Nome da função', + ], + ], + + 'action' => [ + 'acl' => [ + 'label' => 'Controle de acesso', + ], + 'add-role' => [ + 'label' => 'Adicionar função', + ], + 'delete-role' => [ + 'label' => 'Excluir função', + 'heading' => 'Excluir função: :role', + ], + ], + + 'acl' => [ + 'title' => 'Controle de acesso', + 'tooltip' => [ + 'roles-included' => 'Permissão incluída nas seguintes funções', + ], + 'notification' => [ + 'updated' => 'Atualizado', + 'error' => 'Erro', + 'no-role' => 'Função não registrada no Lunar', + 'no-permission' => 'Permissão não registrada no Lunar', + 'no-role-permission' => 'Função e permissão não registradas no Lunar', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/tag.php b/packages/admin/resources/lang/pt_BR/tag.php new file mode 100644 index 0000000000..b45f70b296 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/tag.php @@ -0,0 +1,21 @@ + 'Tag', + + 'plural_label' => 'Tags', + + 'table' => [ + 'value' => [ + 'label' => 'Valor', + ], + ], + + 'form' => [ + 'value' => [ + 'label' => 'Valor', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/tax.php b/packages/admin/resources/lang/pt_BR/tax.php new file mode 100644 index 0000000000..d220c56e54 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/tax.php @@ -0,0 +1,9 @@ + 'Imposto', + + 'plural_label' => 'Impostos', + +]; diff --git a/packages/admin/resources/lang/pt_BR/taxclass.php b/packages/admin/resources/lang/pt_BR/taxclass.php new file mode 100644 index 0000000000..215afc4a56 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/taxclass.php @@ -0,0 +1,32 @@ + 'Classe de imposto', + + 'plural_label' => 'Classes de imposto', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + 'delete' => [ + 'error' => [ + 'title' => 'Não é possível excluir a classe de imposto', + 'body' => 'Esta classe de imposto possui variações de produto associadas e não pode ser excluída.', + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/taxrate.php b/packages/admin/resources/lang/pt_BR/taxrate.php new file mode 100644 index 0000000000..adb9ce6937 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/taxrate.php @@ -0,0 +1,33 @@ + 'Taxa de imposto', + + 'plural_label' => 'Taxas de imposto', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'tax_zone' => [ + 'label' => 'Zona de imposto', + ], + 'priority' => [ + 'label' => 'Prioridade', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'priority' => [ + 'label' => 'Prioridade', + ], + 'tax_zone_id' => [ + 'label' => 'Zona de imposto', + ], + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/taxzone.php b/packages/admin/resources/lang/pt_BR/taxzone.php new file mode 100644 index 0000000000..55f5754ea0 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/taxzone.php @@ -0,0 +1,69 @@ + 'Zona de imposto', + + 'plural_label' => 'Zonas de imposto', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'zone_type' => [ + 'label' => 'Tipo de zona', + ], + 'active' => [ + 'label' => 'Ativa', + ], + 'default' => [ + 'label' => 'Padrão', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'zone_type' => [ + 'label' => 'Tipo de zona', + 'options' => [ + 'country' => 'Limitar a países', + 'states' => 'Limitar a estados', + 'postcodes' => 'Limitar a CEPs', + ], + ], + 'price_display' => [ + 'label' => 'Exibição de preço', + 'options' => [ + 'include_tax' => 'Incluir imposto', + 'exclude_tax' => 'Excluir imposto', + ], + ], + 'active' => [ + 'label' => 'Ativa', + ], + 'default' => [ + 'label' => 'Padrão', + ], + + 'zone_countries' => [ + 'label' => 'Países', + ], + + 'zone_country' => [ + 'label' => 'País', + ], + + 'zone_states' => [ + 'label' => 'Estados', + ], + + 'zone_postcodes' => [ + 'label' => 'CEPs', + 'helper' => 'Liste cada CEP em uma nova linha. Suporta curingas como NW*', + ], + + ], + +]; diff --git a/packages/admin/resources/lang/pt_BR/user.php b/packages/admin/resources/lang/pt_BR/user.php new file mode 100644 index 0000000000..e9fb25aa28 --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/user.php @@ -0,0 +1,29 @@ + 'Usuário', + + 'plural_label' => 'Usuários', + + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'email' => [ + 'label' => 'E-mail', + ], + ], + + 'form' => [ + 'email' => [ + 'label' => 'E-mail', + ], + 'password' => [ + 'label' => 'Nova senha', + ], + 'password_confirmation' => [ + 'label' => 'Confirmar nova senha', + ], + ], +]; diff --git a/packages/admin/resources/lang/pt_BR/widgets.php b/packages/admin/resources/lang/pt_BR/widgets.php new file mode 100644 index 0000000000..96ad1d535a --- /dev/null +++ b/packages/admin/resources/lang/pt_BR/widgets.php @@ -0,0 +1,118 @@ + [ + 'orders' => [ + 'order_stats_overview' => [ + 'stat_one' => [ + 'label' => 'Pedidos hoje', + 'increase' => ':percentage% de aumento em relação a :count ontem', + 'decrease' => ':percentage% de queda em relação a :count ontem', + 'neutral' => 'Sem mudança em relação a ontem', + ], + 'stat_two' => [ + 'label' => 'Pedidos nos últimos 7 dias', + 'increase' => ':percentage% de aumento em relação a :count no período anterior', + 'decrease' => ':percentage% de queda em relação a :count no período anterior', + 'neutral' => 'Sem mudança em relação ao período anterior', + ], + 'stat_three' => [ + 'label' => 'Pedidos nos últimos 30 dias', + 'increase' => ':percentage% de aumento em relação a :count no período anterior', + 'decrease' => ':percentage% de queda em relação a :count no período anterior', + 'neutral' => 'Sem mudança em relação ao período anterior', + ], + 'stat_four' => [ + 'label' => 'Vendas hoje', + 'increase' => ':percentage% de aumento em relação a :total ontem', + 'decrease' => ':percentage% de queda em relação a :total ontem', + 'neutral' => 'Sem mudança em relação a ontem', + ], + 'stat_five' => [ + 'label' => 'Vendas nos últimos 7 dias', + 'increase' => ':percentage% de aumento em relação a :total no período anterior', + 'decrease' => ':percentage% de queda em relação a :total no período anterior', + 'neutral' => 'Sem mudança em relação ao período anterior', + ], + 'stat_six' => [ + 'label' => 'Vendas nos últimos 30 dias', + 'increase' => ':percentage% de aumento em relação a :total no período anterior', + 'decrease' => ':percentage% de queda em relação a :total no período anterior', + 'neutral' => 'Sem mudança em relação ao período anterior', + ], + ], + 'order_totals_chart' => [ + 'heading' => 'Totais de pedidos do último ano', + 'series_one' => [ + 'label' => 'Este período', + ], + 'series_two' => [ + 'label' => 'Período anterior', + ], + 'yaxis' => [ + 'label' => 'Faturamento :currency', + ], + ], + 'order_sales_chart' => [ + 'heading' => 'Relatório de pedidos/vendas', + 'series_one' => [ + 'label' => 'Pedidos', + ], + 'series_two' => [ + 'label' => 'Receita', + ], + 'yaxis' => [ + 'series_one' => [ + 'label' => '# Pedidos', + ], + 'series_two' => [ + 'label' => 'Valor total', + ], + ], + ], + 'average_order_value' => [ + 'heading' => 'Ticket médio', + ], + 'new_returning_customers' => [ + 'heading' => 'Clientes novos x recorrentes', + 'series_one' => [ + 'label' => 'Clientes novos', + ], + 'series_two' => [ + 'label' => 'Clientes recorrentes', + ], + ], + 'popular_products' => [ + 'heading' => 'Mais vendidos (últimos 12 meses)', + 'description' => 'Os números se baseiam no número de vezes que um produto aparece em um pedido, e não na quantidade pedida.', + ], + 'latest_orders' => [ + 'heading' => 'Pedidos mais recentes', + ], + ], + ], + 'customer' => [ + 'stats_overview' => [ + 'total_orders' => [ + 'label' => 'Total de pedidos', + ], + 'avg_spend' => [ + 'label' => 'Gasto médio', + ], + 'total_spend' => [ + 'label' => 'Gasto total', + ], + ], + ], + 'variant_switcher' => [ + 'label' => 'Trocar variação', + 'table' => [ + 'sku' => [ + 'label' => 'SKU', + ], + 'values' => [ + 'label' => 'Valores', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/actions.php b/packages/admin/resources/lang/ro/actions.php new file mode 100644 index 0000000000..42b316669f --- /dev/null +++ b/packages/admin/resources/lang/ro/actions.php @@ -0,0 +1,52 @@ + [ + 'create_root' => [ + 'label' => 'Creează colecție rădăcină', + ], + 'create_child' => [ + 'label' => 'Creează colecție copil', + ], + 'move' => [ + 'label' => 'Mută colecția', + ], + 'delete' => [ + 'label' => 'Șterge', + 'notifications' => [ + 'cannot_delete' => [ + 'title' => 'Nu se poate șterge', + 'body' => 'Această colecție are colecții copil și nu poate fi ștearsă.', + ], + ], + ], + ], + 'orders' => [ + 'update_status' => [ + 'label' => 'Actualizează starea', + 'wizard' => [ + 'step_one' => [ + 'label' => 'Stare', + ], + 'step_two' => [ + 'label' => 'E-mailuri și notificări', + 'no_mailers' => 'Nu există e-mailuri disponibile pentru această stare.', + ], + 'step_three' => [ + 'label' => 'Previzualizare și salvare', + 'no_mailers' => 'Nu s-au ales e-mailuri pentru previzualizare.', + ], + ], + 'notification' => [ + 'label' => 'Starea comenzii a fost actualizată', + ], + 'billing_email' => [ + 'label' => 'E-mail facturare', + ], + 'shipping_email' => [ + 'label' => 'E-mail livrare', + ], + ], + + ], +]; diff --git a/packages/admin/resources/lang/ro/activity.php b/packages/admin/resources/lang/ro/activity.php new file mode 100644 index 0000000000..140f76b6ba --- /dev/null +++ b/packages/admin/resources/lang/ro/activity.php @@ -0,0 +1,29 @@ + 'Activitate', + + 'plural_label' => 'Activități', + + 'table' => [ + 'subject' => 'Subiect', + 'description' => 'Descriere', + 'log' => 'Jurnal', + 'logged_at' => 'Înregistrat la', + 'event' => 'Eveniment', + 'logged_from' => 'Înregistrat de la', + 'logged_until' => 'Înregistrat până la', + ], + + 'form' => [ + 'causer_type' => 'Tipul cauzatorului', + 'causer_id' => 'ID autor', + 'subject_type' => 'Tip subiect', + 'subject_id' => 'ID subiect', + 'description' => 'Descriere', + 'attributes' => 'Atribute', + 'old' => 'Vechi', + ], + +]; diff --git a/packages/admin/resources/lang/ro/address.php b/packages/admin/resources/lang/ro/address.php new file mode 100644 index 0000000000..dd968dfdc1 --- /dev/null +++ b/packages/admin/resources/lang/ro/address.php @@ -0,0 +1,99 @@ + 'Adresă', + + 'plural_label' => 'Adrese', + + 'table' => [ + 'title' => [ + 'label' => 'Titlu', + ], + 'first_name' => [ + 'label' => 'Prenume', + ], + 'last_name' => [ + 'label' => 'Nume', + ], + 'company_name' => [ + 'label' => 'Nume companie', + ], + 'tax_identifier' => [ + 'label' => 'Cod fiscal', + ], + 'line_one' => [ + 'label' => 'Adresă', + ], + 'line_two' => [ + 'label' => 'Linia doi', + ], + 'line_three' => [ + 'label' => 'Linia trei', + ], + 'city' => [ + 'label' => 'Oraș', + ], + 'country_id' => [ + 'label' => 'Țară', + ], + 'state' => [ + 'label' => 'Județ', + ], + 'postcode' => [ + 'label' => 'Cod poștal', + ], + 'contact_email' => [ + 'label' => 'E-mail contact', + ], + 'contact_phone' => [ + 'label' => 'Telefon contact', + ], + ], + + 'form' => [ + 'title' => [ + 'label' => 'Titlu', + ], + 'first_name' => [ + 'label' => 'Prenume', + ], + 'last_name' => [ + 'label' => 'Nume', + ], + 'company_name' => [ + 'label' => 'Nume companie', + ], + 'tax_identifier' => [ + 'label' => 'Cod fiscal', + ], + 'line_one' => [ + 'label' => 'Linia unu', + ], + 'line_two' => [ + 'label' => 'Linia doi', + ], + 'line_three' => [ + 'label' => 'Linia trei', + ], + 'city' => [ + 'label' => 'Oraș', + ], + 'country_id' => [ + 'label' => 'Țară', + ], + 'state' => [ + 'label' => 'Județ', + ], + 'postcode' => [ + 'label' => 'Cod poștal', + ], + 'contact_email' => [ + 'label' => 'E-mail contact', + ], + 'contact_phone' => [ + 'label' => 'Telefon contact', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/attribute.php b/packages/admin/resources/lang/ro/attribute.php new file mode 100644 index 0000000000..2ed68dd12d --- /dev/null +++ b/packages/admin/resources/lang/ro/attribute.php @@ -0,0 +1,55 @@ + 'Atribut', + + 'plural_label' => 'Atribute', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'description' => [ + 'label' => 'Descriere', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'type' => [ + 'label' => 'Tip', + ], + ], + + 'form' => [ + 'attributable_type' => [ + 'label' => 'Tip', + ], + 'name' => [ + 'label' => 'Nume', + ], + 'description' => [ + 'label' => 'Descriere', + 'helper' => 'Folosit pentru a afișa textul de ajutor sub câmp', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'searchable' => [ + 'label' => 'Căutabil', + ], + 'filterable' => [ + 'label' => 'Filtrabil', + ], + 'required' => [ + 'label' => 'Obligatoriu', + ], + 'type' => [ + 'label' => 'Tip', + ], + 'validation_rules' => [ + 'label' => 'Reguli de validare', + 'helper' => 'Reguli pentru câmpul atribut, de ex.: min:1|max:10|...', + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/attributegroup.php b/packages/admin/resources/lang/ro/attributegroup.php new file mode 100644 index 0000000000..b913126cde --- /dev/null +++ b/packages/admin/resources/lang/ro/attributegroup.php @@ -0,0 +1,46 @@ + 'Grup de atribute', + + 'plural_label' => 'Grupe de atribute', + + 'table' => [ + 'attributable_type' => [ + 'label' => 'Tip', + ], + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'position' => [ + 'label' => 'Poziție', + ], + ], + + 'form' => [ + 'attributable_type' => [ + 'label' => 'Tip', + ], + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'position' => [ + 'label' => 'Poziție', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Acest grup de atribute nu poate fi șters deoarece are atribute asociate.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/auth.php b/packages/admin/resources/lang/ro/auth.php new file mode 100644 index 0000000000..69ecef876a --- /dev/null +++ b/packages/admin/resources/lang/ro/auth.php @@ -0,0 +1,32 @@ + 'Administrator', + 'roles.admin.description' => 'Administrator cu acces complet', + 'roles.staff.label' => 'Personal', + 'roles.staff.description' => 'Personal cu acces de bază', + /** + * Permissions. + */ + 'permissions.settings.label' => 'Setări', + 'permissions.settings.description' => 'Oferă acces la zona de setări a hub-ului', + 'permissions.settings:core.label' => 'Setări de bază', + 'permissions.settings:core.description' => 'Acces la setările de bază ale magazinului, precum canale, limbi, monede etc.', + 'permissions.settings:manage-staff.label' => 'Gestionare personal', + 'permissions.settings:manage-staff.description' => 'Permite membrului de personal să editeze alți membri', + 'permissions.settings:manage-attributes.label' => 'Gestionare atribute', + 'permissions.settings:manage-attributes.description' => 'Permite personalului să editeze și să creeze atribute suplimentare', + 'permissions.catalog:manage-products.label' => 'Gestionare produse', + 'permissions.catalog:manage-products.description' => 'Permite personalului să editeze produse, tipuri de produse și mărci', + 'permissions.catalog:manage-collections.label' => 'Gestionare colecții', + 'permissions.catalog:manage-collections.description' => 'Permite personalului să editeze colecții și grupurile acestora', + 'permissions.sales:manage-orders.label' => 'Gestionare comenzi', + 'permissions.sales:manage-orders.description' => 'Permite personalului să gestioneze comenzile', + 'permissions.sales:manage-customers.label' => 'Gestionare clienți', + 'permissions.sales:manage-customers.description' => 'Permite personalului să gestioneze clienții', + 'permissions.sales:manage-discounts.label' => 'Gestionare reduceri', + 'permissions.sales:manage-discounts.description' => 'Permite personalului să gestioneze reducerile', +]; diff --git a/packages/admin/resources/lang/ro/brand.php b/packages/admin/resources/lang/ro/brand.php new file mode 100644 index 0000000000..060b203515 --- /dev/null +++ b/packages/admin/resources/lang/ro/brand.php @@ -0,0 +1,75 @@ + 'Marcă', + + 'plural_label' => 'Mărci', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'products_count' => [ + 'label' => 'Nr. produse', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Această marcă nu poate fi ștearsă deoarece are produse asociate.', + ], + ], + ], + 'pages' => [ + 'edit' => [ + 'title' => 'Informații de bază', + ], + 'products' => [ + 'label' => 'Produse', + 'actions' => [ + 'attach' => [ + 'label' => 'Asociază un produs', + 'form' => [ + 'record_id' => [ + 'label' => 'Produs', + ], + ], + 'notification' => [ + 'success' => 'Produs asociat cu marca', + ], + ], + 'detach' => [ + 'notification' => [ + 'success' => 'Produs detașat.', + ], + ], + ], + ], + 'collections' => [ + 'label' => 'Colecții', + 'table' => [ + 'header_actions' => [ + 'attach' => [ + 'record_select' => [ + 'placeholder' => 'Selectează o colecție', + ], + ], + ], + ], + 'actions' => [ + 'attach' => [ + 'label' => 'Asociază o colecție', + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/channel.php b/packages/admin/resources/lang/ro/channel.php new file mode 100644 index 0000000000..e4a15ee6c2 --- /dev/null +++ b/packages/admin/resources/lang/ro/channel.php @@ -0,0 +1,39 @@ + 'Canal', + + 'plural_label' => 'Canale', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'url' => [ + 'label' => 'URL', + ], + 'default' => [ + 'label' => 'Implicit', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'url' => [ + 'label' => 'URL', + ], + 'default' => [ + 'label' => 'Implicit', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/collection.php b/packages/admin/resources/lang/ro/collection.php new file mode 100644 index 0000000000..623146ba26 --- /dev/null +++ b/packages/admin/resources/lang/ro/collection.php @@ -0,0 +1,45 @@ + 'Colecție', + + 'plural_label' => 'Colecții', + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + ], + + 'pages' => [ + 'children' => [ + 'label' => 'Colecții copil', + 'actions' => [ + 'create_child' => [ + 'label' => 'Creează colecție copil', + ], + ], + 'table' => [ + 'children_count' => [ + 'label' => 'Nr. copii', + ], + 'name' => [ + 'label' => 'Nume', + ], + ], + ], + 'edit' => [ + 'label' => 'Informații de bază', + ], + 'products' => [ + 'label' => 'Produse', + 'actions' => [ + 'attach' => [ + 'label' => 'Atașează produs', + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/collectiongroup.php b/packages/admin/resources/lang/ro/collectiongroup.php new file mode 100644 index 0000000000..59a4ce017a --- /dev/null +++ b/packages/admin/resources/lang/ro/collectiongroup.php @@ -0,0 +1,37 @@ + 'Grup de colecții', + + 'plural_label' => 'Grupe de colecții', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'collections_count' => [ + 'label' => 'Nr. colecții', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Acest grup de colecții nu poate fi șters deoarece are colecții asociate.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/components.php b/packages/admin/resources/lang/ro/components.php new file mode 100644 index 0000000000..f9c5232ea0 --- /dev/null +++ b/packages/admin/resources/lang/ro/components.php @@ -0,0 +1,117 @@ + [ + 'notification' => [ + + 'updated' => 'Etichetele au fost actualizate', + + ], + ], + + 'activity-log' => [ + + 'input' => [ + + 'placeholder' => 'Adaugă un comentariu', + + ], + + 'action' => [ + + 'add-comment' => 'Adaugă comentariu', + + ], + + 'system' => 'Sistem', + + 'partials' => [ + 'orders' => [ + 'order_created' => 'Comandă creată', + + 'status_change' => 'Stare actualizată', + + 'capture' => 'Plată de :amount pe cardul care se termină cu :last_four', + + 'authorized' => 'Autorizare de :amount pe cardul care se termină cu :last_four', + + 'refund' => 'Rambursare de :amount pe cardul care se termină cu :last_four', + + 'address' => ':type actualizată', + + 'billingAddress' => 'Adresă de facturare', + + 'shippingAddress' => 'Adresă de livrare', + ], + + 'update' => [ + 'updated' => ':model actualizat', + ], + + 'create' => [ + 'created' => ':model creat', + ], + + 'tags' => [ + 'updated' => 'Etichetele au fost actualizate', + 'added' => 'Adăugat', + 'removed' => 'Eliminat', + ], + ], + + 'notification' => [ + 'comment_added' => 'Comentariu adăugat', + ], + + ], + + 'forms' => [ + 'youtube' => [ + 'helperText' => 'Introduceți ID-ul videoclipului YouTube, ex.: dQw4w9WgXcQ', + ], + ], + + 'collection-tree-view' => [ + 'actions' => [ + 'move' => [ + 'form' => [ + 'target_id' => [ + 'label' => 'Colecție părinte', + ], + ], + ], + ], + 'notifications' => [ + 'collections-reordered' => [ + 'success' => 'Colecțiile au fost reordonate', + ], + 'node-expanded' => [ + 'danger' => 'Nu s-au putut încărca colecțiile', + ], + 'delete' => [ + 'danger' => 'Nu se poate șterge colecția', + ], + ], + ], + + 'product-options-list' => [ + 'add-option' => [ + 'label' => 'Adaugă opțiune', + ], + 'delete-option' => [ + 'label' => 'Șterge opțiunea', + ], + 'remove-shared-option' => [ + 'label' => 'Elimină opțiunea partajată', + ], + 'add-value' => [ + 'label' => 'Adaugă altă valoare', + ], + 'name' => [ + 'label' => 'Nume', + ], + 'values' => [ + 'label' => 'Valori', + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/currency.php b/packages/admin/resources/lang/ro/currency.php new file mode 100644 index 0000000000..80692f0aff --- /dev/null +++ b/packages/admin/resources/lang/ro/currency.php @@ -0,0 +1,58 @@ + 'Monedă', + + 'plural_label' => 'Monede', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'code' => [ + 'label' => 'Cod', + ], + 'exchange_rate' => [ + 'label' => 'Rată de schimb', + ], + 'decimal_places' => [ + 'label' => 'Zecimale', + ], + 'enabled' => [ + 'label' => 'Activată', + ], + 'sync_prices' => [ + 'label' => 'Sincronizează prețurile', + ], + 'default' => [ + 'label' => 'Implicită', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'code' => [ + 'label' => 'Cod', + ], + 'exchange_rate' => [ + 'label' => 'Rată de schimb', + ], + 'decimal_places' => [ + 'label' => 'Zecimale', + ], + 'enabled' => [ + 'label' => 'Activată', + ], + 'default' => [ + 'label' => 'Implicită', + ], + 'sync_prices' => [ + 'label' => 'Sincronizează prețurile', + 'helper_text' => 'Păstrează prețurile în această monedă sincronizate cu moneda implicită.', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/customer.php b/packages/admin/resources/lang/ro/customer.php new file mode 100644 index 0000000000..7ae1932f4f --- /dev/null +++ b/packages/admin/resources/lang/ro/customer.php @@ -0,0 +1,63 @@ + 'Client', + + 'plural_label' => 'Clienți', + + 'table' => [ + 'full_name' => [ + 'label' => 'Nume', + ], + 'first_name' => [ + 'label' => 'Prenume', + ], + 'last_name' => [ + 'label' => 'Nume', + ], + 'title' => [ + 'label' => 'Titlu', + ], + 'company_name' => [ + 'label' => 'Nume companie', + ], + 'tax_identifier' => [ + 'label' => 'Cod fiscal', + ], + 'account_reference' => [ + 'label' => 'Referință cont', + ], + 'new' => [ + 'label' => 'Nou', + ], + 'returning' => [ + 'label' => 'Revenit', + ], + ], + + 'form' => [ + 'title' => [ + 'label' => 'Titlu', + ], + 'first_name' => [ + 'label' => 'Prenume', + ], + 'last_name' => [ + 'label' => 'Nume', + ], + 'company_name' => [ + 'label' => 'Nume companie', + ], + 'account_ref' => [ + 'label' => 'Referință cont', + ], + 'tax_identifier' => [ + 'label' => 'Cod fiscal', + ], + 'customer_groups' => [ + 'label' => 'Grupuri de clienți', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/customergroup.php b/packages/admin/resources/lang/ro/customergroup.php new file mode 100644 index 0000000000..9f71baf618 --- /dev/null +++ b/packages/admin/resources/lang/ro/customergroup.php @@ -0,0 +1,40 @@ + 'Grup de clienți', + + 'plural_label' => 'Grupe de clienți', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'default' => [ + 'label' => 'Implicit', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'default' => [ + 'label' => 'Implicit', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Acest grup de clienți nu poate fi șters deoarece are clienți asociați.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/discount.php b/packages/admin/resources/lang/ro/discount.php new file mode 100644 index 0000000000..1429cbc48d --- /dev/null +++ b/packages/admin/resources/lang/ro/discount.php @@ -0,0 +1,353 @@ + 'Reduceri', + 'label' => 'Reducere', + 'form' => [ + 'conditions' => [ + 'heading' => 'Condiții', + ], + 'buy_x_get_y' => [ + 'heading' => 'Cumperi X, primești Y', + ], + 'amount_off' => [ + 'heading' => 'Reducere sumă', + ], + 'name' => [ + 'label' => 'Nume', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'starts_at' => [ + 'label' => 'Data de început', + ], + 'ends_at' => [ + 'label' => 'Data de sfârșit', + ], + 'priority' => [ + 'label' => 'Prioritate', + 'helper_text' => 'Reducerile cu prioritate mai mare vor fi aplicate primele.', + 'options' => [ + 'low' => [ + 'label' => 'Scăzută', + ], + 'medium' => [ + 'label' => 'Mediu', + ], + 'high' => [ + 'label' => 'Ridicată', + ], + ], + ], + 'stop' => [ + 'label' => 'Oprește aplicarea altor reduceri după aceasta', + ], + 'coupon' => [ + 'label' => 'Cupon', + 'helper_text' => 'Introduceți cuponul necesar pentru aplicarea reducerii; dacă este lăsat gol, se aplică automat.', + ], + 'max_uses' => [ + 'label' => 'Utilizări maxime', + 'helper_text' => 'Lăsați gol pentru utilizări nelimitate.', + ], + 'max_uses_per_user' => [ + 'label' => 'Utilizări maxime per utilizator', + 'helper_text' => 'Lăsați gol pentru utilizări nelimitate.', + ], + 'minimum_cart_amount' => [ + 'label' => 'Valoare minimă coș', + ], + 'min_qty' => [ + 'label' => 'Cantitate produs', + 'helper_text' => 'Stabiliți câte produse eligibile sunt necesare pentru aplicarea reducerii.', + ], + 'reward_qty' => [ + 'label' => 'Nr. articole gratuite', + 'helper_text' => 'Câte unități din fiecare articol sunt reduse.', + ], + 'max_reward_qty' => [ + 'label' => 'Cantitate maximă recompensă', + 'helper_text' => 'Numărul maxim de produse ce pot fi reduse, indiferent de criterii.', + ], + 'automatic_rewards' => [ + 'label' => 'Adaugă automat recompense', + 'helper_text' => 'Activați pentru a adăuga produse-recompensă când nu sunt în coș.', + ], + 'fixed_value' => [ + 'label' => 'Valoare fixă', + ], + 'percentage' => [ + 'label' => 'Procent', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'status' => [ + 'label' => 'Stare', + \Lunar\Models\Discount::ACTIVE => [ + 'label' => 'Activă', + ], + \Lunar\Models\Discount::PENDING => [ + 'label' => 'În așteptare', + ], + \Lunar\Models\Discount::EXPIRED => [ + 'label' => 'Expirată', + ], + \Lunar\Models\Discount::SCHEDULED => [ + 'label' => 'Programată', + ], + ], + 'type' => [ + 'label' => 'Tip', + ], + 'starts_at' => [ + 'label' => 'Data de început', + ], + 'ends_at' => [ + 'label' => 'Data de sfârșit', + ], + 'created_at' => [ + 'label' => 'Creat la', + ], + 'coupon' => [ + 'label' => 'Cupon', + ], + ], + 'pages' => [ + 'availability' => [ + 'label' => 'Disponibilitate', + ], + 'edit' => [ + 'title' => 'Informații de bază', + ], + 'limitations' => [ + 'label' => 'Limitări', + ], + ], + 'relationmanagers' => [ + 'collections' => [ + 'title' => 'Colecții', + 'description' => 'Selectați colecțiile la care se limitează această reducere.', + 'actions' => [ + 'attach' => [ + 'label' => 'Atașează colecție', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'type' => [ + 'label' => 'Tip', + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + ], + ], + 'customers' => [ + 'title' => 'Clienți', + 'description' => 'Selectați clienții la care se limitează această reducere.', + 'actions' => [ + 'attach' => [ + 'label' => 'Atașează client', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + ], + ], + 'brands' => [ + 'title' => 'Mărci', + 'description' => 'Selectați mărcile la care se limitează această reducere.', + 'actions' => [ + 'attach' => [ + 'label' => 'Atașează marcă', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'type' => [ + 'label' => 'Tip', + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + ], + ], + 'products' => [ + 'title' => 'Produse', + 'description' => 'Selectați produsele la care se limitează această reducere.', + 'actions' => [ + 'attach' => [ + 'label' => 'Adaugă produs', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'type' => [ + 'label' => 'Tip', + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + ], + ], + 'rewards' => [ + 'title' => 'Recompense', + 'description' => 'Selectați produsele care vor fi reduse dacă sunt în coș și sunt îndeplinite condițiile de mai sus.', + 'actions' => [ + 'attach' => [ + 'label' => 'Adaugă recompensă', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'type' => [ + 'label' => 'Tip', + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + ], + ], + 'conditions' => [ + 'title' => 'Condiții', + 'description' => 'Selectați condițiile necesare pentru aplicarea reducerii.', + 'actions' => [ + 'attach' => [ + 'label' => 'Adaugă condiție', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'type' => [ + 'label' => 'Tip', + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + ], + ], + 'productvariants' => [ + 'title' => 'Variante de produs', + 'description' => 'Selectați variantele de produs la care se limitează această reducere.', + 'actions' => [ + 'attach' => [ + 'label' => 'Adaugă variantă de produs', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'sku' => [ + 'label' => 'Cod stoc intern (SKU)', + ], + 'values' => [ + 'label' => 'Opțiune(i)', + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitare', + ], + 'exclusion' => [ + 'label' => 'Excludere', + ], + ], + ], + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/fieldtypes.php b/packages/admin/resources/lang/ro/fieldtypes.php new file mode 100644 index 0000000000..45d32b2ead --- /dev/null +++ b/packages/admin/resources/lang/ro/fieldtypes.php @@ -0,0 +1,72 @@ + [ + 'label' => 'Listă derulantă', + 'form' => [ + 'lookups' => [ + 'label' => 'Valori', + 'key_label' => 'Etichetă', + 'value_label' => 'Valoare', + ], + ], + ], + 'listfield' => [ + 'label' => 'Câmp listă', + ], + 'text' => [ + 'label' => 'Text', + 'form' => [ + 'richtext' => [ + 'label' => 'Text îmbogățit', + ], + ], + ], + 'translatedtext' => [ + 'label' => 'Text tradus', + 'form' => [ + 'richtext' => [ + 'label' => 'Text îmbogățit', + ], + 'locales' => 'Locale', + ], + ], + 'toggle' => [ + 'label' => 'Comutator', + ], + 'youtube' => [ + 'label' => 'YouTube', + ], + 'vimeo' => [ + 'label' => 'Vimeo', + ], + 'number' => [ + 'label' => 'Număr', + 'form' => [ + 'min' => [ + 'label' => 'Min.', + ], + 'max' => [ + 'label' => 'Max.', + ], + ], + ], + 'file' => [ + 'label' => 'Fișier', + 'form' => [ + 'file_types' => [ + 'label' => 'Tipuri de fișiere permise', + 'placeholder' => 'MIME nou', + ], + 'multiple' => [ + 'label' => 'Permite fișiere multiple', + ], + 'min_files' => [ + 'label' => 'Min. fișiere', + ], + 'max_files' => [ + 'label' => 'Max. fișiere', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/global.php b/packages/admin/resources/lang/ro/global.php new file mode 100644 index 0000000000..3bad6cff32 --- /dev/null +++ b/packages/admin/resources/lang/ro/global.php @@ -0,0 +1,12 @@ + [ + 'catalog' => 'Catalog', + 'sales' => 'Vânzări', + 'reports' => 'Rapoarte', + 'settings' => 'Setări', + ], + +]; diff --git a/packages/admin/resources/lang/ro/language.php b/packages/admin/resources/lang/ro/language.php new file mode 100644 index 0000000000..bcb60dbc68 --- /dev/null +++ b/packages/admin/resources/lang/ro/language.php @@ -0,0 +1,33 @@ + 'Limbă', + + 'plural_label' => 'Limbi', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'code' => [ + 'label' => 'Cod', + ], + 'default' => [ + 'label' => 'Implicită', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'code' => [ + 'label' => 'Cod', + ], + 'default' => [ + 'label' => 'Implicită', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/order.php b/packages/admin/resources/lang/ro/order.php new file mode 100644 index 0000000000..8e77534201 --- /dev/null +++ b/packages/admin/resources/lang/ro/order.php @@ -0,0 +1,305 @@ + 'Comandă', + + 'plural_label' => 'Comenzi', + + 'breadcrumb' => [ + 'manage' => 'Gestionează', + ], + + 'tabs' => [ + 'all' => 'Toate', + ], + + 'transactions' => [ + 'capture' => 'Capturat', + 'intent' => 'Intenție', + 'refund' => 'Rambursat', + 'failed' => 'Eșuat', + ], + + 'table' => [ + 'status' => [ + 'label' => 'Stare', + ], + 'reference' => [ + 'label' => 'Referință', + ], + 'customer_reference' => [ + 'label' => 'Referință client', + ], + 'customer' => [ + 'label' => 'Client', + ], + 'tags' => [ + 'label' => 'Etichete', + ], + 'postcode' => [ + 'label' => 'Cod poștal', + ], + 'email' => [ + 'label' => 'E-mail', + 'copy_message' => 'Adresă e-mail copiată', + ], + 'phone' => [ + 'label' => 'Telefon', + ], + 'total' => [ + 'label' => 'Total', + ], + 'date' => [ + 'label' => 'Data', + ], + 'new_customer' => [ + 'label' => 'Tip client', + ], + 'placed_after' => [ + 'label' => 'Plasată după', + ], + 'placed_before' => [ + 'label' => 'Plasată înainte de', + ], + ], + + 'form' => [ + 'address' => [ + 'first_name' => [ + 'label' => 'Prenume', + ], + 'last_name' => [ + 'label' => 'Nume', + ], + 'line_one' => [ + 'label' => 'Adresă linia 1', + ], + 'line_two' => [ + 'label' => 'Adresă linia 2', + ], + 'line_three' => [ + 'label' => 'Adresă linia 3', + ], + 'company_name' => [ + 'label' => 'Nume companie', + ], + 'tax_identifier' => [ + 'label' => 'Cod fiscal', + ], + 'contact_phone' => [ + 'label' => 'Telefon', + ], + 'contact_email' => [ + 'label' => 'Adresă e-mail', + ], + 'city' => [ + 'label' => 'Oraș', + ], + 'state' => [ + 'label' => 'Județ / Provincie', + ], + 'postcode' => [ + 'label' => 'Cod poștal', + ], + 'country_id' => [ + 'label' => 'Țară', + ], + ], + + 'reference' => [ + 'label' => 'Referință', + ], + 'status' => [ + 'label' => 'Stare', + ], + 'transaction' => [ + 'label' => 'Tranzacție', + ], + 'amount' => [ + 'label' => 'Sumă', + + 'hint' => [ + 'less_than_total' => 'Urmează să capturezi o sumă mai mică decât valoarea totală a tranzacției', + ], + ], + + 'notes' => [ + 'label' => 'Note', + ], + 'confirm' => [ + 'label' => 'Confirmă', + + 'alert' => 'Este necesară confirmarea', + + 'hint' => [ + 'capture' => 'Confirmă că dorești să capturezi această plată', + 'refund' => 'Confirmă că dorești să rambursezi această sumă.', + ], + ], + ], + + 'infolist' => [ + 'notes' => [ + 'label' => 'Note', + 'placeholder' => 'Nu există note pentru această comandă', + ], + 'delivery_instructions' => [ + 'label' => 'Instrucțiuni de livrare', + ], + 'shipping_total' => [ + 'label' => 'Total livrare', + ], + 'paid' => [ + 'label' => 'Plătit', + ], + 'refund' => [ + 'label' => 'Rambursare', + ], + 'unit_price' => [ + 'label' => 'Preț unitar', + ], + 'quantity' => [ + 'label' => 'Cantitate', + ], + 'sub_total' => [ + 'label' => 'Subtotal', + ], + 'discount_total' => [ + 'label' => 'Total reducere', + ], + 'total' => [ + 'label' => 'Total', + ], + 'current_stock_level' => [ + 'message' => 'Stoc curent: :count', + ], + 'purchase_stock_level' => [ + 'message' => 'la momentul comenzii: :count', + ], + 'status' => [ + 'label' => 'Stare', + ], + 'reference' => [ + 'label' => 'Referință', + ], + 'customer_reference' => [ + 'label' => 'Referință client', + ], + 'channel' => [ + 'label' => 'Canal', + ], + 'date_created' => [ + 'label' => 'Data creării', + ], + 'date_placed' => [ + 'label' => 'Data plasării', + ], + 'new_returning' => [ + 'label' => 'Nou / Revenit', + ], + 'new_customer' => [ + 'label' => 'Client nou', + ], + 'returning_customer' => [ + 'label' => 'Client revenit', + ], + 'shipping_address' => [ + 'label' => 'Adresă de livrare', + ], + 'billing_address' => [ + 'label' => 'Adresă de facturare', + ], + 'address_not_set' => [ + 'label' => 'Nicio adresă setată', + ], + 'billing_matches_shipping' => [ + 'label' => 'La fel ca adresa de livrare', + ], + 'additional_info' => [ + 'label' => 'Informații suplimentare', + ], + 'no_additional_info' => [ + 'label' => 'Nu există informații suplimentare', + ], + 'tags' => [ + 'label' => 'Etichete', + ], + 'timeline' => [ + 'label' => 'Cronologie', + ], + 'transactions' => [ + 'label' => 'Tranzacții', + 'placeholder' => 'Nicio tranzacție', + ], + 'alert' => [ + 'requires_capture' => 'Această comandă necesită în continuare capturarea plății.', + 'partially_refunded' => 'Această comandă a fost rambursată parțial.', + 'refunded' => 'Această comandă a fost rambursată.', + ], + ], + + 'action' => [ + 'bulk_update_status' => [ + 'label' => 'Actualizează starea', + 'notification' => 'Starea comenzilor a fost actualizată', + ], + 'update_status' => [ + 'new_status' => [ + 'label' => 'Stare nouă', + ], + 'additional_content' => [ + 'label' => 'Conținut suplimentar', + ], + 'additional_email_recipient' => [ + 'label' => 'Destinatar e-mail suplimentar', + 'placeholder' => 'opțional', + ], + ], + 'download_order_pdf' => [ + 'label' => 'Descarcă PDF', + 'notification' => 'Descărcarea PDF-ului comenzii', + ], + 'edit_address' => [ + 'label' => 'Editează', + + 'notification' => [ + 'error' => 'Eroare', + + 'billing_address' => [ + 'saved' => 'Adresa de facturare a fost salvată', + ], + + 'shipping_address' => [ + 'saved' => 'Adresa de livrare a fost salvată', + ], + ], + ], + 'edit_tags' => [ + 'label' => 'Editează', + 'form' => [ + 'tags' => [ + 'label' => 'Etichete', + 'helper_text' => 'Separați etichetele apăsând Enter, Tab sau virgulă (,)', + ], + ], + ], + 'capture_payment' => [ + 'label' => 'Capturează plata', + + 'notification' => [ + 'error' => 'A apărut o problemă la capturare', + 'success' => 'Capturare reușită', + ], + ], + 'refund_payment' => [ + 'label' => 'Rambursare', + + 'notification' => [ + 'error' => 'A apărut o problemă la rambursare', + 'success' => 'Rambursare reușită', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/product.php b/packages/admin/resources/lang/ro/product.php new file mode 100644 index 0000000000..c3c423ad50 --- /dev/null +++ b/packages/admin/resources/lang/ro/product.php @@ -0,0 +1,131 @@ + 'Produs', + + 'plural_label' => 'Produse', + + 'tabs' => [ + 'all' => 'Toate', + ], + + 'status' => [ + 'unpublished' => [ + 'content' => 'În prezent în stadiu de ciornă, acest produs este ascuns în toate canalele și grupurile de clienți.', + ], + 'availability' => [ + 'customer_groups' => 'Acest produs nu este disponibil momentan pentru niciun grup de clienți.', + 'channels' => 'Acest produs nu este disponibil momentan în niciun canal.', + ], + ], + + 'table' => [ + 'status' => [ + 'label' => 'Stare', + 'states' => [ + 'deleted' => 'Șters', + 'draft' => 'Ciornă', + 'published' => 'Publicat', + ], + ], + 'name' => [ + 'label' => 'Nume', + ], + 'brand' => [ + 'label' => 'Marcă', + ], + 'sku' => [ + 'label' => 'Cod stoc intern (SKU)', + ], + 'stock' => [ + 'label' => 'Stoc', + ], + 'producttype' => [ + 'label' => 'Tip produs', + ], + ], + + 'actions' => [ + 'edit_status' => [ + 'label' => 'Actualizează starea', + 'heading' => 'Actualizează starea', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'brand' => [ + 'label' => 'Marcă', + ], + 'sku' => [ + 'label' => 'Cod stoc intern (SKU)', + ], + 'producttype' => [ + 'label' => 'Tip produs', + ], + 'status' => [ + 'label' => 'Stare', + 'options' => [ + 'published' => [ + 'label' => 'Publicat', + 'description' => 'Acest produs va fi disponibil în toate grupurile de clienți și canalele activate', + ], + 'draft' => [ + 'label' => 'Ciornă', + 'description' => 'Acest produs va fi ascuns în toate canalele și grupurile de clienți', + ], + ], + ], + 'tags' => [ + 'label' => 'Etichete', + 'helper_text' => 'Separați etichetele apăsând Enter, Tab sau virgulă (,)', + ], + 'collections' => [ + 'label' => 'Colecții', + 'select_collection' => 'Selectează o colecție', + ], + ], + + 'pages' => [ + 'availability' => [ + 'label' => 'Disponibilitate', + ], + 'edit' => [ + 'title' => 'Informații de bază', + ], + 'identifiers' => [ + 'label' => 'Identificatori produs', + ], + 'inventory' => [ + 'label' => 'Stoc', + ], + 'pricing' => [ + 'form' => [ + 'tax_class_id' => [ + 'label' => 'Clasă de taxe', + ], + 'tax_ref' => [ + 'label' => 'Referință taxe', + 'helper_text' => 'Opțional, pentru integrare cu sisteme terțe.', + ], + ], + ], + 'shipping' => [ + 'label' => 'Livrare', + ], + 'variants' => [ + 'label' => 'Variante', + ], + 'collections' => [ + 'label' => 'Colecții', + 'select_collection' => 'Selectează o colecție', + ], + 'associations' => [ + 'label' => 'Asocieri produs', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/productoption.php b/packages/admin/resources/lang/ro/productoption.php new file mode 100644 index 0000000000..ade933e58b --- /dev/null +++ b/packages/admin/resources/lang/ro/productoption.php @@ -0,0 +1,127 @@ + 'Opțiune produs', + + 'plural_label' => 'Opțiuni produs', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'label' => [ + 'label' => 'Etichetă', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + 'shared' => [ + 'label' => 'Partajată', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'label' => [ + 'label' => 'Etichetă', + ], + 'handle' => [ + 'label' => 'Identificator', + ], + ], + + 'widgets' => [ + 'product-options' => [ + 'notifications' => [ + 'save-variants' => [ + 'success' => [ + 'title' => 'Variantele de produs au fost salvate', + ], + ], + ], + 'actions' => [ + 'cancel' => [ + 'label' => 'Anulează', + ], + 'save-options' => [ + 'label' => 'Salvează opțiunile', + ], + 'add-shared-option' => [ + 'label' => 'Adaugă opțiune partajată', + 'form' => [ + 'product_option' => [ + 'label' => 'Opțiune produs', + ], + 'no_shared_components' => [ + 'label' => 'Nu există opțiuni partajate disponibile.', + ], + 'preselect' => [ + 'label' => 'Preselectează implicit toate valorile.', + ], + ], + ], + 'add-restricted-option' => [ + 'label' => 'Adaugă opțiune', + ], + ], + 'options-list' => [ + 'empty' => [ + 'heading' => 'Nu există opțiuni de produs configurate', + 'description' => 'Adaugă o opțiune partajată sau restricționată pentru a începe generarea variantelor.', + ], + ], + 'options-table' => [ + 'title' => 'Opțiuni produs', + 'configure-options' => [ + 'label' => 'Configurează opțiunile', + ], + 'table' => [ + 'option' => [ + 'label' => 'Opțiune', + ], + 'values' => [ + 'label' => 'Valori', + ], + ], + ], + 'variants-table' => [ + 'title' => 'Variante de produs', + 'actions' => [ + 'create' => [ + 'label' => 'Creează variantă', + ], + 'edit' => [ + 'label' => 'Editează', + ], + 'delete' => [ + 'label' => 'Șterge', + ], + ], + 'empty' => [ + 'heading' => 'Nu există variante configurate', + ], + 'table' => [ + 'new' => [ + 'label' => 'NOU', + ], + 'option' => [ + 'label' => 'Opțiune', + ], + 'sku' => [ + 'label' => 'Cod stoc intern (SKU)', + ], + 'price' => [ + 'label' => 'Preț', + ], + 'stock' => [ + 'label' => 'Stoc', + ], + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/producttype.php b/packages/admin/resources/lang/ro/producttype.php new file mode 100644 index 0000000000..8fcfe81306 --- /dev/null +++ b/packages/admin/resources/lang/ro/producttype.php @@ -0,0 +1,52 @@ + 'Tip produs', + + 'plural_label' => 'Tipuri de produse', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'products_count' => [ + 'label' => 'Număr produse', + ], + 'product_attributes_count' => [ + 'label' => 'Atribute produs', + ], + 'variant_attributes_count' => [ + 'label' => 'Atribute variantă', + ], + ], + + 'tabs' => [ + 'product_attributes' => [ + 'label' => 'Atribute produs', + ], + 'variant_attributes' => [ + 'label' => 'Atribute variantă', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + ], + + 'attributes' => [ + 'no_groups' => 'Nu există grupe de atribute disponibile.', + 'no_attributes' => 'Nu există atribute disponibile.', + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Acest tip de produs nu poate fi șters deoarece are produse asociate.', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/productvariant.php b/packages/admin/resources/lang/ro/productvariant.php new file mode 100644 index 0000000000..a3d933e909 --- /dev/null +++ b/packages/admin/resources/lang/ro/productvariant.php @@ -0,0 +1,105 @@ + 'Variantă produs', + 'plural_label' => 'Variante de produs', + 'pages' => [ + 'edit' => [ + 'title' => 'Informații de bază', + ], + 'media' => [ + 'title' => 'Media', + 'form' => [ + 'no_selection' => [ + 'label' => 'Nu aveți selectată nicio imagine pentru această variantă.', + ], + 'no_media_available' => [ + 'label' => 'Momentan nu există media disponibilă pentru acest produs.', + ], + 'images' => [ + 'label' => 'Imagine principală', + 'helper_text' => 'Selectați imaginea produsului care reprezintă această variantă.', + ], + ], + ], + 'identifiers' => [ + 'title' => 'Identificatori', + ], + 'inventory' => [ + 'title' => 'Stoc', + ], + 'shipping' => [ + 'title' => 'Livrare', + ], + ], + 'form' => [ + 'sku' => [ + 'label' => 'Cod stoc intern (SKU)', + ], + 'gtin' => [ + 'label' => 'Număr global de articol comercial (GTIN)', + ], + 'mpn' => [ + 'label' => 'Număr de parte al producătorului (MPN)', + ], + 'ean' => [ + 'label' => 'Cod de bare (UPC/EAN)', + ], + 'stock' => [ + 'label' => 'În stoc', + ], + 'backorder' => [ + 'label' => 'La precomandă', + ], + 'purchasable' => [ + 'label' => 'Disponibilitate la achiziție', + 'options' => [ + 'always' => 'Întotdeauna', + 'in_stock' => 'În stoc', + 'in_stock_or_on_backorder' => 'În stoc sau la precomandă', + ], + ], + 'unit_quantity' => [ + 'label' => 'Cantitate unitate', + 'helper_text' => 'Câte articole individuale compun 1 unitate.', + ], + 'min_quantity' => [ + 'label' => 'Cantitate minimă', + 'helper_text' => 'Cantitatea minimă dintr-o variantă de produs care poate fi cumpărată într-o singură achiziție.', + ], + 'quantity_increment' => [ + 'label' => 'Increment cantitate', + 'helper_text' => 'Varianta de produs trebuie cumpărată în multipli ai acestei cantități.', + ], + 'tax_class_id' => [ + 'label' => 'Clasă de taxe', + ], + 'shippable' => [ + 'label' => 'Expediabil', + ], + 'length_value' => [ + 'label' => 'Lungime', + ], + 'length_unit' => [ + 'label' => 'Unitate lungime', + ], + 'width_value' => [ + 'label' => 'Lățime', + ], + 'width_unit' => [ + 'label' => 'Unitate lățime', + ], + 'height_value' => [ + 'label' => 'Înălțime', + ], + 'height_unit' => [ + 'label' => 'Unitate înălțime', + ], + 'weight_value' => [ + 'label' => 'Greutate', + ], + 'weight_unit' => [ + 'label' => 'Unitate greutate', + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/relationmanagers.php b/packages/admin/resources/lang/ro/relationmanagers.php new file mode 100644 index 0000000000..100bdb5b0f --- /dev/null +++ b/packages/admin/resources/lang/ro/relationmanagers.php @@ -0,0 +1,285 @@ + [ + 'title' => 'Grupuri de clienți', + 'actions' => [ + 'attach' => [ + 'label' => 'Atașează grup de clienți', + ], + ], + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'enabled' => [ + 'label' => 'Activat', + ], + 'starts_at' => [ + 'label' => 'Data de început', + ], + 'ends_at' => [ + 'label' => 'Data de sfârșit', + ], + 'visible' => [ + 'label' => 'Vizibil', + ], + 'purchasable' => [ + 'label' => 'Achiziționabil', + ], + ], + 'table' => [ + 'description' => 'Asociați grupuri de clienți la acest :type pentru a determina disponibilitatea.', + 'name' => [ + 'label' => 'Nume', + ], + 'enabled' => [ + 'label' => 'Activat', + ], + 'starts_at' => [ + 'label' => 'Data de început', + ], + 'ends_at' => [ + 'label' => 'Data de sfârșit', + ], + 'visible' => [ + 'label' => 'Vizibil', + ], + 'purchasable' => [ + 'label' => 'Achiziționabil', + ], + ], + ], + 'channels' => [ + 'title' => 'Canale', + 'actions' => [ + 'attach' => [ + 'label' => 'Programează alt canal', + ], + ], + 'form' => [ + 'enabled' => [ + 'label' => 'Activat', + 'helper_text_false' => 'Acest canal nu va fi activat chiar dacă există o dată de început.', + ], + 'starts_at' => [ + 'label' => 'Data de început', + 'helper_text' => 'Lăsați gol pentru a fi disponibil de la orice dată.', + ], + 'ends_at' => [ + 'label' => 'Data de sfârșit', + 'helper_text' => 'Lăsați gol pentru a fi disponibil pe termen nelimitat.', + ], + ], + 'table' => [ + 'description' => 'Determinați ce canale sunt activate și programați disponibilitatea.', + 'name' => [ + 'label' => 'Nume', + ], + 'enabled' => [ + 'label' => 'Activat', + ], + 'starts_at' => [ + 'label' => 'Data de început', + ], + 'ends_at' => [ + 'label' => 'Data de sfârșit', + ], + ], + ], + 'medias' => [ + 'title' => 'Media', + 'title_plural' => 'Media', + 'actions' => [ + 'attach' => [ + 'label' => 'Atașează media', + ], + 'create' => [ + 'label' => 'Creează media', + ], + 'detach' => [ + 'label' => 'Detașează', + ], + 'view' => [ + 'label' => 'Vezi', + ], + ], + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'media' => [ + 'label' => 'Imagine', + ], + 'primary' => [ + 'label' => 'Principală', + ], + ], + 'table' => [ + 'image' => [ + 'label' => 'Imagine', + ], + 'file' => [ + 'label' => 'Fișier', + ], + 'name' => [ + 'label' => 'Nume', + ], + 'primary' => [ + 'label' => 'Principală', + ], + ], + 'all_media_attached' => 'Nu există imagini de produs disponibile pentru atașare', + 'variant_description' => 'Atașați imaginile produsului la această variantă', + ], + 'urls' => [ + 'title' => 'URL', + 'title_plural' => 'URL-uri', + 'actions' => [ + 'create' => [ + 'label' => 'Creează URL', + ], + ], + 'filters' => [ + 'language_id' => [ + 'label' => 'Limbă', + ], + ], + 'form' => [ + 'slug' => [ + 'label' => 'Slug', + ], + 'default' => [ + 'label' => 'Implicit', + ], + 'language' => [ + 'label' => 'Limbă', + ], + ], + 'table' => [ + 'slug' => [ + 'label' => 'Slug', + ], + 'default' => [ + 'label' => 'Implicit', + ], + 'language' => [ + 'label' => 'Limbă', + ], + ], + ], + 'customer_group_pricing' => [ + 'title' => 'Prețuri pe grupuri de clienți', + 'title_plural' => 'Prețuri pe grupuri de clienți', + 'table' => [ + 'heading' => 'Prețuri pe grupuri de clienți', + 'description' => 'Asociați prețuri grupurilor de clienți pentru a determina prețul produsului.', + 'empty_state' => [ + 'label' => 'Nu există prețuri pe grupuri de clienți.', + 'description' => 'Creați un preț pentru un grup de clienți pentru a începe.', + ], + 'actions' => [ + 'create' => [ + 'label' => 'Adaugă preț pentru grup de clienți', + 'modal' => [ + 'heading' => 'Creează preț pentru grup de clienți', + ], + ], + ], + ], + ], + 'pricing' => [ + 'title' => 'Prețuri', + 'title_plural' => 'Prețuri', + 'tab_name' => 'Reduceri cantitative', + 'table' => [ + 'heading' => 'Reduceri cantitative', + 'description' => 'Reduceți prețul când un client cumpără cantități mai mari.', + 'empty_state' => [ + 'label' => 'Nu există reduceri cantitative.', + ], + 'actions' => [ + 'create' => [ + 'label' => 'Adaugă reducere cantitativă', + ], + ], + 'price' => [ + 'label' => 'Preț', + ], + 'customer_group' => [ + 'label' => 'Grup de clienți', + 'placeholder' => 'Toate grupurile de clienți', + ], + 'min_quantity' => [ + 'label' => 'Cantitate minimă', + ], + 'currency' => [ + 'label' => 'Monedă', + ], + ], + 'form' => [ + 'price' => [ + 'label' => 'Preț', + 'helper_text' => 'Prețul de achiziție, înainte de reduceri.', + ], + 'customer_group_id' => [ + 'label' => 'Grup de clienți', + 'placeholder' => 'Toate grupurile de clienți', + 'helper_text' => 'Selectați grupul de clienți pentru care se aplică acest preț.', + ], + 'min_quantity' => [ + 'label' => 'Cantitate minimă', + 'helper_text' => 'Selectați cantitatea minimă pentru care va fi disponibil acest preț.', + 'validation' => [ + 'unique' => 'Grupul de clienți și cantitatea minimă trebuie să fie unice.', + ], + ], + 'currency_id' => [ + 'label' => 'Monedă', + 'helper_text' => 'Selectați moneda pentru acest preț.', + ], + 'compare_price' => [ + 'label' => 'Preț de comparație', + 'helper_text' => 'Prețul original sau RRP, pentru comparație cu prețul de achiziție.', + ], + 'basePrices' => [ + 'title' => 'Prețuri', + 'form' => [ + 'price' => [ + 'label' => 'Preț', + 'helper_text' => 'Prețul de achiziție, înainte de reduceri.', + 'sync_price' => 'Prețul este sincronizat cu moneda implicită.', + ], + 'compare_price' => [ + 'label' => 'Preț de comparație', + 'helper_text' => 'Prețul original sau RRP, pentru comparație cu prețul de achiziție.', + ], + ], + 'tooltip' => 'Generat automat pe baza cursurilor de schimb valutar.', + ], + ], + ], + 'tax_rate_amounts' => [ + 'table' => [ + 'description' => '', + 'percentage' => [ + 'label' => 'Procent', + ], + 'tax_class' => [ + 'label' => 'Clasă de taxe', + ], + ], + ], + 'values' => [ + 'title' => 'Valori', + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'position' => [ + 'label' => 'Poziție', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/staff.php b/packages/admin/resources/lang/ro/staff.php new file mode 100644 index 0000000000..706be1c595 --- /dev/null +++ b/packages/admin/resources/lang/ro/staff.php @@ -0,0 +1,81 @@ + 'Personal', + + 'plural_label' => 'Personal', + + 'table' => [ + 'first_name' => [ + 'label' => 'Prenume', + ], + 'last_name' => [ + 'label' => 'Nume', + ], + 'email' => [ + 'label' => 'E-mail', + ], + 'admin' => [ + 'badge' => 'Super administrator', + ], + ], + + 'form' => [ + 'first_name' => [ + 'label' => 'Prenume', + ], + 'last_name' => [ + 'label' => 'Nume', + ], + 'email' => [ + 'label' => 'E-mail', + ], + 'password' => [ + 'label' => 'Parolă', + 'hint' => 'Resetează parola', + ], + 'admin' => [ + 'label' => 'Super administrator', + 'helper' => 'Rolurile de super administrator nu pot fi schimbate în hub.', + ], + 'roles' => [ + 'label' => 'Roluri', + 'helper' => ':roles au acces complet', + ], + 'permissions' => [ + 'label' => 'Permisiuni', + ], + 'role' => [ + 'label' => 'Nume rol', + ], + ], + + 'action' => [ + 'acl' => [ + 'label' => 'Control acces', + ], + 'add-role' => [ + 'label' => 'Adaugă rol', + ], + 'delete-role' => [ + 'label' => 'Șterge rol', + 'heading' => 'Șterge rolul: :role', + ], + ], + + 'acl' => [ + 'title' => 'Control acces', + 'tooltip' => [ + 'roles-included' => 'Permisiunea este inclusă în următoarele roluri', + ], + 'notification' => [ + 'updated' => 'Actualizat', + 'error' => 'Eroare', + 'no-role' => 'Rolul nu este înregistrat în Lunar', + 'no-permission' => 'Permisiunea nu este înregistrată în Lunar', + 'no-role-permission' => 'Rolul și permisiunea nu sunt înregistrate în Lunar', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/tag.php b/packages/admin/resources/lang/ro/tag.php new file mode 100644 index 0000000000..a80ff40058 --- /dev/null +++ b/packages/admin/resources/lang/ro/tag.php @@ -0,0 +1,21 @@ + 'Etichetă', + + 'plural_label' => 'Etichete', + + 'table' => [ + 'value' => [ + 'label' => 'Valoare', + ], + ], + + 'form' => [ + 'value' => [ + 'label' => 'Valoare', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/tax.php b/packages/admin/resources/lang/ro/tax.php new file mode 100644 index 0000000000..fda4604923 --- /dev/null +++ b/packages/admin/resources/lang/ro/tax.php @@ -0,0 +1,9 @@ + 'Taxă', + + 'plural_label' => 'Taxe', + +]; diff --git a/packages/admin/resources/lang/ro/taxclass.php b/packages/admin/resources/lang/ro/taxclass.php new file mode 100644 index 0000000000..1afc3e5070 --- /dev/null +++ b/packages/admin/resources/lang/ro/taxclass.php @@ -0,0 +1,32 @@ + 'Clasă de taxe', + + 'plural_label' => 'Clase de taxe', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'default' => [ + 'label' => 'Implicită', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'default' => [ + 'label' => 'Implicită', + ], + ], + 'delete' => [ + 'error' => [ + 'title' => 'Nu se poate șterge clasa de taxe', + 'body' => 'Această clasă de taxe are variante de produs asociate și nu poate fi ștearsă.', + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/taxrate.php b/packages/admin/resources/lang/ro/taxrate.php new file mode 100644 index 0000000000..946da62fa3 --- /dev/null +++ b/packages/admin/resources/lang/ro/taxrate.php @@ -0,0 +1,33 @@ + 'Rată de taxă', + + 'plural_label' => 'Rate de taxă', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'tax_zone' => [ + 'label' => 'Zonă de taxe', + ], + 'priority' => [ + 'label' => 'Prioritate', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'priority' => [ + 'label' => 'Prioritate', + ], + 'tax_zone_id' => [ + 'label' => 'Zonă de taxe', + ], + ], + +]; diff --git a/packages/admin/resources/lang/ro/taxzone.php b/packages/admin/resources/lang/ro/taxzone.php new file mode 100644 index 0000000000..32663db7b7 --- /dev/null +++ b/packages/admin/resources/lang/ro/taxzone.php @@ -0,0 +1,69 @@ + 'Zonă de taxe', + + 'plural_label' => 'Zone de taxe', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'zone_type' => [ + 'label' => 'Tip zonă', + ], + 'active' => [ + 'label' => 'Activă', + ], + 'default' => [ + 'label' => 'Implicită', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'zone_type' => [ + 'label' => 'Tip zonă', + 'options' => [ + 'country' => 'Limitează la țări', + 'states' => 'Limitează la județe', + 'postcodes' => 'Limitează la coduri poștale', + ], + ], + 'price_display' => [ + 'label' => 'Afișare preț', + 'options' => [ + 'include_tax' => 'Include taxe', + 'exclude_tax' => 'Exclude taxe', + ], + ], + 'active' => [ + 'label' => 'Activă', + ], + 'default' => [ + 'label' => 'Implicită', + ], + + 'zone_countries' => [ + 'label' => 'Țări', + ], + + 'zone_country' => [ + 'label' => 'Țară', + ], + + 'zone_states' => [ + 'label' => 'Județe', + ], + + 'zone_postcodes' => [ + 'label' => 'Coduri poștale', + 'helper' => 'Listați fiecare cod poștal pe o linie nouă. Suportă wildcard-uri precum NW*', + ], + + ], + +]; diff --git a/packages/admin/resources/lang/ro/user.php b/packages/admin/resources/lang/ro/user.php new file mode 100644 index 0000000000..74dae6252b --- /dev/null +++ b/packages/admin/resources/lang/ro/user.php @@ -0,0 +1,29 @@ + 'Utilizator', + + 'plural_label' => 'Utilizatori', + + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'email' => [ + 'label' => 'E-mail', + ], + ], + + 'form' => [ + 'email' => [ + 'label' => 'E-mail', + ], + 'password' => [ + 'label' => 'Parolă nouă', + ], + 'password_confirmation' => [ + 'label' => 'Confirmă parola nouă', + ], + ], +]; diff --git a/packages/admin/resources/lang/ro/widgets.php b/packages/admin/resources/lang/ro/widgets.php new file mode 100644 index 0000000000..bcc167190d --- /dev/null +++ b/packages/admin/resources/lang/ro/widgets.php @@ -0,0 +1,118 @@ + [ + 'orders' => [ + 'order_stats_overview' => [ + 'stat_one' => [ + 'label' => 'Comenzi astăzi', + 'increase' => 'Creștere de :percentage% față de :count ieri', + 'decrease' => 'Scădere de :percentage% față de :count ieri', + 'neutral' => 'Fără schimbare față de ieri', + ], + 'stat_two' => [ + 'label' => 'Comenzi în ultimele 7 zile', + 'increase' => 'Creștere de :percentage% față de :count în perioada anterioară', + 'decrease' => 'Scădere de :percentage% față de :count în perioada anterioară', + 'neutral' => 'Fără schimbare față de perioada anterioară', + ], + 'stat_three' => [ + 'label' => 'Comenzi în ultimele 30 de zile', + 'increase' => 'Creștere de :percentage% față de :count în perioada anterioară', + 'decrease' => 'Scădere de :percentage% față de :count în perioada anterioară', + 'neutral' => 'Fără schimbare față de perioada anterioară', + ], + 'stat_four' => [ + 'label' => 'Vânzări astăzi', + 'increase' => 'Creștere de :percentage% față de :total ieri', + 'decrease' => 'Scădere de :percentage% față de :total ieri', + 'neutral' => 'Fără schimbare față de ieri', + ], + 'stat_five' => [ + 'label' => 'Vânzări în ultimele 7 zile', + 'increase' => 'Creștere de :percentage% față de :total în perioada anterioară', + 'decrease' => 'Scădere de :percentage% față de :total în perioada anterioară', + 'neutral' => 'Fără schimbare față de perioada anterioară', + ], + 'stat_six' => [ + 'label' => 'Vânzări în ultimele 30 de zile', + 'increase' => 'Creștere de :percentage% față de :total în perioada anterioară', + 'decrease' => 'Scădere de :percentage% față de :total în perioada anterioară', + 'neutral' => 'Fără schimbare față de perioada anterioară', + ], + ], + 'order_totals_chart' => [ + 'heading' => 'Total comenzi în ultimul an', + 'series_one' => [ + 'label' => 'Această perioadă', + ], + 'series_two' => [ + 'label' => 'Perioada anterioară', + ], + 'yaxis' => [ + 'label' => 'Cifra de afaceri (:currency)', + ], + ], + 'order_sales_chart' => [ + 'heading' => 'Raport comenzi / vânzări', + 'series_one' => [ + 'label' => 'Comenzi', + ], + 'series_two' => [ + 'label' => 'Venit', + ], + 'yaxis' => [ + 'series_one' => [ + 'label' => 'Nr. comenzi', + ], + 'series_two' => [ + 'label' => 'Valoare totală', + ], + ], + ], + 'average_order_value' => [ + 'heading' => 'Valoarea medie a comenzii', + ], + 'new_returning_customers' => [ + 'heading' => 'Clienți noi vs. recurenți', + 'series_one' => [ + 'label' => 'Clienți noi', + ], + 'series_two' => [ + 'label' => 'Clienți recurenți', + ], + ], + 'popular_products' => [ + 'heading' => 'Cele mai vândute (ultimele 12 luni)', + 'description' => 'Aceste cifre se bazează pe numărul de apariții ale unui produs într-o comandă, nu pe cantitatea comandată.', + ], + 'latest_orders' => [ + 'heading' => 'Cele mai recente comenzi', + ], + ], + ], + 'customer' => [ + 'stats_overview' => [ + 'total_orders' => [ + 'label' => 'Total comenzi', + ], + 'avg_spend' => [ + 'label' => 'Cheltuială medie', + ], + 'total_spend' => [ + 'label' => 'Cheltuială totală', + ], + ], + ], + 'variant_switcher' => [ + 'label' => 'Schimbă varianta', + 'table' => [ + 'sku' => [ + 'label' => 'Cod stoc intern (SKU)', + ], + 'values' => [ + 'label' => 'Valori', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/actions.php b/packages/admin/resources/lang/tr/actions.php new file mode 100644 index 0000000000..552e05afe5 --- /dev/null +++ b/packages/admin/resources/lang/tr/actions.php @@ -0,0 +1,52 @@ + [ + 'create_root' => [ + 'label' => 'Ana Koleksiyon Oluştur', + ], + 'create_child' => [ + 'label' => 'Alt Koleksiyon Oluştur', + ], + 'move' => [ + 'label' => 'Koleksiyonu Taşı', + ], + 'delete' => [ + 'label' => 'Sil', + 'notifications' => [ + 'cannot_delete' => [ + 'title' => 'Silinemiyor', + 'body' => 'Bu koleksiyonun alt koleksiyonları var ve silinemez.', + ], + ], + ], + ], + 'orders' => [ + 'update_status' => [ + 'label' => 'Durumu Güncelle', + 'wizard' => [ + 'step_one' => [ + 'label' => 'Durum', + ], + 'step_two' => [ + 'label' => 'E-posta & Bildirimler', + 'no_mailers' => 'Bu durum için kullanılabilir e-posta şablonu yok.', + ], + 'step_three' => [ + 'label' => 'Önizleme & Kaydet', + 'no_mailers' => 'Önizleme için seçilmiş e-posta şablonu yok.', + ], + ], + 'notification' => [ + 'label' => 'Sipariş durumu güncellendi', + ], + 'billing_email' => [ + 'label' => 'Fatura E-postası', + ], + 'shipping_email' => [ + 'label' => 'Kargo E-postası', + ], + ], + + ], +]; diff --git a/packages/admin/resources/lang/tr/activity.php b/packages/admin/resources/lang/tr/activity.php new file mode 100644 index 0000000000..333871b495 --- /dev/null +++ b/packages/admin/resources/lang/tr/activity.php @@ -0,0 +1,29 @@ + 'Aktivite', + + 'plural_label' => 'Aktiviteler', + + 'table' => [ + 'subject' => 'Konu', + 'description' => 'Açıklama', + 'log' => 'Log', + 'logged_at' => 'Kayıt Tarihi', + 'event' => 'Etkinlik', + 'logged_from' => 'Kayıt Başlangıcı', + 'logged_until' => 'Kayıt Bitişi', + ], + + 'form' => [ + 'causer_type' => 'Oluşturan Türü', + 'causer_id' => 'Oluşturan ID', + 'subject_type' => 'Konu türü', + 'subject_id' => 'Konu ID', + 'description' => 'Açıklama', + 'attributes' => 'Özellikler', + 'old' => 'Eski', + ], + +]; diff --git a/packages/admin/resources/lang/tr/address.php b/packages/admin/resources/lang/tr/address.php new file mode 100644 index 0000000000..47438e05c9 --- /dev/null +++ b/packages/admin/resources/lang/tr/address.php @@ -0,0 +1,99 @@ + 'Adres', + + 'plural_label' => 'Adresler', + + 'table' => [ + 'title' => [ + 'label' => 'Başlık', + ], + 'first_name' => [ + 'label' => 'Ad', + ], + 'last_name' => [ + 'label' => 'Soyad', + ], + 'company_name' => [ + 'label' => 'Şirket Adı', + ], + 'tax_identifier' => [ + 'label' => 'Vergi Kimlik Numarası', + ], + 'line_one' => [ + 'label' => 'Adres', + ], + 'line_two' => [ + 'label' => 'Satır İki', + ], + 'line_three' => [ + 'label' => 'Satır Üç', + ], + 'city' => [ + 'label' => 'Şehir', + ], + 'country_id' => [ + 'label' => 'Ülke', + ], + 'state' => [ + 'label' => 'Eyalet', + ], + 'postcode' => [ + 'label' => 'Posta Kodu', + ], + 'contact_email' => [ + 'label' => 'İletişim E-postası', + ], + 'contact_phone' => [ + 'label' => 'İletişim Telefonu', + ], + ], + + 'form' => [ + 'title' => [ + 'label' => 'Başlık', + ], + 'first_name' => [ + 'label' => 'Ad', + ], + 'last_name' => [ + 'label' => 'Soyad', + ], + 'company_name' => [ + 'label' => 'Şirket Adı', + ], + 'tax_identifier' => [ + 'label' => 'Vergi Kimlik Numarası', + ], + 'line_one' => [ + 'label' => 'Satır Bir', + ], + 'line_two' => [ + 'label' => 'Satır İki', + ], + 'line_three' => [ + 'label' => 'Satır Üç', + ], + 'city' => [ + 'label' => 'Şehir', + ], + 'country_id' => [ + 'label' => 'Ülke', + ], + 'state' => [ + 'label' => 'Eyalet', + ], + 'postcode' => [ + 'label' => 'Posta Kodu', + ], + 'contact_email' => [ + 'label' => 'İletişim E-postası', + ], + 'contact_phone' => [ + 'label' => 'İletişim Telefonu', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/attribute.php b/packages/admin/resources/lang/tr/attribute.php new file mode 100644 index 0000000000..ad46c19cfc --- /dev/null +++ b/packages/admin/resources/lang/tr/attribute.php @@ -0,0 +1,55 @@ + 'Özellik', + + 'plural_label' => 'Özellikler', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'description' => [ + 'label' => 'Açıklama', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'type' => [ + 'label' => 'Tür', + ], + ], + + 'form' => [ + 'attributable_type' => [ + 'label' => 'Tür', + ], + 'name' => [ + 'label' => 'Ad', + ], + 'description' => [ + 'label' => 'Açıklama', + 'helper' => 'Alan altındaki yardım metnini görüntülemek için kullanın', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'searchable' => [ + 'label' => 'Aranabilir', + ], + 'filterable' => [ + 'label' => 'Filtrelenebilir', + ], + 'required' => [ + 'label' => 'Gerekli', + ], + 'type' => [ + 'label' => 'Tür', + ], + 'validation_rules' => [ + 'label' => 'Doğrulama Kuralları', + 'helper' => 'Özellik alanı için kurallar, örnek: min:1|max:10|...', + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/attributegroup.php b/packages/admin/resources/lang/tr/attributegroup.php new file mode 100644 index 0000000000..8e1303d1b6 --- /dev/null +++ b/packages/admin/resources/lang/tr/attributegroup.php @@ -0,0 +1,46 @@ + 'Özellik Grubu', + + 'plural_label' => 'Özellik Grupları', + + 'table' => [ + 'attributable_type' => [ + 'label' => 'Tür', + ], + 'name' => [ + 'label' => 'Ad', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'position' => [ + 'label' => 'Pozisyon', + ], + ], + + 'form' => [ + 'attributable_type' => [ + 'label' => 'Tür', + ], + 'name' => [ + 'label' => 'Ad', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'position' => [ + 'label' => 'Pozisyon', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Bu özellik grubu silinemez çünkü ilişkili özellikler var.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/auth.php b/packages/admin/resources/lang/tr/auth.php new file mode 100644 index 0000000000..54a32b20d9 --- /dev/null +++ b/packages/admin/resources/lang/tr/auth.php @@ -0,0 +1,32 @@ + 'Yönetici', + 'roles.admin.description' => 'Tam yetkili yönetici', + 'roles.staff.label' => 'Personel', + 'roles.staff.description' => 'Temel yetkili personel', + /** + * İzinler. + */ + 'permissions.settings.label' => 'Ayarlar', + 'permissions.settings.description' => 'Yönetim panelinin ayarlar alanına erişim sağlar', + 'permissions.settings:core.label' => 'Ana Ayarlar', + 'permissions.settings:core.description' => 'Kanallar, diller, para birimleri vb. temel mağaza ayarlarına erişim', + 'permissions.settings:manage-staff.label' => 'Personel Yönetimi', + 'permissions.settings:manage-staff.description' => 'Personelin diğer personeli düzenlemesine izin verir', + 'permissions.settings:manage-attributes.label' => 'Özellik Yönetimi', + 'permissions.settings:manage-attributes.description' => 'Personelin ek özellikler düzenlemesi ve oluşturmasına izin verir', + 'permissions.catalog:manage-products.label' => 'Ürün Yönetimi', + 'permissions.catalog:manage-products.description' => 'Personelin ürünleri, ürün türlerini ve markaları düzenlemesine izin verir', + 'permissions.catalog:manage-collections.label' => 'Koleksiyon Yönetimi', + 'permissions.catalog:manage-collections.description' => 'Personelin koleksiyonları ve gruplarını düzenlemesine izin verir', + 'permissions.sales:manage-orders.label' => 'Sipariş Yönetimi', + 'permissions.sales:manage-orders.description' => 'Personelin siparişleri yönetmesine izin verir', + 'permissions.sales:manage-customers.label' => 'Müşteri Yönetimi', + 'permissions.sales:manage-customers.description' => 'Personelin müşterileri yönetmesine izin verir', + 'permissions.sales:manage-discounts.label' => 'İndirim Yönetimi', + 'permissions.sales:manage-discounts.description' => 'Personelin indirimleri yönetmesine izin verir', +]; diff --git a/packages/admin/resources/lang/tr/brand.php b/packages/admin/resources/lang/tr/brand.php new file mode 100644 index 0000000000..8aa75266e5 --- /dev/null +++ b/packages/admin/resources/lang/tr/brand.php @@ -0,0 +1,75 @@ + 'Marka', + + 'plural_label' => 'Markalar', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'products_count' => [ + 'label' => 'Ürün Sayısı', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Bu marka silinemez çünkü ilişkili ürünler var.', + ], + ], + ], + 'pages' => [ + 'edit' => [ + 'title' => 'Temel Bilgiler', + ], + 'products' => [ + 'label' => 'Ürünler', + 'actions' => [ + 'attach' => [ + 'label' => 'Ürün ilişkilendir', + 'form' => [ + 'record_id' => [ + 'label' => 'Ürün', + ], + ], + 'notification' => [ + 'success' => 'Ürün markaya eklendi', + ], + ], + 'detach' => [ + 'notification' => [ + 'success' => 'Ürün ilişkisi kaldırıldı.', + ], + ], + ], + ], + 'collections' => [ + 'label' => 'Koleksiyonlar', + 'table' => [ + 'header_actions' => [ + 'attach' => [ + 'record_select' => [ + 'placeholder' => 'Bir koleksiyon seçin', + ], + ], + ], + ], + 'actions' => [ + 'attach' => [ + 'label' => 'Koleksiyon ilişkilendir', + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/channel.php b/packages/admin/resources/lang/tr/channel.php new file mode 100644 index 0000000000..7f2b6deb91 --- /dev/null +++ b/packages/admin/resources/lang/tr/channel.php @@ -0,0 +1,39 @@ + 'Kanal', + + 'plural_label' => 'Kanallar', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'url' => [ + 'label' => 'URL', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'url' => [ + 'label' => 'URL', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/collection.php b/packages/admin/resources/lang/tr/collection.php new file mode 100644 index 0000000000..458fa637e2 --- /dev/null +++ b/packages/admin/resources/lang/tr/collection.php @@ -0,0 +1,45 @@ + 'Koleksiyon', + + 'plural_label' => 'Koleksiyonlar', + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + ], + + 'pages' => [ + 'children' => [ + 'label' => 'Alt Koleksiyonlar', + 'actions' => [ + 'create_child' => [ + 'label' => 'Alt Koleksiyon Oluştur', + ], + ], + 'table' => [ + 'children_count' => [ + 'label' => 'Alt Koleksiyon Sayısı', + ], + 'name' => [ + 'label' => 'Ad', + ], + ], + ], + 'edit' => [ + 'label' => 'Temel Bilgiler', + ], + 'products' => [ + 'label' => 'Ürünler', + 'actions' => [ + 'attach' => [ + 'label' => 'Ürün Ekle', + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/collectiongroup.php b/packages/admin/resources/lang/tr/collectiongroup.php new file mode 100644 index 0000000000..0669e45953 --- /dev/null +++ b/packages/admin/resources/lang/tr/collectiongroup.php @@ -0,0 +1,37 @@ + 'Koleksiyon Grubu', + + 'plural_label' => 'Koleksiyon Grupları', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'collections_count' => [ + 'label' => 'Koleksiyon Sayısı', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Bu koleksiyon grubu silinemez çünkü ilişkili koleksiyonlar var.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/components.php b/packages/admin/resources/lang/tr/components.php new file mode 100644 index 0000000000..daa0144ca4 --- /dev/null +++ b/packages/admin/resources/lang/tr/components.php @@ -0,0 +1,117 @@ + [ + 'notification' => [ + + 'updated' => 'Etiketler güncellendi', + + ], + ], + + 'activity-log' => [ + + 'input' => [ + + 'placeholder' => 'Yorum ekle', + + ], + + 'action' => [ + + 'add-comment' => 'Yorum Ekle', + + ], + + 'system' => 'Sistem', + + 'partials' => [ + 'orders' => [ + 'order_created' => 'Sipariş Oluşturuldu', + + 'status_change' => 'Durum güncellendi', + + 'capture' => ':last_four ile biten kart üzerinde :amount ödeme', + + 'authorized' => ':last_four ile biten kart üzerinde :amount yetkilendirildi', + + 'refund' => ':last_four ile biten kart üzerinde :amount iade', + + 'address' => ':type güncellendi', + + 'billingAddress' => 'Fatura adresi', + + 'shippingAddress' => 'Kargo adresi', + ], + + 'update' => [ + 'updated' => ':model güncellendi', + ], + + 'create' => [ + 'created' => ':model oluşturuldu', + ], + + 'tags' => [ + 'updated' => 'Etiketler güncellendi', + 'added' => 'Eklendi', + 'removed' => 'Kaldırıldı', + ], + ], + + 'notification' => [ + 'comment_added' => 'Yorum eklendi', + ], + + ], + + 'forms' => [ + 'youtube' => [ + 'helperText' => 'YouTube videosunun ID\'sini girin. örn. dQw4w9WgXcQ', + ], + ], + + 'collection-tree-view' => [ + 'actions' => [ + 'move' => [ + 'form' => [ + 'target_id' => [ + 'label' => 'Ana Koleksiyon', + ], + ], + ], + ], + 'notifications' => [ + 'collections-reordered' => [ + 'success' => 'Koleksiyonlar Yeniden Sıralandı', + ], + 'node-expanded' => [ + 'danger' => 'Koleksiyonlar yüklenemiyor', + ], + 'delete' => [ + 'danger' => 'Koleksiyon silinemiyor', + ], + ], + ], + + 'product-options-list' => [ + 'add-option' => [ + 'label' => 'Seçenek Ekle', + ], + 'delete-option' => [ + 'label' => 'Seçeneği Sil', + ], + 'remove-shared-option' => [ + 'label' => 'Paylaşılan Seçeneği Kaldır', + ], + 'add-value' => [ + 'label' => 'Başka Değer Ekle', + ], + 'name' => [ + 'label' => 'Ad', + ], + 'values' => [ + 'label' => 'Değerler', + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/currency.php b/packages/admin/resources/lang/tr/currency.php new file mode 100644 index 0000000000..11b52d343c --- /dev/null +++ b/packages/admin/resources/lang/tr/currency.php @@ -0,0 +1,58 @@ + 'Para Birimi', + + 'plural_label' => 'Para Birimleri', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'code' => [ + 'label' => 'Kod', + ], + 'exchange_rate' => [ + 'label' => 'Döviz Kuru', + ], + 'decimal_places' => [ + 'label' => 'Ondalık Basamak', + ], + 'enabled' => [ + 'label' => 'Etkin', + ], + 'sync_prices' => [ + 'label' => 'Fiyatları Senkronize Et', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'code' => [ + 'label' => 'Kod', + ], + 'exchange_rate' => [ + 'label' => 'Döviz Kuru', + ], + 'decimal_places' => [ + 'label' => 'Ondalık Basamak', + ], + 'enabled' => [ + 'label' => 'Etkin', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + 'sync_prices' => [ + 'label' => 'Fiyatları Senkronize Et', + 'helper_text' => 'Bu para birimindeki fiyatları varsayılan para birimi ile senkronize tutun.', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/customer.php b/packages/admin/resources/lang/tr/customer.php new file mode 100644 index 0000000000..f37506048a --- /dev/null +++ b/packages/admin/resources/lang/tr/customer.php @@ -0,0 +1,63 @@ + 'Müşteri', + + 'plural_label' => 'Müşteriler', + + 'table' => [ + 'full_name' => [ + 'label' => 'Ad Soyad', + ], + 'first_name' => [ + 'label' => 'Ad', + ], + 'last_name' => [ + 'label' => 'Soyad', + ], + 'title' => [ + 'label' => 'Ünvan', + ], + 'company_name' => [ + 'label' => 'Şirket Adı', + ], + 'tax_identifier' => [ + 'label' => 'Vergi Kimlik Numarası', + ], + 'account_reference' => [ + 'label' => 'Hesap Referansı', + ], + 'new' => [ + 'label' => 'Yeni', + ], + 'returning' => [ + 'label' => 'Geri Dönen', + ], + ], + + 'form' => [ + 'title' => [ + 'label' => 'Ünvan', + ], + 'first_name' => [ + 'label' => 'Ad', + ], + 'last_name' => [ + 'label' => 'Soyad', + ], + 'company_name' => [ + 'label' => 'Şirket Adı', + ], + 'account_ref' => [ + 'label' => 'Hesap Referansı', + ], + 'tax_identifier' => [ + 'label' => 'Vergi Kimlik Numarası', + ], + 'customer_groups' => [ + 'label' => 'Müşteri Grupları', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/customergroup.php b/packages/admin/resources/lang/tr/customergroup.php new file mode 100644 index 0000000000..3aa960ef6a --- /dev/null +++ b/packages/admin/resources/lang/tr/customergroup.php @@ -0,0 +1,40 @@ + 'Müşteri Grubu', + + 'plural_label' => 'Müşteri Grupları', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Bu müşteri grubu silinemez çünkü ilişkili müşteriler var.', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/discount.php b/packages/admin/resources/lang/tr/discount.php new file mode 100644 index 0000000000..eee14df15c --- /dev/null +++ b/packages/admin/resources/lang/tr/discount.php @@ -0,0 +1,353 @@ + 'İndirimler', + 'label' => 'İndirim', + 'form' => [ + 'conditions' => [ + 'heading' => 'Koşullar', + ], + 'buy_x_get_y' => [ + 'heading' => 'X Al Y Kazan', + ], + 'amount_off' => [ + 'heading' => 'Sabit Tutar İndirimi', + ], + 'name' => [ + 'label' => 'İndirim Adı', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'starts_at' => [ + 'label' => 'Başlangıç Tarihi', + ], + 'ends_at' => [ + 'label' => 'Bitiş Tarihi', + ], + 'priority' => [ + 'label' => 'Öncelik', + 'helper_text' => 'Yüksek öncelikli indirimler önce uygulanır.', + 'options' => [ + 'low' => [ + 'label' => 'Düşük', + ], + 'medium' => [ + 'label' => 'Orta', + ], + 'high' => [ + 'label' => 'Yüksek', + ], + ], + ], + 'stop' => [ + 'label' => 'Bundan sonra diğer indirimlerin uygulanmasını durdur', + ], + 'coupon' => [ + 'label' => 'Kupon', + 'helper_text' => 'İndirimin uygulanması için gerekli kuponu girin, boş bırakılırsa otomatik olarak uygulanır.', + ], + 'max_uses' => [ + 'label' => 'Maksimum kullanım', + 'helper_text' => 'Sınırsız kullanım için boş bırakın.', + ], + 'max_uses_per_user' => [ + 'label' => 'Kullanıcı başına maksimum kullanım', + 'helper_text' => 'Sınırsız kullanım için boş bırakın.', + ], + 'minimum_cart_amount' => [ + 'label' => 'Minimum Sepet Tutarı', + ], + 'min_qty' => [ + 'label' => 'Ürün Miktarı', + 'helper_text' => 'İndirimin uygulanması için gerekli ürün adedini belirleyin.', + ], + 'reward_qty' => [ + 'label' => 'Ücretsiz ürün sayısı', + 'helper_text' => 'Her üründen kaçının indirimli olacağı.', + ], + 'max_reward_qty' => [ + 'label' => 'Maksimum ödül miktarı', + 'helper_text' => 'Kriterlere bakılmaksızın indirime tabi tutulabilecek maksimum ürün miktarı.', + ], + 'automatic_rewards' => [ + 'label' => 'Ödülleri otomatik ekle', + 'helper_text' => 'Ödül ürünlerini sepette yokken eklemek için açın.', + ], + 'fixed_value' => [ + 'label' => 'Sabit değer', + ], + 'percentage' => [ + 'label' => 'Yüzde', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'status' => [ + 'label' => 'Durum', + \Lunar\Models\Discount::ACTIVE => [ + 'label' => 'Aktif', + ], + \Lunar\Models\Discount::PENDING => [ + 'label' => 'Beklemede', + ], + \Lunar\Models\Discount::EXPIRED => [ + 'label' => 'Süresi Dolmuş', + ], + \Lunar\Models\Discount::SCHEDULED => [ + 'label' => 'Planlanmış', + ], + ], + 'type' => [ + 'label' => 'Tür', + ], + 'starts_at' => [ + 'label' => 'Başlangıç Tarihi', + ], + 'ends_at' => [ + 'label' => 'Bitiş Tarihi', + ], + 'created_at' => [ + 'label' => 'Oluşturulma Tarihi', + ], + 'coupon' => [ + 'label' => 'Kupon', + ], + ], + 'pages' => [ + 'availability' => [ + 'label' => 'Erişilebilirlik', + ], + 'edit' => [ + 'title' => 'Temel Bilgiler', + ], + 'limitations' => [ + 'label' => 'Sınırlamalar', + ], + ], + 'relationmanagers' => [ + 'collections' => [ + 'title' => 'Koleksiyonlar', + 'description' => 'Bu indirimin hangi koleksiyonlarla sınırlanacağını seçin.', + 'actions' => [ + 'attach' => [ + 'label' => 'Koleksiyon Ekle', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'type' => [ + 'label' => 'Tür', + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + ], + ], + 'customers' => [ + 'title' => 'Müşteriler', + 'description' => 'Bu indirimin hangi müşterilerle sınırlanacağını seçin.', + 'actions' => [ + 'attach' => [ + 'label' => 'Müşteri Ekle', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + ], + ], + 'brands' => [ + 'title' => 'Markalar', + 'description' => 'Bu indirimin hangi markalarla sınırlanacağını seçin.', + 'actions' => [ + 'attach' => [ + 'label' => 'Marka Ekle', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'type' => [ + 'label' => 'Tür', + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + ], + ], + 'products' => [ + 'title' => 'Ürünler', + 'description' => 'Bu indirimin hangi ürünlerle sınırlanacağını seçin.', + 'actions' => [ + 'attach' => [ + 'label' => 'Ürün Ekle', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'type' => [ + 'label' => 'Tür', + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + ], + ], + 'rewards' => [ + 'title' => 'Ödüller', + 'description' => 'Sepette mevcut olmaları ve yukarıdaki koşulların karşılanması durumunda hangi ürünlerin indirimli olacağını seçin.', + 'actions' => [ + 'attach' => [ + 'label' => 'Ödül Ekle', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'type' => [ + 'label' => 'Tür', + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + ], + ], + 'conditions' => [ + 'title' => 'Koşullar', + 'description' => 'İndirimin uygulanması için gereken koşulları seçin.', + 'actions' => [ + 'attach' => [ + 'label' => 'Koşul Ekle', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'type' => [ + 'label' => 'Tür', + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + ], + ], + 'productvariants' => [ + 'title' => 'Ürün Varyantları', + 'description' => 'Bu indirimin hangi ürün varyantlarıyla sınırlanacağını seçin.', + 'actions' => [ + 'attach' => [ + 'label' => 'Ürün Varyantı Ekle', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'sku' => [ + 'label' => 'SKU', + ], + 'values' => [ + 'label' => 'Seçenek(ler)', + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Sınırlama', + ], + 'exclusion' => [ + 'label' => 'Hariç Tutma', + ], + ], + ], + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/fieldtypes.php b/packages/admin/resources/lang/tr/fieldtypes.php new file mode 100644 index 0000000000..794bc18f1f --- /dev/null +++ b/packages/admin/resources/lang/tr/fieldtypes.php @@ -0,0 +1,72 @@ + [ + 'label' => 'Açılır Menü', + 'form' => [ + 'lookups' => [ + 'label' => 'Arama Tablosu', + 'key_label' => 'Etiket', + 'value_label' => 'Değer', + ], + ], + ], + 'listfield' => [ + 'label' => 'Liste Alanı', + ], + 'text' => [ + 'label' => 'Metin', + 'form' => [ + 'richtext' => [ + 'label' => 'Zengin Metin', + ], + ], + ], + 'translatedtext' => [ + 'label' => 'Çevrilmiş Metin', + 'form' => [ + 'richtext' => [ + 'label' => 'Zengin Metin', + ], + 'locales' => 'Diller', + ], + ], + 'toggle' => [ + 'label' => 'Aç/Kapat', + ], + 'youtube' => [ + 'label' => 'YouTube', + ], + 'vimeo' => [ + 'label' => 'Vimeo', + ], + 'number' => [ + 'label' => 'Sayı', + 'form' => [ + 'min' => [ + 'label' => 'Min.', + ], + 'max' => [ + 'label' => 'Maks.', + ], + ], + ], + 'file' => [ + 'label' => 'Dosya', + 'form' => [ + 'file_types' => [ + 'label' => 'İzin Verilen Dosya Türleri', + 'placeholder' => 'Yeni MIME', + ], + 'multiple' => [ + 'label' => 'Birden Fazla Dosyaya İzin Ver', + ], + 'min_files' => [ + 'label' => 'Min. Dosya', + ], + 'max_files' => [ + 'label' => 'Maks. Dosya', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/global.php b/packages/admin/resources/lang/tr/global.php new file mode 100644 index 0000000000..8347b47843 --- /dev/null +++ b/packages/admin/resources/lang/tr/global.php @@ -0,0 +1,12 @@ + [ + 'catalog' => 'Katalog', + 'sales' => 'Satışlar', + 'reports' => 'Raporlar', + 'settings' => 'Ayarlar', + ], + +]; diff --git a/packages/admin/resources/lang/tr/language.php b/packages/admin/resources/lang/tr/language.php new file mode 100644 index 0000000000..81973de7d4 --- /dev/null +++ b/packages/admin/resources/lang/tr/language.php @@ -0,0 +1,33 @@ + 'Dil', + + 'plural_label' => 'Diller', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'code' => [ + 'label' => 'Kod', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'code' => [ + 'label' => 'Kod', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/order.php b/packages/admin/resources/lang/tr/order.php new file mode 100644 index 0000000000..3cc8cf37aa --- /dev/null +++ b/packages/admin/resources/lang/tr/order.php @@ -0,0 +1,305 @@ + 'Sipariş', + + 'plural_label' => 'Siparişler', + + 'breadcrumb' => [ + 'manage' => 'Yönet', + ], + + 'tabs' => [ + 'all' => 'Tümü', + ], + + 'transactions' => [ + 'capture' => 'Tahsil Edildi', + 'intent' => 'Ödeme Niyeti', + 'refund' => 'İade Edildi', + 'failed' => 'Başarısız', + ], + + 'table' => [ + 'status' => [ + 'label' => 'Durum', + ], + 'reference' => [ + 'label' => 'Referans', + ], + 'customer_reference' => [ + 'label' => 'Müşteri Referansı', + ], + 'customer' => [ + 'label' => 'Müşteri', + ], + 'tags' => [ + 'label' => 'Etiketler', + ], + 'postcode' => [ + 'label' => 'Posta Kodu', + ], + 'email' => [ + 'label' => 'E-posta', + 'copy_message' => 'E-posta adresi kopyalandı', + ], + 'phone' => [ + 'label' => 'Telefon', + ], + 'total' => [ + 'label' => 'Toplam', + ], + 'date' => [ + 'label' => 'Tarih', + ], + 'new_customer' => [ + 'label' => 'Müşteri Türü', + ], + 'placed_after' => [ + 'label' => 'Bu Tarihten Sonra', + ], + 'placed_before' => [ + 'label' => 'Bu Tarihten Önce', + ], + ], + + 'form' => [ + 'address' => [ + 'first_name' => [ + 'label' => 'Ad', + ], + 'last_name' => [ + 'label' => 'Soyad', + ], + 'line_one' => [ + 'label' => 'Adres Satırı 1', + ], + 'line_two' => [ + 'label' => 'Adres Satırı 2', + ], + 'line_three' => [ + 'label' => 'Adres Satırı 3', + ], + 'company_name' => [ + 'label' => 'Şirket Adı', + ], + 'tax_identifier' => [ + 'label' => 'Vergi Kimlik Numarası', + ], + 'contact_phone' => [ + 'label' => 'Telefon', + ], + 'contact_email' => [ + 'label' => 'E-posta Adresi', + ], + 'city' => [ + 'label' => 'Şehir', + ], + 'state' => [ + 'label' => 'Eyalet / İl', + ], + 'postcode' => [ + 'label' => 'Posta Kodu', + ], + 'country_id' => [ + 'label' => 'Ülke', + ], + ], + + 'reference' => [ + 'label' => 'Referans', + ], + 'status' => [ + 'label' => 'Durum', + ], + 'transaction' => [ + 'label' => 'İşlem', + ], + 'amount' => [ + 'label' => 'Miktar', + + 'hint' => [ + 'less_than_total' => 'Toplam işlem tutarından daha az bir miktar tahsil etmek üzeresiniz', + ], + ], + + 'notes' => [ + 'label' => 'Notlar', + ], + 'confirm' => [ + 'label' => 'Onayla', + + 'alert' => 'Onay gerekli', + + 'hint' => [ + 'capture' => 'Lütfen bu ödemeyi tahsil etmek istediğinizi onaylayın', + 'refund' => 'Lütfen bu tutarı iade etmek istediğinizi onaylayın.', + ], + ], + ], + + 'infolist' => [ + 'notes' => [ + 'label' => 'Notlar', + 'placeholder' => 'Bu siparişte not yok', + ], + 'delivery_instructions' => [ + 'label' => 'Teslimat Talimatları', + ], + 'shipping_total' => [ + 'label' => 'Kargo Toplamı', + ], + 'paid' => [ + 'label' => 'Ödendi', + ], + 'refund' => [ + 'label' => 'İade', + ], + 'unit_price' => [ + 'label' => 'Birim Fiyat', + ], + 'quantity' => [ + 'label' => 'Miktar', + ], + 'sub_total' => [ + 'label' => 'Ara Toplam', + ], + 'discount_total' => [ + 'label' => 'İndirim Toplamı', + ], + 'total' => [ + 'label' => 'Toplam', + ], + 'current_stock_level' => [ + 'message' => 'Mevcut Stok Seviyesi: :count', + ], + 'purchase_stock_level' => [ + 'message' => 'sipariş anında: :count', + ], + 'status' => [ + 'label' => 'Durum', + ], + 'reference' => [ + 'label' => 'Referans', + ], + 'customer_reference' => [ + 'label' => 'Müşteri Referansı', + ], + 'channel' => [ + 'label' => 'Kanal', + ], + 'date_created' => [ + 'label' => 'Oluşturulma Tarihi', + ], + 'date_placed' => [ + 'label' => 'Sipariş Tarihi', + ], + 'new_returning' => [ + 'label' => 'Yeni / Geri Dönen', + ], + 'new_customer' => [ + 'label' => 'Yeni Müşteri', + ], + 'returning_customer' => [ + 'label' => 'Geri Dönen Müşteri', + ], + 'shipping_address' => [ + 'label' => 'Kargo Adresi', + ], + 'billing_address' => [ + 'label' => 'Fatura Adresi', + ], + 'address_not_set' => [ + 'label' => 'Adres ayarlanmamış', + ], + 'billing_matches_shipping' => [ + 'label' => 'Kargo adresi ile aynı', + ], + 'additional_info' => [ + 'label' => 'Ek Bilgiler', + ], + 'no_additional_info' => [ + 'label' => 'Ek Bilgi Yok', + ], + 'tags' => [ + 'label' => 'Etiketler', + ], + 'timeline' => [ + 'label' => 'Zaman Çizelgesi', + ], + 'transactions' => [ + 'label' => 'İşlemler', + 'placeholder' => 'İşlem yok', + ], + 'alert' => [ + 'requires_capture' => 'Bu sipariş hala ödemenin tahsil edilmesini gerektiriyor.', + 'partially_refunded' => 'Bu sipariş kısmen iade edildi.', + 'refunded' => 'Bu sipariş iade edildi.', + ], + ], + + 'action' => [ + 'bulk_update_status' => [ + 'label' => 'Durumu Güncelle', + 'notification' => 'Siparişlerin durumu güncellendi', + ], + 'update_status' => [ + 'new_status' => [ + 'label' => 'Yeni durum', + ], + 'additional_content' => [ + 'label' => 'Ek içerik', + ], + 'additional_email_recipient' => [ + 'label' => 'Ek e-posta alıcısı', + 'placeholder' => 'opsiyonel', + ], + ], + 'download_order_pdf' => [ + 'label' => 'PDF İndir', + 'notification' => 'Sipariş PDF\'i indiriliyor', + ], + 'edit_address' => [ + 'label' => 'Düzenle', + + 'notification' => [ + 'error' => 'Hata', + + 'billing_address' => [ + 'saved' => 'Fatura adresi kaydedildi', + ], + + 'shipping_address' => [ + 'saved' => 'Kargo adresi kaydedildi', + ], + ], + ], + 'edit_tags' => [ + 'label' => 'Düzenle', + 'form' => [ + 'tags' => [ + 'label' => 'Etiketler', + 'helper_text' => 'Etiketleri Enter, Tab veya virgül (,) tuşuna basarak ayırın', + ], + ], + ], + 'capture_payment' => [ + 'label' => 'Ödemeyi Tahsil Et', + + 'notification' => [ + 'error' => 'Yakalama ile ilgili bir sorun oluştu', + 'success' => 'Yakalama başarılı', + ], + ], + 'refund_payment' => [ + 'label' => 'İade', + + 'notification' => [ + 'error' => 'İade ile ilgili bir sorun oluştu', + 'success' => 'İade başarılı', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/product.php b/packages/admin/resources/lang/tr/product.php new file mode 100644 index 0000000000..ab2458b542 --- /dev/null +++ b/packages/admin/resources/lang/tr/product.php @@ -0,0 +1,131 @@ + 'Ürün', + + 'plural_label' => 'Ürünler', + + 'tabs' => [ + 'all' => 'Tümü', + ], + + 'status' => [ + 'unpublished' => [ + 'content' => 'Şu anda taslak durumunda olan bu ürün, tüm kanallarda ve müşteri gruplarında gizlidir.', + ], + 'availability' => [ + 'customer_groups' => 'Bu ürün şu anda tüm müşteri grupları için mevcut değil.', + 'channels' => 'Bu ürün şu anda tüm kanallar için mevcut değil.', + ], + ], + + 'table' => [ + 'status' => [ + 'label' => 'Durum', + 'states' => [ + 'deleted' => 'Silindi', + 'draft' => 'Taslak', + 'published' => 'Yayınlandı', + ], + ], + 'name' => [ + 'label' => 'Ad', + ], + 'brand' => [ + 'label' => 'Marka', + ], + 'sku' => [ + 'label' => 'SKU', + ], + 'stock' => [ + 'label' => 'Stok', + ], + 'producttype' => [ + 'label' => 'Ürün Türü', + ], + ], + + 'actions' => [ + 'edit_status' => [ + 'label' => 'Durumu Güncelle', + 'heading' => 'Durumu Güncelle', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'brand' => [ + 'label' => 'Marka', + ], + 'sku' => [ + 'label' => 'SKU', + ], + 'producttype' => [ + 'label' => 'Ürün Türü', + ], + 'status' => [ + 'label' => 'Durum', + 'options' => [ + 'published' => [ + 'label' => 'Yayınlandı', + 'description' => 'Bu ürün tüm etkin müşteri grupları ve kanallarda mevcut olacak', + ], + 'draft' => [ + 'label' => 'Taslak', + 'description' => 'Bu ürün tüm kanallarda ve müşteri gruplarında gizlenecek', + ], + ], + ], + 'tags' => [ + 'label' => 'Etiketler', + 'helper_text' => 'Etiketleri Enter, Tab veya virgül (,) tuşuna basarak ayırın', + ], + 'collections' => [ + 'label' => 'Koleksiyonlar', + 'select_collection' => 'Bir koleksiyon seçin', + ], + ], + + 'pages' => [ + 'availability' => [ + 'label' => 'Erişilebilirlik', + ], + 'edit' => [ + 'title' => 'Temel Bilgiler', + ], + 'identifiers' => [ + 'label' => 'Ürün Tanımlayıcıları', + ], + 'inventory' => [ + 'label' => 'Envanter', + ], + 'pricing' => [ + 'form' => [ + 'tax_class_id' => [ + 'label' => 'Vergi Sınıfı', + ], + 'tax_ref' => [ + 'label' => 'Vergi Referansı', + 'helper_text' => 'İsteğe bağlı, 3. parti sistemlerle entegrasyon için.', + ], + ], + ], + 'shipping' => [ + 'label' => 'Kargo', + ], + 'variants' => [ + 'label' => 'Varyantlar', + ], + 'collections' => [ + 'label' => 'Koleksiyonlar', + 'select_collection' => 'Bir koleksiyon seçin', + ], + 'associations' => [ + 'label' => 'Ürün İlişkileri', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/productoption.php b/packages/admin/resources/lang/tr/productoption.php new file mode 100644 index 0000000000..dd0064e517 --- /dev/null +++ b/packages/admin/resources/lang/tr/productoption.php @@ -0,0 +1,127 @@ + 'Ürün Seçeneği', + + 'plural_label' => 'Ürün Seçenekleri', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'label' => [ + 'label' => 'Etiket', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + 'shared' => [ + 'label' => 'Paylaşılan', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'label' => [ + 'label' => 'Etiket', + ], + 'handle' => [ + 'label' => 'Tanımlayıcı', + ], + ], + + 'widgets' => [ + 'product-options' => [ + 'notifications' => [ + 'save-variants' => [ + 'success' => [ + 'title' => 'Ürün Varyantları Kaydedildi', + ], + ], + ], + 'actions' => [ + 'cancel' => [ + 'label' => 'İptal', + ], + 'save-options' => [ + 'label' => 'Seçenekleri Kaydet', + ], + 'add-shared-option' => [ + 'label' => 'Paylaşılan Seçenek Ekle', + 'form' => [ + 'product_option' => [ + 'label' => 'Ürün Seçeneği', + ], + 'no_shared_components' => [ + 'label' => 'Mevcut paylaşılan seçenek yok.', + ], + 'preselect' => [ + 'label' => 'Varsayılan olarak tüm değerleri önceden seç.', + ], + ], + ], + 'add-restricted-option' => [ + 'label' => 'Seçenek Ekle', + ], + ], + 'options-list' => [ + 'empty' => [ + 'heading' => 'Yapılandırılmış ürün seçeneği yok', + 'description' => 'Varyant oluşturmaya başlamak için paylaşılan veya kısıtlanmış ürün seçeneği ekleyin.', + ], + ], + 'options-table' => [ + 'title' => 'Ürün Seçenekleri', + 'configure-options' => [ + 'label' => 'Seçenekleri Yapılandır', + ], + 'table' => [ + 'option' => [ + 'label' => 'Seçenek', + ], + 'values' => [ + 'label' => 'Değerler', + ], + ], + ], + 'variants-table' => [ + 'title' => 'Ürün Varyantları', + 'actions' => [ + 'create' => [ + 'label' => 'Varyant Oluştur', + ], + 'edit' => [ + 'label' => 'Düzenle', + ], + 'delete' => [ + 'label' => 'Sil', + ], + ], + 'empty' => [ + 'heading' => 'Yapılandırılmış Varyant Yok', + ], + 'table' => [ + 'new' => [ + 'label' => 'YENİ', + ], + 'option' => [ + 'label' => 'Seçenek', + ], + 'sku' => [ + 'label' => 'SKU', + ], + 'price' => [ + 'label' => 'Fiyat', + ], + 'stock' => [ + 'label' => 'Stok', + ], + ], + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/producttype.php b/packages/admin/resources/lang/tr/producttype.php new file mode 100644 index 0000000000..f44b138766 --- /dev/null +++ b/packages/admin/resources/lang/tr/producttype.php @@ -0,0 +1,52 @@ + 'Ürün Türü', + + 'plural_label' => 'Ürün Türleri', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'products_count' => [ + 'label' => 'Ürün sayısı', + ], + 'product_attributes_count' => [ + 'label' => 'Ürün Özellikleri', + ], + 'variant_attributes_count' => [ + 'label' => 'Varyant Özellikleri', + ], + ], + + 'tabs' => [ + 'product_attributes' => [ + 'label' => 'Ürün Özellikleri', + ], + 'variant_attributes' => [ + 'label' => 'Varyant Özellikleri', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + ], + + 'attributes' => [ + 'no_groups' => 'Mevcut özellik grubu yok.', + 'no_attributes' => 'Mevcut özellik yok.', + ], + + 'action' => [ + 'delete' => [ + 'notification' => [ + 'error_protected' => 'Bu ürün türü silinemez çünkü ilişkili ürünler var.', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/productvariant.php b/packages/admin/resources/lang/tr/productvariant.php new file mode 100644 index 0000000000..d4b4d6ee3d --- /dev/null +++ b/packages/admin/resources/lang/tr/productvariant.php @@ -0,0 +1,105 @@ + 'Ürün Varyantı', + 'plural_label' => 'Ürün Varyantları', + 'pages' => [ + 'edit' => [ + 'title' => 'Temel Bilgiler', + ], + 'media' => [ + 'title' => 'Medya', + 'form' => [ + 'no_selection' => [ + 'label' => 'Bu varyant için şu anda seçilmiş bir görseliniz yok.', + ], + 'no_media_available' => [ + 'label' => 'Bu üründe şu anda mevcut medya yok.', + ], + 'images' => [ + 'label' => 'Ana Görsel', + 'helper_text' => 'Bu varyantı temsil eden ürün görselini seçin.', + ], + ], + ], + 'identifiers' => [ + 'title' => 'Tanımlayıcılar', + ], + 'inventory' => [ + 'title' => 'Envanter', + ], + 'shipping' => [ + 'title' => 'Kargo', + ], + ], + 'form' => [ + 'sku' => [ + 'label' => 'SKU', + ], + 'gtin' => [ + 'label' => 'Küresel Ticaret Ürün Numarası (GTIN)', + ], + 'mpn' => [ + 'label' => 'Üretici Parça Numarası (MPN)', + ], + 'ean' => [ + 'label' => 'UPC/EAN', + ], + 'stock' => [ + 'label' => 'Stokta', + ], + 'backorder' => [ + 'label' => 'Sipariş Üzerine', + ], + 'purchasable' => [ + 'label' => 'Satın Alınabilirlik', + 'options' => [ + 'always' => 'Her Zaman', + 'in_stock' => 'Stokta', + 'in_stock_or_on_backorder' => 'Stokta veya Sipariş Üzerine', + ], + ], + 'unit_quantity' => [ + 'label' => 'Birim Miktarı', + 'helper_text' => '1 birimi kaç adet ürün oluşturur.', + ], + 'min_quantity' => [ + 'label' => 'Minimum Miktar', + 'helper_text' => 'Tek bir satın alma işleminde satın alınabilecek minimum ürün varyantı miktarı.', + ], + 'quantity_increment' => [ + 'label' => 'Miktar Artışı', + 'helper_text' => 'Ürün varyantı bu miktarın katları halinde satın alınmalıdır.', + ], + 'tax_class_id' => [ + 'label' => 'Vergi Sınıfı', + ], + 'shippable' => [ + 'label' => 'Kargo Edilebilir', + ], + 'length_value' => [ + 'label' => 'Uzunluk', + ], + 'length_unit' => [ + 'label' => 'Uzunluk Birimi', + ], + 'width_value' => [ + 'label' => 'Genişlik', + ], + 'width_unit' => [ + 'label' => 'Genişlik Birimi', + ], + 'height_value' => [ + 'label' => 'Yükseklik', + ], + 'height_unit' => [ + 'label' => 'Yükseklik Birimi', + ], + 'weight_value' => [ + 'label' => 'Ağırlık', + ], + 'weight_unit' => [ + 'label' => 'Ağırlık Birimi', + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/relationmanagers.php b/packages/admin/resources/lang/tr/relationmanagers.php new file mode 100644 index 0000000000..afc80b551c --- /dev/null +++ b/packages/admin/resources/lang/tr/relationmanagers.php @@ -0,0 +1,285 @@ + [ + 'title' => 'Müşteri Grupları', + 'actions' => [ + 'attach' => [ + 'label' => 'Müşteri Grubu Ekle', + ], + ], + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'enabled' => [ + 'label' => 'Etkin', + ], + 'starts_at' => [ + 'label' => 'Başlangıç Tarihi', + ], + 'ends_at' => [ + 'label' => 'Bitiş Tarihi', + ], + 'visible' => [ + 'label' => 'Görünür', + ], + 'purchasable' => [ + 'label' => 'Satın Alınabilir', + ], + ], + 'table' => [ + 'description' => 'Müşteri gruplarını bu :type ile ilişkilendirerek kullanılabilirliğini belirleyin.', + 'name' => [ + 'label' => 'Ad', + ], + 'enabled' => [ + 'label' => 'Etkin', + ], + 'starts_at' => [ + 'label' => 'Başlangıç Tarihi', + ], + 'ends_at' => [ + 'label' => 'Bitiş Tarihi', + ], + 'visible' => [ + 'label' => 'Görünür', + ], + 'purchasable' => [ + 'label' => 'Satın Alınabilir', + ], + ], + ], + 'channels' => [ + 'title' => 'Kanallar', + 'actions' => [ + 'attach' => [ + 'label' => 'Başka Bir Kanal Zamanla', + ], + ], + 'form' => [ + 'enabled' => [ + 'label' => 'Etkin', + 'helper_text_false' => 'Bu kanal, başlangıç tarihi mevcut olsa bile etkinleştirilmeyecek.', + ], + 'starts_at' => [ + 'label' => 'Başlangıç Tarihi', + 'helper_text' => 'Herhangi bir tarihten itibaren kullanılabilir olması için boş bırakın.', + ], + 'ends_at' => [ + 'label' => 'Bitiş Tarihi', + 'helper_text' => 'Süresiz olarak kullanılabilir olması için boş bırakın.', + ], + ], + 'table' => [ + 'description' => 'Hangi kanalların etkin olduğunu belirleyin ve kullanılabilirliği zamanlayin.', + 'name' => [ + 'label' => 'Ad', + ], + 'enabled' => [ + 'label' => 'Etkin', + ], + 'starts_at' => [ + 'label' => 'Başlangıç Tarihi', + ], + 'ends_at' => [ + 'label' => 'Bitiş Tarihi', + ], + ], + ], + 'medias' => [ + 'title' => 'Medya', + 'title_plural' => 'Medya', + 'actions' => [ + 'attach' => [ + 'label' => 'Medya Ekle', + ], + 'create' => [ + 'label' => 'Medya Oluştur', + ], + 'detach' => [ + 'label' => 'Ayır', + ], + 'view' => [ + 'label' => 'Görüntüle', + ], + ], + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'media' => [ + 'label' => 'Görsel', + ], + 'primary' => [ + 'label' => 'Ana', + ], + ], + 'table' => [ + 'image' => [ + 'label' => 'Görsel', + ], + 'file' => [ + 'label' => 'Dosya', + ], + 'name' => [ + 'label' => 'Ad', + ], + 'primary' => [ + 'label' => 'Ana', + ], + ], + 'all_media_attached' => 'Eklenecek ürün görseli yok', + 'variant_description' => 'Bu varyanta ürün görsellerini ekleyin', + ], + 'urls' => [ + 'title' => 'URL', + 'title_plural' => 'URL\'ler', + 'actions' => [ + 'create' => [ + 'label' => 'URL Oluştur', + ], + ], + 'filters' => [ + 'language_id' => [ + 'label' => 'Dil', + ], + ], + 'form' => [ + 'slug' => [ + 'label' => 'Slug', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + 'language' => [ + 'label' => 'Dil', + ], + ], + 'table' => [ + 'slug' => [ + 'label' => 'Slug', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + 'language' => [ + 'label' => 'Dil', + ], + ], + ], + 'customer_group_pricing' => [ + 'title' => 'Müşteri Grubu Fiyatlandırması', + 'title_plural' => 'Müşteri Grubu Fiyatlandırması', + 'table' => [ + 'heading' => 'Müşteri Grubu Fiyatlandırması', + 'description' => 'Ürün fiyatını belirlemek için müşteri gruplarına fiyat ilişkilendirin.', + 'empty_state' => [ + 'label' => 'Müşteri grubu fiyatlandırması yok.', + 'description' => 'Başlamak için bir müşteri grubu fiyatı oluşturun.', + ], + 'actions' => [ + 'create' => [ + 'label' => 'Müşteri Grubu Fiyatı Ekle', + 'modal' => [ + 'heading' => 'Müşteri Grubu Fiyatı Oluştur', + ], + ], + ], + ], + ], + 'pricing' => [ + 'title' => 'Fiyatlandırma', + 'title_plural' => 'Fiyatlandırma', + 'tab_name' => 'Basamaklı Fiyatlandırma', + 'table' => [ + 'heading' => 'Basamaklı Fiyatlandırma', + 'description' => 'Müşteri daha büyük miktarlarda satın aldığında fiyatı düşürün.', + 'empty_state' => [ + 'label' => 'Basamaklı fiyatlandırma yok.', + ], + 'actions' => [ + 'create' => [ + 'label' => 'Basamaklı Fiyat Ekle', + ], + ], + 'price' => [ + 'label' => 'Fiyat', + ], + 'customer_group' => [ + 'label' => 'Müşteri Grubu', + 'placeholder' => 'Tüm Müşteri Grupları', + ], + 'min_quantity' => [ + 'label' => 'Minimum Miktar', + ], + 'currency' => [ + 'label' => 'Para Birimi', + ], + ], + 'form' => [ + 'price' => [ + 'label' => 'Fiyat', + 'helper_text' => 'İndirimlerden önce satın alma fiyatı.', + ], + 'customer_group_id' => [ + 'label' => 'Müşteri Grubu', + 'placeholder' => 'Tüm Müşteri Grupları', + 'helper_text' => 'Bu fiyatın uygulanacağı müşteri grubunu seçin.', + ], + 'min_quantity' => [ + 'label' => 'Minimum Miktar', + 'helper_text' => 'Bu fiyatın geçerli olacağı minimum miktarı seçin.', + 'validation' => [ + 'unique' => 'Müşteri Grubu ve Minimum Miktar benzersiz olmalıdır.', + ], + ], + 'currency_id' => [ + 'label' => 'Para Birimi', + 'helper_text' => 'Bu fiyat için para birimini seçin.', + ], + 'compare_price' => [ + 'label' => 'Karşılaştırma Fiyatı', + 'helper_text' => 'Satın alma fiyatıyla karşılaştırma için orijinal fiyat veya tavsiye edilen satış fiyat (RRP).', + ], + 'basePrices' => [ + 'title' => 'Fiyatlar', + 'form' => [ + 'price' => [ + 'label' => 'Fiyat', + 'helper_text' => 'İndirimlerden önce satın alma fiyatı.', + 'sync_price' => 'Fiyat varsayılan para birimi ile senkronize edildi.', + ], + 'compare_price' => [ + 'label' => 'Karşılaştırma Fiyatı', + 'helper_text' => 'Satın alma fiyatıyla karşılaştırma için orijinal fiyat veya tavsiye edilen satış fiyat (RRP).', + ], + ], + 'tooltip' => 'Döviz kurlarına göre otomatik olarak oluşturuldu.', + ], + ], + ], + 'tax_rate_amounts' => [ + 'table' => [ + 'description' => '', + 'percentage' => [ + 'label' => 'Yüzde', + ], + 'tax_class' => [ + 'label' => 'Vergi Sınıfı', + ], + ], + ], + 'values' => [ + 'title' => 'Değerler', + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'position' => [ + 'label' => 'Pozisyon', + ], + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/staff.php b/packages/admin/resources/lang/tr/staff.php new file mode 100644 index 0000000000..33dc21374c --- /dev/null +++ b/packages/admin/resources/lang/tr/staff.php @@ -0,0 +1,81 @@ + 'Personel', + + 'plural_label' => 'Personel', + + 'table' => [ + 'first_name' => [ + 'label' => 'Ad', + ], + 'last_name' => [ + 'label' => 'Soyad', + ], + 'email' => [ + 'label' => 'E-posta', + ], + 'admin' => [ + 'badge' => 'Süper Yönetici', + ], + ], + + 'form' => [ + 'first_name' => [ + 'label' => 'Ad', + ], + 'last_name' => [ + 'label' => 'Soyad', + ], + 'email' => [ + 'label' => 'E-posta', + ], + 'password' => [ + 'label' => 'Şifre', + 'hint' => 'Şifreyi sıfırla', + ], + 'admin' => [ + 'label' => 'Süper Yönetici', + 'helper' => 'Süper yönetici rolleri yönetim panelinde değiştirilemez.', + ], + 'roles' => [ + 'label' => 'Roller', + 'helper' => ':roles tam erişime sahip', + ], + 'permissions' => [ + 'label' => 'İzinler', + ], + 'role' => [ + 'label' => 'Rol Adı', + ], + ], + + 'action' => [ + 'acl' => [ + 'label' => 'Erişim Kontrolü', + ], + 'add-role' => [ + 'label' => 'Rol Ekle', + ], + 'delete-role' => [ + 'label' => 'Rolü Sil', + 'heading' => 'Rolü sil: :role', + ], + ], + + 'acl' => [ + 'title' => 'Erişim Kontrolü', + 'tooltip' => [ + 'roles-included' => 'İzin şu rollere dahildir', + ], + 'notification' => [ + 'updated' => 'Güncellendi', + 'error' => 'Hata', + 'no-role' => 'Rol Lunar\'da kayıtlı değil', + 'no-permission' => 'İzin Lunar\'da kayıtlı değil', + 'no-role-permission' => 'Rol ve İzin Lunar\'da kayıtlı değil', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/tag.php b/packages/admin/resources/lang/tr/tag.php new file mode 100644 index 0000000000..8e9701d19f --- /dev/null +++ b/packages/admin/resources/lang/tr/tag.php @@ -0,0 +1,21 @@ + 'Etiket', + + 'plural_label' => 'Etiketler', + + 'table' => [ + 'value' => [ + 'label' => 'Değer', + ], + ], + + 'form' => [ + 'value' => [ + 'label' => 'Değer', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/tax.php b/packages/admin/resources/lang/tr/tax.php new file mode 100644 index 0000000000..8904313b47 --- /dev/null +++ b/packages/admin/resources/lang/tr/tax.php @@ -0,0 +1,9 @@ + 'Vergi', + + 'plural_label' => 'Vergiler', + +]; diff --git a/packages/admin/resources/lang/tr/taxclass.php b/packages/admin/resources/lang/tr/taxclass.php new file mode 100644 index 0000000000..c40413fdd9 --- /dev/null +++ b/packages/admin/resources/lang/tr/taxclass.php @@ -0,0 +1,32 @@ + 'Vergi Sınıfı', + + 'plural_label' => 'Vergi Sınıfları', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + 'delete' => [ + 'error' => [ + 'title' => 'Vergi sınıfı silinemez', + 'body' => 'Bu vergi sınıfının ilişkili ürün varyantları var ve silinemez.', + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/taxrate.php b/packages/admin/resources/lang/tr/taxrate.php new file mode 100644 index 0000000000..643ce27f66 --- /dev/null +++ b/packages/admin/resources/lang/tr/taxrate.php @@ -0,0 +1,33 @@ + 'Vergi Oranı', + + 'plural_label' => 'Vergi Oranları', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'tax_zone' => [ + 'label' => 'Vergi Bölgesi', + ], + 'priority' => [ + 'label' => 'Öncelik', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'priority' => [ + 'label' => 'Öncelik', + ], + 'tax_zone_id' => [ + 'label' => 'Vergi Bölgesi', + ], + ], + +]; diff --git a/packages/admin/resources/lang/tr/taxzone.php b/packages/admin/resources/lang/tr/taxzone.php new file mode 100644 index 0000000000..230cc39198 --- /dev/null +++ b/packages/admin/resources/lang/tr/taxzone.php @@ -0,0 +1,69 @@ + 'Vergi Bölgesi', + + 'plural_label' => 'Vergi Bölgeleri', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'zone_type' => [ + 'label' => 'Bölge Türü', + ], + 'active' => [ + 'label' => 'Aktif', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + ], + + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'zone_type' => [ + 'label' => 'Bölge Türü', + 'options' => [ + 'country' => 'Ülkelerle Sınırla', + 'states' => 'Eyaletlerle Sınırla', + 'postcodes' => 'Posta Kodlarıyla Sınırla', + ], + ], + 'price_display' => [ + 'label' => 'Fiyat Gösterimi', + 'options' => [ + 'include_tax' => 'Vergi Dahil', + 'exclude_tax' => 'Vergi Hariç', + ], + ], + 'active' => [ + 'label' => 'Aktif', + ], + 'default' => [ + 'label' => 'Varsayılan', + ], + + 'zone_countries' => [ + 'label' => 'Ülkeler', + ], + + 'zone_country' => [ + 'label' => 'Ülke', + ], + + 'zone_states' => [ + 'label' => 'Eyaletler', + ], + + 'zone_postcodes' => [ + 'label' => 'Posta Kodları', + 'helper' => 'Her posta kodunu yeni bir satırda listeleyin. NW* gibi joker karakterleri destekler', + ], + + ], + +]; diff --git a/packages/admin/resources/lang/tr/user.php b/packages/admin/resources/lang/tr/user.php new file mode 100644 index 0000000000..9400eacb46 --- /dev/null +++ b/packages/admin/resources/lang/tr/user.php @@ -0,0 +1,29 @@ + 'Kullanıcı', + + 'plural_label' => 'Kullanıcılar', + + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'email' => [ + 'label' => 'E-posta', + ], + ], + + 'form' => [ + 'email' => [ + 'label' => 'E-posta', + ], + 'password' => [ + 'label' => 'Yeni Şifre', + ], + 'password_confirmation' => [ + 'label' => 'Yeni Şifreyi Onayla', + ], + ], +]; diff --git a/packages/admin/resources/lang/tr/widgets.php b/packages/admin/resources/lang/tr/widgets.php new file mode 100644 index 0000000000..5746f5453b --- /dev/null +++ b/packages/admin/resources/lang/tr/widgets.php @@ -0,0 +1,118 @@ + [ + 'orders' => [ + 'order_stats_overview' => [ + 'stat_one' => [ + 'label' => 'Bugünkü siparişler', + 'increase' => 'Düne göre %:percentage artış (Dün: :count)', + 'decrease' => 'Düne göre %:percentage azalış (Dün: :count)', + 'neutral' => 'Düne göre değişiklik yok', + ], + 'stat_two' => [ + 'label' => 'Son 7 günün siparişleri', + 'increase' => 'Önceki döneme göre %:percentage artış (Önceki: :count)', + 'decrease' => 'Önceki döneme göre %:percentage azalış (Önceki: :count)', + 'neutral' => 'Önceki döneme göre değişiklik yok', + ], + 'stat_three' => [ + 'label' => 'Son 30 günün siparişleri', + 'increase' => 'Önceki döneme göre %:percentage artış (Önceki: :count)', + 'decrease' => 'Önceki döneme göre %:percentage azalış (Önceki: :count)', + 'neutral' => 'Önceki döneme göre değişiklik yok', + ], + 'stat_four' => [ + 'label' => 'Bugünkü satışlar', + 'increase' => 'Düne göre %:percentage artış (Dün: :total)', + 'decrease' => 'Düne göre %:percentage azalış (Dün: :total)', + 'neutral' => 'Düne göre değişiklik yok', + ], + 'stat_five' => [ + 'label' => 'Son 7 günün satışları', + 'increase' => 'Önceki döneme göre %:percentage artış (Önceki: :total)', + 'decrease' => 'Önceki döneme göre %:percentage azalış (Önceki: :total)', + 'neutral' => 'Önceki döneme göre değişiklik yok', + ], + 'stat_six' => [ + 'label' => 'Son 30 günün satışları', + 'increase' => 'Önceki döneme göre %:percentage artış (Önceki: :total)', + 'decrease' => 'Önceki döneme göre %:percentage azalış (Önceki: :total)', + 'neutral' => 'Önceki döneme göre değişiklik yok', + ], + ], + 'order_totals_chart' => [ + 'heading' => 'Geçen yılın sipariş toplamları', + 'series_one' => [ + 'label' => 'Bu Dönem', + ], + 'series_two' => [ + 'label' => 'Önceki Dönem', + ], + 'yaxis' => [ + 'label' => 'Ciro :currency', + ], + ], + 'order_sales_chart' => [ + 'heading' => 'Siparişler / Satışlar Raporu', + 'series_one' => [ + 'label' => 'Siparişler', + ], + 'series_two' => [ + 'label' => 'Gelir', + ], + 'yaxis' => [ + 'series_one' => [ + 'label' => '# Siparişler', + ], + 'series_two' => [ + 'label' => 'Toplam Değer', + ], + ], + ], + 'average_order_value' => [ + 'heading' => 'Ortalama Sipariş Değeri', + ], + 'new_returning_customers' => [ + 'heading' => 'Yeni vs Geri Dönen Müşteriler', + 'series_one' => [ + 'label' => 'Yeni Müşteriler', + ], + 'series_two' => [ + 'label' => 'Geri Dönen Müşteriler', + ], + ], + 'popular_products' => [ + 'heading' => 'En çok satanlar (son 12 ay)', + 'description' => 'Bu rakamlar bir ürünün siparişte kaç kez göründüğüne dayanır, sipariş edilen miktara değil.', + ], + 'latest_orders' => [ + 'heading' => 'Son siparişler', + ], + ], + ], + 'customer' => [ + 'stats_overview' => [ + 'total_orders' => [ + 'label' => 'Toplam siparişler', + ], + 'avg_spend' => [ + 'label' => 'Ort. Harcama', + ], + 'total_spend' => [ + 'label' => 'Toplam Harcama', + ], + ], + ], + 'variant_switcher' => [ + 'label' => 'Varyant Değiştir', + 'table' => [ + 'sku' => [ + 'label' => 'SKU', + ], + 'values' => [ + 'label' => 'Değerler', + ], + ], + ], +]; diff --git a/packages/admin/resources/lang/vi/relationmanagers.php b/packages/admin/resources/lang/vi/relationmanagers.php index 2ecf6043e0..12000b6011 100644 --- a/packages/admin/resources/lang/vi/relationmanagers.php +++ b/packages/admin/resources/lang/vi/relationmanagers.php @@ -263,6 +263,11 @@ ], 'values' => [ 'title' => 'Giá trị', + 'form' => [ + 'name' => [ + 'label' => 'Tên', + ], + ], 'table' => [ 'name' => [ 'label' => 'Tên', diff --git a/packages/admin/resources/lang/vi/tax.php b/packages/admin/resources/lang/vi/tax.php new file mode 100644 index 0000000000..4e10fd586e --- /dev/null +++ b/packages/admin/resources/lang/vi/tax.php @@ -0,0 +1,9 @@ + 'Thuế', + + 'plural_label' => 'Thuế', + +]; diff --git a/packages/admin/resources/views/livewire/components/activity-log-feed.blade.php b/packages/admin/resources/views/livewire/components/activity-log-feed.blade.php index 7b42494b83..a938dd68e4 100644 --- a/packages/admin/resources/views/livewire/components/activity-log-feed.blade.php +++ b/packages/admin/resources/views/livewire/components/activity-log-feed.blade.php @@ -1,5 +1,5 @@
-
+
- - {{ $this->form }} - + + {{ $this->form }} +
{{ $this->addCommentAction }}
-
+
@@ -30,7 +30,7 @@ class="inline-block w-8 h-8 rounded-full" /> {{ $log['date']->format('F jS, Y') }}

-
    +
      @foreach ($log['items'] as $item) @php $logUserName = $item['log']->causer ? ($item['log']->causer->fullName ?: $item['log']->causer->name) : null; @@ -43,7 +43,7 @@ class="inline-block w-8 h-8 rounded-full" /> '-left-[calc(0.5rem_-_1px)]' => !$item['log']->causer, ])> @if ($email = $item['log']->causer?->email) - {{ $logUserName }} + @if ($record->shippingAddress)

      Shipping

      {{ $record->shippingAddress->fullName }}
      @@ -135,6 +136,7 @@ {{ $record->shippingAddress->postcode }}
      {{ $record->shippingAddress->country->name }}
      + @endif diff --git a/packages/admin/src/Filament/Pages/Dashboard.php b/packages/admin/src/Filament/Pages/Dashboard.php index 0c3b933f14..a675799201 100644 --- a/packages/admin/src/Filament/Pages/Dashboard.php +++ b/packages/admin/src/Filament/Pages/Dashboard.php @@ -10,23 +10,52 @@ use Lunar\Admin\Filament\Widgets\Dashboard\Orders\OrderStatsOverview; use Lunar\Admin\Filament\Widgets\Dashboard\Orders\OrderTotalsChart; use Lunar\Admin\Filament\Widgets\Dashboard\Orders\PopularProductsTable; +use Lunar\Admin\Support\Concerns\CallsHooks; use Lunar\Admin\Support\Pages\BaseDashboard; class Dashboard extends BaseDashboard { + use CallsHooks; + protected static ?int $navigationSort = 1; public function getWidgets(): array + { + return self::callLunarHook('getWidgets', $this->getDefaultWidgets()); + } + + public function getDefaultWidgets(): array { return [ + ...$this->getDefaultOverviewWidgets(), + ...$this->getDefaultChartsWidgets(), + ...$this->getDefaultTableWidgets(), + ]; + } + + public function getDefaultOverviewWidgets(): array + { + return self::callLunarHook('getOverviewWidgets', [ OrderStatsOverview::class, + ]); + } + + public function getDefaultChartsWidgets(): array + { + return self::callLunarHook('getChartWidgets', [ OrderTotalsChart::class, OrdersSalesChart::class, AverageOrderValueChart::class, NewVsReturningCustomersChart::class, + ]); + } + + public function getDefaultTableWidgets(): array + { + return self::callLunarHook('getTableWidgets', [ PopularProductsTable::class, LatestOrdersTable::class, - ]; + ]); } public static function getNavigationIcon(): ?string diff --git a/packages/admin/src/Filament/Resources/CollectionResource.php b/packages/admin/src/Filament/Resources/CollectionResource.php index 58fd33664c..ee85d19e67 100644 --- a/packages/admin/src/Filament/Resources/CollectionResource.php +++ b/packages/admin/src/Filament/Resources/CollectionResource.php @@ -47,7 +47,7 @@ public static function getCollectionBreadcrumbs(CollectionContract $collection): ]) => $collection->group->name, ]; - foreach ($collection->children as $childCollection) { + foreach ($collection->ancestors as $childCollection) { $crumbs[ CollectionResource::getUrl('edit', [ 'record' => $childCollection, diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index c9a1f3b3c6..df2eff8934 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -13,6 +13,7 @@ use Lunar\Admin\Base\LunarPanelDiscountInterface; use Lunar\Admin\Filament\Resources\DiscountResource\Pages; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\BrandLimitationRelationManager; +use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\CollectionConditionRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\CollectionLimitationRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\CustomerLimitationRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductConditionRelationManager; @@ -419,8 +420,7 @@ protected static function getDefaultRelations(): array CustomerLimitationRelationManager::class, ProductRewardRelationManager::class, ProductConditionRelationManager::class, - ProductRewardRelationManager::class, - ProductConditionRelationManager::class, + CollectionConditionRelationManager::class, ]; } diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php index 8e43707d91..f84c61e340 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php @@ -3,6 +3,7 @@ namespace Lunar\Admin\Filament\Resources\DiscountResource\Pages; use Filament\Actions; +use Filament\Resources\RelationManagers\RelationGroup; use Lunar\Admin\Base\LunarPanelDiscountInterface; use Lunar\Admin\Filament\Resources\DiscountResource; use Lunar\Admin\Support\Pages\BaseEditRecord; @@ -87,7 +88,10 @@ public function getRelationManagers(): array $managers = []; if ($this->record->type == BuyXGetY::class) { - $managers[] = DiscountResource\RelationManagers\ProductConditionRelationManager::class; + $managers[] = RelationGroup::make('Conditions', [ + DiscountResource\RelationManagers\ProductConditionRelationManager::class, + DiscountResource\RelationManagers\CollectionConditionRelationManager::class, + ]); $managers[] = DiscountResource\RelationManagers\ProductRewardRelationManager::class; } diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionConditionRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionConditionRelationManager.php new file mode 100644 index 0000000000..3eaa8fe48a --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionConditionRelationManager.php @@ -0,0 +1,67 @@ +heading( + __('lunarpanel::discount.relationmanagers.collection_conditions.title') + ) + ->description( + __('lunarpanel::discount.relationmanagers.collection_conditions.description') + ) + ->paginated(false) + ->modifyQueryUsing( + fn ($query) => $query->whereIn($prefix.'collection_discount.type', ['condition']) + ) + ->headerActions([ + Tables\Actions\AttachAction::make()->form(fn (Tables\Actions\AttachAction $action): array => [ + $action->getRecordSelect(), + Forms\Components\Hidden::make('type')->default('condition'), + ])->recordTitle(function ($record) { + return $record->attr('name'); + })->recordSelectSearchColumns(['attribute_data->name']) + ->preloadRecordSelect() + ->label( + __('lunarpanel::discount.relationmanagers.collection_conditions.actions.attach.label') + ), + ])->columns([ + Tables\Columns\TextColumn::make('id') + ->label( + __('lunarpanel::discount.relationmanagers.collection_conditions.table.name.label') + ) + ->formatStateUsing( + fn (Model $record) => $record->attr('name') + ), + ])->actions([ + Tables\Actions\DeleteAction::make(), + ])->bulkActions([ + Tables\Actions\DeleteBulkAction::make(), + ]); + } +} diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionLimitationRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionLimitationRelationManager.php index 57120c3a0f..eae687ff26 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionLimitationRelationManager.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionLimitationRelationManager.php @@ -27,11 +27,15 @@ public function isReadOnly(): bool public function getDefaultTable(Table $table): Table { + $prefix = config('lunar.database.table_prefix'); return $table ->description( __('lunarpanel::discount.relationmanagers.collections.description') ) + ->modifyQueryUsing( + fn ($query) => $query->whereIn($prefix.'collection_discount.type', ['limitation', 'exclusion']) + ) ->paginated(false) ->headerActions([ Tables\Actions\AttachAction::make()->form(fn (Tables\Actions\AttachAction $action): array => [ diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CustomerLimitationRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CustomerLimitationRelationManager.php index 598597c0b8..ff329055af 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CustomerLimitationRelationManager.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CustomerLimitationRelationManager.php @@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Model; use Lunar\Admin\Support\RelationManagers\BaseRelationManager; +use function Filament\Support\generate_search_column_expression; + class CustomerLimitationRelationManager extends BaseRelationManager { protected static bool $isLazy = false; @@ -37,6 +39,19 @@ public function getDefaultTable(Table $table): Table ])->recordTitle(function ($record) { return $record->full_name; })->preloadRecordSelect() + ->recordSelectOptionsQuery(function ($query, $search) { + if (! filled($search)) { + return $query; + } + + foreach (explode(' ', $search) as $word) { + $query->where(function ($query) use ($word) { + foreach (['first_name', 'last_name', 'company_name'] as $index => $column) { + $query->{$index == 0 ? 'where' : 'orWhere'}(generate_search_column_expression($query->qualifyColumn($column), true, $query->getConnection()), 'like', "%{$word}%"); + } + }); + } + }) ->label( __('lunarpanel::discount.relationmanagers.customers.actions.attach.label') ), diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductConditionRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductConditionRelationManager.php index 178cb0c26e..2ddfddd580 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductConditionRelationManager.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductConditionRelationManager.php @@ -8,8 +8,6 @@ use Illuminate\Database\Eloquent\Model; use Lunar\Admin\Support\RelationManagers\BaseRelationManager; use Lunar\Admin\Support\Tables\Columns\ThumbnailImageColumn; -use Lunar\Models\Collection; -use Lunar\Models\Contracts\Collection as CollectionContract; use Lunar\Models\Contracts\Product as ProductContract; use Lunar\Models\Contracts\ProductVariant as ProductVariantContract; use Lunar\Models\Product; @@ -33,6 +31,7 @@ public function isReadOnly(): bool public function getDefaultTable(Table $table): Table { + $prefix = config('lunar.database.table_prefix'); return $table ->heading( @@ -44,7 +43,7 @@ public function getDefaultTable(Table $table): Table ->paginated(false) ->modifyQueryUsing( fn ($query) => $query->whereIn('type', ['condition']) - ->whereIn('discountable_type', [Collection::morphName(), Product::morphName(), ProductVariant::morphName()]) + ->whereIn('discountable_type', [Product::morphName(), ProductVariant::morphName()]) ->whereHas('discountable') ) ->headerActions([ @@ -52,15 +51,6 @@ public function getDefaultTable(Table $table): Table Forms\Components\MorphToSelect::make('discountable') ->searchable(true) ->types([ - Forms\Components\MorphToSelect\Type::make(Collection::modelClass()) - ->titleAttribute('name.en') - ->getSearchResultsUsing(static function (Forms\Components\Select $component, string $search): array { - return get_search_builder(Collection::modelClass(), $search) - ->get() - ->mapWithKeys(fn (CollectionContract $record): array => [$record->getKey() => $record->attr('name')]) - ->all(); - }), - Forms\Components\MorphToSelect\Type::make(Product::modelClass()) ->titleAttribute('name.en') ->getSearchResultsUsing(static function (Forms\Components\Select $component, string $search): array { diff --git a/packages/admin/src/Filament/Resources/OrderResource/Concerns/DisplaysOrderSummary.php b/packages/admin/src/Filament/Resources/OrderResource/Concerns/DisplaysOrderSummary.php index aacfa14a98..98474fed4f 100644 --- a/packages/admin/src/Filament/Resources/OrderResource/Concerns/DisplaysOrderSummary.php +++ b/packages/admin/src/Filament/Resources/OrderResource/Concerns/DisplaysOrderSummary.php @@ -131,6 +131,6 @@ public static function getDefaultOrderSummaryInfolist(): Infolists\Components\Se public static function getOrderSummaryInfolist(): Infolists\Components\Section { - return self::callStaticLunarHook('exendOrderSummaryInfolist', static::getDefaultOrderSummaryInfolist()); + return self::callStaticLunarHook('extendOrderSummaryInfolist', static::getDefaultOrderSummaryInfolist()); } } diff --git a/packages/admin/src/Filament/Resources/ProductResource.php b/packages/admin/src/Filament/Resources/ProductResource.php index f5d6b1202c..0725483236 100644 --- a/packages/admin/src/Filament/Resources/ProductResource.php +++ b/packages/admin/src/Filament/Resources/ProductResource.php @@ -271,6 +271,7 @@ public static function getTableColumns(): array 'draft' => 'warning', 'published' => 'success', 'deleted' => 'danger', + default => 'primary', }), SpatieMediaLibraryImageColumn::make('thumbnail') ->collection(config('lunar.media.collection')) diff --git a/packages/admin/src/Filament/Resources/ProductResource/Pages/ListProducts.php b/packages/admin/src/Filament/Resources/ProductResource/Pages/ListProducts.php index e6f7f44a40..7b38d40f02 100644 --- a/packages/admin/src/Filament/Resources/ProductResource/Pages/ListProducts.php +++ b/packages/admin/src/Filament/Resources/ProductResource/Pages/ListProducts.php @@ -84,9 +84,9 @@ public function getDefaultTabs(): array { return [ 'all' => Tab::make(__('lunarpanel::product.tabs.all')), - 'published' => Tab::make('Published') + 'published' => Tab::make(__('lunarpanel::product.tabs.published')) ->modifyQueryUsing(fn (Builder $query) => $query->where('status', 'published')), - 'draft' => Tab::make('Draft') + 'draft' => Tab::make(__('lunarpanel::product.tabs.draft')) ->modifyQueryUsing(fn (Builder $query) => $query->where('status', 'draft')) ->badge(Product::query()->where('status', 'draft')->count()), ]; diff --git a/packages/admin/src/Filament/Resources/ProductResource/Pages/ManageProductAssociations.php b/packages/admin/src/Filament/Resources/ProductResource/Pages/ManageProductAssociations.php index a4134b2d52..bc961c53e6 100644 --- a/packages/admin/src/Filament/Resources/ProductResource/Pages/ManageProductAssociations.php +++ b/packages/admin/src/Filament/Resources/ProductResource/Pages/ManageProductAssociations.php @@ -52,11 +52,7 @@ public function form(Form $form): Form }), Forms\Components\Select::make('type') ->required() - ->options([ - ProductAssociation::ALTERNATE => 'Alternate', - ProductAssociation::CROSS_SELL => 'Cross-Sell', - ProductAssociation::UP_SELL => 'Upsell', - ]), + ->options(ProductAssociation::getTypes()), ]); } @@ -82,7 +78,11 @@ public function table(Table $table): Table ->label(__('lunarpanel::product.table.name.label')), Tables\Columns\TextColumn::make('target.variants.sku') ->label('SKU'), - Tables\Columns\TextColumn::make('type'), + Tables\Columns\TextColumn::make('type')->formatStateUsing(function ($state) { + $enum = config('lunar.products.association_types_enum', \Lunar\Base\Enums\ProductAssociation::class); + + return $enum::tryFrom($state)?->label() ?: $state; + }), ]) ->filters([ // diff --git a/packages/admin/src/Filament/Resources/ProductResource/RelationManagers/CustomerGroupRelationManager.php b/packages/admin/src/Filament/Resources/ProductResource/RelationManagers/CustomerGroupRelationManager.php index c348dfe6c2..2c30fd3a13 100644 --- a/packages/admin/src/Filament/Resources/ProductResource/RelationManagers/CustomerGroupRelationManager.php +++ b/packages/admin/src/Filament/Resources/ProductResource/RelationManagers/CustomerGroupRelationManager.php @@ -81,13 +81,8 @@ public function getDefaultTable(Table $table): Table return Tables\Columns\IconColumn::make($column)->label( __("lunarpanel::relationmanagers.customer_groups.table.{$column}.label") ) - ->color(fn (string $state): string => match ($state) { - '1' => 'success', - '0' => 'warning', - })->icon(fn (string $state): string => match ($state) { - '0' => 'heroicon-o-x-circle', - '1' => 'heroicon-o-check-circle', - }); + ->color(fn ($state): string => $state ? 'success' : 'warning') + ->icon(fn ($state): string => $state ? 'heroicon-o-check-circle' : 'heroicon-o-x-circle'); })->toArray(); return $table diff --git a/packages/admin/src/Filament/Widgets/Dashboard/Orders/AverageOrderValueChart.php b/packages/admin/src/Filament/Widgets/Dashboard/Orders/AverageOrderValueChart.php index 031701b041..8e2ba11ece 100644 --- a/packages/admin/src/Filament/Widgets/Dashboard/Orders/AverageOrderValueChart.php +++ b/packages/admin/src/Filament/Widgets/Dashboard/Orders/AverageOrderValueChart.php @@ -2,6 +2,7 @@ namespace Lunar\Admin\Filament\Widgets\Dashboard\Orders; +use Carbon\CarbonInterface; use Carbon\CarbonPeriod; use Leandrocfe\FilamentApexCharts\Widgets\ApexChartWidget; use Lunar\Facades\DB; @@ -23,7 +24,7 @@ protected function getHeading(): ?string return __('lunarpanel::widgets.dashboard.orders.average_order_value.heading'); } - protected function getOrderQuery(?\DateTime $from = null, ?\DateTime $to = null) + protected function getOrderQuery(\DateTime|CarbonInterface|null $from = null, \DateTime|CarbonInterface|null $to = null) { return Order::whereNotNull('placed_at') ->with(['currency']) diff --git a/packages/admin/src/Filament/Widgets/Dashboard/Orders/NewVsReturningCustomersChart.php b/packages/admin/src/Filament/Widgets/Dashboard/Orders/NewVsReturningCustomersChart.php index 686aea1cbf..261ed94e90 100644 --- a/packages/admin/src/Filament/Widgets/Dashboard/Orders/NewVsReturningCustomersChart.php +++ b/packages/admin/src/Filament/Widgets/Dashboard/Orders/NewVsReturningCustomersChart.php @@ -2,6 +2,7 @@ namespace Lunar\Admin\Filament\Widgets\Dashboard\Orders; +use Carbon\CarbonInterface; use Carbon\CarbonPeriod; use Leandrocfe\FilamentApexCharts\Widgets\ApexChartWidget; use Lunar\Facades\DB; @@ -22,7 +23,7 @@ protected function getHeading(): ?string return __('lunarpanel::widgets.dashboard.orders.new_returning_customers.heading'); } - protected function getOrderQuery(?\DateTime $from = null, ?\DateTime $to = null) + protected function getOrderQuery(\DateTime|CarbonInterface|null $from = null, \DateTime|CarbonInterface|null $to = null) { return Order::whereNotNull('placed_at') ->whereBetween('placed_at', [ diff --git a/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrderStatsOverview.php b/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrderStatsOverview.php index 01247ad7a8..48427028e1 100644 --- a/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrderStatsOverview.php +++ b/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrderStatsOverview.php @@ -2,6 +2,7 @@ namespace Lunar\Admin\Filament\Widgets\Dashboard\Orders; +use Carbon\CarbonInterface; use Filament\Support\Facades\FilamentIcon; use Filament\Widgets\StatsOverviewWidget as BaseWidget; use Filament\Widgets\StatsOverviewWidget\Stat; @@ -12,7 +13,7 @@ class OrderStatsOverview extends BaseWidget { protected static ?string $pollingInterval = '60s'; - protected function getOrderQuery(?\DateTime $from = null, ?\DateTime $to = null) + protected function getOrderQuery(\DateTime|CarbonInterface|null $from = null, \DateTime|CarbonInterface|null $to = null) { return Order::whereNotNull('placed_at') ->whereBetween('placed_at', [ diff --git a/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrderTotalsChart.php b/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrderTotalsChart.php index 40a8ab4f2b..c6c85b3f86 100644 --- a/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrderTotalsChart.php +++ b/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrderTotalsChart.php @@ -2,6 +2,7 @@ namespace Lunar\Admin\Filament\Widgets\Dashboard\Orders; +use Carbon\CarbonInterface; use Carbon\CarbonPeriod; use Filament\Widgets\Concerns\InteractsWithPageFilters; use Leandrocfe\FilamentApexCharts\Widgets\ApexChartWidget; @@ -25,7 +26,7 @@ protected function getHeading(): ?string return __('lunarpanel::widgets.dashboard.orders.order_totals_chart.heading'); } - protected function getOrderQuery(?\DateTime $from = null, ?\DateTime $to = null) + protected function getOrderQuery(\DateTime|CarbonInterface|null $from = null, \DateTime|CarbonInterface|null $to = null) { return Order::whereNotNull('placed_at') ->with(['currency']) diff --git a/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrdersSalesChart.php b/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrdersSalesChart.php index 8c8da66d47..1c93b3cefa 100644 --- a/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrdersSalesChart.php +++ b/packages/admin/src/Filament/Widgets/Dashboard/Orders/OrdersSalesChart.php @@ -2,6 +2,7 @@ namespace Lunar\Admin\Filament\Widgets\Dashboard\Orders; +use Carbon\CarbonInterface; use Filament\Widgets\Concerns\InteractsWithPageFilters; use Leandrocfe\FilamentApexCharts\Widgets\ApexChartWidget; use Lunar\Facades\DB; @@ -24,7 +25,7 @@ protected function getHeading(): ?string return __('lunarpanel::widgets.dashboard.orders.order_sales_chart.heading'); } - protected function getOrderQuery(?\DateTime $from = null, ?\DateTime $to = null) + protected function getOrderQuery(\DateTime|CarbonInterface|null $from = null, \DateTime|CarbonInterface|null $to = null) { return Order::whereNotNull('placed_at') ->with(['currency']) diff --git a/packages/admin/src/LunarPanelProvider.php b/packages/admin/src/LunarPanelProvider.php index d7fdc06157..b97f6dc5b3 100644 --- a/packages/admin/src/LunarPanelProvider.php +++ b/packages/admin/src/LunarPanelProvider.php @@ -74,6 +74,10 @@ public function boot(): void __DIR__.'/../resources/lang' => $this->app->langPath('vendor/lunarpanel'), ]); + $this->publishes([ + __DIR__.'/../resources/views/pdf' => resource_path('views/vendor/lunarpanel/pdf'), + ], 'lunarpanel.pdf'); + collect($this->configFiles)->each(function ($config) { $this->mergeConfigFrom("{$this->root}/config/$config.php", "lunar.$config"); }); diff --git a/packages/admin/src/Support/Forms/Components/AttributeSelector.php b/packages/admin/src/Support/Forms/Components/AttributeSelector.php index f6820986b3..069fd2b844 100644 --- a/packages/admin/src/Support/Forms/Components/AttributeSelector.php +++ b/packages/admin/src/Support/Forms/Components/AttributeSelector.php @@ -65,7 +65,7 @@ public function getAttributeGroups() $type = $this->attributableType; } - return AttributeGroup::whereAttributableType($type)->get(); + return AttributeGroup::whereAttributableType($type)->orderBy('position')->get(); } public function getSelectedAttributes($groupId) @@ -75,6 +75,6 @@ public function getSelectedAttributes($groupId) public function getAttributes($groupId) { - return Attribute::where('attribute_group_id', $groupId)->get(); + return Attribute::where('attribute_group_id', $groupId)->orderBy('position')->get(); } } diff --git a/packages/core/LICENSE.md b/packages/core/LICENSE.md deleted file mode 100644 index a5dec88053..0000000000 --- a/packages/core/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) LunarPHP Ltd. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/packages/core/README.md b/packages/core/README.md deleted file mode 100644 index 03ff78a374..0000000000 --- a/packages/core/README.md +++ /dev/null @@ -1,16 +0,0 @@ -

      Lunar

      - -## About Lunar - -Lunar is an open source E-commerce platform which embraces Laravel as it's foundation and uses it to build a highly -extensible, robust and feature rich application you can build any store on. - -We put developers first and try to ensure your experience is as smooth as possible. - -## Documentation - -- [Full documentation](https://docs.lunarphp.io/) - Includes in-depth guides on everything Lunar - -## License - -Lunar is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/packages/core/config/cart_session.php b/packages/core/config/cart_session.php index 849235881c..3fd80bbb39 100644 --- a/packages/core/config/cart_session.php +++ b/packages/core/config/cart_session.php @@ -35,4 +35,14 @@ | */ 'allow_multiple_orders_per_cart' => false, + + /* + |-------------------------------------------------------------------------- + | Delete cart on logout + |-------------------------------------------------------------------------- + | + | Determines whether the cart sholud be soft deleted when the user logs out. + | + */ + 'delete_on_forget' => true, ]; diff --git a/packages/core/config/products.php b/packages/core/config/products.php new file mode 100644 index 0000000000..550f3c65b4 --- /dev/null +++ b/packages/core/config/products.php @@ -0,0 +1,5 @@ + \Lunar\Base\Enums\ProductAssociation::class, +]; diff --git a/packages/core/database/migrations/2025_10_29_084000_add_missing_indexes_to_tables.php b/packages/core/database/migrations/2025_10_29_084000_add_missing_indexes_to_tables.php new file mode 100644 index 0000000000..a00ee885cb --- /dev/null +++ b/packages/core/database/migrations/2025_10_29_084000_add_missing_indexes_to_tables.php @@ -0,0 +1,64 @@ + [ + 'enabled', + 'starts_at', + ], + 'products' => [ + 'deleted_at', + ], + 'product_variants' => [ + 'deleted_at', + ], + ]; + + /** + * Run the migrations. + */ + public function up(): void + { + foreach ($this->columnsToUpdate as $table => $columns) { + $fullTableName = $this->getTableName($table); + + Schema::table($fullTableName, function (Blueprint $tableBlueprint) use ($columns) { + foreach ($columns as $column) { + $tableBlueprint->index($column); + } + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + foreach ($this->columnsToUpdate as $table => $columns) { + $fullTableName = $this->getTableName($table); + + Schema::table($fullTableName, function (Blueprint $tableBlueprint) use ($columns) { + foreach ($columns as $column) { + $tableBlueprint->dropIndex([$column]); + } + }); + } + } + + /** + * Get the full table name with appropriate prefix. + */ + private function getTableName(string $table): string + { + return in_array($table, ['activity_log', 'media']) ? $table : $this->prefix.$table; + } +}; diff --git a/packages/core/database/migrations/2025_11_12_005200_add_meta_to_product_options.php b/packages/core/database/migrations/2025_11_12_005200_add_meta_to_product_options.php new file mode 100644 index 0000000000..7ecb7e7c64 --- /dev/null +++ b/packages/core/database/migrations/2025_11_12_005200_add_meta_to_product_options.php @@ -0,0 +1,30 @@ +prefix.'product_options', function (Blueprint $table) { + $table->jsonb('meta')->nullable()->after('shared'); + }); + + Schema::table($this->prefix.'product_option_values', function (Blueprint $table) { + $table->jsonb('meta')->nullable()->after('position'); + }); + } + + public function down(): void + { + Schema::table($this->prefix.'product_options', function (Blueprint $table) { + $table->dropColumn('meta'); + }); + + Schema::table($this->prefix.'product_option_values', function (Blueprint $table) { + $table->dropColumn('meta'); + }); + } +}; diff --git a/packages/core/phpunit.xml b/packages/core/phpunit.xml deleted file mode 100644 index d9c55ad9d7..0000000000 --- a/packages/core/phpunit.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - src/ - - - - - ./tests/Unit - - - ./tests/Feature - - - ./tests/Database - - - ./tests/Utils - - - - - - - - diff --git a/packages/core/resources/lang/de/base.php b/packages/core/resources/lang/de/base.php new file mode 100644 index 0000000000..8e37047b90 --- /dev/null +++ b/packages/core/resources/lang/de/base.php @@ -0,0 +1,9 @@ + [ + 'cross-sell' => 'Cross-Selling', + 'up-sell' => 'Up-Selling', + 'alternate' => 'Alternative', + ], +]; diff --git a/packages/core/resources/lang/en/base.php b/packages/core/resources/lang/en/base.php index 59511ed390..4ddde51027 100644 --- a/packages/core/resources/lang/en/base.php +++ b/packages/core/resources/lang/en/base.php @@ -6,4 +6,9 @@ 'images' => 'Images', ], ], + 'product-association-types' => [ + 'cross-sell' => 'Cross Sell', + 'up-sell' => 'Up Sell', + 'alternate' => 'Alternate', + ], ]; diff --git a/packages/core/resources/lang/es/base.php b/packages/core/resources/lang/es/base.php index 526e2863ca..fea8f8ac48 100644 --- a/packages/core/resources/lang/es/base.php +++ b/packages/core/resources/lang/es/base.php @@ -6,4 +6,9 @@ 'images' => 'Imágenes', ], ], + 'product-association-types' => [ + 'cross-sell' => 'Venta cruzada', + 'up-sell' => 'Venta adicional', + 'alternate' => 'Alternativa', + ], ]; diff --git a/packages/core/resources/lang/fr/base.php b/packages/core/resources/lang/fr/base.php index 59511ed390..f9c7a1f88f 100644 --- a/packages/core/resources/lang/fr/base.php +++ b/packages/core/resources/lang/fr/base.php @@ -6,4 +6,9 @@ 'images' => 'Images', ], ], + 'product-association-types' => [ + 'cross-sell' => 'Vente croisée', + 'up-sell' => 'Montée en gamme', + 'alternate' => 'Alternative', + ], ]; diff --git a/packages/core/resources/lang/hu/base.php b/packages/core/resources/lang/hu/base.php new file mode 100644 index 0000000000..229a903212 --- /dev/null +++ b/packages/core/resources/lang/hu/base.php @@ -0,0 +1,14 @@ + [ + 'collection-titles' => [ + 'images' => 'Képek', + ], + ], + 'product-association-types' => [ + 'cross-sell' => 'Keresztértékesítés', + 'up-sell' => 'Felülértékesítés', + 'alternate' => 'Alternatíva', + ], +]; diff --git a/packages/core/resources/lang/hu/exceptions.php b/packages/core/resources/lang/hu/exceptions.php new file mode 100644 index 0000000000..35d947a6f9 --- /dev/null +++ b/packages/core/resources/lang/hu/exceptions.php @@ -0,0 +1,21 @@ + 'A ":class" modell nem valósítja meg a Purchasable interfészt.', + 'cart_line_id_mismatch' => 'Ez a kosártétel nem ehhez a kosárhoz tartozik.', + 'invalid_cart_line_quantity' => 'A mennyiségnek legalább "1"-nek kell lennie, de ":quantity" lett megadva.', + 'maximum_cart_line_quantity' => 'A mennyiség nem haladhatja meg a :quantity értéket.', + 'carts.invalid_action' => 'A kosárművelet érvénytelen volt.', + 'carts.shipping_missing' => 'Szállítási cím megadása kötelező.', + 'carts.billing_missing' => 'Számlázási cím megadása kötelező.', + 'carts.billing_incomplete' => 'A számlázási cím hiányos.', + 'carts.order_exists' => 'Ehhez a kosárhoz már létezik rendelés.', + 'carts.shipping_option_missing' => 'Hiányzik a szállítási opció.', + 'missing_currency_price' => 'A ":currency" pénznemhez nem létezik ár.', + 'minimum_quantity' => 'Legalább :quantity tételt kell hozzáadni.', + 'quantity_increment' => 'A mennyiségnek :increment lépésközzel adható meg.', + 'fieldtype_missing' => 'A ":class" FieldType nem létezik.', + 'invalid_fieldtype' => 'A ":class" osztály nem valósítja meg a FieldType interfészt.', + 'discounts.invalid_type' => 'A gyűjtemény kizárólag ":expected" elemeket tartalmazhat, de ":actual" található.', + 'disallow_multiple_cart_orders' => 'Egy kosárhoz csak egy rendelés társítható.', +]; diff --git a/packages/core/resources/lang/nl/base.php b/packages/core/resources/lang/nl/base.php index 9f681208d9..498e53a621 100644 --- a/packages/core/resources/lang/nl/base.php +++ b/packages/core/resources/lang/nl/base.php @@ -6,4 +6,9 @@ 'images' => 'Afbeeldingen', ], ], + 'product-association-types' => [ + 'cross-sell' => 'Cross-sell', + 'up-sell' => 'Up-sell', + 'alternate' => 'Alternatief', + ], ]; diff --git a/packages/core/resources/lang/pl/base.php b/packages/core/resources/lang/pl/base.php index 364b5cfc3a..fbe4e6008c 100644 --- a/packages/core/resources/lang/pl/base.php +++ b/packages/core/resources/lang/pl/base.php @@ -6,4 +6,9 @@ 'images' => 'Obrazy', ], ], + 'product-association-types' => [ + 'cross-sell' => 'Sprzedaż krzyżowa', + 'up-sell' => 'Sprzedaż dodatkowa', + 'alternate' => 'Alternatywa', + ], ]; diff --git a/packages/core/resources/lang/pt_BR/base.php b/packages/core/resources/lang/pt_BR/base.php new file mode 100644 index 0000000000..1d302cfa01 --- /dev/null +++ b/packages/core/resources/lang/pt_BR/base.php @@ -0,0 +1,14 @@ + [ + 'collection-titles' => [ + 'images' => 'Imagens', + ], + ], + 'product-association-types' => [ + 'cross-sell' => 'Venda cruzada', + 'up-sell' => 'Venda adicional', + 'alternate' => 'Alternativa', + ], +]; diff --git a/packages/core/resources/lang/pt_BR/exceptions.php b/packages/core/resources/lang/pt_BR/exceptions.php new file mode 100644 index 0000000000..6089c1f419 --- /dev/null +++ b/packages/core/resources/lang/pt_BR/exceptions.php @@ -0,0 +1,21 @@ + 'O modelo ":class" não implementa a interface de compra (purchasable).', + 'cart_line_id_mismatch' => 'Esta linha de carrinho não pertence a este carrinho', + 'invalid_cart_line_quantity' => 'Quantidade esperada de pelo menos "1"; encontrado ":quantity".', + 'maximum_cart_line_quantity' => 'A quantidade não pode exceder :quantity.', + 'carts.invalid_action' => 'A ação do carrinho é inválida', + 'carts.shipping_missing' => 'É necessário um endereço de entrega', + 'carts.billing_missing' => 'É necessário um endereço de cobrança', + 'carts.billing_incomplete' => 'O endereço de cobrança está incompleto', + 'carts.order_exists' => 'Já existe um pedido para este carrinho', + 'carts.shipping_option_missing' => 'Opção de envio ausente', + 'missing_currency_price' => 'Não existe preço para a moeda ":currency"', + 'minimum_quantity' => 'Você deve adicionar no mínimo :quantity itens.', + 'quantity_increment' => 'A quantidade :quantity deve estar em incrementos de :increment', + 'fieldtype_missing' => 'FieldType ":class" não existe', + 'invalid_fieldtype' => 'A classe ":class" não implementa a interface FieldType.', + 'discounts.invalid_type' => 'A coleção deve conter apenas ":expected"; encontrado ":actual"', + 'disallow_multiple_cart_orders' => 'Carrinhos só podem ter um pedido associado a eles.', +]; diff --git a/packages/core/resources/lang/ro/base.php b/packages/core/resources/lang/ro/base.php new file mode 100644 index 0000000000..4ae27ad4f8 --- /dev/null +++ b/packages/core/resources/lang/ro/base.php @@ -0,0 +1,14 @@ + [ + 'collection-titles' => [ + 'images' => 'Imagini', + ], + ], + 'product-association-types' => [ + 'cross-sell' => 'Vânzare încrucișată', + 'up-sell' => 'Vânzare superioară', + 'alternate' => 'Alternativă', + ], +]; diff --git a/packages/core/resources/lang/ro/exceptions.php b/packages/core/resources/lang/ro/exceptions.php new file mode 100644 index 0000000000..fcfe0427f8 --- /dev/null +++ b/packages/core/resources/lang/ro/exceptions.php @@ -0,0 +1,21 @@ + 'Modelul ":class" nu implementează interfața de achiziționare.', + 'cart_line_id_mismatch' => 'Această linie din coș nu aparține acestui coș', + 'invalid_cart_line_quantity' => 'Cantitatea așteptată trebuie să fie cel puțin 1, s-a găsit: :quantity.', + 'maximum_cart_line_quantity' => 'Cantitatea nu poate depăși :quantity.', + 'carts.invalid_action' => 'Acțiunea pentru coș este invalidă', + 'carts.shipping_missing' => 'Este necesară o adresă de livrare', + 'carts.billing_missing' => 'Este necesară o adresă de facturare', + 'carts.billing_incomplete' => 'Adresa de facturare este incompletă', + 'carts.order_exists' => 'Există deja o comandă pentru acest coș', + 'carts.shipping_option_missing' => 'Opțiune de livrare lipsă', + 'missing_currency_price' => 'Nu există un preț pentru moneda ":currency"', + 'minimum_quantity' => 'Trebuie să adăugați cel puțin :quantity articole.', + 'quantity_increment' => 'Cantitatea :quantity trebuie să fie în multipli de :increment', + 'fieldtype_missing' => 'FieldType ":class" nu există', + 'invalid_fieldtype' => 'Clasa ":class" nu implementează interfața FieldType.', + 'discounts.invalid_type' => 'Colecția trebuie să conțină doar ":expected", s-a găsit ":actual"', + 'disallow_multiple_cart_orders' => 'Un coș poate avea asociată doar o singură comandă.', +]; diff --git a/packages/core/resources/lang/tr/base.php b/packages/core/resources/lang/tr/base.php new file mode 100644 index 0000000000..3305fe8995 --- /dev/null +++ b/packages/core/resources/lang/tr/base.php @@ -0,0 +1,14 @@ + [ + 'collection-titles' => [ + 'images' => 'Görseller', + ], + ], + 'product-association-types' => [ + 'cross-sell' => 'Çapraz satış', + 'up-sell' => 'Üst satış', + 'alternate' => 'Alternatif', + ], +]; diff --git a/packages/core/resources/lang/tr/exceptions.php b/packages/core/resources/lang/tr/exceptions.php new file mode 100644 index 0000000000..0cb321b1db --- /dev/null +++ b/packages/core/resources/lang/tr/exceptions.php @@ -0,0 +1,21 @@ + '":class" modeli satın alınabilir arayüzünü uygulamamaktadır.', + 'cart_line_id_mismatch' => 'Bu sepet satırı bu sepete ait değil', + 'invalid_cart_line_quantity' => 'Miktar en az "1" olması bekleniyordu, ":quantity" bulundu.', + 'maximum_cart_line_quantity' => 'Miktar :quantity\'yi aşamaz.', + 'carts.invalid_action' => 'Sepet işlemi geçersizdi', + 'carts.shipping_missing' => 'Kargo adresi gerekli', + 'carts.billing_missing' => 'Fatura adresi gerekli', + 'carts.billing_incomplete' => 'Fatura adresi eksik', + 'carts.order_exists' => 'Bu sepet için zaten bir sipariş var', + 'carts.shipping_option_missing' => 'Kargo Seçeneği Eksik', + 'missing_currency_price' => '":currency" para birimi için fiyat mevcut değil', + 'minimum_quantity' => 'En az :quantity adet eklemelisiniz.', + 'quantity_increment' => ':quantity miktarı :increment\'lik artışlarla olmalıdır', + 'fieldtype_missing' => 'FieldType ":class" mevcut değil', + 'invalid_fieldtype' => '":class" sınıfı FieldType arayüzünü uygulamamaktadır.', + 'discounts.invalid_type' => 'Koleksiyon sadece ":expected" içermeli, ":actual" bulundu', + 'disallow_multiple_cart_orders' => 'Sepetler sadece bir siparişle ilişkilendirilebilir.', +]; diff --git a/packages/core/resources/lang/vi/base.php b/packages/core/resources/lang/vi/base.php index a666ea6abc..df5770d24b 100644 --- a/packages/core/resources/lang/vi/base.php +++ b/packages/core/resources/lang/vi/base.php @@ -6,4 +6,9 @@ 'images' => 'Hình ảnh', ], ], + 'product-association-types' => [ + 'cross-sell' => 'Bán chéo', + 'up-sell' => 'Bán nâng cấp', + 'alternate' => 'Thay thế', + ], ]; diff --git a/packages/core/src/Addons/Manifest.php b/packages/core/src/Addons/Manifest.php index 59ed478cc0..fde1ae96d2 100644 --- a/packages/core/src/Addons/Manifest.php +++ b/packages/core/src/Addons/Manifest.php @@ -70,7 +70,7 @@ protected function formatPackage($package) 'id' => base64_encode($package['name']), 'slug' => $lunar['slug'] ?? null, 'editions' => $lunar['editions'] ?? [], - 'github_url' => $package['source']['url'], + 'github_url' => $package['source']['url'] ?? '', 'version' => $package['version'], 'namespace' => $namespace, 'autoload' => $autoload, diff --git a/packages/core/src/Base/Enums/Concerns/ProvidesProductAssociationType.php b/packages/core/src/Base/Enums/Concerns/ProvidesProductAssociationType.php new file mode 100644 index 0000000000..0648945d50 --- /dev/null +++ b/packages/core/src/Base/Enums/Concerns/ProvidesProductAssociationType.php @@ -0,0 +1,13 @@ + __('lunar::base.product-association-types.cross-sell'), + self::UP_SELL => __('lunar::base.product-association-types.up-sell'), + self::ALTERNATE => __('lunar::base.product-association-types.alternate'), + }; + } +} diff --git a/packages/core/src/Base/Purchasable.php b/packages/core/src/Base/Purchasable.php index 5256c38ef2..a299268ed7 100644 --- a/packages/core/src/Base/Purchasable.php +++ b/packages/core/src/Base/Purchasable.php @@ -52,6 +52,11 @@ public function getDescription(); */ public function getOption(); + /** + * Return the options for this purchasable. + */ + public function getOptions(): Collection; + /** * Return a unique string which identifies the purchasable item. * diff --git a/packages/core/src/Base/Traits/HasModelExtending.php b/packages/core/src/Base/Traits/HasModelExtending.php index 342ad0c456..20790d29c4 100644 --- a/packages/core/src/Base/Traits/HasModelExtending.php +++ b/packages/core/src/Base/Traits/HasModelExtending.php @@ -9,6 +9,7 @@ use Illuminate\Support\Str; use Lunar\Base\BaseModel; use Lunar\Facades\ModelManifest; +use ReflectionClass; trait HasModelExtending { @@ -67,7 +68,44 @@ public function getTable() { $parentClass = get_parent_class($this); - return $parentClass == BaseModel::class ? parent::getTable() : (new $parentClass)->table; + if ($parentClass === BaseModel::class) { + return parent::getTable(); + } + + if (! empty($this->table)) { + return $this->table; + } + + $rootModelClass = $this->resolveRootModelClass($parentClass); + + return $this->resolveBaseTableName($rootModelClass); + } + + protected function resolveRootModelClass(string $childClass): string + { + $parentClass = get_parent_class($childClass); + + while ($parentClass && $parentClass !== BaseModel::class) { + $childClass = $parentClass; + $parentClass = get_parent_class($parentClass); + } + + return $childClass; + } + + protected function resolveBaseTableName(string $modelClass): string + { + $reflection = new ReflectionClass($modelClass); + $defaultProperties = $reflection->getDefaultProperties(); + + if (! empty($defaultProperties['table'])) { + return $defaultProperties['table']; + } + + /** @var Model $modelInstance */ + $modelInstance = $reflection->newInstanceWithoutConstructor(); + + return $modelInstance->getTable(); } public static function __callStatic($method, $parameters) diff --git a/packages/core/src/Console/Commands/AddonsDiscover.php b/packages/core/src/Console/Commands/AddonsDiscover.php index 72d2fef6e5..b4fc510e70 100644 --- a/packages/core/src/Console/Commands/AddonsDiscover.php +++ b/packages/core/src/Console/Commands/AddonsDiscover.php @@ -29,7 +29,7 @@ public function handle(Manifest $manifest) $manifest->build(); foreach (array_keys($manifest->manifest) as $package) { - $this->components->line("Discovered Addon: {$package}"); + $this->components->info("Discovered Addon: {$package}"); } $this->components->info('Addon manifest generated successfully.'); diff --git a/packages/core/src/DataTypes/ShippingOption.php b/packages/core/src/DataTypes/ShippingOption.php index 578d6e4968..f74b8ec90e 100644 --- a/packages/core/src/DataTypes/ShippingOption.php +++ b/packages/core/src/DataTypes/ShippingOption.php @@ -17,7 +17,7 @@ public function __construct( public ?string $taxReference = null, public ?string $option = null, public bool $collect = false, - public ?array $meta = null + public ?array $meta = null, ) { // .. } @@ -108,6 +108,16 @@ public function getOption() return $this->option; } + /** + * Return the options for this purchasable + */ + public function getOptions(): Collection + { + return collect([ + $this->option, + ]); + } + /** * Return a unique string which identifies the purchasable item. * diff --git a/packages/core/src/DiscountTypes/AbstractDiscountType.php b/packages/core/src/DiscountTypes/AbstractDiscountType.php index ab9f68db7d..87735a83c1 100644 --- a/packages/core/src/DiscountTypes/AbstractDiscountType.php +++ b/packages/core/src/DiscountTypes/AbstractDiscountType.php @@ -74,7 +74,7 @@ protected function checkDiscountConditions(CartContract $cart): bool $cartCoupon = strtoupper($cart->coupon_code ?? ''); $conditionCoupon = strtoupper($this->discount->coupon ?? ''); - $validCoupon = $cartCoupon ? ($cartCoupon === $conditionCoupon) : blank($conditionCoupon); + $validCoupon = filled($conditionCoupon) ? ($cartCoupon === $conditionCoupon) : true; $minSpend = (int) ($data['min_prices'][$cart->currency->code] ?? 0) / (int) $cart->currency->factor; $minSpend = (int) bcmul($minSpend, $cart->currency->factor); diff --git a/packages/core/src/DiscountTypes/BuyXGetY.php b/packages/core/src/DiscountTypes/BuyXGetY.php index 1271db4538..f7524f2ebd 100644 --- a/packages/core/src/DiscountTypes/BuyXGetY.php +++ b/packages/core/src/DiscountTypes/BuyXGetY.php @@ -173,6 +173,7 @@ public function apply(CartContract $cart): CartContract $remainingRewardQty -= $qtyToAllocate; $subTotal = $rewardLine->subTotal->value; + $unitPrice = $rewardLine->unitPrice->value; $lineDiscountTotal = $unitPrice * $qtyToAllocate; @@ -283,14 +284,18 @@ private function processAutomaticRewards(CartContract $cart, int $remainingRewar $discountTotal += $unitPrice; + if ($discountTotal > $rewardLine->subTotal->value) { + $discountTotal = $rewardLine->subTotal->value; + } + $rewardLine->discountTotal = new Price( - ($rewardLine->discountTotal?->value ?? 0) + $unitPrice, + $discountTotal, $cart->currency, 1 ); $rewardLine->subTotalDiscounted = new Price( - $rewardLine->subTotal->value - $rewardLine->discountTotal->value, + max(0, $rewardLine->subTotal->value - $rewardLine->discountTotal->value), $cart->currency, 1 ); diff --git a/packages/core/src/Jobs/Products/Associations/Associate.php b/packages/core/src/Jobs/Products/Associations/Associate.php index 281fad21f3..070c7a496d 100644 --- a/packages/core/src/Jobs/Products/Associations/Associate.php +++ b/packages/core/src/Jobs/Products/Associations/Associate.php @@ -8,6 +8,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; +use Lunar\Base\Enums\Concerns\ProvidesProductAssociationType; use Lunar\Facades\DB; use Lunar\Models\Contracts\Product as ProductContract; use Lunar\Models\Product; @@ -34,19 +35,14 @@ class Associate implements ShouldQueue protected ProductContract $product; /** - * The SKU for the generated variant. - * - * @var string + * The product association type. */ - protected $type = null; + protected ProvidesProductAssociationType|string $type; /** * Create a new job instance. - * - * @param mixed $targets - * @param string $type */ - public function __construct(ProductContract $product, $targets, $type = null) + public function __construct(ProductContract $product, mixed $targets, ProvidesProductAssociationType|string $type) { if (is_array($targets)) { $targets = collect($targets); @@ -73,7 +69,7 @@ public function handle() $this->targets->map(function ($model) { return [ 'product_target_id' => $model->id, - 'type' => $this->type, + 'type' => is_string($this->type) ? $this->type : $this->type->value, ]; }) ); diff --git a/packages/core/src/Jobs/Products/Associations/Dissociate.php b/packages/core/src/Jobs/Products/Associations/Dissociate.php index 4709b97eab..5ffe62b1e1 100644 --- a/packages/core/src/Jobs/Products/Associations/Dissociate.php +++ b/packages/core/src/Jobs/Products/Associations/Dissociate.php @@ -8,6 +8,8 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; +use Lunar\Base\BaseModel; +use Lunar\Base\Enums\Concerns\ProvidesProductAssociationType; use Lunar\Facades\DB; use Lunar\Models\Contracts\Product as ProductContract; use Lunar\Models\Product; @@ -42,11 +44,8 @@ class Dissociate implements ShouldQueue /** * Create a new job instance. - * - * @param mixed $targets - * @param string $type */ - public function __construct(ProductContract $product, $targets, $type = null) + public function __construct(ProductContract $product, Collection|BaseModel|array $targets, ProvidesProductAssociationType|string|null $type = null) { if (is_array($targets)) { $targets = collect($targets); @@ -72,11 +71,14 @@ public function handle() $query = $this->product->associations()->whereIn( 'product_target_id', $this->targets->pluck('id') + )->when( + $this->type, + fn ($query) => $query->where( + 'type', + is_string($this->type) ? $this->type : $this->type->value + ) ); - if ($this->type) { - $query->whereType($this->type); - } $query->delete(); }); } diff --git a/packages/core/src/LunarServiceProvider.php b/packages/core/src/LunarServiceProvider.php index fd29e2583d..37daed2ae3 100644 --- a/packages/core/src/LunarServiceProvider.php +++ b/packages/core/src/LunarServiceProvider.php @@ -113,6 +113,7 @@ class LunarServiceProvider extends ServiceProvider 'orders', 'payments', 'pricing', + 'products', 'search', 'shipping', 'taxes', diff --git a/packages/core/src/Managers/CartSessionManager.php b/packages/core/src/Managers/CartSessionManager.php index a23f715e4c..5335674179 100644 --- a/packages/core/src/Managers/CartSessionManager.php +++ b/packages/core/src/Managers/CartSessionManager.php @@ -30,7 +30,7 @@ public function __construct( public function allowsMultipleOrdersPerCart(): bool { - return config('lunar.cart_session.allow_multiple_per_order', false); + return config('lunar.cart_session.allow_multiple_orders_per_cart', false); } /** @@ -68,8 +68,10 @@ public function getShippingEstimateMeta(): array /** * {@inheritDoc} */ - public function forget(bool $delete = true): void + public function forget(?bool $delete = null): void { + $delete = is_null($delete) ? config('lunar.cart_session.delete_on_forget', true) : $delete; + if ($delete) { Cart::destroy( $this->sessionManager->get( diff --git a/packages/core/src/Managers/DiscountManager.php b/packages/core/src/Managers/DiscountManager.php index 53273cad72..080a8a242f 100644 --- a/packages/core/src/Managers/DiscountManager.php +++ b/packages/core/src/Managers/DiscountManager.php @@ -156,7 +156,12 @@ function ($query, $value) { ) ) ->orWhere(fn ($query) => $query->collections( - $value->lines->map(fn ($line) => $line->purchasable->product->collections->pluck('id'))->flatten()->filter()->values(), + $value->lines->map(fn ($line) => $line->purchasable?->product?->collections?->pluck('id'))->flatten()->filter()->values(), + ['condition'] + ) + ) + ->orWhere(fn ($query) => $query->brands( + $value->lines->map(fn ($line) => $line->purchasable?->product?->brand_id)->flatten()->filter()->values(), ['condition'] ) ); diff --git a/packages/core/src/Models/Contracts/Product.php b/packages/core/src/Models/Contracts/Product.php index 5157df08f1..bf3d5d518c 100644 --- a/packages/core/src/Models/Contracts/Product.php +++ b/packages/core/src/Models/Contracts/Product.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\MorphMany; +use Lunar\Base\Enums\Concerns\ProvidesProductAssociationType; interface Product { @@ -56,12 +57,12 @@ public function inverseAssociations(): HasMany; /** * Associate a product to another with a type. */ - public function associate(mixed $product, string $type): void; + public function associate(mixed $product, ProvidesProductAssociationType $type): void; /** * Dissociate a product to another with a type. */ - public function dissociate(mixed $product, ?string $type = null): void; + public function dissociate(mixed $product, ?ProvidesProductAssociationType $type = null): void; /** * Return the customer groups relationship. diff --git a/packages/core/src/Models/Contracts/ProductAssociation.php b/packages/core/src/Models/Contracts/ProductAssociation.php index 6673ce4882..ef81248c18 100644 --- a/packages/core/src/Models/Contracts/ProductAssociation.php +++ b/packages/core/src/Models/Contracts/ProductAssociation.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Lunar\Base\Enums\Concerns\ProvidesProductAssociationType; interface ProductAssociation { @@ -35,5 +36,5 @@ public function scopeAlternate(Builder $query): Builder; /** * Apply the type scope. */ - public function scopeType(Builder $query, string $type): Builder; + public function scopeType(Builder $query, ProvidesProductAssociationType $type): Builder; } diff --git a/packages/core/src/Models/Discount.php b/packages/core/src/Models/Discount.php index b8bf7f3dbe..8d5822bc49 100644 --- a/packages/core/src/Models/Discount.php +++ b/packages/core/src/Models/Discount.php @@ -188,15 +188,36 @@ public function scopeCollections(Builder $query, iterable $collectionIds = [], a } $types = Arr::wrap($types); + $prefix = config('lunar.database.table_prefix'); return $query->where( - fn ($subQuery) => $subQuery->whereDoesntHave('discountables', fn ($query) => $query->when($types, fn ($query) => $query->whereIn('type', $types))) - ->orWhereHas('discountables', - fn ($relation) => $relation->whereIn('discountable_id', $collectionIds) - ->whereDiscountableType(Collection::morphName()) + fn ($subQuery) => $subQuery->whereDoesntHave('collections', fn ($query) => $query->when($types, fn ($query) => $query->whereIn("{$prefix}collection_discount.type", $types))) + ->orWhereHas('collections', + fn ($relation) => $relation->whereIn('collection_id', $collectionIds) + ->when( + $types, + fn ($query) => $query->whereIn("{$prefix}collection_discount.type", $types) + ) + ) + ); + } + + public function scopeBrands(Builder $query, iterable $brandIds = [], array|string $types = []): Builder + { + if (is_array($brandIds)) { + $brandIds = collect($brandIds); + } + + $types = Arr::wrap($types); + $prefix = config('lunar.database.table_prefix'); + + return $query->where( + fn ($subQuery) => $subQuery->whereDoesntHave('brands', fn ($query) => $query->when($types, fn ($query) => $query->whereIn("{$prefix}brand_discount.type", $types))) + ->orWhereHas('brands', + fn ($relation) => $relation->whereIn('brand_id', $brandIds) ->when( $types, - fn ($query) => $query->whereIn('type', $types) + fn ($query) => $query->whereIn("{$prefix}brand_discount.type", $types) ) ) ); @@ -209,15 +230,16 @@ public function scopeProducts(Builder $query, iterable $productIds = [], array|s } $types = Arr::wrap($types); + $prefix = config('lunar.database.table_prefix'); return $query->where( - fn ($subQuery) => $subQuery->whereDoesntHave('discountables', fn ($query) => $query->when($types, fn ($query) => $query->whereIn('type', $types))) + fn ($subQuery) => $subQuery->whereDoesntHave('discountables', fn ($query) => $query->whereDiscountableType(Product::morphName())->when($types, fn ($query) => $query->whereIn("{$prefix}discountables.type", $types))) ->orWhereHas('discountables', fn ($relation) => $relation->whereIn('discountable_id', $productIds) ->whereDiscountableType(Product::morphName()) ->when( $types, - fn ($query) => $query->whereIn('type', $types) + fn ($query) => $query->whereIn("{$prefix}discountables.type", $types) ) ) ); @@ -230,15 +252,16 @@ public function scopeProductVariants(Builder $query, iterable $variantIds = [], } $types = Arr::wrap($types); + $prefix = config('lunar.database.table_prefix'); return $query->where( - fn ($subQuery) => $subQuery->whereDoesntHave('discountables', fn ($query) => $query->when($types, fn ($query) => $query->whereIn('type', $types))) + fn ($subQuery) => $subQuery->whereDoesntHave('discountables', fn ($query) => $query->whereDiscountableType(ProductVariant::morphName())->when($types, fn ($query) => $query->whereIn("{$prefix}discountables.type", $types))) ->orWhereHas('discountables', fn ($relation) => $relation->whereIn('discountable_id', $variantIds) ->whereDiscountableType(ProductVariant::morphName()) ->when( $types, - fn ($query) => $query->whereIn('type', $types) + fn ($query) => $query->whereIn("{$prefix}discountables.type", $types) ) ) ); diff --git a/packages/core/src/Models/Product.php b/packages/core/src/Models/Product.php index 0f372ad7f9..0338aa3be5 100644 --- a/packages/core/src/Models/Product.php +++ b/packages/core/src/Models/Product.php @@ -15,6 +15,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Lunar\Base\BaseModel; use Lunar\Base\Casts\AsAttributeData; +use Lunar\Base\Enums\Concerns\ProvidesProductAssociationType; use Lunar\Base\HasThumbnailImage; use Lunar\Base\Traits\HasChannels; use Lunar\Base\Traits\HasCustomerGroups; @@ -144,7 +145,7 @@ public function inverseAssociations(): HasMany return $this->hasMany(ProductAssociation::modelClass(), 'product_target_id'); } - public function associate(mixed $product, string $type): void + public function associate(mixed $product, ProvidesProductAssociationType|string $type): void { Associate::dispatch($this, $product, $type); } @@ -152,7 +153,7 @@ public function associate(mixed $product, string $type): void /** * Dissociate a product to another with a type. */ - public function dissociate(mixed $product, ?string $type = null): void + public function dissociate(mixed $product, ProvidesProductAssociationType|string|null $type = null): void { Dissociate::dispatch($this, $product, $type); } diff --git a/packages/core/src/Models/ProductAssociation.php b/packages/core/src/Models/ProductAssociation.php index a23760e87a..f2f7891b99 100644 --- a/packages/core/src/Models/ProductAssociation.php +++ b/packages/core/src/Models/ProductAssociation.php @@ -6,6 +6,8 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Lunar\Base\BaseModel; +use Lunar\Base\Enums\Concerns\ProvidesProductAssociationType; +use Lunar\Base\Enums\ProductAssociation as ProductAssociationEnum; use Lunar\Base\Traits\HasMacros; use Lunar\Database\Factories\ProductAssociationFactory; @@ -24,16 +26,25 @@ class ProductAssociation extends BaseModel implements Contracts\ProductAssociati /** * Define the cross-sell type. + * + * @deprecated 1.2.0 + * @see \Lunar\Base\Enums\ProductAssociation */ const CROSS_SELL = 'cross-sell'; /** * Define the upsell type. + * + * @deprecated 1.2.0 + * @see \Lunar\Base\Enums\ProductAssociation */ const UP_SELL = 'up-sell'; /** * Define the alternate type. + * + * @deprecated 1.2.0 + * @see \Lunar\Base\Enums\ProductAssociation */ const ALTERNATE = 'alternate'; @@ -77,7 +88,7 @@ public function target(): BelongsTo */ public function scopeCrossSell(Builder $query): Builder { - return $query->type(self::CROSS_SELL); + return $query->type(ProductAssociationEnum::CROSS_SELL); } /** @@ -85,7 +96,7 @@ public function scopeCrossSell(Builder $query): Builder */ public function scopeUpSell(Builder $query): Builder { - return $query->type(self::UP_SELL); + return $query->type(ProductAssociationEnum::UP_SELL); } /** @@ -93,14 +104,29 @@ public function scopeUpSell(Builder $query): Builder */ public function scopeAlternate(Builder $query): Builder { - return $query->type(self::ALTERNATE); + return $query->type(ProductAssociationEnum::ALTERNATE); } /** * Apply the type scope. */ - public function scopeType(Builder $query, string $type): Builder + public function scopeType(Builder $query, ProvidesProductAssociationType|string $type): Builder { - return $query->whereType($type); + return $query->where( + 'type', + '=', + is_string($type) ? $type : $type->value + ); + } + + public static function getTypes(): array + { + $enum = config('lunar.products.association_types_enum', \Lunar\Base\Enums\ProductAssociation::class); + + return collect($enum::cases())->mapWithKeys(function ($item) { + return [ + $item->value => $item->label(), + ]; + })->toArray(); } } diff --git a/packages/core/src/Models/ProductOption.php b/packages/core/src/Models/ProductOption.php index e1a0a86543..e41848b1f8 100644 --- a/packages/core/src/Models/ProductOption.php +++ b/packages/core/src/Models/ProductOption.php @@ -18,10 +18,12 @@ /** * @property int $id - * @property \Illuminate\Support\Collection $name - * @property \Illuminate\Support\Collection $label + * @property AsArrayObject $name + * @property ?AsArrayObject $label * @property int $position * @property ?string $handle + * @property bool $shared + * @property ?AsArrayObject $meta * @property ?\Illuminate\Support\Carbon $created_at * @property ?\Illuminate\Support\Carbon $updated_at */ @@ -43,6 +45,7 @@ class ProductOption extends BaseModel implements Contracts\ProductOption, Spatie 'name' => AsArrayObject::class, 'label' => AsArrayObject::class, 'shared' => 'boolean', + 'meta' => AsArrayObject::class, ]; /** diff --git a/packages/core/src/Models/ProductOptionValue.php b/packages/core/src/Models/ProductOptionValue.php index 02ed9066b8..9270e8c3d6 100644 --- a/packages/core/src/Models/ProductOptionValue.php +++ b/packages/core/src/Models/ProductOptionValue.php @@ -2,7 +2,7 @@ namespace Lunar\Models; -use Illuminate\Database\Eloquent\Casts\AsCollection; +use Illuminate\Database\Eloquent\Casts\AsArrayObject; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -16,8 +16,9 @@ /** * @property int $id * @property int $product_option_id - * @property string $name + * @property AsArrayObject $name * @property int $position + * @property ?AsArrayObject $meta * @property ?\Illuminate\Support\Carbon $created_at * @property ?\Illuminate\Support\Carbon $updated_at */ @@ -34,7 +35,8 @@ class ProductOptionValue extends BaseModel implements Contracts\ProductOptionVal * @var array */ protected $casts = [ - 'name' => AsCollection::class, + 'name' => AsArrayObject::class, + 'meta' => AsArrayObject::class, ]; /** diff --git a/packages/core/src/Observers/PriceObserver.php b/packages/core/src/Observers/PriceObserver.php index 426cecb2fc..8a6590f423 100644 --- a/packages/core/src/Observers/PriceObserver.php +++ b/packages/core/src/Observers/PriceObserver.php @@ -2,10 +2,11 @@ namespace Lunar\Observers; +use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit; use Lunar\Jobs\Currencies\SyncPriceCurrencies; use Lunar\Models\Contracts\Price; -class PriceObserver +class PriceObserver implements ShouldHandleEventsAfterCommit { public function created(Price $price): void { diff --git a/packages/core/src/Observers/ProductObserver.php b/packages/core/src/Observers/ProductObserver.php index 18163224c3..cd98d6edd6 100644 --- a/packages/core/src/Observers/ProductObserver.php +++ b/packages/core/src/Observers/ProductObserver.php @@ -24,6 +24,8 @@ public function deleting(ProductContract $product): void $product->associations()->delete(); + $product->inverseAssociations()->delete(); + $product->channels()->detach(); $product->tags()->detach(); diff --git a/packages/opayo/README.md b/packages/opayo/README.md deleted file mode 100644 index 18b212b178..0000000000 --- a/packages/opayo/README.md +++ /dev/null @@ -1,169 +0,0 @@ -

      - - - -

      This addon enables Opayo payments on your Lunar storefront.

      - -## Alpha Release - -This addon is currently in Alpha, whilst every step is taken to ensure this is working as intended, it will not be considered out of Alpha until more tests have been added and proved. - -## Minimum Requirements - -- Lunar `1.x` -- An [Elavon](https://www.elavon.com/) merchant account - -## Installation - -### Require the composer package - -```sh -composer require lunarphp/opayo -``` - -### Configure the service - -Add the opayo config to the `config/services.php` file. - -```php -// ... -'opayo' => [ - 'vendor' => env('OPAYO_VENDOR'), - 'env' => env('OPAYO_ENV', 'test'), - 'key' => env('OPAYO_KEY'), - 'password' => env('OPAYO_PASSWORD'), - 'host' => env('OPAYO_HOST'), -], -``` - - -### Enable the driver - -Set the driver in `config/lunar/payments.php` - -```php - [ - 'card' => [ - // ... - 'driver' => 'opayo', - ], - ], -]; -``` - - - -## Configuration - -Below is a list of the available configuration options this package uses in `config/lunar/opayo.php` - -| Key | Default | Description | -| --- | --- | --- | -| `policy` | `automatic` | Determines the policy for taking payments and whether you wish to capture the payment manually later or take payment straight away. Available options `deferred` or `automatic` | - ---- - -## Backend Usage - -### Get a merchant key - -```php -Lunar\Opayo\Facades\Opayo::getMerchantKey(); -``` - -### Authorize a charge - -```php -$response = \Lunar\Facades\Payments::driver('opayo')->cart( - $cart = CartSession::current()->calculate() -)->withData([ - 'merchant_key' => $request->get('merchantSessionKey'), - 'card_identifier' => $request->get('cardToken'), - 'browserLanguage' => $request->get('browserLanguage'), - 'challengeWindowSize' => $request->get('challengeWindowSize'), - 'browserIP' => $request->ip(), - 'browserAcceptHeader' => $request->header('accept'), - 'browserUserAgent' => $request->get('browserUserAgent'), - 'browserJavaEnabled' => $request->get('browserJavaEnabled', false), - 'browserColorDepth' => $request->get('browserColorDepth'), - 'browserScreenHeight' => $request->get('browserScreenHeight'), - 'browserScreenWidth' => $request->get('browserScreenWidth'), - 'browserTZ' => $request->get('browserTZ'), - 'status' => 'payment-received', -])->authorize(); -``` - -When authorizing a charge, you may be required to submit extra authentication in the form of 3DSV2, you can handle this in your payment endpoint. - -```php -if (is_a($response, \Lunar\Opayo\Responses\ThreeDSecureResponse::class)) { - return response()->json([ - 'requires_auth' => true, - 'data' => $response, - ]); -} -``` - -`$response` will contain all the 3DSV2 information from Opayo. - -You can find more information about this using the following links: - -- [3-D Secure explained](https://www.elavon.co.uk/resource-center/help-with-your-solutions/opayo/fraud-prevention/3D-Secure.html) -- [3D Secure Transactions](https://developer.elavon.com/products/opayo-direct/v1/3d-secure-transactions) -- Stack overflow [SagePay 3D Secure V2 Flow](https://stackoverflow.com/questions/65329436/sagepay-3d-secure-v2-flow) - -Once you have handled the 3DSV2 response on your storefront, you can then authorize again. - -```php -$response = Payments::driver('opayo')->cart( - $cart = CartSession::current()->calculate() -)->withData([ - 'cres' => $request->get('cres'), - 'pares' => $request->get('pares'), - 'transaction_id' => $request->get('transaction_id'), -])->threedsecure(); - -if (! $response->success) { - abort(401); -} - -``` - -### Opayo card tokens - -When authenticated users make an order on your store, it can be good to offer the ability to save their card information for future use. Whilst we don't store the actual card details, we can use card tokens which represent the card the user has used before. - -> You must have saved payments enabled on your Opayo account because you can use these. - -To save a card, pass in the `saveCard` data key when authorizing a payment. - -```php -$response = \Lunar\Facades\Payments::driver('opayo')->cart( - $cart = CartSession::current()->calculate() -)->withData([ - // ... - 'saveCard' => true -])->authorize(); -``` - -Assuming everything went well, there will be a new entry in the `opayo_tokens` table, associated to the authenticated user. You can then display these card representations at checkout for the user to select. The `token` is what replaces the `card_identifier` data key. - -```php -$response = \Lunar\Facades\Payments::driver('opayo')->cart( - $cart = CartSession::current()->calculate() -)->withData([ - // ... - 'card_identifier' => $request->get('cardToken'), - 'reusable' => true -])->authorize(); -``` - -Responses are then handled the same as any other transaction. - -## Contributing - -Contributions are welcome, if you are thinking of adding a feature, please submit an issue first so we can determine whether it should be included. diff --git a/packages/paypal/README.md b/packages/paypal/README.md deleted file mode 100644 index 91c3a064cf..0000000000 --- a/packages/paypal/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Lunar PayPal Payments - -WIP diff --git a/packages/search/README.md b/packages/search/README.md deleted file mode 100644 index 7d6d9cd819..0000000000 --- a/packages/search/README.md +++ /dev/null @@ -1,125 +0,0 @@ - -## Lunar Search - -This packages brings E-Commerce search to Lunar. ---- - -## Requirements -- Lunar >= 1.x - -## License - -Lunar is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). - -## Installation - -### Require the composer package - -```sh -composer require lunarphp/search -``` - -## Usage - -### Configuration - -Most configuration is done via `config/lunar/search.php`. Here you can specify which facets should be used and how they are displayed. - -```php -'facets' => [ - \Lunar\Models\Product::class => [ - 'brand' => [ - 'label' => 'Brand', - ], - 'colour' => [ - 'label' => 'Colour', - ], - 'size' => [ - 'label' => 'Size', - ], - 'shoe-size' => [ - 'label' => 'Shoe Size', - ] - ] -], -``` - -### Basic Search - -At a basic level, you can search models using the provided facade. - -```php -use Lunar\Search\Facades\Search; - -// Search on a specific model -$results = Search::on(\Lunar\Models\Collection::class)->query('Hoodies')->get(); - -// Search on Lunar\Models\Product by default. -$results = Search::query('Hoodies')->get(); -``` - -Under the hood this will detect what Scout driver is mapped under `lunar.search.engine_map` and -then perform a search using that given driver. To increase performance the results will not be -hydrated from the database, but instead will be the raw results from the search provider. - -### Response format - -This packages makes use of Spatie Data to transform search results from the provider into consistent responses you can use on your frontend. - -You can view all available Data classes in the `src/Data` directory. - -### Transforming Data classes to Typescript - -If you use Spatie Typescript Transformer, you can add the following to your `typescript-transformer.php` config: - -```php -return [ - //... - 'auto_discover_types' => [ - // ... - \Lunar\Search\data_path(), - ], -]; -``` -Then in your Javascript the data classes will be available under the `Lunar.Search` namespace. - -```js -defineProps<{ - results: Lunar.Search.SearchResults -}>() -``` - -### Handling the response - -Searching returns a `Lunar\Data\SearchResult` DTO which you can use in your templates: - -```php -use Lunar\Search\Facades\Search; -$results = Search::query('Hoodies')->get(); -``` - -```bladehtml - -@foreach($results->hits as $hit) - {{ $hit->document['name'] }} -@endforeach - - -@foreach($results->facets as $facet) - - {{ $facet->label }} - @foreach($facet->values as $facetValue) - - $facetValue->active, - ]) - > - {{ $facetValue->label }} - - {{ $facetValue->count }} - @endforeach -
-@endforeach -``` - diff --git a/packages/stripe/README.md b/packages/stripe/README.md deleted file mode 100644 index 45c2f4248f..0000000000 --- a/packages/stripe/README.md +++ /dev/null @@ -1,435 +0,0 @@ -

- - -

This addon enables Stripe payments on your Lunar storefront.

- -## Alpha Release - -This addon is currently in Alpha, whilst every step is taken to ensure this is working as intended, it will not be considered out of Alpha until more tests have been added and proved. - -## Tests required: - -- [ ] Successful charge response from Stripe. -- [ ] Unsuccessful charge response from Stripe. -- [ ] Test `manual` config reacts appropriately. -- [x] Test `automatic` config reacts appropriately. -- [ ] Ensure transactions are stored correctly in the database -- [x] Ensure that the payment intent is not duplicated when using the same Cart -- [ ] Ensure appropriate responses are returned based on Stripe's responses. -- [ ] Test refunds and partial refunds create the expected transactions -- [ ] Make sure we can manually release a payment or part payment and handle the different responses. - -## Minimum Requirements - -- Lunar `1.x` -- A [Stripe](http://stripe.com/) account with secret and public keys - -## Optional Requirements - -- Laravel Livewire (if using frontend components) -- Alpinejs (if using frontend components) -- Javascript framework - -## Installation - -### Require the composer package - -```sh -composer require lunarphp/stripe -``` - -### Publish the configuration - -This will publish the configuration under `config/lunar/stripe.php`. - -```sh -php artisan vendor:publish --tag=lunar.stripe.config -``` - -### Publish the views (optional) - -Lunar Stripe comes with some helper components for you to use on your checkout, if you intend to edit the views they provide, you can publish them. - -```sh -php artisan vendor:publish --tag=lunar.stripe.components -``` - -### Enable the driver - -Set the driver in `config/lunar/payments.php` - -```php - [ - 'card' => [ - // ... - 'driver' => 'stripe', - ], - ], -]; -``` - -### Add your Stripe credentials - -Make sure you have the Stripe credentials set in `config/services.php` - -```php -'stripe' => [ - 'key' => env('STRIPE_SECRET'), - 'public_key' => env('STRIPE_PK'), - 'webhooks' => [ - 'lunar' => env('LUNAR_STRIPE_WEBHOOK_SECRET'), - ], -], -``` - -> Keys can be found in your Stripe account https://dashboard.stripe.com/apikeys - -## Configuration - -Below is a list of the available configuration options this package uses in `config/lunar/stripe.php` - -| Key | Default | Description | -|------------------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `policy` | `automatic` | Determines the policy for taking payments and whether you wish to capture the payment manually later or take payment straight away. Available options `manual` or `automatic` | -| `sync_addresses` | `true` | When enabled, the Stripe addon will attempt to sync the billing and shipping addresses which have been stored against the payment intent on Stripe. | ---- - -## Backend Usage - -### Create a PaymentIntent - -```php -use \Lunar\Stripe\Facades\Stripe; - -Stripe::createIntent(\Lunar\Models\Cart $cart, $options = []); -``` - -This method will create a Stripe PaymentIntent from a Cart and add the resulting ID to the meta for retrieval later. If a PaymentIntent already exists for a cart this will fetch it from Stripe and return that instead to avoid duplicate PaymentIntents being created. - -You can pass any additional parameters you need, by default the following are sent: - -```php -[ - 'amount' => 1099, - 'currency' => 'GBP', - 'automatic_payment_methods' => ['enabled' => true], - 'capture_method' => config('lunar.stripe.policy', 'automatic'), - // If a shipping address exists on a cart - // $shipping = $cart->shippingAddress - 'shipping' => [ - 'name' => "{$shipping->first_name} {$shipping->last_name}", - 'phone' => $shipping->contact_phone, - 'address' => [ - 'city' => $shipping->city, - 'country' => $shipping->country->iso2, - 'line1' => $shipping->line_one, - 'line2' => $shipping->line_two, - 'postal_code' => $shipping->postcode, - 'state' => $shipping->state, - ], - ] -] -``` - -```php -$paymentIntentId = $cart->meta['payment_intent']; // The resulting ID from the method above. -``` -```php -$cart->meta->payment_intent; -``` - -### Fetch an existing PaymentIntent - -```php -use \Lunar\Stripe\Facades\Stripe; - -Stripe::fetchIntent($paymentIntentId); -``` - -### Syncing an existing intent - -If a payment intent has been created and there are changes to the cart, you will want to update the intent so it has the correct totals. - -```php -use \Lunar\Stripe\Facades\Stripe; - -Stripe::syncIntent(\Lunar\Models\Cart $cart); -``` - - -### Update an existing intent - -For when you want to update certain properties on the PaymentIntent, without needing to recalculate the cart. - -See https://docs.stripe.com/api/payment_intents/update - -```php -use \Lunar\Stripe\Facades\Stripe; - -Stripe::updateIntent(\Lunar\Models\Cart $cart, [ - 'shipping' => [/*..*/] -]); -``` - -### Cancel an existing intent - -If you need to cancel a PaymentIntent, you can do so. You will need to provide a valid reason, those of which can be found in the Stripe docs: https://docs.stripe.com/api/payment_intents/cancel. - -Lunar Stripe includes a PHP Enum to make this easier for you: - -```php -use Lunar\Stripe\Enums\CancellationReason; - -CancellationReason::ABANDONED; -CancellationReason::DUPLICATE; -CancellationReason::REQUESTED_BY_CUSTOMER; -CancellationReason::FRAUDULENT; -``` - -```php -use Lunar\Stripe\Facades\Stripe; -use Lunar\Stripe\Enums\CancellationReason; - -Stripe::cancelIntent(\Lunar\Models\Cart $cart, CancellationReason $reason); -``` - -### Update the address on Stripe - -So you don't have to manually specify all the shipping address fields you can use the helper function to do it for you. - -```php -use \Lunar\Stripe\Facades\Stripe; - -Stripe::updateShippingAddress(\Lunar\Models\Cart $cart); -``` - -## Charges - -### Retrieve a specific charge - -```php -use \Lunar\Stripe\Facades\Stripe; - -Stripe::getCharge(string $chargeId); -``` - -### Get all charges for a payment intent - -```php -use \Lunar\Stripe\Facades\Stripe; - -Stripe::getCharges(string $paymentIntentId); -``` - -## Webhooks - -The add-on provides an optional webhook you may add to Stripe. You can read the guide on how to do this on the Stripe website [https://stripe.com/docs/webhooks/quickstart](https://stripe.com/docs/webhooks/quickstart). - -The events you should listen to are `payment_intent.payment_failed`, `payment_intent.succeeded`. - -The path to the webhook will be `http:://yoursite.com/stripe/webhook`. - -You can customise the path for the webhook in `config/lunar/stripe.php`. - -You will also need to add the webhook signing secret to the `services.php` config file: - -```php - [ - // ... - 'webhooks' => [ - 'lunar' => '...' - ], - ], -]; -``` - -If you do not wish to use the webhook, or would like to manually process an order as well, you are able to do so. - -```php -$cart = CartSession::current(); - -// With a draft order... -$draftOrder = $cart->createOrder(); -Payments::driver('stripe')->order($draftOrder)->withData([ - 'payment_intent' => $draftOrder->meta['payment_intent'], -])->authorize(); - -// Using just the cart... -Payments::driver('stripe')->cart($cart)->withData([ - 'payment_intent' => $cart->meta['payment_intent'], -])->authorize(); -``` - - -## Storefront Examples - -First we need to set up the backend API call to fetch or create the intent, this isn't Vue specific but will likely be different if you're using Livewire. - -```php -use \Lunar\Stripe\Facades\Stripe; - -Route::post('api/payment-intent', function () { - $cart = CartSession::current(); - - $cartData = CartData::from($cart); - - if ($paymentIntent = $cartData->meta['payment_intent'] ?? false) { - $intent = StripeFacade::fetchIntent($paymentIntent); - } else { - $intent = StripeFacade::createIntent($cart); - } - - if ($intent->amount != $cart->total->value) { - StripeFacade::syncIntent($cart); - } - - return $intent; -})->middleware('web'); -``` - -### Vuejs - -This is just using Stripe's payment elements, for more information [check out the Stripe guides](https://stripe.com/docs/payments/elements) - -### Payment component - -```js - -``` - -```html - -``` ---- - -## Extending - -### Webhook event params - -In order to process the payment intent and link it to an order, we need the PaymentIntent ID and an optional Order ID. - -By default Lunar Stripe will look for the PaymentIntent ID via the Stripe Event and try and determine whether an existing order ID has been defined on the PaymentIntent meta. - -You can customise this behaviour by overriding the `ProcessesEventParameters` instance. - -```php -// AppServiceProvider -use Lunar\Stripe\Concerns\ProcessesEventParameters; -use Lunar\Stripe\DataTransferObjects\EventParameters; - -public function boot() -{ - $this->app->instance(ProcessesEventParameters::class, new class implements ProcessesEventParameters - { - public function handle(\Stripe\Event $event): EventParameters - { - $paymentIntentId = $event->data->object->id; - // Setting $orderId to null will mean a new order is created. - $orderId = null; - - return new EventParameters($paymentIntentId, $orderId); - } - }); - -## Events - -Below are the events which are dispatched under specific situations within the addon. - -### `Lunar\Stripe\Events\Webhook\CartMissingForIntent` - -Dispatched when attempting to process a payment intent, but no matching Order or Cart model can be found. - -```php -public function handle(\Lunar\Stripe\Events\Webhook\CartMissingForIntent $event) -{ - echo $event->paymentIntentId; -} -``` - -## Contributing - -Contributions are welcome, if you are thinking of adding a feature, please submit an issue first so we can determine whether it should be included. - - -## Testing - -A [MockClient](https://github.com/lunar/stripe/blob/main/tests/Stripe/MockClient.php) class is used to mock responses the Stripe API will return. diff --git a/packages/table-rate-shipping/README.md b/packages/table-rate-shipping/README.md deleted file mode 100644 index 4b90525994..0000000000 --- a/packages/table-rate-shipping/README.md +++ /dev/null @@ -1,116 +0,0 @@ -# Lunar Table Rate Shipping - - -# Requirements - -- LunarPHP Admin `>` `1.x` - -# Installation - -Install via Composer - -``` -composer require lunarphp/table-rate-shipping -``` - -Then register the plugin in your service provider - -```php -use Lunar\Admin\Support\Facades\LunarPanel; -use Lunar\Shipping\ShippingPlugin; -// ... - -public function register(): void -{ - LunarPanel::panel(function (Panel $panel) { - return $panel->plugin(new ShippingPlugin()); - })->register(); - - // ... -} -``` -# Getting Started -This addon provides an easy way for you to add different shipping to your storefront and allow your customers to choose from the different shipping rates you have set up, based on various factors such as zones, minimum spend etc - -## Shipping Methods - -Shipping Methods are the different ways in which your storefront can send orders to customers, you could also allow customers to collect their order from your store which this addon supports. - -## Shipping Zones - -Shipping Zones allow you to section of area's of the countries you ship to, providing you with an easy way to offer distinct shipping methods and pricing to each zone, each zone can be restricted by the following: - -- Postal codes -- Country -- State/Province (based on country) - -## Shipping Rates - -Shipping Rates are the prices you offer for each of your shipping zones, they are linked to a shipping method. So for example you might have a Courier Area Shipping Zone and an Everywhere Else Shipping Zone, you can offer different pricing restrictions using the same shipping methods. - -## Shipping Exclusion Lists - -Sometimes, you might not want to ship certain items to particular Shipping Zone, this is where exclusion lists come in. You can associate purchasables to a list which you can then associate to a shipping zone, if a cart contains any of them then they won't be able to select a shipping rate. - -# Storefront usage - -This addon uses the shipping modifiers provided by the Lunar core, so you shouldn't need to change your existing implementation. - -```php -$options = \Lunar\Base\ShippingManifest::getOptions( - $cart -); -``` - -# Advanced usage - -## Return available drivers - -```php -\Lunar\Shipping\Facades\Shipping::getSupportedDrivers(); -``` - -## Using the driver directly - -```php -\Lunar\Shipping\Facades\Shipping::with('ship-by')->resolve( - new \Lunar\Shipping\DataTransferObjects\ShippingOptionRequest( - shippingRate: \Lunar\Shipping\Models\ShippingRate $shippingRate, - cart: \Lunar\Models\Cart $cart - ) -); -``` - -## Shipping Zones - -Each method is optional, the more you add the more strict it becomes. - -```php -$shippingZones = Lunar\Shipping\Facades\Shipping::zones() - ->country(\Lunar\Models\Country $country) - ->state(\Lunar\Models\State $state) - ->postcode( - new \Lunar\Shipping\DataTransferObjects\PostcodeLookup( - country: \Lunar\Models\Country $country, - postcode: 'NW1' - ) - )->get() - -$shippingZones->map(/* .. */); -``` - -## Shipping Rates - -```php -$shippingRates = \Lunar\Shipping\Facades\Shipping::shippingRates( - \Lunar\Models\Cart $cart -); -``` - -## Shipping Options - -```php -$shippingOptions = \Lunar\Shipping\Facades\Shipping::shippingOptions( - \Lunar\Models\Cart $cart -); -``` \ No newline at end of file diff --git a/packages/table-rate-shipping/database/migrations/2024_03_19_100000_remap_shipping_polymorphic_relations.php b/packages/table-rate-shipping/database/migrations/2024_03_19_100000_remap_shipping_polymorphic_relations.php index 2bcae006b4..6716355ade 100644 --- a/packages/table-rate-shipping/database/migrations/2024_03_19_100000_remap_shipping_polymorphic_relations.php +++ b/packages/table-rate-shipping/database/migrations/2024_03_19_100000_remap_shipping_polymorphic_relations.php @@ -1,18 +1,19 @@ prefix.'prices') + DB::table($this->prefix.'prices') ->where('priceable_type', '=', \Lunar\Shipping\Models\ShippingRate::class) ->update([ 'priceable_type' => 'shipping_rate', ]); - \Illuminate\Support\Facades\DB::table($this->prefix.'shipping_exclusions') + DB::table($this->prefix.'shipping_exclusions') ->where('purchasable_type', '=', \Lunar\Models\Product::class) ->update([ 'purchasable_type' => 'product', @@ -21,16 +22,16 @@ public function up() public function down() { - \Illuminate\Support\Facades\DB::table($this->prefix.'prices') + DB::table($this->prefix.'prices') ->where('priceable_type', '=', 'shipping_rate') ->update([ 'priceable_type' => \Lunar\Shipping\Models\ShippingRate::class, ]); - \Illuminate\Support\Facades\DB::table($this->prefix.'shipping_exclusions') + DB::table($this->prefix.'shipping_exclusions') ->where('purchasable_type', '=', 'product') ->update([ 'purchasable_type' => \Lunar\Models\Product::class, ]); } -} +}; diff --git a/packages/table-rate-shipping/database/migrations/2024_06_28_100000_create_customer_group_shipping_method_table.php b/packages/table-rate-shipping/database/migrations/2024_06_28_100000_create_customer_group_shipping_method_table.php index 66bb1186c9..8ed215f29a 100644 --- a/packages/table-rate-shipping/database/migrations/2024_06_28_100000_create_customer_group_shipping_method_table.php +++ b/packages/table-rate-shipping/database/migrations/2024_06_28_100000_create_customer_group_shipping_method_table.php @@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema; use Lunar\Base\Migration; -class CreateCustomerGroupShippingMethodTable extends Migration +return new class extends Migration { public function up() { @@ -26,4 +26,4 @@ public function down() { Schema::dropIfExists($this->prefix.'customer_group_shipping_method'); } -} +}; diff --git a/packages/table-rate-shipping/database/migrations/2025_08_18_100000_switch_shipping_to_jsonb_columns.php b/packages/table-rate-shipping/database/migrations/2025_08_18_100000_switch_shipping_to_jsonb_columns.php new file mode 100644 index 0000000000..6f49544b57 --- /dev/null +++ b/packages/table-rate-shipping/database/migrations/2025_08_18_100000_switch_shipping_to_jsonb_columns.php @@ -0,0 +1,33 @@ +prefix.'shipping_methods', function (Blueprint $table) { + $table->jsonb('data')->nullable()->change(); + }); + } + + public function down() + { + // Only run for PostgreSQL + if (DB::getDriverName() !== 'pgsql') { + return; + } + + Schema::table($this->prefix.'shipping_methods', function (Blueprint $table) { + $table->json('data')->nullable()->change(); + }); + } +}; diff --git a/packages/table-rate-shipping/phpunit.xml b/packages/table-rate-shipping/phpunit.xml deleted file mode 100644 index 1b64e22fb6..0000000000 --- a/packages/table-rate-shipping/phpunit.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - ./tests/Unit - - - ./tests/Feature - - - - - ./app - - - - - - - - - diff --git a/packages/table-rate-shipping/resources/lang/en/relationmanagers.php b/packages/table-rate-shipping/resources/lang/en/relationmanagers.php index 09bcc1e522..26eae22285 100644 --- a/packages/table-rate-shipping/resources/lang/en/relationmanagers.php +++ b/packages/table-rate-shipping/resources/lang/en/relationmanagers.php @@ -47,8 +47,15 @@ ], ], 'table' => [ + 'enabled' => [ + 'label' => 'Enabled', + ], + 'disabled' => [ + 'label' => 'disabled', + ], 'shipping_method' => [ 'label' => 'Shipping Method', + 'disabled' => 'Disabled', ], 'price' => [ 'label' => 'Price', diff --git a/packages/table-rate-shipping/resources/lang/hu/plugin.php b/packages/table-rate-shipping/resources/lang/hu/plugin.php new file mode 100644 index 0000000000..c6694b8728 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/hu/plugin.php @@ -0,0 +1,7 @@ + [ + 'group' => 'Szállítás', + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/hu/relationmanagers.php b/packages/table-rate-shipping/resources/lang/hu/relationmanagers.php new file mode 100644 index 0000000000..a65667e819 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/hu/relationmanagers.php @@ -0,0 +1,80 @@ + [ + 'customer_groups' => [ + 'description' => 'Rendeld hozzá a vásárlói csoportokat ehhez a szállítási módhoz az elérhetőség meghatározásához.', + ], + ], + 'shipping_rates' => [ + 'title_plural' => 'Szállítási díjak', + 'actions' => [ + 'create' => [ + 'label' => 'Szállítási díj létrehozása', + ], + ], + 'notices' => [ + 'prices_incl_tax' => 'Minden ár tartalmazza az adót, ezt figyelembe vesszük a minimális költés számításakor.', + 'prices_excl_tax' => 'Minden ár adó nélkül értendő, a minimális költés a kosár részösszege alapján kerül kiszámításra.', + ], + 'form' => [ + 'shipping_method_id' => [ + 'label' => 'Szállítási mód', + ], + 'price' => [ + 'label' => 'Ár', + ], + 'prices' => [ + 'label' => 'Árlépcsők', + 'repeater' => [ + 'customer_group_id' => [ + 'label' => 'Vásárlói csoport', + 'placeholder' => 'Bármely', + ], + 'currency_id' => [ + 'label' => 'Pénznem', + ], + 'min_spend' => [ + 'label' => 'Min. költés', + ], + 'min_weight' => [ + 'label' => 'Min. súly', + ], + 'price' => [ + 'label' => 'Ár', + ], + ], + ], + ], + 'table' => [ + 'shipping_method' => [ + 'label' => 'Szállítási mód', + ], + 'price' => [ + 'label' => 'Ár', + ], + 'price_breaks_count' => [ + 'label' => 'Árlépcsők', + ], + ], + ], + 'exclusions' => [ + 'title_plural' => 'Szállítási kizárások', + 'form' => [ + 'purchasable' => [ + 'label' => 'Termék', + ], + ], + 'actions' => [ + 'create' => [ + 'label' => 'Szállítási kizárási lista hozzáadása', + ], + 'attach' => [ + 'label' => 'Kizárási lista hozzáadása', + ], + 'detach' => [ + 'label' => 'Eltávolítás', + ], + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/hu/shippingexclusionlist.php b/packages/table-rate-shipping/resources/lang/hu/shippingexclusionlist.php new file mode 100644 index 0000000000..ed16f994d8 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/hu/shippingexclusionlist.php @@ -0,0 +1,19 @@ + 'Szállítási kizáró lista', + 'label_plural' => 'Szállítási kizáró listák', + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'exclusions_count' => [ + 'label' => 'Termékek száma', + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/hu/shippingmethod.php b/packages/table-rate-shipping/resources/lang/hu/shippingmethod.php new file mode 100644 index 0000000000..92df485f40 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/hu/shippingmethod.php @@ -0,0 +1,58 @@ + 'Szállítási módok', + 'label' => 'Szállítási mód', + 'form' => [ + 'name' => [ + 'label' => 'Név', + ], + 'description' => [ + 'label' => 'Leírás', + ], + 'code' => [ + 'label' => 'Kód', + ], + 'cutoff' => [ + 'label' => 'Határidő', + ], + 'charge_by' => [ + 'label' => 'Számlázás', + 'options' => [ + 'cart_total' => 'Kosár végösszeg', + 'weight' => 'Súly', + ], + ], + 'driver' => [ + 'label' => 'Típus', + 'options' => [ + 'ship-by' => 'Házhozszállítás', + 'collection' => 'Személyes átvétel', + ], + ], + 'stock_available' => [ + 'label' => 'A kosárban lévő összes terméknek raktáron kell lennie', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'code' => [ + 'label' => 'Kód', + ], + 'driver' => [ + 'label' => 'Típus', + 'options' => [ + 'ship-by' => 'Házhozszállítás', + 'collection' => 'Személyes átvétel', + ], + ], + ], + 'pages' => [ + 'availability' => [ + 'label' => 'Elérhetőség', + 'customer_groups' => 'Ez a szállítási mód jelenleg egyik vásárlói csoport számára sem elérhető.', + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/hu/shippingzone.php b/packages/table-rate-shipping/resources/lang/hu/shippingzone.php new file mode 100644 index 0000000000..beffed5ff0 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/hu/shippingzone.php @@ -0,0 +1,50 @@ + 'Szállítási zóna', + 'label_plural' => 'Szállítási zónák', + 'form' => [ + 'unrestricted' => [ + 'content' => 'Ehhez a szállítási zónához nincs korlátozás, ezért a pénztárnál minden vásárló számára elérhető lesz.', + ], + 'name' => [ + 'label' => 'Név', + ], + 'type' => [ + 'label' => 'Típus', + 'options' => [ + 'unrestricted' => 'Korlátozás nélkül', + 'countries' => 'Országokra korlátozva', + 'states' => 'Államokra / megyékre korlátozva', + 'postcodes' => 'Irányítószámokra korlátozva', + ], + ], + 'country' => [ + 'label' => 'Ország', + ], + 'states' => [ + 'label' => 'Megyék', + ], + 'countries' => [ + 'label' => 'Országok', + ], + 'postcodes' => [ + 'label' => 'Irányítószámok', + 'helper' => 'Minden irányítószámot új sorba írj. Támogatja a helyettesítő karaktereket, például: NW*', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Név', + ], + 'type' => [ + 'label' => 'Típus', + 'options' => [ + 'unrestricted' => 'Korlátozás nélkül', + 'countries' => 'Országokra korlátozva', + 'states' => 'Államokra / megyékre korlátozva', + 'postcodes' => 'Irányítószámokra korlátozva', + ], + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/pt_BR/plugin.php b/packages/table-rate-shipping/resources/lang/pt_BR/plugin.php new file mode 100644 index 0000000000..c0ca4022de --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/pt_BR/plugin.php @@ -0,0 +1,7 @@ + [ + 'group' => 'Envio', + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/pt_BR/relationmanagers.php b/packages/table-rate-shipping/resources/lang/pt_BR/relationmanagers.php new file mode 100644 index 0000000000..3a2e56f568 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/pt_BR/relationmanagers.php @@ -0,0 +1,80 @@ + [ + 'customer_groups' => [ + 'description' => 'Associe grupos de clientes a este método de envio para determinar sua disponibilidade.', + ], + ], + 'shipping_rates' => [ + 'title_plural' => 'Taxas de envio', + 'actions' => [ + 'create' => [ + 'label' => 'Criar taxa de envio', + ], + ], + 'notices' => [ + 'prices_incl_tax' => 'Todos os preços incluem imposto, o que será considerado ao calcular o gasto mínimo.', + 'prices_excl_tax' => 'Todos os preços excluem imposto; o gasto mínimo será baseado no subtotal do carrinho.', + ], + 'form' => [ + 'shipping_method_id' => [ + 'label' => 'Método de envio', + ], + 'price' => [ + 'label' => 'Preço', + ], + 'prices' => [ + 'label' => 'Faixas de preço', + 'repeater' => [ + 'customer_group_id' => [ + 'label' => 'Grupo de clientes', + 'placeholder' => 'Qualquer', + ], + 'currency_id' => [ + 'label' => 'Moeda', + ], + 'min_spend' => [ + 'label' => 'Gasto mín.', + ], + 'min_weight' => [ + 'label' => 'Peso mín.', + ], + 'price' => [ + 'label' => 'Preço', + ], + ], + ], + ], + 'table' => [ + 'shipping_method' => [ + 'label' => 'Método de envio', + ], + 'price' => [ + 'label' => 'Preço', + ], + 'price_breaks_count' => [ + 'label' => 'Faixas de preço', + ], + ], + ], + 'exclusions' => [ + 'title_plural' => 'Exclusões de envio', + 'form' => [ + 'purchasable' => [ + 'label' => 'Produto', + ], + ], + 'actions' => [ + 'create' => [ + 'label' => 'Adicionar lista de exclusões de envio', + ], + 'attach' => [ + 'label' => 'Adicionar lista de exclusões', + ], + 'detach' => [ + 'label' => 'Remover', + ], + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/pt_BR/shippingexclusionlist.php b/packages/table-rate-shipping/resources/lang/pt_BR/shippingexclusionlist.php new file mode 100644 index 0000000000..0429dc87b2 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/pt_BR/shippingexclusionlist.php @@ -0,0 +1,19 @@ + 'Lista de exclusões de envio', + 'label_plural' => 'Listas de exclusões de envio', + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'exclusions_count' => [ + 'label' => 'Qtd. de produtos', + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/pt_BR/shippingmethod.php b/packages/table-rate-shipping/resources/lang/pt_BR/shippingmethod.php new file mode 100644 index 0000000000..7b4b7cd19f --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/pt_BR/shippingmethod.php @@ -0,0 +1,58 @@ + 'Métodos de envio', + 'label' => 'Método de envio', + 'form' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'description' => [ + 'label' => 'Descrição', + ], + 'code' => [ + 'label' => 'Código', + ], + 'cutoff' => [ + 'label' => 'Horário limite', + ], + 'charge_by' => [ + 'label' => 'Cobrar por', + 'options' => [ + 'cart_total' => 'Total do carrinho', + 'weight' => 'Peso', + ], + ], + 'driver' => [ + 'label' => 'Tipo', + 'options' => [ + 'ship-by' => 'Padrão', + 'collection' => 'Coleta', + ], + ], + 'stock_available' => [ + 'label' => 'Estoque de todos os itens do carrinho deve estar disponível', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'code' => [ + 'label' => 'Código', + ], + 'driver' => [ + 'label' => 'Tipo', + 'options' => [ + 'ship-by' => 'Padrão', + 'collection' => 'Coleta', + ], + ], + ], + 'pages' => [ + 'availability' => [ + 'label' => 'Disponibilidade', + 'customer_groups' => 'Este método de envio está indisponível para todos os grupos de clientes.', + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/pt_BR/shippingzone.php b/packages/table-rate-shipping/resources/lang/pt_BR/shippingzone.php new file mode 100644 index 0000000000..81267c8598 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/pt_BR/shippingzone.php @@ -0,0 +1,50 @@ + 'Zona de envio', + 'label_plural' => 'Zonas de envio', + 'form' => [ + 'unrestricted' => [ + 'content' => 'Esta zona de envio não possui restrições e estará disponível para todos os clientes no checkout.', + ], + 'name' => [ + 'label' => 'Nome', + ], + 'type' => [ + 'label' => 'Tipo', + 'options' => [ + 'unrestricted' => 'Sem restrições', + 'countries' => 'Limitar a países', + 'states' => 'Limitar a estados / municípios', + 'postcodes' => 'Limitar a CEPs', + ], + ], + 'country' => [ + 'label' => 'País', + ], + 'states' => [ + 'label' => 'Estados', + ], + 'countries' => [ + 'label' => 'Estados', + ], + 'postcodes' => [ + 'label' => 'CEPs', + 'helper' => 'Liste cada CEP em uma nova linha. Suporta curingas como NW*', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nome', + ], + 'type' => [ + 'label' => 'Tipo', + 'options' => [ + 'unrestricted' => 'Sem restrições', + 'countries' => 'Limitar a países', + 'states' => 'Limitar a estados / municípios', + 'postcodes' => 'Limitar a CEPs', + ], + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/ro/plugin.php b/packages/table-rate-shipping/resources/lang/ro/plugin.php new file mode 100644 index 0000000000..4e0e298b96 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/ro/plugin.php @@ -0,0 +1,7 @@ + [ + 'group' => 'Livrare', + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/ro/relationmanagers.php b/packages/table-rate-shipping/resources/lang/ro/relationmanagers.php new file mode 100644 index 0000000000..d80c48d523 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/ro/relationmanagers.php @@ -0,0 +1,80 @@ + [ + 'customer_groups' => [ + 'description' => 'Asociați grupuri de clienți acestei metode de livrare pentru a-i stabili disponibilitatea.', + ], + ], + 'shipping_rates' => [ + 'title_plural' => 'Tarife de livrare', + 'actions' => [ + 'create' => [ + 'label' => 'Creează tarif de livrare', + ], + ], + 'notices' => [ + 'prices_incl_tax' => 'Toate prețurile includ taxa, care va fi luată în calcul la determinarea cheltuielii minime.', + 'prices_excl_tax' => 'Toate prețurile exclud taxa; cheltuiala minimă va fi calculată pe baza subtotalului coșului.', + ], + 'form' => [ + 'shipping_method_id' => [ + 'label' => 'Metodă de livrare', + ], + 'price' => [ + 'label' => 'Preț', + ], + 'prices' => [ + 'label' => 'Transe de preț', + 'repeater' => [ + 'customer_group_id' => [ + 'label' => 'Grup de clienți', + 'placeholder' => 'Oricare', + ], + 'currency_id' => [ + 'label' => 'Monedă', + ], + 'min_spend' => [ + 'label' => 'Cheltuială min.', + ], + 'min_weight' => [ + 'label' => 'Greutate min.', + ], + 'price' => [ + 'label' => 'Preț', + ], + ], + ], + ], + 'table' => [ + 'shipping_method' => [ + 'label' => 'Metodă de livrare', + ], + 'price' => [ + 'label' => 'Preț', + ], + 'price_breaks_count' => [ + 'label' => 'Transe de preț', + ], + ], + ], + 'exclusions' => [ + 'title_plural' => 'Excluderi de livrare', + 'form' => [ + 'purchasable' => [ + 'label' => 'Produs', + ], + ], + 'actions' => [ + 'create' => [ + 'label' => 'Adaugă listă de excluderi pentru livrare', + ], + 'attach' => [ + 'label' => 'Adaugă listă de excluderi', + ], + 'detach' => [ + 'label' => 'Elimină', + ], + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/ro/shippingexclusionlist.php b/packages/table-rate-shipping/resources/lang/ro/shippingexclusionlist.php new file mode 100644 index 0000000000..2b082e3c7f --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/ro/shippingexclusionlist.php @@ -0,0 +1,19 @@ + 'Listă de excluderi pentru livrare', + 'label_plural' => 'Liste de excluderi pentru livrare', + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'exclusions_count' => [ + 'label' => 'Nr. produse', + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/ro/shippingmethod.php b/packages/table-rate-shipping/resources/lang/ro/shippingmethod.php new file mode 100644 index 0000000000..be456ced2a --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/ro/shippingmethod.php @@ -0,0 +1,58 @@ + 'Metode de livrare', + 'label' => 'Metodă de livrare', + 'form' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'description' => [ + 'label' => 'Descriere', + ], + 'code' => [ + 'label' => 'Cod', + ], + 'cutoff' => [ + 'label' => 'Termen limită', + ], + 'charge_by' => [ + 'label' => 'Taxare după', + 'options' => [ + 'cart_total' => 'Total coș', + 'weight' => 'Greutate', + ], + ], + 'driver' => [ + 'label' => 'Tip', + 'options' => [ + 'ship-by' => 'Standard', + 'collection' => 'Ridicare', + ], + ], + 'stock_available' => [ + 'label' => 'Stocul tuturor articolelor din coș trebuie să fie disponibil', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'code' => [ + 'label' => 'Cod', + ], + 'driver' => [ + 'label' => 'Tip', + 'options' => [ + 'ship-by' => 'Standard', + 'collection' => 'Ridicare', + ], + ], + ], + 'pages' => [ + 'availability' => [ + 'label' => 'Disponibilitate', + 'customer_groups' => 'Această metodă de livrare este momentan indisponibilă pentru toate grupurile de clienți.', + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/ro/shippingzone.php b/packages/table-rate-shipping/resources/lang/ro/shippingzone.php new file mode 100644 index 0000000000..15adb01a7c --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/ro/shippingzone.php @@ -0,0 +1,50 @@ + 'Zonă de livrare', + 'label_plural' => 'Zone de livrare', + 'form' => [ + 'unrestricted' => [ + 'content' => 'Această zonă de livrare nu are restricții și va fi disponibilă pentru toți clienții la finalizarea comenzii.', + ], + 'name' => [ + 'label' => 'Nume', + ], + 'type' => [ + 'label' => 'Tip', + 'options' => [ + 'unrestricted' => 'Fără restricții', + 'countries' => 'Limitat la țări', + 'states' => 'Limitat la state / provincii', + 'postcodes' => 'Limitat la coduri poștale', + ], + ], + 'country' => [ + 'label' => 'Țară', + ], + 'states' => [ + 'label' => 'Județe', + ], + 'countries' => [ + 'label' => 'Țări', + ], + 'postcodes' => [ + 'label' => 'Coduri poștale', + 'helper' => 'Listați fiecare cod poștal pe o linie separată. Suportă wildcard-uri precum NW*', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Nume', + ], + 'type' => [ + 'label' => 'Tip', + 'options' => [ + 'unrestricted' => 'Fără restricții', + 'countries' => 'Limitat la țări', + 'states' => 'Limitat la state / provincii', + 'postcodes' => 'Limitat la coduri poștale', + ], + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/tr/plugin.php b/packages/table-rate-shipping/resources/lang/tr/plugin.php new file mode 100644 index 0000000000..ab9a765aaa --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/tr/plugin.php @@ -0,0 +1,7 @@ + [ + 'group' => 'Kargo', + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/tr/relationmanagers.php b/packages/table-rate-shipping/resources/lang/tr/relationmanagers.php new file mode 100644 index 0000000000..2c483f4bfc --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/tr/relationmanagers.php @@ -0,0 +1,80 @@ + [ + 'customer_groups' => [ + 'description' => 'Müşteri gruplarını bu kargo yöntemiyle ilişkilendirerek kullanılabilirliğini belirleyin.', + ], + ], + 'shipping_rates' => [ + 'title_plural' => 'Kargo Tarifeleri', + 'actions' => [ + 'create' => [ + 'label' => 'Kargo Tarifesi Oluştur', + ], + ], + 'notices' => [ + 'prices_incl_tax' => 'Tüm fiyatlar vergi dahildir, minimum harcama hesaplanırken dikkate alınacaktır.', + 'prices_excl_tax' => 'Tüm fiyatlar vergi hariçtir, minimum harcama sepet ara toplamına göre olacaktır.', + ], + 'form' => [ + 'shipping_method_id' => [ + 'label' => 'Kargo Yöntemi', + ], + 'price' => [ + 'label' => 'Fiyat', + ], + 'prices' => [ + 'label' => 'Fiyat Aralıkları', + 'repeater' => [ + 'customer_group_id' => [ + 'label' => 'Müşteri Grubu', + 'placeholder' => 'Herhangi', + ], + 'currency_id' => [ + 'label' => 'Para Birimi', + ], + 'min_spend' => [ + 'label' => 'Min. Harcama', + ], + 'min_weight' => [ + 'label' => 'Min. Ağırlık', + ], + 'price' => [ + 'label' => 'Fiyat', + ], + ], + ], + ], + 'table' => [ + 'shipping_method' => [ + 'label' => 'Kargo Yöntemi', + ], + 'price' => [ + 'label' => 'Fiyat', + ], + 'price_breaks_count' => [ + 'label' => 'Fiyat Aralıkları', + ], + ], + ], + 'exclusions' => [ + 'title_plural' => 'Kargo İstisnaları', + 'form' => [ + 'purchasable' => [ + 'label' => 'Ürün', + ], + ], + 'actions' => [ + 'create' => [ + 'label' => 'Kargo istisna listesi ekle', + ], + 'attach' => [ + 'label' => 'İstisna listesi ekle', + ], + 'detach' => [ + 'label' => 'Kaldır', + ], + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/tr/shippingexclusionlist.php b/packages/table-rate-shipping/resources/lang/tr/shippingexclusionlist.php new file mode 100644 index 0000000000..e61cad7d3a --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/tr/shippingexclusionlist.php @@ -0,0 +1,19 @@ + 'Kargo İstisna Listesi', + 'label_plural' => 'Kargo İstisna Listeleri', + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'exclusions_count' => [ + 'label' => 'Ürün Sayısı', + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/tr/shippingmethod.php b/packages/table-rate-shipping/resources/lang/tr/shippingmethod.php new file mode 100644 index 0000000000..0c8e902ab8 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/tr/shippingmethod.php @@ -0,0 +1,58 @@ + 'Kargo Yöntemleri', + 'label' => 'Kargo Yöntemi', + 'form' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'description' => [ + 'label' => 'Açıklama', + ], + 'code' => [ + 'label' => 'Kod', + ], + 'cutoff' => [ + 'label' => 'Son Sipariş Saati', + ], + 'charge_by' => [ + 'label' => 'Ücretlendirme Ölçütü', + 'options' => [ + 'cart_total' => 'Sepet Toplamı', + 'weight' => 'Ağırlık', + ], + ], + 'driver' => [ + 'label' => 'Tür', + 'options' => [ + 'ship-by' => 'Standart', + 'collection' => 'Mağazadan Teslim Alma', + ], + ], + 'stock_available' => [ + 'label' => 'Tüm sepet öğelerinin stoku mevcut olmalı', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'code' => [ + 'label' => 'Kod', + ], + 'driver' => [ + 'label' => 'Tür', + 'options' => [ + 'ship-by' => 'Standart', + 'collection' => 'Mağazadan Teslim Alma', + ], + ], + ], + 'pages' => [ + 'availability' => [ + 'label' => 'Kullanılabilirlik', + 'customer_groups' => 'Bu kargo yöntemi şu anda tüm müşteri grupları için mevcut değil.', + ], + ], +]; diff --git a/packages/table-rate-shipping/resources/lang/tr/shippingzone.php b/packages/table-rate-shipping/resources/lang/tr/shippingzone.php new file mode 100644 index 0000000000..307365d765 --- /dev/null +++ b/packages/table-rate-shipping/resources/lang/tr/shippingzone.php @@ -0,0 +1,50 @@ + 'Kargo Bölgesi', + 'label_plural' => 'Kargo Bölgeleri', + 'form' => [ + 'unrestricted' => [ + 'content' => 'Bu kargo bölgesinin herhangi bir kısıtlaması yoktur ve ödeme sırasında tüm müşteriler için mevcut olacaktır.', + ], + 'name' => [ + 'label' => 'Ad', + ], + 'type' => [ + 'label' => 'Tür', + 'options' => [ + 'unrestricted' => 'Kısıtsız', + 'countries' => 'Ülkelerle Sınırla', + 'states' => 'Eyaletler / İller ile Sınırla', + 'postcodes' => 'Posta Kodlarıyla Sınırla', + ], + ], + 'country' => [ + 'label' => 'Ülke', + ], + 'states' => [ + 'label' => 'Eyaletler', + ], + 'countries' => [ + 'label' => 'Eyaletler', + ], + 'postcodes' => [ + 'label' => 'Posta Kodları', + 'helper' => 'Her posta kodunu yeni bir satırda listeleyin. NW* gibi joker karakterleri destekler', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Ad', + ], + 'type' => [ + 'label' => 'Tür', + 'options' => [ + 'unrestricted' => 'Kısıtsız', + 'countries' => 'Ülkelerle Sınırla', + 'states' => 'Eyaletler / İller ile Sınırla', + 'postcodes' => 'Posta Kodlarıyla Sınırla', + ], + ], + ], +]; diff --git a/packages/table-rate-shipping/src/Filament/Resources/ShippingMethodResource/Pages/EditShippingMethod.php b/packages/table-rate-shipping/src/Filament/Resources/ShippingMethodResource/Pages/EditShippingMethod.php index 7279be485a..957497254a 100644 --- a/packages/table-rate-shipping/src/Filament/Resources/ShippingMethodResource/Pages/EditShippingMethod.php +++ b/packages/table-rate-shipping/src/Filament/Resources/ShippingMethodResource/Pages/EditShippingMethod.php @@ -17,6 +17,13 @@ protected function getDefaultHeaderActions(): array ]; } + public static function getNavigationLabel(): string + { + return __('filament-panels::resources/pages/edit-record.title', [ + 'label' => __('lunarpanel.shipping::shippingmethod.label'), + ]); + } + protected function getRedirectUrl(): string { return $this->getResource()::getUrl('index'); diff --git a/packages/table-rate-shipping/src/Filament/Resources/ShippingZoneResource/Pages/EditShippingZone.php b/packages/table-rate-shipping/src/Filament/Resources/ShippingZoneResource/Pages/EditShippingZone.php index 754106f684..fd4075aebe 100644 --- a/packages/table-rate-shipping/src/Filament/Resources/ShippingZoneResource/Pages/EditShippingZone.php +++ b/packages/table-rate-shipping/src/Filament/Resources/ShippingZoneResource/Pages/EditShippingZone.php @@ -17,6 +17,13 @@ protected function getDefaultHeaderActions(): array ]; } + public static function getNavigationLabel(): string + { + return __('filament-panels::resources/pages/edit-record.title', [ + 'label' => __('lunarpanel.shipping::shippingzone.label'), + ]); + } + protected function getRedirectUrl(): string { return $this->getResource()::getUrl('index'); diff --git a/packages/table-rate-shipping/src/Filament/Resources/ShippingZoneResource/Pages/ManageShippingRates.php b/packages/table-rate-shipping/src/Filament/Resources/ShippingZoneResource/Pages/ManageShippingRates.php index 93e9b1a0d0..41e7fb8438 100644 --- a/packages/table-rate-shipping/src/Filament/Resources/ShippingZoneResource/Pages/ManageShippingRates.php +++ b/packages/table-rate-shipping/src/Filament/Resources/ShippingZoneResource/Pages/ManageShippingRates.php @@ -2,6 +2,8 @@ namespace Lunar\Shipping\Filament\Resources\ShippingZoneResource\Pages; +use Awcodes\FilamentBadgeableColumn\Components\Badge; +use Awcodes\FilamentBadgeableColumn\Components\BadgeableColumn; use Awcodes\Shout\Components\Shout; use Filament\Forms; use Filament\Forms\Form; @@ -147,10 +149,15 @@ static function (Forms\Components\Repeater $component, ?Model $record = null): v public function table(Table $table): Table { return $table->columns([ - TextColumn::make('shippingMethod.name') - ->label( - __('lunarpanel.shipping::relationmanagers.shipping_rates.table.shipping_method.label') - ), + BadgeableColumn::make('shippingMethod.name') + ->separator('') + ->suffixBadges([ + Badge::make('default') + ->label(__('lunarpanel.shipping::relationmanagers.shipping_rates.table.shipping_method.disabled')) + ->color('warning') + ->visible(fn (Model $record) => ! $record->enabled), + ]) + ->label(__('lunarpanel.shipping::relationmanagers.shipping_rates.table.shipping_method.label')), TextColumn::make('basePrices.0')->formatStateUsing( fn ($state = null) => $state->price->formatted )->label( @@ -178,6 +185,21 @@ public function table(Table $table): Table static::saveShippingRate($shippingRate, $data); }), Tables\Actions\DeleteAction::make()->requiresConfirmation(), + Tables\Actions\Action::make('disable')->color('warning')->action(function (ShippingRate $shippingRate) { + $shippingRate->updateQuietly([ + 'enabled' => false, + ]); + })->hidden( + fn (ShippingRate $shippingRate) => ! $shippingRate->enabled + ), + Tables\Actions\Action::make('enable')->color('success')->action(function (ShippingRate $shippingRate) { + $shippingRate->updateQuietly([ + 'enabled' => true, + ]); + })->hidden( + fn (ShippingRate $shippingRate) => (bool) $shippingRate->enabled + ), + ]); } diff --git a/packages/table-rate-shipping/src/Resolvers/ShippingRateResolver.php b/packages/table-rate-shipping/src/Resolvers/ShippingRateResolver.php index 51f6bb5b67..af8cd3bc10 100644 --- a/packages/table-rate-shipping/src/Resolvers/ShippingRateResolver.php +++ b/packages/table-rate-shipping/src/Resolvers/ShippingRateResolver.php @@ -151,6 +151,9 @@ public function get(): Collection foreach ($zones as $zone) { $zoneShippingRates = $zone->rates + ->reject(function ($state) { + return ! $state->enabled; + }) ->reject(function ($rate) { $method = $rate->shippingMethod()->customerGroup($this->customerGroups)->first(); diff --git a/tests/core/Stubs/Models/Custom/DeepCustomProduct.php b/tests/core/Stubs/Models/Custom/DeepCustomProduct.php new file mode 100644 index 0000000000..d312ebe201 --- /dev/null +++ b/tests/core/Stubs/Models/Custom/DeepCustomProduct.php @@ -0,0 +1,11 @@ +option; } + public function getOptions(): Collection + { + return collect([ + $this->option, + ]); + } + /** * Return a unique string which identifies the purchasable item. * diff --git a/tests/core/Unit/Base/Traits/HasModelExtendingTest.php b/tests/core/Unit/Base/Traits/HasModelExtendingTest.php index 8636a2f615..d927fa3be3 100644 --- a/tests/core/Unit/Base/Traits/HasModelExtendingTest.php +++ b/tests/core/Unit/Base/Traits/HasModelExtendingTest.php @@ -93,3 +93,22 @@ function () { 'eloquent.deleted: '.Product::class ); }); + +test('multi-level extended model returns correct table name without prefix duplication', function () { + $lunarProduct = new \Lunar\Models\Product; + $customProduct = new \Lunar\Tests\Core\Stubs\Models\Custom\CustomProduct; + $deepCustomProduct = new \Lunar\Tests\Core\Stubs\Models\Custom\DeepCustomProduct; + + // All three models should return the same table name + expect($customProduct->getTable()) + ->toBe($lunarProduct->getTable()) + ->and($deepCustomProduct->getTable()) + ->toBe($lunarProduct->getTable()); + + // Verify the table name is correctly prefixed (not duplicated) + $expectedTable = config('lunar.database.table_prefix').'products'; + expect($deepCustomProduct->getTable())->toBe($expectedTable); + + // Ensure prefix is not duplicated + expect($deepCustomProduct->getTable())->not->toContain(config('lunar.database.table_prefix').config('lunar.database.table_prefix')); +}); diff --git a/tests/core/Unit/DiscountTypes/AmountOffTest.php b/tests/core/Unit/DiscountTypes/AmountOffTest.php index 6f7a032f10..5b2adfb82b 100644 --- a/tests/core/Unit/DiscountTypes/AmountOffTest.php +++ b/tests/core/Unit/DiscountTypes/AmountOffTest.php @@ -963,6 +963,7 @@ $cart = Cart::factory()->create([ 'currency_id' => $currency->id, 'channel_id' => $channel->id, + 'coupon_code' => 'NOTAPPLICABLE', ]); $purchasableA = ProductVariant::factory()->create(); diff --git a/tests/core/Unit/DiscountTypes/BuyXGetYTest.php b/tests/core/Unit/DiscountTypes/BuyXGetYTest.php index 615ab7d30b..ade9a1c865 100644 --- a/tests/core/Unit/DiscountTypes/BuyXGetYTest.php +++ b/tests/core/Unit/DiscountTypes/BuyXGetYTest.php @@ -1242,3 +1242,115 @@ $this->assertCount(1, $cart->freeItems); }); + +test('discounted sub total will not fall below zero', function () { + $customerGroup = CustomerGroup::factory()->create([ + 'default' => true, + ]); + + $channel = Channel::factory()->create([ + 'default' => true, + ]); + + $currency = Currency::factory()->create([ + 'code' => 'GBP', + ]); + + /** + * Product set up. + */ + $productA = Product::factory()->create(); + $productB = Product::factory()->create(); + + $purchasableA = ProductVariant::factory()->create([ + 'product_id' => $productA->id, + ]); + $purchasableB = ProductVariant::factory()->create([ + 'product_id' => $productB->id, + ]); + + Price::factory()->create([ + 'price' => 1000, // £10 + 'min_quantity' => 1, + 'currency_id' => $currency->id, + 'priceable_type' => $purchasableA->getMorphClass(), + 'priceable_id' => $purchasableA->id, + ]); + + Price::factory()->create([ + 'price' => 500, // £5 + 'min_quantity' => 1, + 'currency_id' => $currency->id, + 'priceable_type' => $purchasableB->getMorphClass(), + 'priceable_id' => $purchasableB->id, + ]); + + /** + * Cart set up. + */ + $cart = Cart::factory()->create([ + 'channel_id' => $channel->id, + 'currency_id' => $currency->id, + ]); + + $cart->lines()->create([ + 'purchasable_type' => $purchasableA->getMorphClass(), + 'purchasable_id' => $purchasableA->id, + 'quantity' => 20, + ]); + + $cart->lines()->create([ + 'purchasable_type' => $purchasableB->getMorphClass(), + 'purchasable_id' => $purchasableB->id, + 'quantity' => 1, + ]); + + /** + * Discount set up. + */ + $discountA = Discount::factory()->create([ + 'type' => BuyXGetY::class, + 'priority' => 1, + 'name' => 'Test Product Discount', + 'data' => [ + 'min_qty' => 10, + 'reward_qty' => 1, + 'max_reward_qty' => null, + 'automatically_add_rewards' => true, + ], + ]); + + foreach ([$discountA] as $discount) { + $discount->customerGroups()->sync([ + $customerGroup->id => [ + 'enabled' => true, + 'starts_at' => now(), + ], + ]); + $discount->channels()->sync([ + $channel->id => [ + 'enabled' => true, + 'starts_at' => now()->subHour(), + ], + ]); + } + + $discountA->discountableConditions()->create([ + 'discountable_type' => $productA->getMorphClass(), + 'discountable_id' => $productA->id, + ]); + + $discountA->discountableRewards()->create([ + 'discountable_type' => $productB->getMorphClass(), + 'discountable_id' => $productB->id, + 'type' => 'reward', + ]); + + $cart = $cart->calculate(); + + $lineB = $cart->lines->first(function ($line) use ($purchasableB) { + return $line->purchasable_id == $purchasableB->id; + }); + + expect($lineB->subTotalDiscounted->value)->toEqual(0); +}); diff --git a/tests/core/Unit/Listeners/CartSessionAuthListenerTest.php b/tests/core/Unit/Listeners/CartSessionAuthListenerTest.php new file mode 100644 index 0000000000..4f9ce6fc07 --- /dev/null +++ b/tests/core/Unit/Listeners/CartSessionAuthListenerTest.php @@ -0,0 +1,72 @@ +group('cart_session'); +uses(\Illuminate\Foundation\Testing\RefreshDatabase::class); + +test('cart is soft deleted on logout when delete_on_logout is true', function () { + // Ensure required defaults exist + Currency::factory()->create(['default' => true]); + Channel::factory()->create(['default' => true]); + + // Create a session cart + Config::set('lunar.cart_session.auto_create', true); + + $cart = CartSession::current(); + + // Authenticate a Lunar user so the Logout listener will act on it + $user = \Lunar\Tests\Core\Stubs\User::factory()->create(); + actingAs($user); + + // Sanity checks + expect($cart)->toBeInstanceOf(Cart::class); + expect(Session::get(config('lunar.cart_session.session_key')))->toEqual($cart->id); + + // Config dictates cart should be soft deleted on logout + Config::set('lunar.cart_session.delete_on_forget', true); + + // Fire the logout event (this triggers CartSessionAuthListener@logout) + event(new Logout('web', $user)); + + // Session cart should be cleared and the cart soft deleted + expect(Session::get(config('lunar.cart_session.session_key')))->toBeNull(); + expect($cart->refresh()->deleted_at)->not->toBeNull(); +}); + +test('cart is not soft deleted on logout when delete_on_logout is false', function () { + // Ensure required defaults exist + Currency::factory()->create(['default' => true]); + Channel::factory()->create(['default' => true]); + + // Create a session cart + Config::set('lunar.cart_session.auto_create', true); + + $cart = CartSession::current(); + + // Authenticate a Lunar user so the Logout listener will act on it + $user = \Lunar\Tests\Core\Stubs\User::factory()->create(); + actingAs($user); + + // Sanity checks + expect($cart)->toBeInstanceOf(Cart::class); + expect(Session::get(config('lunar.cart_session.session_key')))->toEqual($cart->id); + + // Config dictates cart should NOT be soft deleted on logout + Config::set('lunar.cart_session.delete_on_forget', false); + + // Fire the logout event (this triggers CartSessionAuthListener@logout) + event(new Logout('web', $user)); + + // Session cart should be cleared but the cart should remain not-deleted + expect(Session::get(config('lunar.cart_session.session_key')))->toBeNull(); + expect($cart->refresh()->deleted_at)->toBeNull(); +}); diff --git a/tests/core/Unit/Models/DiscountTest.php b/tests/core/Unit/Models/DiscountTest.php index 14e1f2db67..44a0625fab 100644 --- a/tests/core/Unit/Models/DiscountTest.php +++ b/tests/core/Unit/Models/DiscountTest.php @@ -1,7 +1,12 @@ $discount->id == $discountC->id ))->toBeNull(); }); + +test('can apply collections scope', function () { + $collectionA = Collection::factory()->create(); + $collectionB = Collection::factory()->create(); + $collectionC = Collection::factory()->create(); + + // Discount with collection relationships using pivot table + $discountWithCollectionA = Discount::factory()->create(); + $discountWithCollectionA->collections()->attach($collectionA->id, ['type' => 'limitation']); + + // Discount with multiple collections and different types + $discountWithMultipleCollections = Discount::factory()->create(); + $discountWithMultipleCollections->collections()->attach($collectionA->id, ['type' => 'condition']); + $discountWithMultipleCollections->collections()->attach($collectionB->id, ['type' => 'limitation']); + + // Discount with collection of different type + $discountWithCollectionBReward = Discount::factory()->create(); + $discountWithCollectionBReward->collections()->attach($collectionB->id, ['type' => 'reward']); + + // Discount with no collections + $discountWithoutCollections = Discount::factory()->create(); + + // Test with specific collection IDs - should return discounts that either have NO collections OR have matching collections + $discounts = Discount::query()->collections([$collectionA->id])->get(); + expect($discounts)->toHaveCount(3); + expect($discounts->contains($discountWithCollectionA))->toBeTrue(); // Has matching collection + expect($discounts->contains($discountWithMultipleCollections))->toBeTrue(); // Has matching collection + expect($discounts->contains($discountWithoutCollections))->toBeTrue(); // No collections at all + expect($discounts->contains($discountWithCollectionBReward))->toBeFalse(); // Has collections but not matching + + // Test with different collection ID + $discounts = Discount::query()->collections([$collectionC->id])->get(); + expect($discounts)->toHaveCount(1); + expect($discounts->contains($discountWithoutCollections))->toBeTrue(); + expect($discounts->contains($discountWithCollectionA))->toBeFalse(); + expect($discounts->contains($discountWithMultipleCollections))->toBeFalse(); + expect($discounts->contains($discountWithCollectionBReward))->toBeFalse(); + + // Test with empty array (should return all discounts without any collection restrictions) + $discounts = Discount::query()->collections([])->get(); + expect($discounts)->toHaveCount(1); + expect($discounts->contains($discountWithoutCollections))->toBeTrue(); + expect($discounts->contains($discountWithCollectionA))->toBeFalse(); + expect($discounts->contains($discountWithMultipleCollections))->toBeFalse(); + expect($discounts->contains($discountWithCollectionBReward))->toBeFalse(); + + // Test with types filter - limitation type + $discounts = Discount::query()->collections([$collectionA->id], ['limitation'])->get(); + expect($discounts)->toHaveCount(3); + expect($discounts->contains($discountWithCollectionA))->toBeTrue(); // Has limitation type for collectionA + expect($discounts->contains($discountWithMultipleCollections))->toBeFalse(); // Has limitation type collections but not for collectionA + expect($discounts->contains($discountWithoutCollections))->toBeTrue(); // No limitation type collections + expect($discounts->contains($discountWithCollectionBReward))->toBeTrue(); // Doesn't have limitation type collections + + // Test with types filter - reward type + $discounts = Discount::query()->collections([$collectionB->id], ['reward'])->get(); + expect($discounts)->toHaveCount(4); + expect($discounts->contains($discountWithCollectionBReward))->toBeTrue(); // Has reward type for collectionB + expect($discounts->contains($discountWithoutCollections))->toBeTrue(); // No reward type collections + expect($discounts->contains($discountWithCollectionA))->toBeTrue(); // Doesn't have reward type collections + expect($discounts->contains($discountWithMultipleCollections))->toBeTrue(); // Doesn't have reward type collections + + // Test with multiple types + $discounts = Discount::query()->collections([$collectionA->id], ['limitation', 'condition'])->get(); + expect($discounts)->toHaveCount(4); + expect($discounts->contains($discountWithCollectionA))->toBeTrue(); // Has limitation type for collectionA + expect($discounts->contains($discountWithMultipleCollections))->toBeTrue(); // Has condition type for collectionA + expect($discounts->contains($discountWithoutCollections))->toBeTrue(); // No limitation/condition type collections + expect($discounts->contains($discountWithCollectionBReward))->toBeTrue(); // Doesn't have limitation/condition type collections + + // Test with string type instead of array + $discounts = Discount::query()->collections([$collectionA->id], 'limitation')->get(); + expect($discounts)->toHaveCount(3); + expect($discounts->contains($discountWithCollectionA))->toBeTrue(); // Has limitation type for collectionA + expect($discounts->contains($discountWithMultipleCollections))->toBeFalse(); // Has limitation type collections but not for collectionA + expect($discounts->contains($discountWithoutCollections))->toBeTrue(); // No limitation type collections + expect($discounts->contains($discountWithCollectionBReward))->toBeTrue(); // Doesn't have limitation type collections +}); + +test('can apply brands scope', function () { + $brandA = Brand::factory()->create(); + $brandB = Brand::factory()->create(); + $brandC = Brand::factory()->create(); + + // Discount with brand relationships using pivot table + $discountWithBrandA = Discount::factory()->create(); + $discountWithBrandA->brands()->attach($brandA->id, ['type' => 'limitation']); + + // Discount with multiple brands and different types + $discountWithMultipleBrands = Discount::factory()->create(); + $discountWithMultipleBrands->brands()->attach($brandA->id, ['type' => 'condition']); + $discountWithMultipleBrands->brands()->attach($brandB->id, ['type' => 'limitation']); + + // Discount with brand of different type + $discountWithBrandBReward = Discount::factory()->create(); + $discountWithBrandBReward->brands()->attach($brandB->id, ['type' => 'reward']); + + // Discount with no brands + $discountWithoutBrands = Discount::factory()->create(); + + // Test with specific brand IDs - should return discounts that either have NO brands OR have matching brands + $discounts = Discount::query()->brands([$brandA->id])->get(); + expect($discounts)->toHaveCount(3); + expect($discounts->contains($discountWithBrandA))->toBeTrue(); // Has matching brand + expect($discounts->contains($discountWithMultipleBrands))->toBeTrue(); // Has matching brand + expect($discounts->contains($discountWithoutBrands))->toBeTrue(); // No brands at all + expect($discounts->contains($discountWithBrandBReward))->toBeFalse(); // Has brands but not matching + + // Test with different brand ID + $discounts = Discount::query()->brands([$brandC->id])->get(); + expect($discounts)->toHaveCount(1); + expect($discounts->contains($discountWithoutBrands))->toBeTrue(); + expect($discounts->contains($discountWithBrandA))->toBeFalse(); + expect($discounts->contains($discountWithMultipleBrands))->toBeFalse(); + expect($discounts->contains($discountWithBrandBReward))->toBeFalse(); + + // Test with empty array (should return all discounts without any brand restrictions) + $discounts = Discount::query()->brands([])->get(); + expect($discounts)->toHaveCount(1); + expect($discounts->contains($discountWithoutBrands))->toBeTrue(); + expect($discounts->contains($discountWithBrandA))->toBeFalse(); + expect($discounts->contains($discountWithMultipleBrands))->toBeFalse(); + expect($discounts->contains($discountWithBrandBReward))->toBeFalse(); + + // Test with types filter - limitation type + $discounts = Discount::query()->brands([$brandA->id], ['limitation'])->get(); + expect($discounts)->toHaveCount(3); + expect($discounts->contains($discountWithBrandA))->toBeTrue(); // Has limitation type for brandA + expect($discounts->contains($discountWithMultipleBrands))->toBeFalse(); // Has limitation type brands but not for brandA + expect($discounts->contains($discountWithoutBrands))->toBeTrue(); // No limitation type brands + expect($discounts->contains($discountWithBrandBReward))->toBeTrue(); // Doesn't have limitation type brands + + // Test with types filter - reward type + $discounts = Discount::query()->brands([$brandB->id], ['reward'])->get(); + expect($discounts)->toHaveCount(4); + expect($discounts->contains($discountWithBrandBReward))->toBeTrue(); // Has reward type for brandB + expect($discounts->contains($discountWithoutBrands))->toBeTrue(); // No reward type brands + expect($discounts->contains($discountWithBrandA))->toBeTrue(); // Doesn't have reward type brands + expect($discounts->contains($discountWithMultipleBrands))->toBeTrue(); // Doesn't have reward type brands + + // Test with multiple types + $discounts = Discount::query()->brands([$brandA->id], ['limitation', 'condition'])->get(); + expect($discounts)->toHaveCount(4); + expect($discounts->contains($discountWithBrandA))->toBeTrue(); // Has limitation type for brandA + expect($discounts->contains($discountWithMultipleBrands))->toBeTrue(); // Has condition type for brandA + expect($discounts->contains($discountWithoutBrands))->toBeTrue(); // No limitation/condition type brands + expect($discounts->contains($discountWithBrandBReward))->toBeTrue(); // Doesn't have limitation/condition type brands + + // Test with string type instead of array + $discounts = Discount::query()->brands([$brandA->id], 'limitation')->get(); + expect($discounts)->toHaveCount(3); + expect($discounts->contains($discountWithBrandA))->toBeTrue(); // Has limitation type for brandA + expect($discounts->contains($discountWithMultipleBrands))->toBeFalse(); // Has limitation type brands but not for brandA + expect($discounts->contains($discountWithoutBrands))->toBeTrue(); // No limitation type brands + expect($discounts->contains($discountWithBrandBReward))->toBeTrue(); // Doesn't have limitation type brands +}); + +test('can apply products scope', function () { + $productA = Product::factory()->create(); + $productB = Product::factory()->create(); + $collection = Collection::factory()->create(); + + // Discount with product discountables + $discountWithProducts = Discount::factory()->create(); + $discountWithProducts->discountables()->create([ + 'discountable_type' => Product::morphName(), + 'discountable_id' => $productA->id, + 'type' => 'limitation', + ]); + + // Discount with collection discountables (different type) + $discountWithCollections = Discount::factory()->create(); + $discountWithCollections->discountables()->create([ + 'discountable_type' => Collection::morphName(), + 'discountable_id' => $collection->id, + 'type' => 'limitation', + ]); + + // Discount with no discountables + $discountWithoutDiscountables = Discount::factory()->create(); + + // Test with specific product IDs + $discounts = Discount::query()->products([$productA->id])->get(); + expect($discounts)->toHaveCount(3); + expect($discounts->contains($discountWithProducts))->toBeTrue(); // Matches product + expect($discounts->contains($discountWithCollections))->toBeTrue(); // No product restrictions + expect($discounts->contains($discountWithoutDiscountables))->toBeTrue(); // No product restrictions + + // Test with different product ID + $discounts = Discount::query()->products([$productB->id])->get(); + expect($discounts)->toHaveCount(2); + expect($discounts->contains($discountWithCollections))->toBeTrue(); + expect($discounts->contains($discountWithoutDiscountables))->toBeTrue(); + expect($discounts->contains($discountWithProducts))->toBeFalse(); // Doesn't match product + + // Test with empty array + $discounts = Discount::query()->products([])->get(); + expect($discounts)->toHaveCount(2); + expect($discounts->contains($discountWithoutDiscountables))->toBeTrue(); + expect($discounts->contains($discountWithCollections))->toBeTrue(); + expect($discounts->contains($discountWithProducts))->toBeFalse(); +}); + +test('can apply product variants scope', function () { + $product = Product::factory()->create(); + $variantA = ProductVariant::factory()->create(['product_id' => $product->id]); + $variantB = ProductVariant::factory()->create(['product_id' => $product->id]); + $collection = Collection::factory()->create(); + + // Discount with variant discountables + $discountWithVariants = Discount::factory()->create(); + $discountWithVariants->discountables()->create([ + 'discountable_type' => ProductVariant::morphName(), + 'discountable_id' => $variantA->id, + 'type' => 'limitation', + ]); + + // Discount with collection discountables (different type) + $discountWithCollections = Discount::factory()->create(); + $discountWithCollections->discountables()->create([ + 'discountable_type' => Collection::morphName(), + 'discountable_id' => $collection->id, + 'type' => 'limitation', + ]); + + // Discount with no discountables + $discountWithoutDiscountables = Discount::factory()->create(); + + // Test with specific variant IDs + $discounts = Discount::query()->productVariants([$variantA->id])->get(); + expect($discounts)->toHaveCount(3); + expect($discounts->contains($discountWithVariants))->toBeTrue(); // Matches variant + expect($discounts->contains($discountWithCollections))->toBeTrue(); // No variant restrictions + expect($discounts->contains($discountWithoutDiscountables))->toBeTrue(); // No variant restrictions + + // Test with different variant ID + $discounts = Discount::query()->productVariants([$variantB->id])->get(); + expect($discounts)->toHaveCount(2); + expect($discounts->contains($discountWithCollections))->toBeTrue(); + expect($discounts->contains($discountWithoutDiscountables))->toBeTrue(); + expect($discounts->contains($discountWithVariants))->toBeFalse(); // Doesn't match variant + + // Test with empty array + $discounts = Discount::query()->productVariants([])->get(); + expect($discounts)->toHaveCount(2); + expect($discounts->contains($discountWithoutDiscountables))->toBeTrue(); + expect($discounts->contains($discountWithCollections))->toBeTrue(); + expect($discounts->contains($discountWithVariants))->toBeFalse(); +}); diff --git a/tests/core/Unit/Models/ProductTest.php b/tests/core/Unit/Models/ProductTest.php index 6cc4c1f514..62eaf3ce7d 100644 --- a/tests/core/Unit/Models/ProductTest.php +++ b/tests/core/Unit/Models/ProductTest.php @@ -407,9 +407,9 @@ $targetA = Product::factory()->create(); $targetB = Product::factory()->create(); - $parent->associate([$targetA, $targetB], ProductAssociation::UP_SELL); + $parent->associate([$targetA, $targetB], \Lunar\Base\Enums\ProductAssociation::UP_SELL); - $assoc = $parent->associations()->type(ProductAssociation::UP_SELL)->get(); + $assoc = $parent->associations()->type(\Lunar\Base\Enums\ProductAssociation::UP_SELL)->get(); expect($assoc)->toHaveCount(2); }); @@ -418,12 +418,12 @@ $parent = Product::factory()->create(); $target = Product::factory()->create(); - $parent->associate($target, 'custom-type'); + $parent->associate($target, \Lunar\Base\Enums\ProductAssociation::UP_SELL); - $assoc = $parent->associations()->type('custom-type')->get(); + $assoc = $parent->associations()->type(\Lunar\Base\Enums\ProductAssociation::UP_SELL)->get(); - expect($assoc)->toHaveCount(1); - expect($assoc->first()->type)->toEqual('custom-type'); + expect($assoc)->toHaveCount(1) + ->and($assoc->first()->type)->toEqual(\Lunar\Base\Enums\ProductAssociation::UP_SELL->value); }); test('can remove all associations', function () { @@ -450,21 +450,23 @@ ProductAssociation::factory()->create([ 'product_parent_id' => $parent, 'product_target_id' => $target, - 'type' => 'cross-sell', + 'type' => \Lunar\Base\Enums\ProductAssociation::CROSS_SELL->value, ]); ProductAssociation::factory()->create([ 'product_parent_id' => $parent, 'product_target_id' => $target, - 'type' => 'up-sell', + 'type' => \Lunar\Base\Enums\ProductAssociation::UP_SELL->value, ]); expect($parent->refresh()->associations)->toHaveCount(2); - $parent->dissociate($target, 'cross-sell'); + $parent->dissociate($target, \Lunar\Base\Enums\ProductAssociation::CROSS_SELL); - expect($parent->refresh()->associations)->toHaveCount(1); - expect($parent->refresh()->associations->first()->type)->toEqual('up-sell'); + expect($parent->refresh()->associations)->toHaveCount(1) + ->and($parent->refresh()->associations->first()->type)->toEqual( + \Lunar\Base\Enums\ProductAssociation::UP_SELL->value + ); }); test('can have collections relationship', function () { @@ -472,11 +474,11 @@ $product = Product::factory()->create(); $product->collections()->sync($collection); - expect($product->collections)->toBeInstanceOf(EloquentCollection::class); - expect($product->collections)->toHaveCount(1); - expect($product->collections->first())->toBeInstanceOf(Collection::class); - expect($product->collections->first()->pivot)->not->toBeNull(); - expect($product->collections->first()->pivot->position)->not->toBeNull(); + expect($product->collections)->toBeInstanceOf(EloquentCollection::class) + ->and($product->collections)->toHaveCount(1) + ->and($product->collections->first())->toBeInstanceOf(Collection::class) + ->and($product->collections->first()->pivot)->not->toBeNull() + ->and($product->collections->first()->pivot->position)->not->toBeNull(); }); test('can retrieve prices', function () { diff --git a/tests/shipping/Unit/Resolvers/ShippingRateResolverTest.php b/tests/shipping/Unit/Resolvers/ShippingRateResolverTest.php index cbbf503f48..c02f03a170 100644 --- a/tests/shipping/Unit/Resolvers/ShippingRateResolverTest.php +++ b/tests/shipping/Unit/Resolvers/ShippingRateResolverTest.php @@ -454,3 +454,97 @@ expect($shippingRates)->toHaveCount(0); }); + +test('will not resolve rates which are not enabled.', function () { + $currency = Currency::factory()->create([ + 'default' => true, + ]); + + $country = Country::factory()->create(); + + TaxClass::factory()->create([ + 'default' => true, + ]); + + $shippingZone = ShippingZone::factory()->create([ + 'type' => 'postcodes', + ]); + + $shippingZone->postcodes()->create([ + 'postcode' => 'AB1', + ]); + + $shippingZone->countries()->attach($country); + + $shippingMethod = ShippingMethod::factory()->create([ + 'driver' => 'ship-by', + 'data' => [], + 'stock_available' => 0, + ]); + + $customerGroup = \Lunar\Models\CustomerGroup::factory()->create([ + 'default' => true, + ]); + $shippingMethod->customerGroups()->sync([ + $customerGroup->id => ['enabled' => true, 'visible' => true, 'starts_at' => now(), 'ends_at' => null], + ]); + + $shippingRate = \Lunar\Shipping\Models\ShippingRate::factory()->create([ + 'shipping_method_id' => $shippingMethod->id, + 'shipping_zone_id' => $shippingZone->id, + 'enabled' => false, + ]); + + $shippingRate->prices()->createMany([ + [ + 'price' => 600, + 'min_quantity' => 1, + 'currency_id' => $currency->id, + ], + [ + 'price' => 500, + 'min_quantity' => 700, + 'currency_id' => $currency->id, + ], + [ + 'price' => 0, + 'min_quantity' => 800, + 'currency_id' => $currency->id, + ], + ]); + + $cart = $this->createCart($currency, 500); + + $cart->lines()->delete(); + + $purchasable = ProductVariant::factory()->create(); + $purchasable->shippable = true; + + Price::factory()->create([ + 'price' => 200, + 'min_quantity' => 1, + 'currency_id' => $currency->id, + 'priceable_type' => $purchasable->getMorphClass(), + 'priceable_id' => $purchasable->id, + ]); + + $cart->lines()->create([ + 'purchasable_type' => $purchasable->getMorphClass(), + 'purchasable_id' => $purchasable->id, + 'quantity' => 1, + ]); + + $cart->shippingAddress()->create( + CartAddress::factory()->make([ + 'country_id' => $country->id, + 'state' => null, + 'postcode' => 'AB1 1CD', + ])->toArray() + ); + + $shippingRates = Shipping::shippingRates( + $cart->refresh()->calculate() + )->get(); + + expect($shippingRates)->toHaveCount(0); +});