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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/postman.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: API Contract (Postman)

# Boots a full Fleetbase stack (published image) and runs the Storefront API
# Postman collection against the live API. Delegates to the reusable workflow in
# fleetbase/fleetbase. Requires org secrets POSTMAN_API_KEY + _GITHUB_AUTH_TOKEN
# (inherited); no-ops until POSTMAN_API_KEY is set.
# TODO: change @dev-v0.7.53 to @main once that branch is merged.

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read

jobs:
contract:
uses: fleetbase/fleetbase/.github/workflows/api-contract.yml@dev-v0.7.53
with:
collections: "Fleetbase Storefront API"
build-from-source: false
secrets: inherit
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
9 changes: 9 additions & 0 deletions .github/workflows/server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,12 @@ jobs:
name: storefront-coverage-clover
path: coverage/clover.xml
if-no-files-found: error

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage/clover.xml
disable_search: true
flags: backend
fail_ci_if_error: false
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

<p align="center">
<img src="https://img.shields.io/badge/license-AGPL--3.0--or--later-blue" alt="License: AGPL-3.0-or-later">
<a href="https://codecov.io/gh/fleetbase/storefront"><img src="https://codecov.io/gh/fleetbase/storefront/branch/main/graph/badge.svg" alt="Coverage"></a>
<img src="https://img.shields.io/badge/php-%5E8.0-777bb4" alt="PHP ^8.0">
<img src="https://img.shields.io/badge/node-%3E%3D18-339933" alt="Node >=18">
<img src="https://img.shields.io/badge/ember-engine-e04e39" alt="Ember Engine">
Expand Down
1 change: 0 additions & 1 deletion addon/controllers/customers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ export default class CustomersIndexController extends BaseController {
},
{
label: this.intl.t('storefront.customers.index.edit-customer'),

// fn: this.editVendor,
},
{
Expand Down
3 changes: 2 additions & 1 deletion addon/routes/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export default class ApplicationRoute extends Route {
controller.loadProductCategories();
}

afterModel() {
afterModel(stores) {
this.storefront.synchronizeActiveStore(stores);
this.storefront.listenForIncomingOrders();
}

Expand Down
18 changes: 1 addition & 17 deletions addon/services/storefront-dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ export default class StorefrontDashboardService extends Service.extend(Evented)
@tracked datePickerValue = '';
@tracked datePickerButtons = [];

isSelectingPreset = false;

constructor() {
super(...arguments);
const [startDate, endDate] = getDateRangeByLabel(DEFAULT_RANGE_LABEL);
Expand All @@ -36,17 +34,7 @@ export default class StorefrontDashboardService extends Service.extend(Evented)
createDatePickerButtons() {
return createDateRangeButtons((range) => {
this.setRange(range.startDate, range.endDate, range.label);
}).map((button) => ({
...button,
onClick: (datepicker) => {
this.isSelectingPreset = true;
try {
button.onClick(datepicker);
} finally {
this.isSelectingPreset = false;
}
},
}));
});
}

withStore(store) {
Expand All @@ -57,10 +45,6 @@ export default class StorefrontDashboardService extends Service.extend(Evented)
}

@action selectDates({ date, formattedDate }) {
if (this.isSelectingPreset) {
return;
}

if (!formattedDate) {
return;
}
Expand Down
44 changes: 22 additions & 22 deletions addon/services/storefront.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ export default class StorefrontService extends Service.extend(Evented) {

/**
* Gets the active store.
* @returns {Object} The active store object.
* @returns {Object|null} The active store object.
*/
get activeStore() {
return this.findActiveStore();
const activeStoreId = this.activeStoreId ?? this.currentUser.getOption('activeStorefront');

return activeStoreId ? this.store.peekRecord('store', activeStoreId) : null;
}

/**
Expand Down Expand Up @@ -65,29 +67,27 @@ export default class StorefrontService extends Service.extend(Evented) {
* @returns {Object|null} The active store object or null if not found.
*/
findActiveStore() {
const activeStoreId = this.activeStoreId ?? this.currentUser.getOption('activeStorefront');

if (!activeStoreId) {
const stores = this.store.peekAll('store');

if (stores.firstObject) {
this.currentUser.setOption('activeStorefront', stores.firstObject.id);
this.activeStoreId = stores.firstObject.id;
}

return stores.firstObject;
}

const activeStore = this.store.peekRecord('store', activeStoreId);
return this.activeStore;
}

if (!activeStore) {
this.currentUser.setOption('activeStorefront', undefined);
this.activeStoreId = undefined;
/**
* Synchronizes active storefront state after the store collection has loaded.
* All tracked writes happen here instead of inside render-time getters.
*
* @param {Array|Object|null} stores loaded store collection
* @returns {Object|null} the selected store
*/
synchronizeActiveStore(stores = this.store.peekAll('store')) {
const activeStoreId = this.activeStoreId ?? this.currentUser.getOption('activeStorefront');
const activeStore = activeStoreId ? this.store.peekRecord('store', activeStoreId) : null;
const firstStore = stores?.firstObject ?? Array.from(stores ?? [])[0] ?? null;
const nextStore = activeStore ?? firstStore;
const nextStoreId = nextStore?.id;

return this.findActiveStore();
}
this.currentUser.setOption('activeStorefront', nextStoreId);
this.activeStoreId = nextStoreId;

return activeStore;
return nextStore;
}

/**
Expand Down
5 changes: 3 additions & 2 deletions addon/utils/commerce-date-ranges.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,7 @@ export function createDateRangeButtons(onRangeSelect) {
className: 'custom-date-range-btn',
onClick: (datepicker) => {
const [startDate, endDate] = range.getValue();
datepicker.selectDate([startDate, endDate]);
datepicker.hide();
datepicker.selectDate([startDate, endDate], { silent: true });

// Call the callback if provided
if (typeof onRangeSelect === 'function') {
Expand All @@ -161,6 +160,8 @@ export function createDateRangeButtons(onRangeSelect) {
formattedRange: `${format(startDate, 'MMM dd, yyyy')} - ${format(endDate, 'MMM dd, yyyy')}`,
});
}

datepicker.hide();
},
}));
}
Expand Down
21 changes: 21 additions & 0 deletions scripts/coverage-summary.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@ function intMetric(SimpleXMLElement $node, string $name): int

$files = [];
$directories = [];
$classNodes = $project->xpath('.//class') ?: [];

if ($classNodes !== []) {
$classes = 0;
$coveredClasses = 0;

foreach ($classNodes as $classNode) {
$classStatements = intMetric($classNode, 'statements');
$coveredClassStatements = intMetric($classNode, 'coveredstatements');

if ($classStatements === 0) {
continue;
}

$classes++;

if ($coveredClassStatements === $classStatements) {
$coveredClasses++;
}
}
}

foreach ($project->xpath('.//file') ?: [] as $file) {
$path = (string) $file['name'];
Expand Down
Loading
Loading