From 4c52f6c6f5bd92e60007e434a429dc2f07c7e930 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Mon, 15 Oct 2018 19:27:08 -0300 Subject: [PATCH 01/14] Adds a button to filter opened/closed tickets to ticket-list and shows it under 'my-tickets' --- client/src/app-components/ticket-list.js | 20 +++++++++++--- client/src/app-components/ticket-list.scss | 13 ++++++++-- .../panel/tickets/admin-panel-my-tickets.js | 26 +++++++++++++++++-- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/client/src/app-components/ticket-list.js b/client/src/app-components/ticket-list.js index c3d95be4..9739e4ce 100644 --- a/client/src/app-components/ticket-list.js +++ b/client/src/app-components/ticket-list.js @@ -9,6 +9,7 @@ import Table from 'core-components/table'; import Button from 'core-components/button'; import Tooltip from 'core-components/tooltip'; import DropDown from 'core-components/drop-down'; +import Checkbox from 'core-components/checkbox'; class TicketList extends React.Component { static propTypes = { @@ -21,7 +22,10 @@ class TicketList extends React.Component { type: React.PropTypes.oneOf([ 'primary', 'secondary' - ]) + ]), + showClosedTickets: React.PropTypes.bool, + filterClosedTickets: React.PropTypes.bool, + onShowClosedTicketsChange: React.PropTypes.func }; static defaultProps = { @@ -30,7 +34,9 @@ class TicketList extends React.Component { tickets: [], departments: [], ticketPath: '/dashboard/ticket/', - type: 'primary' + type: 'primary', + showClosedTickets: false, + filterClosedTickets: false }; state = { @@ -40,12 +46,20 @@ class TicketList extends React.Component { render() { return (
- {(this.props.type === 'secondary' && this.props.showDepartmentDropdown) ? this.renderDepartmentsDropDown() : null} +
+ {(this.props.type === 'secondary' && this.props.showDepartmentDropdown) ? this.renderDepartmentsDropDown() : null} + {this.props.filterClosedTickets ? this.renderFilterCheckbox() : null} +
); } + + renderFilterCheckbox() { + return + } + renderDepartmentsDropDown() { return (
diff --git a/client/src/app-components/ticket-list.scss b/client/src/app-components/ticket-list.scss index e0cb9ceb..7e8c6d81 100644 --- a/client/src/app-components/ticket-list.scss +++ b/client/src/app-components/ticket-list.scss @@ -2,10 +2,19 @@ .ticket-list { - &__department-selector { + &__filters { margin-bottom: 25px; } + &__department-selector { + display: inline-block; + } + + &__checkbox { + display: inline-block; + margin-left: 25px; + } + &__number { text-align: left; } @@ -52,4 +61,4 @@ &__priority-high { background-color: $primary-red; } -} \ No newline at end of file +} diff --git a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js index 2b85418b..35c47b26 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js @@ -1,5 +1,6 @@ import React from 'react'; import {connect} from 'react-redux'; +import _ from 'lodash'; import i18n from 'lib-app/i18n'; @@ -21,6 +22,10 @@ class AdminPanelMyTickets extends React.Component { tickets: [] }; + state = { + showClosedTickets: false + }; + componentDidMount() { this.props.dispatch(AdminDataAction.retrieveMyTickets()); } @@ -39,17 +44,34 @@ class AdminPanelMyTickets extends React.Component { ); } + filterOpenedTickets(tickets){ + return _.filter(tickets, (ticket) => { + return !ticket.closed + }); + } + getProps() { return { userId: this.props.userId, departments: this.props.departments, - tickets: this.props.tickets, + tickets: this.state.showClosedTickets ? this.props.tickets : this.filterOpenedTickets(this.props.tickets), type: 'secondary', loading: this.props.loading, - ticketPath: '/admin/panel/tickets/view-ticket/' + ticketPath: '/admin/panel/tickets/view-ticket/', + filterClosedTickets: true, + showClosedTickets: this.state.showClosedTickets, + onShowClosedTicketsChange: this.onShowClosedTicketsChange.bind(this) }; } + onShowClosedTicketsChange() { + this.setState(function(state) { + return { + showClosedTickets: !state.showClosedTickets + }; + }); + } + onCreateTicket() { ModalContainer.openModal(
From a2d3908c4dadbef11ae82ff6ba5f9c01d412ccf7 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Tue, 16 Oct 2018 12:13:02 -0300 Subject: [PATCH 02/14] Renamed showClosedTickets to closedTicketsShown and updated --- client/src/app-components/ticket-list.js | 12 +++++------- .../admin/panel/tickets/admin-panel-my-tickets.js | 13 ++++++------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/client/src/app-components/ticket-list.js b/client/src/app-components/ticket-list.js index 9739e4ce..3404ad0e 100644 --- a/client/src/app-components/ticket-list.js +++ b/client/src/app-components/ticket-list.js @@ -23,9 +23,8 @@ class TicketList extends React.Component { 'primary', 'secondary' ]), - showClosedTickets: React.PropTypes.bool, - filterClosedTickets: React.PropTypes.bool, - onShowClosedTicketsChange: React.PropTypes.func + closedTicketsShown: React.PropTypes.bool, + onClosedTicketsShownChange: React.PropTypes.func }; static defaultProps = { @@ -35,8 +34,7 @@ class TicketList extends React.Component { departments: [], ticketPath: '/dashboard/ticket/', type: 'primary', - showClosedTickets: false, - filterClosedTickets: false + closedTicketsShown: false }; state = { @@ -48,7 +46,7 @@ class TicketList extends React.Component {
{(this.props.type === 'secondary' && this.props.showDepartmentDropdown) ? this.renderDepartmentsDropDown() : null} - {this.props.filterClosedTickets ? this.renderFilterCheckbox() : null} + {this.props.onClosedTicketsShownChange ? this.renderFilterCheckbox() : null}
@@ -57,7 +55,7 @@ class TicketList extends React.Component { renderFilterCheckbox() { - return + return } renderDepartmentsDropDown() { diff --git a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js index 35c47b26..82d18344 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js @@ -23,7 +23,7 @@ class AdminPanelMyTickets extends React.Component { }; state = { - showClosedTickets: false + closedTicketsShown: false }; componentDidMount() { @@ -54,20 +54,19 @@ class AdminPanelMyTickets extends React.Component { return { userId: this.props.userId, departments: this.props.departments, - tickets: this.state.showClosedTickets ? this.props.tickets : this.filterOpenedTickets(this.props.tickets), + tickets: this.state.closedTicketsShown ? this.props.tickets : this.filterOpenedTickets(this.props.tickets), type: 'secondary', loading: this.props.loading, ticketPath: '/admin/panel/tickets/view-ticket/', - filterClosedTickets: true, - showClosedTickets: this.state.showClosedTickets, - onShowClosedTicketsChange: this.onShowClosedTicketsChange.bind(this) + closedTicketsShown: this.state.closedTicketsShown, + onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this) }; } - onShowClosedTicketsChange() { + onClosedTicketsShownChange() { this.setState(function(state) { return { - showClosedTickets: !state.showClosedTickets + closedTicketsShown: !state.closedTicketsShown }; }); } From 73b92bba86c7ec7e6fd6dde9d1acab6a55d189a9 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Wed, 17 Oct 2018 11:26:11 -0300 Subject: [PATCH 03/14] Moves filtering functionality to backend and adapts frontend --- client/src/actions/.admin-data-actions.js.swp | Bin 0 -> 12288 bytes client/src/actions/admin-data-actions.js | 4 ++-- .../tickets/.admin-panel-my-tickets.js.swp | Bin 0 -> 12288 bytes .../panel/tickets/admin-panel-my-tickets.js | 10 +++------- .../controllers/staff/.get-all-tickets.php.swp | Bin 0 -> 12288 bytes server/controllers/staff/.get-tickets.php.swp | Bin 0 -> 12288 bytes server/controllers/staff/get-tickets.php | 11 +++++++++-- server/models/DataStore.php | 4 ++++ 8 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 client/src/actions/.admin-data-actions.js.swp create mode 100644 client/src/app/admin/panel/tickets/.admin-panel-my-tickets.js.swp create mode 100644 server/controllers/staff/.get-all-tickets.php.swp create mode 100644 server/controllers/staff/.get-tickets.php.swp diff --git a/client/src/actions/.admin-data-actions.js.swp b/client/src/actions/.admin-data-actions.js.swp new file mode 100644 index 0000000000000000000000000000000000000000..d8617a8d7aeb7418ad2bd0c99f1ff3871ed3b1dd GIT binary patch literal 12288 zcmeI2Pixa)9LJxjFq}@sT~rh@6}sWFZ2sHa9IVz3hwC=l&M6|}Y5KH_rcIVSah6d% zsNg%$gU5OB4ea7o5q$w(^rGm&-4qeL`AwVbkI=1H6qMfspCrxm+VYDbU+5kKy3q_%&GmY z?8Ncj9>39%!|h7j-l^K{sfG-Y0Wv@a$N(8217v^YS?TpD5OdkLL4+k0E?PlyPcnw~G zr{EEAKoT4TTfw(ojC}?lz}JxC3p$V{jKpV1NwZ;4C->TEH?gKLZcJ15gI@ zz<1FPsE`3NKnBPF86X2>fDEh}C=W6PrCixoj;bg8L%)g7 z)ytAC&{cG7Y$iP#yBJTW)^y+q={ZHEdPQ}Zbaj-DId5ft%8=6AiAI%v%d5i&;loMS z_Ow~RypSqpzGh;6pLHfnrK2MwGl_WOTs&Db8-hB9xG7S;(1a*tgyVKJl&PMD3v&JO zl?&5FO*33Re|mpq-HKwqv3%q4>CNuUFyFYC5A!v&c1BS*y{w3tmMgOL+?)+PdWkFb z=1Y{qcYgjDg6as@#wY6HH{Ua{sZ@F*F_Vm^CMU*I@l7Ad zSY^~?IB4~m6u0~@=asC;anF?go5v_r&fp!VEpm6vwBOua>ra32{P9w@AAyb=L`Bjkdd~LbAZpB#kzqs8m(t*pppTf5{%V zO;-z23kQxZaNxiV!Ho-7RFFXQ2nUc*;e^O8jSRukFN1QAH0Gw)ETe&b%4F z`Oll@NjocJM>})$K>bOA>pnupr!F6Vn0!GFJVJSG zz+yJ{l?Bh~d8&nqp5nO7_5;^-JXC0=0RzSNG(~k?Mb%-VxT?)eg&W#Q1`}CUlVvq) ztriv6U-3r|EN%(~3I#$10=`?`f14?gf>p-!Pdp+KQPp+KQPp+KQP zp+KQPp}_x00q$-iuVK8mMPpu$zPGLWj(!$jg#v{Fg#v{Fg#v{Fg#v{Fg#v{Fg#v{F zg#v{Fx1a*5PRL{U;UiYB;+WV z0DHk6upM06LCD|WS8xS<49rO)c1V4h$z(?REun%0i0~&$1!JFVbI0tla2wc0JkUzjx@EiCVd=B0N255nu;0Ngc zEfDiyf>rP`m;gJ#FPNiC@juesf^l|pTpm^{Q{rUEf;bp5x3(0X45=O@6xT5P61VL7 zhS#?c9ic1FbG!zpetJ2maY{*c09)5~p^S>)hNzH4|9&Ln$wQWRl{apT{9h@$+AjhzFR4yNMN`p@QC#&+mP}#m$gc!VV^8b z4vg^$yHcK>J+jy?SBFg{>?h~h@CY-#X0p~`z}Xo-Wg0!ZLCYFWY%#Aq9Okr1kN#Q5 zv*2DNLTe!9&lRY?V%T~`)K`a8$#&b!2(xaG;w)LHKM|T_JI|ASxK8&Q3kliE=a}a( z1oS8)E%u$$b~;{$`zgGT(J1_juT;v4i)1Ke`jIO${}Ci^CB8>%A}$QQV|-CKSXm8h z8QEAG_$eY$*omk?>}DP~-B;}%%T+B&v0FE8gllZ`-cvJ8r6Zz0apjGFOPQ0jA)1G& zX+hpGcY7x&8=_NPI zc}B0#%loONdOgEla@+Rck)a@YeRQBDl!hq1JQ9 zQjfq})7L!174N%^2Fo+;#p!NmVX1Ru9{%K|V=PwH_vhuL=AX_@2_3~{g$T7hX2Z9< z6qAbV8+bQsj^#Qw-b*uvKIa5BPdy^ZU3ocXRW*GkeUtf-wnne&q}^y;A3q^2#Yj?1 zwLy=cNY-JzRL8<1(W{}w8x^@I!)HhhY0kEm;#CWoeA;EI#_QAZ(1c?KhKX%Y99yfN zW8r;_{a$UPfUj6#-=9?N=)rn!W|HwC8k((13$3AS>7=-P_ zp>GSDDMeTP{)(e|dU99ORqRctO2U$WZ*I_f(ki`cuNAlkeojZ=#(w_!N_ zH502aX(T5u**C_Il&{+O%({+kWeszPb>^p}5KA($H;t8=kU$irESBSHxa{|nhYsMq Z*yj#uWB`FQUh|k9oJp)?+@uXq^$#!kjeh_D literal 0 HcmV?d00001 diff --git a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js index 82d18344..80ec2d4d 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js @@ -44,17 +44,11 @@ class AdminPanelMyTickets extends React.Component { ); } - filterOpenedTickets(tickets){ - return _.filter(tickets, (ticket) => { - return !ticket.closed - }); - } - getProps() { return { userId: this.props.userId, departments: this.props.departments, - tickets: this.state.closedTicketsShown ? this.props.tickets : this.filterOpenedTickets(this.props.tickets), + tickets: this.props.tickets, type: 'secondary', loading: this.props.loading, ticketPath: '/admin/panel/tickets/view-ticket/', @@ -68,6 +62,8 @@ class AdminPanelMyTickets extends React.Component { return { closedTicketsShown: !state.closedTicketsShown }; + }, () => { + this.props.dispatch(AdminDataAction.retrieveMyTickets(this.state.closedTicketsShown * 1)); }); } diff --git a/server/controllers/staff/.get-all-tickets.php.swp b/server/controllers/staff/.get-all-tickets.php.swp new file mode 100644 index 0000000000000000000000000000000000000000..707340d36ff599530e296e46a91ea033524671bd GIT binary patch literal 12288 zcmeI2O>7%Q6vwAM&_G)#a70K0jkI>C?SvLpxJ`;%JB_6zc5OFml}6EeJ&qUFyW5%B z^h*T}fK*f=xBwS!oVk@76d|A|t{^11bEw1(Bm@%w*$?m52}T@2v&zrjyqS6L&40cW zWxBI#^%Z)&c#PmVM97l$$@=!gUy9LlkV4JfFED@O_V?Krp_|c**iy$vwwm942Y@`=u==3#Q`~q;0*zq$v6We-uE! zXa%$a6BIZ|jy^v(t129R_8I!*6Xz$?)kRtXt$F*)0 zp;LR~j&@ICkrd;-qB;T`ZH_yD{Pt^=j*6`&ujfL1^&pcT*x+$0oF^r_xBlL2?>^yPr@YbOX%HuXdX zTAZiGVr98q%Twb72}|&iHdmHR)gJRrF4YygO}jU5&?22qO@#%!2a7=)t*o=Y$C+sj zXl^>d<-5Xy7?_;(sv}R!wvf~mSxriK8P=nnQ{tG%CCDM(637(M`T`8F%=W zy{#?QJCCwlKI)Lq?QK&s+7Dby+MY{MtF5m5r74JWN}Wa9k*XPjhfz`$Vi7H7JGoAH zKd@Z3yug*YD7!o@(YaZ~OS230Mtak7V2i@SKgIm}R6CwDou;|P#DvU}r24koOLE$o zjT-Dmt6FQ((-$ems#P-Vu1;flJaCdzIaY{V^-EJJs~pz{kE^|LaLl2R><_`>IS{cxD0kc9>5 zRX8`V1YbuqGNU)RvY>fT}u|7A9NktN|(#PbbGibyQ}hJogqn- zMU@zgbB~9D*IKPct6eVFSJx`b_3CD`a<;a6n{Csvd!|~zVZ*8^&9%tn@p9S4ynt^@SPMq8(eA|CZdBB)be2@X_-$lVvK&(gdX~wG<3#I0gu7U}|50{TGPfr( zTVlAC&6Eh8!4G}O^zC?-(i^M6u*>*O3Qf(TT7ZI{3rttq@*4H}G_(|Vv|5isZ{!Zc zS3ya)*_J%8aL6!GXmnp@mW-<8*EeG4@dBga5>mbsgL)k>Y6YC*L00+m&8>T6}yju^FPT)|W56-hl5@la)Y&L7Hm3q5fZ>(nIYMQu3o{AN2 zVjbihzF^WpLX~n7qB)u~G@LGTnX)Pq7PtNI2W&qU7O{s!{iHX1Psp2e_Y3bkyp?8V2*Dv6?kX78 ZOYw4p-{jTf>fnB0DV`uFPpPZ(e*r)2+2H^H literal 0 HcmV?d00001 diff --git a/server/controllers/staff/.get-tickets.php.swp b/server/controllers/staff/.get-tickets.php.swp new file mode 100644 index 0000000000000000000000000000000000000000..c6af777cfde4025ee6f14537ce57174ea727b369 GIT binary patch literal 12288 zcmeHNU5i^q7@kT|Y^{pk^hOwM;Ur;`?Ak(0KT?;bw88EsY|_1nVVRsWNltrm<~TEF zwXCsMR(d6ZAYOXwU+}Jo{Q-jh48aR43dLvUoFvDsTlUh*4EyBFdEd$V@w_twVc+5E zc58#4tDPknuM_fR^HKlBV?U5{ZxJ#L<6)F?qj}~?{Aa2c%1?8H^``LCDUY?Pccd6g zHicqb7YUD5nj}I(p}5@TvhIsmOA$p}LZsPfR3CF))uDffYgJ1o$-=msI0hU82Q!fB zQT6-@a^~H&RkP^SJ8#oB-?(+~dZ%#=I0hU8jseGjW56-s7;p?Y2L5*p=;R2whpb-6 zGh4}@NA^AQSNCuXI0hU8jseGjW56-s7;p?Y1{?#90mp!2;5lS~1%!NmjF8WdBY6D( zfByUb&({cf1pEj*1ik|90iOT?z<^EQec-oO3HcTH8Tbi!0Ne+rKnOfJLC9agW8in- zA@D75ANUMlKnExTF9T0rA>G4KoU9dH*I0w;l&fNv1*7r+C+#NGfd0Y`zak&hdW z0mp!2z%k$$a11yG90UJN24;j}WS?eMuh0#4hf_V_biLD}lIv8)cnuW|M^>CLbd1s| z)Bc33Oe6V+DOb8iD|KS5m{G56@xxi)ziJuH+>S~_toZEdLClKf`sK{Xx424{Um;j% z2PS6vZx1W8>cz~w8AB$33m2rF1>My5xl*OmDf(oqjgLY-xhmoy)S-w=o*xOt1A3WW z@m6TxTlEIzYC}z!Y3oF%NEmk5axkNrzGB(@`4xh8H?vaezkR zlJRcyTUKkZWWXfs=w7Weoiz`!g)_XUMqSykB1%AYoqSv|3YTGqk z(n~5S|5)=lPz82v`sSci@JHWcNtkaZy4TNa3`(aUt(x?Ax>}|_&LCIS_V70Mb2sJw zU`CMxqjnQffwP%)WmAL`j$>>sharedTicketList->toArray()); + $closed = Controller::request('closed'); + if ($closed) { + Response::respondSuccess($user->sharedTicketList->toArray()); + } else { + Response::respondSuccess($user->withCondition('closed = ?', ['0'])->sharedTicketList->toArray()); + } } -} \ No newline at end of file +} diff --git a/server/models/DataStore.php b/server/models/DataStore.php index 43ec9d22..d4310fdc 100755 --- a/server/models/DataStore.php +++ b/server/models/DataStore.php @@ -150,6 +150,10 @@ abstract class DataStore { } } + public function withCondition($condition, $values) { + return new static($this->_bean->withCondition($condition, $values)); + } + private function updateBeanProp($key, $value) { if ($value instanceof DataStoreList) { $this->_bean[$key] = $value->toBeanList(); From c9ef8f176671a92fee361972eee8a8a6d0e4cb39 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 18 Oct 2018 12:02:39 -0300 Subject: [PATCH 04/14] Adds 'include closed' checkbox to all-tickets view with no integration with the search bar --- client/src/actions/admin-data-actions.js | 6 ++++-- .../panel/tickets/admin-panel-all-tickets.js | 17 +++++++++++++++-- server/controllers/staff/get-all-tickets.php | 13 ++++++++++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/client/src/actions/admin-data-actions.js b/client/src/actions/admin-data-actions.js index 24f2cbb6..ca348588 100644 --- a/client/src/actions/admin-data-actions.js +++ b/client/src/actions/admin-data-actions.js @@ -32,12 +32,14 @@ export default { }; }, - retrieveAllTickets(page = 1) { + retrieveAllTickets(page = 1, closed = 0) { + console.log('Closed is:'); + console.log(closed); return { type: 'ALL_TICKETS', payload: API.call({ path: '/staff/get-all-tickets', - data: {page} + data: {page, closed} }) }; }, diff --git a/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js index ec6be25d..c3ce18ed 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js @@ -20,7 +20,8 @@ class AdminPanelAllTickets extends React.Component { state = { page: 1, - query: '' + query: '', + closedTicketsShown: false }; componentDidMount() { @@ -52,10 +53,22 @@ class AdminPanelAllTickets extends React.Component { ticketPath: '/admin/panel/tickets/view-ticket/', onPageChange: this.onPageChange.bind(this), page: this.state.page, - pages: this.props.pages + pages: this.props.pages, + closedTicketsShown: this.state.closedTicketsShown, + onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this) }; } + onClosedTicketsShownChange() { + this.setState(function(state) { + return { + closedTicketsShown: !state.closedTicketsShown + }; + }, () => { + this.props.dispatch(AdminDataAction.retrieveAllTickets(this.state.page, this.state.closedTicketsShown * 1)); + }); + } + onSearch(query) { this.setState({query, page: 1}); diff --git a/server/controllers/staff/get-all-tickets.php b/server/controllers/staff/get-all-tickets.php index 03d2c8bf..a90fccfe 100755 --- a/server/controllers/staff/get-all-tickets.php +++ b/server/controllers/staff/get-all-tickets.php @@ -14,6 +14,7 @@ use Respect\Validation\Validator as DataValidator; * @apiPermission staff1 * * @apiParam {Number} page The page number. + * @apiParam {Boolean} closed Include closed tickets. * * @apiUse NO_PERMISSION * @apiUse INVALID_PAGE @@ -59,7 +60,8 @@ class GetAllTicketsStaffController extends Controller { $page = Controller::request('page'); $query = $this->getStaffDepartmentsQueryFilter(); - $query .= 'ORDER BY id DESC LIMIT 10 OFFSET ' . (($page-1)*10); + $query .= $this->getClosedFilter(); + $query .= ' ORDER BY id DESC LIMIT 10 OFFSET ' . (($page-1)*10); return Ticket::find($query); } @@ -81,4 +83,13 @@ class GetAllTicketsStaffController extends Controller { return $query; } + + private function getClosedFilter() { + $closed = Controller::request('closed')*1; + if ($closed) { + return ''; + } else { + return " AND (closed = '0')"; + } + } } From bd5fa9d520579a734c5a483d46d5b2d2db6ba786 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 18 Oct 2018 12:05:31 -0300 Subject: [PATCH 05/14] Removes lodash dependency from my-tickets --- client/src/app/admin/panel/tickets/admin-panel-my-tickets.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js index 80ec2d4d..a5f827ac 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js @@ -1,6 +1,5 @@ import React from 'react'; import {connect} from 'react-redux'; -import _ from 'lodash'; import i18n from 'lib-app/i18n'; From 5405f2b9ecc2480e66b3c6ef1a5ada152cc66245 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Mon, 22 Oct 2018 11:26:28 -0300 Subject: [PATCH 06/14] Aligns dropdown and checkbox to the left --- client/src/app-components/ticket-list.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/app-components/ticket-list.scss b/client/src/app-components/ticket-list.scss index 7e8c6d81..33783043 100644 --- a/client/src/app-components/ticket-list.scss +++ b/client/src/app-components/ticket-list.scss @@ -4,15 +4,17 @@ &__filters { margin-bottom: 25px; + text-align: left; } &__department-selector { display: inline-block; + margin-right: 25px; + text-align: center; } &__checkbox { display: inline-block; - margin-left: 25px; } &__number { From ac11db5505a2c281eba6ea1ae02c405a3f706d38 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Sat, 27 Oct 2018 17:42:45 -0300 Subject: [PATCH 07/14] Merges search-tickets with get-all-tickets --- client/src/actions/admin-data-actions.js | 6 ++--- server/controllers/staff/get-all-tickets.php | 24 ++++++++++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/client/src/actions/admin-data-actions.js b/client/src/actions/admin-data-actions.js index ca348588..8926b1f4 100644 --- a/client/src/actions/admin-data-actions.js +++ b/client/src/actions/admin-data-actions.js @@ -32,14 +32,12 @@ export default { }; }, - retrieveAllTickets(page = 1, closed = 0) { - console.log('Closed is:'); - console.log(closed); + retrieveAllTickets(page = 1, query = '', closed = 0) { return { type: 'ALL_TICKETS', payload: API.call({ path: '/staff/get-all-tickets', - data: {page, closed} + data: {page, query, closed} }) }; }, diff --git a/server/controllers/staff/get-all-tickets.php b/server/controllers/staff/get-all-tickets.php index a90fccfe..6957c118 100755 --- a/server/controllers/staff/get-all-tickets.php +++ b/server/controllers/staff/get-all-tickets.php @@ -2,18 +2,19 @@ use Respect\Validation\Validator as DataValidator; /** - * @api {post} /staff/get-all-tickets Get all tickets + * @api {post} /staff/get-all-tickets Get all tickets according to search * @apiVersion 4.3.0 * * @apiName Get all tickets * * @apiGroup Staff * - * @apiDescription This path retrieves all tickets. + * @apiDescription This path retrieves all tickets according to search and opened/closed filters. * * @apiPermission staff1 * * @apiParam {Number} page The page number. + * @apiParam {String} query Query string to search. * @apiParam {Boolean} closed Include closed tickets. * * @apiUse NO_PERMISSION @@ -59,11 +60,24 @@ class GetAllTicketsStaffController extends Controller { private function getTicketList() { $page = Controller::request('page'); - $query = $this->getStaffDepartmentsQueryFilter(); + $query = $this->getSearchQuery(); + $query .= $this->getStaffDepartmentsQueryFilter(); $query .= $this->getClosedFilter(); - $query .= ' ORDER BY id DESC LIMIT 10 OFFSET ' . (($page-1)*10); + $query .= "ORDER BY CASE WHEN (title LIKE ?) THEN 1 ELSE 2 END ASC LIMIT 10 OFFSET " . (($page-1)*10); - return Ticket::find($query); + return Ticket::find($query, [ + Controller::request('query') . '%', + '%' . Controller::request('query') . '%', + Controller::request('query') . '%' + ]); + } + + private function getSearchQuery() { + $page = Controller::request('page'); + + $query = " (title LIKE ? OR title LIKE ?) AND "; + + return $query; } private function getTotalPages() { From d3fc1489205327148a05cac63245c54af6e1b3fc Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Sat, 27 Oct 2018 18:00:20 -0300 Subject: [PATCH 08/14] Adapts frontend to match new backend merged path get-all-tickets --- .../panel/tickets/admin-panel-all-tickets.js | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js index c3ce18ed..bbc7ef70 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js @@ -25,7 +25,7 @@ class AdminPanelAllTickets extends React.Component { }; componentDidMount() { - this.props.dispatch(AdminDataAction.retrieveAllTickets()); + this.updateTicketList(); } render() { @@ -42,6 +42,14 @@ class AdminPanelAllTickets extends React.Component { ); } + updateTicketList() { + this.props.dispatch(AdminDataAction.retrieveAllTickets( + this.state.page, + this.state.query, + this.state.closedTicketsShown*1 + )); + } + getTicketListProps() { return { userId: this.props.userId, @@ -65,28 +73,20 @@ class AdminPanelAllTickets extends React.Component { closedTicketsShown: !state.closedTicketsShown }; }, () => { - this.props.dispatch(AdminDataAction.retrieveAllTickets(this.state.page, this.state.closedTicketsShown * 1)); + this.updateTicketList(); }); } onSearch(query) { - this.setState({query, page: 1}); - - if(query) { - this.props.dispatch(AdminDataAction.searchTickets(query)); - } else { - this.props.dispatch(AdminDataAction.retrieveAllTickets()); - } + this.setState({query, page: 1}, () => { + this.updateTicketList(); + }); } onPageChange(event) { - this.setState({page: event.target.value}); - - if(this.state.query) { - this.props.dispatch(AdminDataAction.searchTickets(this.state.query, event.target.value)); - } else { - this.props.dispatch(AdminDataAction.retrieveAllTickets(event.target.value)); - } + this.setState({page: event.target.value}, () => { + this.updateTicketList(); + }); } } From debe851546b95408e1039e5d3f0df961d9fa2e25 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Sat, 27 Oct 2018 18:03:33 -0300 Subject: [PATCH 09/14] Deletes deprecated search-tickets path --- server/controllers/staff/search-tickets.php | 100 -------------------- 1 file changed, 100 deletions(-) delete mode 100755 server/controllers/staff/search-tickets.php diff --git a/server/controllers/staff/search-tickets.php b/server/controllers/staff/search-tickets.php deleted file mode 100755 index 783e6424..00000000 --- a/server/controllers/staff/search-tickets.php +++ /dev/null @@ -1,100 +0,0 @@ - 'staff_1', - 'requestData' => [ - 'query' => [ - 'validation' => DataValidator::length(1), - 'error' => ERRORS::INVALID_QUERY - ], - 'page' => [ - 'validation' => DataValidator::numeric(), - 'error' => ERRORS::INVALID_PAGE - ] - ] - ]; - } - - public function handler() { - Response::respondSuccess([ - 'tickets' => $this->getTicketList()->toArray(), - 'pages' => $this->getTotalPages() - ]); - } - - private function getTicketList() { - $query = $this->getSearchQuery(); - - return Ticket::find($query, [ - Controller::request('query') . '%', - '%' . Controller::request('query') . '%', - Controller::request('query') . '%' - ]); - } - - private function getSearchQuery() { - $page = Controller::request('page'); - - $query = " (title LIKE ? OR title LIKE ?) AND "; - $query .= $this->getStaffDepartmentsQueryFilter(); - $query .= "ORDER BY CASE WHEN (title LIKE ?) THEN 1 ELSE 2 END ASC LIMIT 10 OFFSET " . (($page-1)*10); - - return $query; - } - - private function getTotalPages() { - $query = " (title LIKE ? OR title LIKE ?) AND "; - $query .= $this->getStaffDepartmentsQueryFilter(); - - $ticketQuantity = Ticket::count($query, [ - Controller::request('query') . '%', - '%' . Controller::request('query') . '%' - ]); - - return ceil($ticketQuantity / 10); - } - - private function getStaffDepartmentsQueryFilter() { - $user = Controller::getLoggedUser(); - - $query = ' ('; - foreach ($user->sharedDepartmentList as $department) { - $query .= 'department_id=' . $department->id . ' OR '; - } - $query = substr($query, 0, -3); - $query .= ') '; - - return $query; - } -} \ No newline at end of file From 1edf28294299a75a6cea2d03e00fcec9b8ec2fb4 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Sat, 27 Oct 2018 18:22:08 -0300 Subject: [PATCH 10/14] Revert "Deletes deprecated search-tickets path" This reverts commit debe851546b95408e1039e5d3f0df961d9fa2e25. --- server/controllers/staff/search-tickets.php | 100 ++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100755 server/controllers/staff/search-tickets.php diff --git a/server/controllers/staff/search-tickets.php b/server/controllers/staff/search-tickets.php new file mode 100755 index 00000000..783e6424 --- /dev/null +++ b/server/controllers/staff/search-tickets.php @@ -0,0 +1,100 @@ + 'staff_1', + 'requestData' => [ + 'query' => [ + 'validation' => DataValidator::length(1), + 'error' => ERRORS::INVALID_QUERY + ], + 'page' => [ + 'validation' => DataValidator::numeric(), + 'error' => ERRORS::INVALID_PAGE + ] + ] + ]; + } + + public function handler() { + Response::respondSuccess([ + 'tickets' => $this->getTicketList()->toArray(), + 'pages' => $this->getTotalPages() + ]); + } + + private function getTicketList() { + $query = $this->getSearchQuery(); + + return Ticket::find($query, [ + Controller::request('query') . '%', + '%' . Controller::request('query') . '%', + Controller::request('query') . '%' + ]); + } + + private function getSearchQuery() { + $page = Controller::request('page'); + + $query = " (title LIKE ? OR title LIKE ?) AND "; + $query .= $this->getStaffDepartmentsQueryFilter(); + $query .= "ORDER BY CASE WHEN (title LIKE ?) THEN 1 ELSE 2 END ASC LIMIT 10 OFFSET " . (($page-1)*10); + + return $query; + } + + private function getTotalPages() { + $query = " (title LIKE ? OR title LIKE ?) AND "; + $query .= $this->getStaffDepartmentsQueryFilter(); + + $ticketQuantity = Ticket::count($query, [ + Controller::request('query') . '%', + '%' . Controller::request('query') . '%' + ]); + + return ceil($ticketQuantity / 10); + } + + private function getStaffDepartmentsQueryFilter() { + $user = Controller::getLoggedUser(); + + $query = ' ('; + foreach ($user->sharedDepartmentList as $department) { + $query .= 'department_id=' . $department->id . ' OR '; + } + $query = substr($query, 0, -3); + $query .= ') '; + + return $query; + } +} \ No newline at end of file From b2fb45262b8d8971134646e206d1e64649f2b00e Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Sat, 27 Oct 2018 22:25:29 -0300 Subject: [PATCH 11/14] Adds i18n for show_closed_tickets --- client/src/app-components/ticket-list.js | 2 +- client/src/data/languages/br.js | 1 + client/src/data/languages/cn.js | 1 + client/src/data/languages/de.js | 1 + client/src/data/languages/en.js | 1 + client/src/data/languages/es.js | 1 + client/src/data/languages/fr.js | 1 + client/src/data/languages/gr.js | 1 + client/src/data/languages/in.js | 1 + client/src/data/languages/it.js | 1 + client/src/data/languages/jp.js | 1 + client/src/data/languages/main.py | 78 ++++++++++++++++++++++++ client/src/data/languages/nl.js | 1 + client/src/data/languages/pt.js | 1 + client/src/data/languages/ru.js | 1 + client/src/data/languages/tr.js | 1 + 16 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 client/src/data/languages/main.py diff --git a/client/src/app-components/ticket-list.js b/client/src/app-components/ticket-list.js index 3404ad0e..86ee0403 100644 --- a/client/src/app-components/ticket-list.js +++ b/client/src/app-components/ticket-list.js @@ -55,7 +55,7 @@ class TicketList extends React.Component { renderFilterCheckbox() { - return + return } renderDepartmentsDropDown() { diff --git a/client/src/data/languages/br.js b/client/src/data/languages/br.js index dd90c3cb..4c42eb70 100644 --- a/client/src/data/languages/br.js +++ b/client/src/data/languages/br.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privado', 'ENABLE_USER': 'Ativar usuário', 'DISABLE_USER': 'Desativar usuário', + 'SHOW_CLOSED_TICKETS': 'Mostrar ingressos fechados', 'CHART_CREATE_TICKET': 'Chamados criados', 'CHART_CLOSE': 'Chamados fechados', diff --git a/client/src/data/languages/cn.js b/client/src/data/languages/cn.js index 1927a8f4..7646a97c 100644 --- a/client/src/data/languages/cn.js +++ b/client/src/data/languages/cn.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': '私人的', 'ENABLE_USER': '启用用户', 'DISABLE_USER': '禁用用户', + 'SHOW_CLOSED_TICKETS': '显示已关闭的门票', 'CHART_CREATE_TICKET': '已創建門票', 'CHART_CLOSE': '門票已關閉', diff --git a/client/src/data/languages/de.js b/client/src/data/languages/de.js index bc04b41a..2d106a88 100644 --- a/client/src/data/languages/de.js +++ b/client/src/data/languages/de.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'Privatgelände', 'ENABLE_USER': 'Benutzer aktivieren', 'DISABLE_USER': 'Benutzer deaktivieren', + 'SHOW_CLOSED_TICKETS': 'Geschlossene Tickets anzeigen', 'CHART_CREATE_TICKET': 'Tickets erstellt', 'CHART_CLOSE': 'Tickets geschlossen', diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index fa7cbaef..6f106162 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'Private', 'ENABLE_USER': 'Enable User', 'DISABLE_USER': 'Disable User', + 'SHOW_CLOSED_TICKETS': 'Show Closed Tickets', 'CHART_CREATE_TICKET': 'Tickets created', 'CHART_CLOSE': 'Tickets closed', diff --git a/client/src/data/languages/es.js b/client/src/data/languages/es.js index 957f536f..a90ff4dd 100644 --- a/client/src/data/languages/es.js +++ b/client/src/data/languages/es.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privado', 'ENABLE_USER': 'Habilitar usuario', 'DISABLE_USER': 'Deshabilitar usuario', + 'SHOW_CLOSED_TICKETS': 'Mostrar Tickets Cerrados', 'CHART_CREATE_TICKET': 'Tickets creados', 'CHART_CLOSE': 'Tickets cerrados', diff --git a/client/src/data/languages/fr.js b/client/src/data/languages/fr.js index bfe97276..b4266a8f 100644 --- a/client/src/data/languages/fr.js +++ b/client/src/data/languages/fr.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privé', 'ENABLE_USER': 'Activer l\'utilisateur', 'DISABLE_USER': 'Désactiver l\'utilisateur', + 'SHOW_CLOSED_TICKETS': 'Afficher les billets fermés', 'CHART_CREATE_TICKET': 'Tickets créés', 'CHART_CLOSE': 'Tickets fermés', diff --git a/client/src/data/languages/gr.js b/client/src/data/languages/gr.js index 4c3e89ba..f0a7644a 100644 --- a/client/src/data/languages/gr.js +++ b/client/src/data/languages/gr.js @@ -187,6 +187,7 @@ 'PRIVATE': 'ιδιωτικός', 'ENABLE_USER': 'Ενεργοποίηση χρήστη', 'DISABLE_USER': 'Απενεργοποίηση χρήστη', + 'SHOW_CLOSED_TICKETS': 'Εμφάνιση κλειστών εισιτηρίων', 'CHART_CREATE_TICKET': 'Τα εισιτήρια δημιουργήθηκαν', 'CHART_CLOSE': 'Τα εισιτήρια κλείσανε', diff --git a/client/src/data/languages/in.js b/client/src/data/languages/in.js index bf38b957..b8ae5d20 100644 --- a/client/src/data/languages/in.js +++ b/client/src/data/languages/in.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'निजी', 'ENABLE_USER': 'उपयोगकर्ता सक्षम करें', 'DISABLE_USER': 'उपयोगकर्ता को अक्षम करें', + 'SHOW_CLOSED_TICKETS': 'बंद टिकट दिखाएं', 'CHART_CREATE_TICKET': 'टिकट बनाया', 'CHART_CLOSE': 'टिकट बंद कर दिया', diff --git a/client/src/data/languages/it.js b/client/src/data/languages/it.js index 001867b2..9477d161 100644 --- a/client/src/data/languages/it.js +++ b/client/src/data/languages/it.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privato', 'ENABLE_USER': 'Abilita utente', 'DISABLE_USER': 'Disabilita utente', + 'SHOW_CLOSED_TICKETS': 'Mostra biglietti chiusi', 'CHART_CREATE_TICKET': 'Tickets creato', 'CHART_CLOSE': 'Tickets chiuso', diff --git a/client/src/data/languages/jp.js b/client/src/data/languages/jp.js index 77f35216..b7d2b5d1 100644 --- a/client/src/data/languages/jp.js +++ b/client/src/data/languages/jp.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'プライベート', 'ENABLE_USER': 'ユーザーを有効にする', 'DISABLE_USER': 'ユーザーを無効にする', + 'SHOW_CLOSED_TICKETS': 'クローズドチケットを表示する', 'CHART_CREATE_TICKET': '作成されたチケット', 'CHART_CLOSE': 'チケットが閉じられました', diff --git a/client/src/data/languages/main.py b/client/src/data/languages/main.py new file mode 100644 index 00000000..027b778f --- /dev/null +++ b/client/src/data/languages/main.py @@ -0,0 +1,78 @@ +from googletrans import Translator +import colorama +import ast + +class Translater: + def __init__(self): + colorama.init(autoreset=True) + self.data = self.get_language_data('en') + + def get_language_description(self, data): + return data[15:len(data)-2]; + + def get_dest_language(self, lang): + if lang == 'br': + return 'pt' + if lang == 'cn': + return 'zh-cn' + if lang == 'gr': + return 'el' + if lang == 'in': + return 'hi' + if lang == 'jp': + return 'ja' + return lang + + def get_language_data(self, lang): + data = {} + with open(lang + '.js', encoding='utf-8') as f: + lineno = 0 + for l in f: + lineno += 1 + last_char = l[-2:-1] + if last_char != ',' and last_char != "'": + continue + line_data = ast.literal_eval('{'+l+'}') + if line_data.keys(): + key = list(line_data.keys())[0] + data[key] = { + 'value': line_data[key], + 'lineno': lineno, + } + return data + + def add_property(self, lang, key, value, line): + """Adds a property to a lang.js file. + """ + f = open(lang + '.js', 'r') + contents = f.readlines() + f.close() + + new_line = " '{0}': '{1}',\n".format(key, value) + print(colorama.Fore.GREEN + str(line) + ': ' + new_line[:-1]) + contents.insert(line - 1, new_line) + + f = open(lang + '.js', 'w') + contents = "".join(contents) + f.write(contents) + f.close() + + def main(self): + translator = Translator() + language_list = ['br', 'cn', 'de', 'es', 'fr', 'gr', 'in', 'it', + 'jp', 'nl', 'pt', 'ru', 'tr'] + + for language in language_list: + print('Translating for language: ' + language) + dest_language = self.get_dest_language(language) + odata = self.get_language_data(language) + + for key in self.data.keys(): + if not key in odata: + value = translator.translate(self.data[key]['value'], + src='en', dest=dest_language).text + self.add_property(language, key, value, self.data[key]['lineno']) + +if __name__ == "__main__": + translater = Translater() + translater.main() diff --git a/client/src/data/languages/nl.js b/client/src/data/languages/nl.js index df5daa20..a2408144 100644 --- a/client/src/data/languages/nl.js +++ b/client/src/data/languages/nl.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privaat', 'ENABLE_USER': 'Schakel gebruiker in', 'DISABLE_USER': 'Gebruiker uitschakelen', + 'SHOW_CLOSED_TICKETS': 'Toon gesloten tickets', 'CHART_CREATE_TICKET': 'Aangemaakte incidenten', 'CHART_CLOSE': 'Gesloten incidenten', diff --git a/client/src/data/languages/pt.js b/client/src/data/languages/pt.js index f7c338d8..6d529db8 100644 --- a/client/src/data/languages/pt.js +++ b/client/src/data/languages/pt.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privado', 'ENABLE_USER': 'Ativar usuário', 'DISABLE_USER': 'Desativar usuário', + 'SHOW_CLOSED_TICKETS': 'Mostrar ingressos fechados', 'CHART_CREATE_TICKET': 'Ingressos criados', 'CHART_CLOSE': 'Ingressos fechados', diff --git a/client/src/data/languages/ru.js b/client/src/data/languages/ru.js index 1da1eb24..ba4f2bda 100644 --- a/client/src/data/languages/ru.js +++ b/client/src/data/languages/ru.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'частный', 'ENABLE_USER': 'Включить пользователя', 'DISABLE_USER': 'Отключить пользователя', + 'SHOW_CLOSED_TICKETS': 'Показать закрытые билеты', 'CHART_CREATE_TICKET': 'Билеты создано', 'CHART_CLOSE': ' Билеты закрыты', diff --git a/client/src/data/languages/tr.js b/client/src/data/languages/tr.js index 404ff4b4..48c932df 100644 --- a/client/src/data/languages/tr.js +++ b/client/src/data/languages/tr.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'gizli', 'ENABLE_USER': 'Kullanıcıyı Etkinleştir', 'DISABLE_USER': 'Kullanıcıyı Devre Dışı Bırak', + 'SHOW_CLOSED_TICKETS': 'Kapalı Biletleri Göster', 'CHART_CREATE_TICKET': 'Biletler oluşturuldu', 'CHART_CLOSE': 'Biletler kapandı', From 5680660e5e7bec1c77edfd048f90917796d2f1ae Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Sat, 27 Oct 2018 22:25:29 -0300 Subject: [PATCH 12/14] Adds i18n for show_closed_tickets --- client/src/app-components/ticket-list.js | 2 +- client/src/data/languages/br.js | 1 + client/src/data/languages/cn.js | 1 + client/src/data/languages/de.js | 1 + client/src/data/languages/en.js | 1 + client/src/data/languages/es.js | 1 + client/src/data/languages/fr.js | 1 + client/src/data/languages/gr.js | 1 + client/src/data/languages/in.js | 1 + client/src/data/languages/it.js | 1 + client/src/data/languages/jp.js | 1 + client/src/data/languages/nl.js | 1 + client/src/data/languages/pt.js | 1 + client/src/data/languages/ru.js | 1 + client/src/data/languages/tr.js | 1 + 15 files changed, 15 insertions(+), 1 deletion(-) diff --git a/client/src/app-components/ticket-list.js b/client/src/app-components/ticket-list.js index 3404ad0e..86ee0403 100644 --- a/client/src/app-components/ticket-list.js +++ b/client/src/app-components/ticket-list.js @@ -55,7 +55,7 @@ class TicketList extends React.Component { renderFilterCheckbox() { - return + return } renderDepartmentsDropDown() { diff --git a/client/src/data/languages/br.js b/client/src/data/languages/br.js index dd90c3cb..4c42eb70 100644 --- a/client/src/data/languages/br.js +++ b/client/src/data/languages/br.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privado', 'ENABLE_USER': 'Ativar usuário', 'DISABLE_USER': 'Desativar usuário', + 'SHOW_CLOSED_TICKETS': 'Mostrar ingressos fechados', 'CHART_CREATE_TICKET': 'Chamados criados', 'CHART_CLOSE': 'Chamados fechados', diff --git a/client/src/data/languages/cn.js b/client/src/data/languages/cn.js index 1927a8f4..7646a97c 100644 --- a/client/src/data/languages/cn.js +++ b/client/src/data/languages/cn.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': '私人的', 'ENABLE_USER': '启用用户', 'DISABLE_USER': '禁用用户', + 'SHOW_CLOSED_TICKETS': '显示已关闭的门票', 'CHART_CREATE_TICKET': '已創建門票', 'CHART_CLOSE': '門票已關閉', diff --git a/client/src/data/languages/de.js b/client/src/data/languages/de.js index bc04b41a..2d106a88 100644 --- a/client/src/data/languages/de.js +++ b/client/src/data/languages/de.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'Privatgelände', 'ENABLE_USER': 'Benutzer aktivieren', 'DISABLE_USER': 'Benutzer deaktivieren', + 'SHOW_CLOSED_TICKETS': 'Geschlossene Tickets anzeigen', 'CHART_CREATE_TICKET': 'Tickets erstellt', 'CHART_CLOSE': 'Tickets geschlossen', diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index fa7cbaef..6f106162 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'Private', 'ENABLE_USER': 'Enable User', 'DISABLE_USER': 'Disable User', + 'SHOW_CLOSED_TICKETS': 'Show Closed Tickets', 'CHART_CREATE_TICKET': 'Tickets created', 'CHART_CLOSE': 'Tickets closed', diff --git a/client/src/data/languages/es.js b/client/src/data/languages/es.js index 957f536f..a90ff4dd 100644 --- a/client/src/data/languages/es.js +++ b/client/src/data/languages/es.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privado', 'ENABLE_USER': 'Habilitar usuario', 'DISABLE_USER': 'Deshabilitar usuario', + 'SHOW_CLOSED_TICKETS': 'Mostrar Tickets Cerrados', 'CHART_CREATE_TICKET': 'Tickets creados', 'CHART_CLOSE': 'Tickets cerrados', diff --git a/client/src/data/languages/fr.js b/client/src/data/languages/fr.js index bfe97276..b4266a8f 100644 --- a/client/src/data/languages/fr.js +++ b/client/src/data/languages/fr.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privé', 'ENABLE_USER': 'Activer l\'utilisateur', 'DISABLE_USER': 'Désactiver l\'utilisateur', + 'SHOW_CLOSED_TICKETS': 'Afficher les billets fermés', 'CHART_CREATE_TICKET': 'Tickets créés', 'CHART_CLOSE': 'Tickets fermés', diff --git a/client/src/data/languages/gr.js b/client/src/data/languages/gr.js index 4c3e89ba..f0a7644a 100644 --- a/client/src/data/languages/gr.js +++ b/client/src/data/languages/gr.js @@ -187,6 +187,7 @@ 'PRIVATE': 'ιδιωτικός', 'ENABLE_USER': 'Ενεργοποίηση χρήστη', 'DISABLE_USER': 'Απενεργοποίηση χρήστη', + 'SHOW_CLOSED_TICKETS': 'Εμφάνιση κλειστών εισιτηρίων', 'CHART_CREATE_TICKET': 'Τα εισιτήρια δημιουργήθηκαν', 'CHART_CLOSE': 'Τα εισιτήρια κλείσανε', diff --git a/client/src/data/languages/in.js b/client/src/data/languages/in.js index bf38b957..b8ae5d20 100644 --- a/client/src/data/languages/in.js +++ b/client/src/data/languages/in.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'निजी', 'ENABLE_USER': 'उपयोगकर्ता सक्षम करें', 'DISABLE_USER': 'उपयोगकर्ता को अक्षम करें', + 'SHOW_CLOSED_TICKETS': 'बंद टिकट दिखाएं', 'CHART_CREATE_TICKET': 'टिकट बनाया', 'CHART_CLOSE': 'टिकट बंद कर दिया', diff --git a/client/src/data/languages/it.js b/client/src/data/languages/it.js index 001867b2..9477d161 100644 --- a/client/src/data/languages/it.js +++ b/client/src/data/languages/it.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privato', 'ENABLE_USER': 'Abilita utente', 'DISABLE_USER': 'Disabilita utente', + 'SHOW_CLOSED_TICKETS': 'Mostra biglietti chiusi', 'CHART_CREATE_TICKET': 'Tickets creato', 'CHART_CLOSE': 'Tickets chiuso', diff --git a/client/src/data/languages/jp.js b/client/src/data/languages/jp.js index 77f35216..b7d2b5d1 100644 --- a/client/src/data/languages/jp.js +++ b/client/src/data/languages/jp.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'プライベート', 'ENABLE_USER': 'ユーザーを有効にする', 'DISABLE_USER': 'ユーザーを無効にする', + 'SHOW_CLOSED_TICKETS': 'クローズドチケットを表示する', 'CHART_CREATE_TICKET': '作成されたチケット', 'CHART_CLOSE': 'チケットが閉じられました', diff --git a/client/src/data/languages/nl.js b/client/src/data/languages/nl.js index df5daa20..a2408144 100644 --- a/client/src/data/languages/nl.js +++ b/client/src/data/languages/nl.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privaat', 'ENABLE_USER': 'Schakel gebruiker in', 'DISABLE_USER': 'Gebruiker uitschakelen', + 'SHOW_CLOSED_TICKETS': 'Toon gesloten tickets', 'CHART_CREATE_TICKET': 'Aangemaakte incidenten', 'CHART_CLOSE': 'Gesloten incidenten', diff --git a/client/src/data/languages/pt.js b/client/src/data/languages/pt.js index f7c338d8..6d529db8 100644 --- a/client/src/data/languages/pt.js +++ b/client/src/data/languages/pt.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'privado', 'ENABLE_USER': 'Ativar usuário', 'DISABLE_USER': 'Desativar usuário', + 'SHOW_CLOSED_TICKETS': 'Mostrar ingressos fechados', 'CHART_CREATE_TICKET': 'Ingressos criados', 'CHART_CLOSE': 'Ingressos fechados', diff --git a/client/src/data/languages/ru.js b/client/src/data/languages/ru.js index 1da1eb24..ba4f2bda 100644 --- a/client/src/data/languages/ru.js +++ b/client/src/data/languages/ru.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'частный', 'ENABLE_USER': 'Включить пользователя', 'DISABLE_USER': 'Отключить пользователя', + 'SHOW_CLOSED_TICKETS': 'Показать закрытые билеты', 'CHART_CREATE_TICKET': 'Билеты создано', 'CHART_CLOSE': ' Билеты закрыты', diff --git a/client/src/data/languages/tr.js b/client/src/data/languages/tr.js index 404ff4b4..48c932df 100644 --- a/client/src/data/languages/tr.js +++ b/client/src/data/languages/tr.js @@ -187,6 +187,7 @@ export default { 'PRIVATE': 'gizli', 'ENABLE_USER': 'Kullanıcıyı Etkinleştir', 'DISABLE_USER': 'Kullanıcıyı Devre Dışı Bırak', + 'SHOW_CLOSED_TICKETS': 'Kapalı Biletleri Göster', 'CHART_CREATE_TICKET': 'Biletler oluşturuldu', 'CHART_CLOSE': 'Biletler kapandı', From 917520601e5ae00d877f093d234f1e61101a9381 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Wed, 7 Nov 2018 10:34:14 -0300 Subject: [PATCH 13/14] Fixes order of tickets retrieved by get-all-tickets --- server/controllers/staff/get-all-tickets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/controllers/staff/get-all-tickets.php b/server/controllers/staff/get-all-tickets.php index 6957c118..7c38a489 100755 --- a/server/controllers/staff/get-all-tickets.php +++ b/server/controllers/staff/get-all-tickets.php @@ -63,7 +63,7 @@ class GetAllTicketsStaffController extends Controller { $query = $this->getSearchQuery(); $query .= $this->getStaffDepartmentsQueryFilter(); $query .= $this->getClosedFilter(); - $query .= "ORDER BY CASE WHEN (title LIKE ?) THEN 1 ELSE 2 END ASC LIMIT 10 OFFSET " . (($page-1)*10); + $query .= "ORDER BY CASE WHEN (title LIKE ?) THEN 1 ELSE 2 END ASC, id DESC LIMIT 10 OFFSET " . (($page-1)*10); return Ticket::find($query, [ Controller::request('query') . '%', From 0657857981e0c5d8002c1915503790d240bea927 Mon Sep 17 00:00:00 2001 From: Maxi Redigonda Date: Thu, 15 Nov 2018 16:22:35 -0300 Subject: [PATCH 14/14] Deleting files uploaded by mistake :'( --- client/src/actions/.admin-data-actions.js.swp | Bin 12288 -> 0 bytes .../tickets/.admin-panel-my-tickets.js.swp | Bin 12288 -> 0 bytes client/src/data/languages/main.py | 78 ------------------ .../staff/.get-all-tickets.php.swp | Bin 12288 -> 0 bytes server/controllers/staff/.get-tickets.php.swp | Bin 12288 -> 0 bytes 5 files changed, 78 deletions(-) delete mode 100644 client/src/actions/.admin-data-actions.js.swp delete mode 100644 client/src/app/admin/panel/tickets/.admin-panel-my-tickets.js.swp delete mode 100644 client/src/data/languages/main.py delete mode 100644 server/controllers/staff/.get-all-tickets.php.swp delete mode 100644 server/controllers/staff/.get-tickets.php.swp diff --git a/client/src/actions/.admin-data-actions.js.swp b/client/src/actions/.admin-data-actions.js.swp deleted file mode 100644 index d8617a8d7aeb7418ad2bd0c99f1ff3871ed3b1dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2Pixa)9LJxjFq}@sT~rh@6}sWFZ2sHa9IVz3hwC=l&M6|}Y5KH_rcIVSah6d% zsNg%$gU5OB4ea7o5q$w(^rGm&-4qeL`AwVbkI=1H6qMfspCrxm+VYDbU+5kKy3q_%&GmY z?8Ncj9>39%!|h7j-l^K{sfG-Y0Wv@a$N(8217v^YS?TpD5OdkLL4+k0E?PlyPcnw~G zr{EEAKoT4TTfw(ojC}?lz}JxC3p$V{jKpV1NwZ;4C->TEH?gKLZcJ15gI@ zz<1FPsE`3NKnBPF86X2>fDEh}C=W6PrCixoj;bg8L%)g7 z)ytAC&{cG7Y$iP#yBJTW)^y+q={ZHEdPQ}Zbaj-DId5ft%8=6AiAI%v%d5i&;loMS z_Ow~RypSqpzGh;6pLHfnrK2MwGl_WOTs&Db8-hB9xG7S;(1a*tgyVKJl&PMD3v&JO zl?&5FO*33Re|mpq-HKwqv3%q4>CNuUFyFYC5A!v&c1BS*y{w3tmMgOL+?)+PdWkFb z=1Y{qcYgjDg6as@#wY6HH{Ua{sZ@F*F_Vm^CMU*I@l7Ad zSY^~?IB4~m6u0~@=asC;anF?go5v_r&fp!VEpm6vwBOua>ra32{P9w@AAyb=L`Bjkdd~LbAZpB#kzqs8m(t*pppTf5{%V zO;-z23kQxZaNxiV!Ho-7RFFXQ2nUc*;e^O8jSRukFN1QAH0Gw)ETe&b%4F z`Oll@NjocJM>})$K>bOA>pnupr!F6Vn0!GFJVJSG zz+yJ{l?Bh~d8&nqp5nO7_5;^-JXC0=0RzSNG(~k?Mb%-VxT?)eg&W#Q1`}CUlVvq) ztriv6U-3r|EN%(~3I#$10=`?`f14?gf>p-!Pdp+KQPp+KQPp+KQP zp+KQPp}_x00q$-iuVK8mMPpu$zPGLWj(!$jg#v{Fg#v{Fg#v{Fg#v{Fg#v{Fg#v{F zg#v{Fx1a*5PRL{U;UiYB;+WV z0DHk6upM06LCD|WS8xS<49rO)c1V4h$z(?REun%0i0~&$1!JFVbI0tla2wc0JkUzjx@EiCVd=B0N255nu;0Ngc zEfDiyf>rP`m;gJ#FPNiC@juesf^l|pTpm^{Q{rUEf;bp5x3(0X45=O@6xT5P61VL7 zhS#?c9ic1FbG!zpetJ2maY{*c09)5~p^S>)hNzH4|9&Ln$wQWRl{apT{9h@$+AjhzFR4yNMN`p@QC#&+mP}#m$gc!VV^8b z4vg^$yHcK>J+jy?SBFg{>?h~h@CY-#X0p~`z}Xo-Wg0!ZLCYFWY%#Aq9Okr1kN#Q5 zv*2DNLTe!9&lRY?V%T~`)K`a8$#&b!2(xaG;w)LHKM|T_JI|ASxK8&Q3kliE=a}a( z1oS8)E%u$$b~;{$`zgGT(J1_juT;v4i)1Ke`jIO${}Ci^CB8>%A}$QQV|-CKSXm8h z8QEAG_$eY$*omk?>}DP~-B;}%%T+B&v0FE8gllZ`-cvJ8r6Zz0apjGFOPQ0jA)1G& zX+hpGcY7x&8=_NPI zc}B0#%loONdOgEla@+Rck)a@YeRQBDl!hq1JQ9 zQjfq})7L!174N%^2Fo+;#p!NmVX1Ru9{%K|V=PwH_vhuL=AX_@2_3~{g$T7hX2Z9< z6qAbV8+bQsj^#Qw-b*uvKIa5BPdy^ZU3ocXRW*GkeUtf-wnne&q}^y;A3q^2#Yj?1 zwLy=cNY-JzRL8<1(W{}w8x^@I!)HhhY0kEm;#CWoeA;EI#_QAZ(1c?KhKX%Y99yfN zW8r;_{a$UPfUj6#-=9?N=)rn!W|HwC8k((13$3AS>7=-P_ zp>GSDDMeTP{)(e|dU99ORqRctO2U$WZ*I_f(ki`cuNAlkeojZ=#(w_!N_ zH502aX(T5u**C_Il&{+O%({+kWeszPb>^p}5KA($H;t8=kU$irESBSHxa{|nhYsMq Z*yj#uWB`FQUh|k9oJp)?+@uXq^$#!kjeh_D diff --git a/client/src/data/languages/main.py b/client/src/data/languages/main.py deleted file mode 100644 index 027b778f..00000000 --- a/client/src/data/languages/main.py +++ /dev/null @@ -1,78 +0,0 @@ -from googletrans import Translator -import colorama -import ast - -class Translater: - def __init__(self): - colorama.init(autoreset=True) - self.data = self.get_language_data('en') - - def get_language_description(self, data): - return data[15:len(data)-2]; - - def get_dest_language(self, lang): - if lang == 'br': - return 'pt' - if lang == 'cn': - return 'zh-cn' - if lang == 'gr': - return 'el' - if lang == 'in': - return 'hi' - if lang == 'jp': - return 'ja' - return lang - - def get_language_data(self, lang): - data = {} - with open(lang + '.js', encoding='utf-8') as f: - lineno = 0 - for l in f: - lineno += 1 - last_char = l[-2:-1] - if last_char != ',' and last_char != "'": - continue - line_data = ast.literal_eval('{'+l+'}') - if line_data.keys(): - key = list(line_data.keys())[0] - data[key] = { - 'value': line_data[key], - 'lineno': lineno, - } - return data - - def add_property(self, lang, key, value, line): - """Adds a property to a lang.js file. - """ - f = open(lang + '.js', 'r') - contents = f.readlines() - f.close() - - new_line = " '{0}': '{1}',\n".format(key, value) - print(colorama.Fore.GREEN + str(line) + ': ' + new_line[:-1]) - contents.insert(line - 1, new_line) - - f = open(lang + '.js', 'w') - contents = "".join(contents) - f.write(contents) - f.close() - - def main(self): - translator = Translator() - language_list = ['br', 'cn', 'de', 'es', 'fr', 'gr', 'in', 'it', - 'jp', 'nl', 'pt', 'ru', 'tr'] - - for language in language_list: - print('Translating for language: ' + language) - dest_language = self.get_dest_language(language) - odata = self.get_language_data(language) - - for key in self.data.keys(): - if not key in odata: - value = translator.translate(self.data[key]['value'], - src='en', dest=dest_language).text - self.add_property(language, key, value, self.data[key]['lineno']) - -if __name__ == "__main__": - translater = Translater() - translater.main() diff --git a/server/controllers/staff/.get-all-tickets.php.swp b/server/controllers/staff/.get-all-tickets.php.swp deleted file mode 100644 index 707340d36ff599530e296e46a91ea033524671bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2O>7%Q6vwAM&_G)#a70K0jkI>C?SvLpxJ`;%JB_6zc5OFml}6EeJ&qUFyW5%B z^h*T}fK*f=xBwS!oVk@76d|A|t{^11bEw1(Bm@%w*$?m52}T@2v&zrjyqS6L&40cW zWxBI#^%Z)&c#PmVM97l$$@=!gUy9LlkV4JfFED@O_V?Krp_|c**iy$vwwm942Y@`=u==3#Q`~q;0*zq$v6We-uE! zXa%$a6BIZ|jy^v(t129R_8I!*6Xz$?)kRtXt$F*)0 zp;LR~j&@ICkrd;-qB;T`ZH_yD{Pt^=j*6`&ujfL1^&pcT*x+$0oF^r_xBlL2?>^yPr@YbOX%HuXdX zTAZiGVr98q%Twb72}|&iHdmHR)gJRrF4YygO}jU5&?22qO@#%!2a7=)t*o=Y$C+sj zXl^>d<-5Xy7?_;(sv}R!wvf~mSxriK8P=nnQ{tG%CCDM(637(M`T`8F%=W zy{#?QJCCwlKI)Lq?QK&s+7Dby+MY{MtF5m5r74JWN}Wa9k*XPjhfz`$Vi7H7JGoAH zKd@Z3yug*YD7!o@(YaZ~OS230Mtak7V2i@SKgIm}R6CwDou;|P#DvU}r24koOLE$o zjT-Dmt6FQ((-$ems#P-Vu1;flJaCdzIaY{V^-EJJs~pz{kE^|LaLl2R><_`>IS{cxD0kc9>5 zRX8`V1YbuqGNU)RvY>fT}u|7A9NktN|(#PbbGibyQ}hJogqn- zMU@zgbB~9D*IKPct6eVFSJx`b_3CD`a<;a6n{Csvd!|~zVZ*8^&9%tn@p9S4ynt^@SPMq8(eA|CZdBB)be2@X_-$lVvK&(gdX~wG<3#I0gu7U}|50{TGPfr( zTVlAC&6Eh8!4G}O^zC?-(i^M6u*>*O3Qf(TT7ZI{3rttq@*4H}G_(|Vv|5isZ{!Zc zS3ya)*_J%8aL6!GXmnp@mW-<8*EeG4@dBga5>mbsgL)k>Y6YC*L00+m&8>T6}yju^FPT)|W56-hl5@la)Y&L7Hm3q5fZ>(nIYMQu3o{AN2 zVjbihzF^WpLX~n7qB)u~G@LGTnX)Pq7PtNI2W&qU7O{s!{iHX1Psp2e_Y3bkyp?8V2*Dv6?kX78 ZOYw4p-{jTf>fnB0DV`uFPpPZ(e*r)2+2H^H diff --git a/server/controllers/staff/.get-tickets.php.swp b/server/controllers/staff/.get-tickets.php.swp deleted file mode 100644 index c6af777cfde4025ee6f14537ce57174ea727b369..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeHNU5i^q7@kT|Y^{pk^hOwM;Ur;`?Ak(0KT?;bw88EsY|_1nVVRsWNltrm<~TEF zwXCsMR(d6ZAYOXwU+}Jo{Q-jh48aR43dLvUoFvDsTlUh*4EyBFdEd$V@w_twVc+5E zc58#4tDPknuM_fR^HKlBV?U5{ZxJ#L<6)F?qj}~?{Aa2c%1?8H^``LCDUY?Pccd6g zHicqb7YUD5nj}I(p}5@TvhIsmOA$p}LZsPfR3CF))uDffYgJ1o$-=msI0hU82Q!fB zQT6-@a^~H&RkP^SJ8#oB-?(+~dZ%#=I0hU8jseGjW56-s7;p?Y2L5*p=;R2whpb-6 zGh4}@NA^AQSNCuXI0hU8jseGjW56-s7;p?Y1{?#90mp!2;5lS~1%!NmjF8WdBY6D( zfByUb&({cf1pEj*1ik|90iOT?z<^EQec-oO3HcTH8Tbi!0Ne+rKnOfJLC9agW8in- zA@D75ANUMlKnExTF9T0rA>G4KoU9dH*I0w;l&fNv1*7r+C+#NGfd0Y`zak&hdW z0mp!2z%k$$a11yG90UJN24;j}WS?eMuh0#4hf_V_biLD}lIv8)cnuW|M^>CLbd1s| z)Bc33Oe6V+DOb8iD|KS5m{G56@xxi)ziJuH+>S~_toZEdLClKf`sK{Xx424{Um;j% z2PS6vZx1W8>cz~w8AB$33m2rF1>My5xl*OmDf(oqjgLY-xhmoy)S-w=o*xOt1A3WW z@m6TxTlEIzYC}z!Y3oF%NEmk5axkNrzGB(@`4xh8H?vaezkR zlJRcyTUKkZWWXfs=w7Weoiz`!g)_XUMqSykB1%AYoqSv|3YTGqk z(n~5S|5)=lPz82v`sSci@JHWcNtkaZy4TNa3`(aUt(x?Ax>}|_&LCIS_V70Mb2sJw zU`CMxqjnQffwP%)WmAL`j$>>