Can anyone help me in the implementation of nested set behavior https://github.com/creocoder/yii2-nested-sets with Yii2 Menu Widget?
Basically I want the hierarchical sidebar navigation menu like this http://www.surtex-instruments.co.uk/surgical/general-surgical-instruments
Thanks.
In your Model class define this functions:
public static function getTree($selected, $search)
{
$categories = Categories::find()->orderBy('lft')->asArray()->all();
$tree = [];
$index = 0;
foreach ($categories as $category) {
if ($category['parent_category_id'] === NULL) {
$tree[$index]['text'] = $category['category_' . Yii::$app->language];
if ($search) {
$tree[$index]['href'] = Url::to(['products', 'category' => $category['category_id'], 'search' => $search, 'string' => $category['category_' . Yii::$app->language]]);
} else {
$tree[$index]['href'] = Url::to(['products', 'category' => $category['category_id'], 'string' => $category['category_' . Yii::$app->language] ]);
}
$tree[$index]['id'] = $category['category_id'];
if ($selected) {
if ($selected['category_id'] == $category['category_id']) {
$tree[$index]['state']['selected'] = true;
}
if ($selected['lft'] >= $category['lft'] && $selected['rgt'] <= $category['rgt']) {
$tree[$index]['state']['expanded'] = true;
}
}
if ($category['lft'] + 1 != $category['rgt']) {
Categories::getNodes($tree[$index], $categories, $selected, $search);
}
$index++;
}
}
return $tree;
}
private static function getNodes(&$tree, $categories, $selected, $search)
{
$index = 0;
foreach ($categories as $category) {
if ($tree['id'] == $category['parent_category_id']) {
$tree['nodes'][$index]['text'] = $category['category_' . Yii::$app->language];
if ($search) {
$tree['nodes'][$index]['href'] = Url::to(['products', 'category' => $category['category_id'], 'search' => $search, 'string' => $category['category_' . Yii::$app->language]]);
} else {
$tree['nodes'][$index]['href'] = Url::to(['products', 'category' => $category['category_id'], 'string' => $category['category_' . Yii::$app->language]]);
}
$tree['nodes'][$index]['id'] = $category['category_id'];
if ($selected) {
if ($selected['category_id'] == $category['category_id']) {
$tree['nodes'][$index]['state']['selected'] = true;
}
if ($selected['lft'] >= $category['lft'] && $selected['rgt'] <= $category['rgt']) {
$tree['nodes'][$index]['state']['expanded'] = true;
}
}
if ($category['lft'] + 1 != $category['rgt']) {
Categories::getNodes($tree['nodes'][$index], $categories, $selected, $search);
}
$index++;
}
}
}
and use this extension execut/yii2-widget-bootstraptreeview
In the controller file get the menu like this:
public function actionProducts($category = false)
{
...
$data = Categories::getTree($category,'');
In your view file
<?php
...
use execut\widget\TreeView;
...
$onSelect = new JsExpression(<<<JS
function (undefined, item) {
window.location.href = item.href;
}
JS
);
?>
<?= TreeView::widget([
'data' => $data,
'template' => TreeView::TEMPLATE_SIMPLE,
'clientOptions' => [
'onNodeSelected' => $onSelect,
'onhoverColor' => "#fff",
'enableLinks' => true,
'borderColor' => '#fff',
'collapseIcon' => 'fa fa-angle-down',
'expandIcon' => 'fa fa-angle-right',
'levels' => 1,
'selectedBackColor' => '#fff',
'selectedColor' => '#2eaadc',
],
]); ?>
Related
I have a key array of objects which user have for days and their timings for their shop i.e
user A -
{
"name" : "shop1",
"timings" : [
{
"Monday" : "10am to 7pm"
},
{
"Thursday" : "Closed"
},
{
"Friday" : "9am to 6pm"
},
{
"Sunday" : "Closed"
},
{
"Wednesday" : "10am to 7pm"
},
{
"Tuesday" : "10am to 7pm"
},
{
"Saturday" : "10am to 7pm"
}
]}
Now I need to show these timing by days in frontend i.e
Monday-Saturday:9am to 7pm, Thursday,Sunday: Closed
So far I've tried to loop them and searched all days which are closed i.e
function filterByValue(array, string) {
return array.filter(o =>
Object.keys(o).some(k => o[k].toLowerCase().includes(string.toLowerCase())));
}
let closedDays = filterByValue(element.timings, 'Closed')
console.log("whtt",closedDays); // [{name: 'Lea', country: 'Italy'}]
let res = closedDays.map(x => Object.keys(x)[0]);
but for open days I can't figure out any suitable solution which can be used here. It would be a great help if you suggest something
const timings = [{"Monday": "10am to 7pm"}, {"Thursday": "Closed"}, {"Friday": "9am to 6pm"}, {"Sunday": "Closed"}, {"Wednesday": "10am to 7pm"}, {"Tuesday": "10am to 7pm"}, {"Saturday": "10am to 7pm"}];
const weekDaysInOrder = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
//Step1 little change list of objects
const newTimings = changeTimingsStructure(timings);
let obj = {}
sortTimings(newTimings).forEach(el => {
if (el.timing in obj) {
const maxIndex = Math.max(...obj[el.timing].map(el => el.indexOfDay));
if (maxIndex + 1 < el.indexOfDay && el.timing !== 'Closed') {
obj = pushToObjectArr(el.timing + '-v2', el, obj);
} else {
obj = pushToObjectArr(el.timing, el, obj);
}
} else {
obj = pushToObjectArr(el.timing, el, obj);
}
})
displayTimings(obj);
function changeTimingsStructure(timings) {
const newTimings = [];
timings.forEach(day => {
const dayName = Object.keys(day)[0]
const timing = day[dayName];
newTimings.push({
day: dayName,
timing,
indexOfDay: weekDaysInOrder.findIndex(dayInWeek => dayInWeek === dayName)
})
})
return newTimings;
}
function sortTimings(timings) {
return timings.sort(((a, b) => a.indexOfDay - b.indexOfDay));
}
function pushToObjectArr(key, el, obj) {
if (key in obj) {
obj[key].push(el);
} else {
obj[key] = [el]
}
return obj
}
function displayTimings(obj) {
let closedText = '';
for (const groupOfDays in obj) {
if (groupOfDays !== 'Closed') {
if (obj[groupOfDays].length > 1) {
console.log(`${obj[groupOfDays][0].day} - ${obj[groupOfDays][obj[groupOfDays].length - 1].day}: ${groupOfDays}`);
} else {
console.log(`${obj[groupOfDays][0].day}: ${obj[groupOfDays][0].timing}`);
}
} else {
closedText = `${obj[groupOfDays].map(dayObj => dayObj.day).join(', ')}: Closed`;
}
}
console.log(closedText);
}
Try this, final output:
Here I have created this utility in order to format the input as per your expected output.
let data = {name:'shop1',timings:[{Monday:'10am to 7pm'},{Thursday:'Closed'},{Friday:'9am to 6pm'},{Sunday:'Closed'},{Wednesday:'10am to 7pm'},{Tuesday:'10am to 7pm'},{Saturday:'10am to 7pm'}]};
const formatData = data => {
return data.reduce(
(result, d) => {
const value = Object.values(d)[0];
const key = Object.keys(d)[0];
if (value === 'Closed') {
result.closed.days.push(key);
if (!result.closed.values) {
result.closed.value = value;
}
} else {
result.open.days.push(key);
result.open.timings.push(value);
}
return result;
},
{
closed: { days: [], value: '' },
open: { days: [], timings: [] },
}
);
};
const formattedData = formatData(data.timings);
const formatTimings = timings => {
const formattedTimings= timings.reduce((result, time) => {
const times = time
.split('to')
.map(t => parseInt(t.replace(/\D+/g, '')))
.filter(Boolean);
result.from.push(times[0]);
result.to.push(times[1]);
return result
}, {from :[], to: []});
return `${Math.min(...formattedTimings.from)}am to ${Math.max(...formattedTimings.to)}pm`
};
const openDaysTimings = formatTimings(formattedData.open.timings);
const displayTimings = (days, time) => {
return `${days.join(", ")}: ${time}`
}
console.log(displayTimings(formattedData.closed.days, formattedData.closed.value));
console.log(displayTimings(formattedData.open.days, openDaysTimings));
I have been working on a project to generate configurable dashboards.
so i have to generate schema dynamically based on an api request. is there any way to do that?
it will be very helpful if there is any working example!
There's an asyncModule function for this scenario. You can check example below:
const fetch = require('node-fetch');
const Funnels = require('Funnels');
asyncModule(async () => {
const funnels = await (await fetch('http://your-api-endpoint/funnels')).json();
class Funnel {
constructor({ title, steps }) {
this.title = title;
this.steps = steps;
}
get transformedSteps() {
return Object.keys(this.steps).map((key, index) => {
const value = this.steps[key];
let where = null
if (value[0] === PAGE_VIEW_EVENT) {
if (value.length === 1) {
where = `event = '${value[0]}'`
} else {
where = `event = '${value[0]}' AND page_title = '${value[1]}'`
}
} else {
where = `event = 'se' AND se_category = '${value[0]}' AND se_action = '${value[1]}'`
}
return {
name: key,
eventsView: {
sql: () => `select * from (${eventsSQl}) WHERE ${where}`
},
timeToConvert: index > 0 ? '30 day' : null
}
});
}
get config() {
return {
userId: {
sql: () => `user_id`
},
time: {
sql: () => `time`
},
steps: this.transformedSteps
}
}
}
funnels.forEach((funnel) => {
const funnelObject = new Funnel(funnel);
cube(funnelObject.title, {
extends: Funnels.eventFunnel(funnelObject.config),
preAggregations: {
main: {
type: `originalSql`,
}
}
});
});
})
More info: https://cube.dev/docs/schema-execution-environment#async-module
I have a menu in my app:
<?php
NavBar::begin([
'brandLabel' => Html::img('#web/images/logo-top.png', ['id' => 'logo']),
'brandUrl' => Yii::$app->homeUrl,
'options' => [
'class' => 'navbar-inverse navbar-static-top',
],
]);
if (count($menuItems)) {
echo Nav::widget([
'options' => ['class' => 'navbar-nav'],
'items' => $menuItems,
]);
}
?>
<?php NavBar::end(); ?>
$menuItems is generated within a Controller:
private function constructMenu($categories) {
$menu = [];
if (is_array($categories) && (count($categories) > 0)) {
foreach($categories as $key => $category) {
$menu[$key] = [
'label' => $category['name'],
'url' => Url::to([
'category/view',
'slug' => $category['slug']
]),
];
if (is_array($category['children']) && (count($category['children']) > 0)) {
$menu[$key]['items'] = $this->constructMenu($category['children']);
}
}
}
return $menu;
}
Also I have urlManager config:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'category/<slug:[\w_-]+>' => 'category/view',
'item/<slug:[\w_-]+>' => 'item/view',
'cart/remove/<item_id:\d+>' => 'cart/remove',
'cart/add/<item_id:\d+>' => 'cart/add',
],
],
So, the only problem is that the menu items are always active = false. How should I modify constructMenu method to set proper value for active key? Or maybe I should do it within the template?
Here is content of $menuItems from PhpStrom debug panel right before it will be passed to Nav::widget:
Here is where I am now:
NavBar::begin([
'brandLabel' => Html::img('#web/images/logo-top.png',
['id' => 'logo', 'style' => 'height: 40px; filter: invert(100%);']),
'brandUrl' => Yii::$app->homeUrl,
'options' => [
'class' => 'navbar-inverse navbar-static-top',
],
]);
$controllerAndSlug = $this->context->id . '/' . $this->context->actionParams['slug'];
$menuItems = array_map(
function($item) use ($controllerAndSlug) {
$item['active'] = strpos($item['url'], $controllerAndSlug) !== false;
return $item;
},
$menuItems
);
if (count($menuItems)) {
echo Nav::widget([
'options' => ['class' => 'navbar-nav'],
'items' => $menuItems,
]);
}
NavBar::end();
It works fine, but sets active flag only on top level menu items. Now I wonder about how to pass a param to callback-function.
Although if you provide the full URL with /controller/action format it should work if it still does not, then you can use the active option of the item by checking the current controller, action, and slug
protected function constructMenu($categories) {
$menu = [];
$controller = Yii::$app->controller->id;
$action = Yii::$app->controller->action->id;
$slug = Yii::$app->request->get('slug');
if( is_array($categories) && (count($categories) > 0) ){
foreach( $categories as $key => $category ){
$isActive = ($controller == 'category' && $action == 'view' && $slug == $category['slug']);
$menu[$key] = [
'label' => $category['name'],
'url' => Url::to([
'category/view',
'slug' => $category['slug']
]),
'active' => $isActive
];
if( is_array($category['children']) && (count($category['children']) > 0) ){
$menu[$key]['items'] = $this->constructMenu($category['children']);
}
}
}
return $menu;
}
Try this:
private function constructMenu($categories) {
$menu = [];
if (is_array($categories) && (count($categories) > 0)) {
foreach($categories as $key => $category) {
$menu[$key] = [
'label' => $category['name'],
'url' => Url::to([
'category/view',
'slug' => $category['slug']
]),
'active' => Yii::$app->controller->id == 'category'
&& Yii::$app->controller->action->id == 'view'
&& Yii::$app->request->get('slug') == $category['slug']
];
if (is_array($category['children']) && (count($category['children']) > 0)) {
$menu[$key]['items'] = $this->constructMenu($category['children']);
}
}
}
return $menu;
}
i am new in react native,i try to developing native application in react native but i have onPress click event issue.
InfoSwipper.js file i was click on Sign In button display "undefined is not an object(evaluating 'this.props.navigator.push') ". I am not sure what I am missing. please some one help me.
Thank you in advance.
index.android.js file code
"use strict";
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
StatusBar,
AsyncStorage,
} from 'react-native';
var SignUp = require("./components/SignUp");
var InfoScreens = require("./components/InfoScreens");
import SplashScreen from 'react-native-splash-screen'
var Kahaani = React.createClass({
_renderScene(route, navigator) {
if (route.id === 0) {
return <InfoScreens navigator={navigator} />
}
else if (route.id === 1) {
StatusBar.setHidden(false);
console.log('WRONG SIGNIN', "here");
return <LogIn navigator={navigator} />
}
},
componentDidMount() {
// do anything while splash screen keeps, use await to wait for an async task.
SplashScreen.hide();
},
render: function() {
return (
<Navigator
style={styles.navigationContainer}
initialRoute={{id: 0, }}
renderScene={this._renderScene} />
);
}
});
var styles = StyleSheet.create({
navigationContainer: {
flex: 1,
},
});
AppRegistry.registerComponent('Kahaani', () => Kahaani);
swipperScreen.js
'use strict';
import React, { Component } from 'react';
import {
Dimensions, // Detects screen dimensions
Platform, // Detects platform running the app
ScrollView, // Handles navigation between screens
StyleSheet, // CSS-like styles
View,
Text,Navigator, TouchableHighlight, // Container component
} from 'react-native';
import LogIn from './LogIn';
var navigator;
class InfoScreenSwiper extends Component {
constructor(props)
{
super(props);
}
static defaultProps = {
horizontal: true,
pagingEnabled: true,
showsHorizontalScrollIndicator: false,
showsVerticalScrollIndicator: false,
bounces: false,
scrollsToTop: false,
removeClippedSubviews: true,
automaticallyAdjustContentInsets: false,
index: 0
};
state = this.initState(this.props);
initState(props) {
const total = props.children ? props.children.length || 1 : 0,
index = total > 1 ? Math.min(props.index, total - 1) : 0,
offset = width * index;
const state = {
total,
index,
offset,
width,
height,
};
this.internals = {
isScrolling: false,
offset
};
return state;
}
onScrollBegin = e => {
// Update internal isScrolling state
this.internals.isScrolling = true;
}
onScrollEnd = e => {
// Update internal isScrolling state
this.internals.isScrolling = false;
// Update index
this.updateIndex(e.nativeEvent.contentOffset
? e.nativeEvent.contentOffset.x
// When scrolled with .scrollTo() on Android there is no contentOffset
: e.nativeEvent.position * this.state.width
);
}
onScrollEndDrag = e => {
const { contentOffset: { x: newOffset } } = e.nativeEvent,
{ children } = this.props,
{ index } = this.state,
{ offset } = this.internals;
if (offset === newOffset &&
(index === 0 || index === children.length - 1)) {
this.internals.isScrolling = false;
}
}
updateIndex = (offset) => {
const state = this.state,
diff = offset - this.internals.offset,
step = state.width;
let index = state.index;
if (!diff) {
return;
}
index = parseInt(index + Math.round(diff / step), 10);
this.internals.offset = offset;
this.setState({
index
});
}
swipe = () => {
if (this.internals.isScrolling || this.state.total < 2) {
return;
}
const state = this.state,
diff = this.state.index + 1,
x = diff * state.width,
y = 0;
this.scrollView && this.scrollView.scrollTo({ x, y, animated: true });
this.internals.isScrolling = true;
if (Platform.OS === 'android') {
setImmediate(() => {
this.onScrollEnd({
nativeEvent: {
position: diff
}
});
});
}
}
renderScrollView = pages => {
return (
<ScrollView ref={component => { this.scrollView = component; }}
{...this.props}
contentContainerStyle={[styles.wrapper, this.props.style]}
onScrollBeginDrag={this.onScrollBegin}
onMomentumScrollEnd={this.onScrollEnd}
onScrollEndDrag={this.onScrollEndDrag}
>
{pages.map((page, i) =>
// Render each slide inside a View
<View style={[styles.fullScreen, styles.slide]} key={i}>
{page}
</View>
)}
</ScrollView>
);
}
renderPagination = () => {
if (this.state.total <= 1) {
return null;
}
const ActiveDot = <View style={[styles.dot, styles.activeDot]} />,
Dot = <View style={styles.dot} />;
let dots = [];
for (let key = 0; key < this.state.total; key++) {
dots.push(key === this.state.index
// Active dot
? React.cloneElement(ActiveDot, { key })
// Other dots
: React.cloneElement(Dot, { key })
);
}
return (
<View
pointerEvents="none"
style={[styles.pagination, styles.fullScreen]}>
{dots}
</View>
);
}
onMainPressLogIn= () =>
{
console.log('WRONG SIGNIN', "onMainPressLogIn");
this.props.navigator.push({
id:1
});
}
onSubmitPressed () {
console.log('WRONG SIGNIN', "onSubmitPressed");
this.props.navigator.push({
id:2
})
}
renderButtonBottom = () => {
const lastScreen = this.state.index === this.state.total - 1;
return (
<View pointerEvents="box-none" style={[styles.buttonWrapper]}>
<View style={ [styles.viewButton]}>
<Text style={[styles.buttonLogIn,styles.buttonAll]} onPress={this.onMainPressLogIn} >LOG IN</Text>
<Text style={[styles.buttonSignUp,styles.buttonAll]} onPress={(this.onSubmitPressed.bind(this))}>SIGN UP</Text>
</View>
</View>
);
}
render = ({ children } = this.props) => {
return (
<View style={[styles.container, styles.fullScreen]}>
{/* Render screens */}
{this.renderScrollView(children)}
{/* Render pagination */}
{this.renderPagination()}
{/* Render Continue or Done button */}
{this.renderButtonBottom()}
</View>
);
}
}
module.exports = InfoScreenSwiper;
Invalid shipping method in magento 1.8.1?
When my cart value in 950 - 999 INR but below 950 or above 999 working fine?
One page controller code is below.
Kindly help me
onePageController.php
<?php
require_once Mage::getModuleDir('controllers', 'Mage_Checkout').DS.'OnepageController.php';
class Webly_QuickCheckout_OnepageController extends Mage_Checkout_OnepageController
{
public function saveBillingAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->getRequest()->isPost()) {
// $postData = $this->getRequest()->getPost('billing', array());
// $data = $this->_filterPostData($postData);
$data = $this->getRequest()->getPost('billing', array());
$customerAddressId = $this->getRequest()->getPost('billing_address_id', false);
if (isset($data['email'])) {
$data['email'] = trim($data['email']);
}
$data['use_for_shipping'] = 1;
$result = $this->getOnepage()->saveBilling($data, $customerAddressId);
if (!isset($result['error'])) {
if (!isset($result['error'])) {
if ($this->getOnepage()->getQuote()->isVirtual()) {
$result['goto_section'] = 'payment';
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
} elseif (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1) {
$quote =$this->getOnepage()->getQuote();
$quote->getBaseGrandTotal();
$result = $this->getOnepage()->saveShipping($data, $customerAddressId);
if ($quote->getBaseGrandTotal() < 1000) {
$method = 'inchoo_shipping_fixed';
} else {
$method = 'inchoo_shipping_free';
}
// $method = 'flatrate_flatrate';
$result = $this->getOnepage()->saveShippingMethod($method);
Mage::getSingleton('checkout/type_onepage')->getQuote()->getShippingAddress()-> setShippingMethod($method)->save();
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
$result['goto_section'] = 'payment';
} else {
$result['goto_section'] = 'shipping';
}
}
}
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
}
}
}