OroCommerce: Customize buttons on the Order view page? - orocommerce

I need to add my custom button to the order page, best if near edit/delete, it should just open link for some route with orderId parameter.
Also it would be nice to remove unnecesary buttons like discount and coupon code.
How to do that? I tried to do smth with placeholders but didnt succeed.

These buttons are rendered using actions:
The code that renders "Edit" and "Delete" buttons
Actions documentation
But, if your button just renders a link, you can override the {% block navButtons %} block in OroOrderBundle:Order:view.html.twig template. See how to override templates.
Or, you can extend the button list without template inheritance, using placeholder view_navButtons. See placeholders documentation.

Decided to use action as its very smart and powerful mechanism even for such simple thing as download button, dont need to override templates or work with templates at all. So here is all that need to display the button:
#MyBundle/Resources/config/oro/actions.yml
operations:
download_action:
label: Download
button_options:
icon: fa-download
routes:
- oro_order_view
entities: ['Oro\Bundle\OrderBundle\Entity\Order']
for_all_entities: false
actions:
- '#redirect':
route: 'test_download'
route_parameters: {id: $id}
new_tab: true
preconditions:
'#equal': [$internalStatus.id, 'open']
The only confusing thing is that button appeared not only on oro_order_view page but on edit page too despite of specified route, anybody knows why?

Related

Nuxt-link delay transition to new page?

This is a follow up to #1458. I'm looking for some direction on how Nuxt expects this to be handled.
I have a menu. When I click on a nuxt-link in the menu, I want to have time to close the menu before the page transition happens. The thing is, I only want that to happen when you click on the nuxt-link in the menu, not every time I go to a certain route (as the previous issue described using a middlewear on the route).
So there are a few different ways to do this, and I'm curious what the "Nuxt" way is?
The way we currently do this, disable the nuxt-link and capture the click, then do a router.push().
<nuxt-link :to="path" event="disabled" #click.native="delayLoad"/>
// Methods
delayLoad(event) {
this.$store.commit("CLOSE_MENU")
setTimeout(()=>{
this.$router.push(event.target.pathname)
}, 2000)
}
Is this a good idea? I just always have an aversion to hijacking nuxt-link and browser navigation like this. It seems janky.
The other ideas we played with were using a query param on the nuxt-link, and then using that in a middlewear to delay the page transition. That seemed worse to me, because now my URL's have a query param in them that is used for an animation, seems like that is abusing query params. This also triggers the page loading progress bar, which isn't really the intent, it's to have a sequenced animation happen, then page load.
It seems to me that perhaps nuxt-link should have a delay prop, or perhaps the page transition config should allow for a delay (like it does with duration)?
I wanted to do this as well and came up with the following solution. Using the new slots api you can more elegantly customise the nuxt-link behaviour:
<nuxt-link v-slot="{ route, href }" :to="path" custom>
<a :href="href" #click.prevent="$emit('navigate', route)">
<slot></slot>
</a>
</nuxt-link>
This will make the link emit a navigate event with the route as a param. You then listen for this event wherever you include your menu component, like this:
<template>
<transition
name="fade"
#after-leave="maybeNavigate"
>
<MainMenu
v-if="menuIsVisible"
#navigate="navigate"
/>
</transition>
</template>
<script>
export default {
data: () => ({
menuIsVisible: false,
navigateToOnMainMenuClose: null,
}),
methods: {
navigate(route) {
this.navigateToOnMainMenuClose = route
this.menuIsVisible = false
},
maybeNavigate() {
if (this.navigateToOnMainMenuClose) {
this.$router.push(this.navigateToOnMainMenuClose)
this.navigateToOnMainMenuClose = null
}
},
},
}
</script>
Whenever you click a nav link in the menu, the route will be stored and the menu will close. After the menu out animation has finished, maybeNavigate() will push the stored route, if there is one. This removes the need for a setTimeout and if you manage to click multiple links in quick succession only the last one will be stored and navigated to.
Since nuxt-link is essentially a wrapped version of vue's router-link, if you look at the documentation for that there is an event property that accepts string or string[], looking at it's source code here: https://github.com/vuejs/vue-router/blob/dev/src/components/link.js#L86
you can see it will register a listener for disabled in this instance. It may make more sense to pass an empty array so that no event listeners are registered, but that's at the cost of readability.
Otherwise, #click.native is the suggested way to handle custom click handlers for router-link (see: https://github.com/vuejs/vue-router/issues/800#issuecomment-254623582).
The only other concerns I can think of are what happens if you click 2 different links in rapid succession and what happens if you click more than once. May just want to add a variable to track if a link has been clicked to prevent firing setTimeout multiple times, which could navigate you from page A to B and then to C as all timeouts will fire if not canceled. Or maybe you want to only navigate to the 'last' link clicked, so if another link is clicked, you would cancel the earlier setTimeout. This is realistically an edge case that probably won't be an issue, but worth exploring.
Otherwise, IMO, looks good to me. This seems like the simplest way to implement this without having to create a custom component / plugin. I'm no expert, but is likely how I would implement this functionality as well. It would be nice to see a delay option though since I can see myself using that functionality as well with vuetify.
Another potential method would be to do your store commit in beforeTransition: https://nuxtjs.org/api/configuration-transition/
Though I'm not sure that there is access to the store there, so you might have to write a custom plugin for that as well. Again, seems more complicated than it's worth for a simple delayed animation. Simple, working code is sometimes the best solution, even if it's not the most extensible option.
See also: How can I transition between two nuxt pages, while first waiting on a child component transition/animation to finish?
for another way of handling this.

Clicking on active router-link

In my Vue 2 app, I have a menu bar whose menu items use router-link. One of these menu items is 'Customers', which takes the user to a customer editor. Now, if the user clicks on this same 'Customers' menu item while they're in the customer editor, nothing happens. I presume this is because of this behaviour mentioned in the docs: "In HTML5 history mode, router-link will intercept the click event so that the browser doesn't try to reload the page."
Now, that's perfectly understandable and for the majority of the time it would be the behaviour I want. But actually here I want the component to be reloaded, to go back to a clean slate. Or perhaps more accurately, for some event to occur which I can handle within the customer editor to set things how I want them to be. I assumed this would be the case, in fact, but when I set up a watch, thus
watch: {
'$route' (to, from) {
console.log("Route changed");
}
},
I don't see anything logged to the console. It may be that some event is occurring which I'm not handling and that Vue is simply reusing the component. But how can I stop it from doing so?
According to this issue, you can add a #click.native binding to the current router-link and reinitialize your component data in there.
But a quick and dirty solution would be to append a unique parameter to your route, like a date. Check this fiddle
The intended method of accomplishing this seems to be to implement a beforeRouteUpdate function to reset the properties of the route component. See this issue on vue-router's GitHub: Routing the same component, the component is not reload, but be reused? #1490.
This is expected behaviour, Vue re-uses components where possible.
You can use the beforeRouteUpdate hook to react to a route switch that
uses the same component.

Aurelia Routing: Append Views into Tabbed Interface

I'm practically brand new to Aurelia, but over the course of a few days I've picked up the starter template and gone through some video training in Pluralsight. I have a unique vision that I can't seem to decide whether compose element, custom element, or router is best to use for this scenario - or if I need to write something completely custom.
I prefer to continue using the router because it gives you the
URLs and history state. Linking deep within the web app may be necessary.
When a view / viewmodel is initialized, I want the view appended to the DOM, NOT replaced. The <router-view> element works by replacing the view.
With each view appended to the DOM, I would like to create a set of tabs representing every view that has been opened so far. Think of any modern text editor, IDE, or even a web browser shows tabs.
Sometimes it would be necessary to detect whether a view is already rendered in the DOM (viewmodel + parameter) and just bring that view to the front -vs- appending the new one.
Do you have any suggestions, examples, etc for someone relatively new to Aurelia, SPAs, and MVVM?
Thank you.
I believe the easiest way is using the compose element. You would need an array containing all screens, and another array to hold the opened screens. Something like this:
screens = [
{ id: 1, name: 'Test 1', view: './test-1.html', viewModel: './test-1' },
{ id: 2, name: 'Test 2', view: './test-2.html', viewModel: './test-2' }
];
_activeScreens = [];
get activeScreens() {
return this.screens.filter((s) => this._activeScreens.indexOf(s.id) !== -1);
}
In the HTML you just have to use <compose></compose> for each iteration of activeScreens.
I made this example https://gist.run/?id=c32f322b1f56e6f0a83679512247af7b to show you the idea. In my case, I've used an html table. In your case, you could use a tab plugin, like Bootstrap or jQuery.
Hope this helps!

how to create a pdf editor for grails

In my Grails project I'm using the PDF plugin to generate PDFs from gsp page.
It works well, but I would like to add more functionalities to users, so I would like to allow user to edit the PDF basic template (defined in a gsp page), in particular I would like to allow the editing of the text inside the template and store it somewhere.
Anybody knows how could it be done?
If you only want to change the text, you could store the edited text in a database with the user id and loading it into the gsp page instead of the standard text.
If you also want to change the style of the page you could try to store the whole gsp page in the database and let the user editing it with an HTML Editor.
that's how i would start with, maybe someone has an better idea
The underlying components of the pdf plugin don't strictly require a .gsp file. It simply uses .gsps rendered as strings and feeds those into the flyingsaucer lib. So you could use a WYSIWYG type editor to allow users to create html snippets, save those strings somehow and then feed those though to the flyingsaucer libs yourself. just look into the included service methods of the plugin for an example. That might sound scary, but it really isn't very complicated.
You might want to wrap the user generated content with some of your own HTML markup for sanity and styling purposes of course, but the idea you are going for is entirely doable.
You could have a GSP that behaves of two different ways. First of all the GSP will be rendered in a Editable state. In this state the user could do some edits in some parts of the GSP. After that, the GSP will be rendered in a Preview state, where user could check the modifications that he has done in the previous step (nothing can be edited in this state). Finally, the GSP will be rendered as a PDF (using Grails Rendering Plugin).
Note that user will not edit the GSP itself. You need to allow him to edit through HTML elements as Text Areas, for instance. In this case we're using an WYSWYG editor. This editor allows the user to put text as Bold, Italic, etc.
Therefore, the most important step of this solution is to distinguish the two states in the same GSP. To do that you can use a boolean variable (called editing, for instance). This variable, if true, will render the GSP with the elements that will allow him to perform changes in the document. For other side, if the editing variable is false, the GSP will be rendered just with texts, not allowing any kind of editing.
The user could Check or Uncheck checkboxes (to show or hide some part of the document) and write or change texts in the Text Areas elements.
Below I'll show how this solution works.
GSP
The GSP is a template GSP and is called _quote.gsp
The piece of code below shows the use of the editing variable. Note that if editing = true, then a textarea is rendered and user can edit the text. There is a standard text that can be changed.
The post variable keeps what user has done after the editing phase. We use JQuery serialize to get all paramaters and pass it to a Grails Controller.
<p>
<g:if test="${editing}">
<pgs:textArea html="true" autosize="true" name="fraseInicial" rows="2" cols="80">
${post?.fraseInicial?post.fraseInicial:"Conforme sua solicitação, a empresa tem a satisfação de informar-lhe os métodos e preços."}
</pgs:textArea>
</g:if>
<g:else>
${post.fraseInicial}
</g:else>
</p>
pgs:textArea is a specific taglib of this system and is used to render a WYSWYG editor, you can replace it for a simple TextArea HTML element.
An example with checkbox:
<g:if test="${editing || post.temPrazoAnalise}">
<h1>
Teste teste
</h1>
<g:if test="${editing}"><g:checkBox name="temPrazoAnalise" value="${!post?true:post?.temPrazoAnalise == null?false:true}"/></g:if>
<g:if test="${editing || post.temPrazoAnalise}">
<p>Teste teste teste </p>
</g:if>
</g:if>
Controller
The previewQuote() is called from an AJAX call that serializes (via JQuery) all parameters of GSP.
The back() action allows the user to back to the editing state from the preview state. This is the reason why we set session["paramsReport"] = params inside previewQuote(). Doing this way it's possible to use session["paramsReport"] inside back() and restore the values changed by the user.
def editQuote() {
def quote = Quote.get(params.id)
render(template: "/quote/report/quote", model: [editing:true, quote:quote])
}
def previewQuote() {
Quote quote = Quote.get(params.id)
session["paramsReport"] = params
render(template: "/quote/report/quote", model: [quote:quote, post:params])
}
def back() {
def quote = Quote.get(params.id)
if (session["paramsReport"]) {
render(template: "/quote/report/quote", model: [editing:true, post:session["paramsReport"], quote:quote])
}
}
def generateQuote() {
Quote quote = Quote.get(params.id)
def f = new File(grailsApplication.mainContext.servletContext.getRealPath("/app/temp/${quote.code}.pdf"))
if (f.exists())
f.delete()
f.withOutputStream { os ->
pdfRenderingService.render([template: '/quote/report/quote', model: [quote:this, post:session["paramsReport"], pdf:true]], os)
}
}
This solution was developed by wanderson-santos (https://stackoverflow.com/users/128857/wanderson-santos) and me.
I hope you understand the overall idea of the solution. I understand that could be a bit complicated at a first view. Anyway, is a solution that allows flexibility for this kind of requirement (i.e. allow the user to customize a report before the PDF is generated).

seaside : 2 events on html anchor

In seaside I need to do two events - callback and go to the other url (google.com) on clicking a link but I am not able to do both of them. What could be possibly wrong here? Is there any way I can do both the events?
html anchor
newTarget url: 'www.google.com';
callback: [Transcript show: 'clicked on google.com'];
with: ('www.google.com') .
Because >>callback: and >>url: both update the href attribute on an Anchor tag, you can't use both on the same anchor. You will have to solve this by writing a callback method which executes your logic, and then uses javascript to open the page in a new window.
Hope that helps!
You cannot use the default click behaviour of an anchor to accomplish this.
The code snippet below demonstrates how to do this with an ajax call that executes your action in a callback and sets the navigation to the other url as the ajax 'oncomplete' action.
html anchor
url: 'javascript:{}';
onClick: (html jQuery ajax
callback: [ ... ];
onComplete:(html javascript goto:'http://www.google.com'));
with: 'http://www.google.com'.
The snippet cancels the default click action on the anchor using the 'javascript:{}' code as the url. In some cases, I have also seen the use of '#' but in my experience that also scrolls your browser to the top. Next, it sets its proper 'click' handler that launches an ajax request, as I described before.