169 Commits

Author SHA1 Message Date
Senad Uka
d97516e24e thrown out ringeraja banner 2016-06-09 12:42:22 +02:00
Senad Uka
9820f6a363 another trello card formatting fix 2016-05-07 09:26:02 +02:00
Senad Uka
6b4bc6aa20 fixed trello formating again 2016-05-07 09:21:34 +02:00
Senad Uka
98e64830e1 fixed trello formating 2016-05-07 09:17:30 +02:00
Senad Uka
5c899ca6ed added paypal notice 2016-05-07 09:14:06 +02:00
Senad Uka
138c5de68e added coupon code to label on remark 2016-05-07 08:49:11 +02:00
Senad Uka
edb1325c07 fixed search on enter 2016-05-07 06:41:12 +02:00
Senad Uka
6b97c2342c fixed trello 2016-05-07 06:10:04 +02:00
Senad Uka
70ee58e989 slugify fixed 2016-03-16 12:13:47 +01:00
Senad Uka
7e62881d16 SOlved conflict 2016-03-16 12:12:26 +01:00
adam.harbas@a-net.ba
fdc3362719 Remove cart from state 2016-02-16 14:37:18 +01:00
adam.harbas@a-net.ba
2534ee71e1 Fix for cartLoad 2016-02-16 14:34:29 +01:00
adam.harbas@a-net.ba
a2bc1e192c Fix for cartLoad 2016-02-16 14:31:51 +01:00
Senad Uka
d111406f03 fixed bugs with trello 2016-02-16 12:34:21 +01:00
adam.harbas@a-net.ba
006829bc77 Create trello card before purchasing 2016-02-16 12:28:49 +01:00
Senad Uka
1368a71f26 Merge branch 'develop'
paypal, pikpay and gift support
2016-02-07 19:54:08 +01:00
Senad Uka
92f45c378e disable pikpay for now 2016-02-07 19:53:48 +01:00
Adam
44f5b22c82 Fix for validate logger 2016-02-02 17:09:06 +01:00
Adam
97f6b34518 Fix for reindex console output too large 2016-02-02 17:06:22 +01:00
Adam
1b9f38a9c0 Fix for reindex logger error 2016-01-26 11:09:18 +01:00
Senad Uka
e0fda3e9ee add robots.txt - because of the SEO 2016-01-26 06:09:08 +01:00
Senad Uka
30e48cd1b6 put menu items into links - because of the SEO 2016-01-26 05:43:05 +01:00
Senad Uka
0e2e5b8fb9 put items into links - because of the SEO 2016-01-26 05:01:18 +01:00
Adam
338d4e7966 Paypal notify url fix 2016-01-24 23:40:26 +01:00
Adam
770138ba68 Removing migration 2016-01-22 16:27:19 +01:00
Adam
10b3109771 for pikpay send only integer num 2016-01-22 16:23:58 +01:00
Adam
63b2506872 Remove column - fixing error 2016-01-22 16:02:29 +01:00
Adam
13757d5a7f Schema updated 2016-01-22 15:55:40 +01:00
Adam
c6c04a1405 Root path for import log files added 2016-01-22 15:37:29 +01:00
Adam
185cdb2a14 Rails root poject path added to command 2016-01-22 15:30:13 +01:00
Adam
5fc1279c2a Reverting Gemfile state 2016-01-21 11:30:28 +01:00
Adam
973f39eec3 Fixing bundler error 2016-01-21 10:17:03 +01:00
Senad Uka
418eb028ae changed text of newsleetter sweepstake 2016-01-18 11:06:49 +01:00
Senad Uka
1c0c4fa78a changed date of newsleetter sweepstake 2016-01-18 10:28:28 +01:00
adam.harbas@a-net.ba
8675dc724f Mobile design fixed 2016-01-13 09:51:36 +01:00
adam.harbas@a-net.ba
9e200ca910 Adding Cart id for order number 2016-01-13 09:26:28 +01:00
adam.harbas@a-net.ba
2f673c9877 Fix hardcoded host 2016-01-13 08:51:05 +01:00
Adam
90d5e855db Sha1 added to package.json 2016-01-12 21:03:06 +01:00
adam.harbas@a-net.ba
828aac9a49 Gift info added to trello 2016-01-12 15:43:11 +01:00
adam.harbas@a-net.ba
54d7cdb04e Delivery cost depend on gift option 2016-01-12 15:18:27 +01:00
adam.harbas@a-net.ba
68c7a620ef Gift button added 2016-01-12 14:23:40 +01:00
adam.harbas@a-net.ba
7a053a60ba Delete unnecessary files 2016-01-12 13:27:40 +01:00
adam.harbas@a-net.ba
9142e0c4db Gift support changes 2016-01-12 13:26:43 +01:00
Adam
aca81d97ec Add Recipient destination model and functionality on front end 2016-01-12 09:05:32 +01:00
Adam
5ee38f6d9a Add Recipient destination model and functionality on front end 2016-01-12 09:01:46 +01:00
adam.harbas@a-net.ba
794cc2ec69 Gift interface added 2016-01-11 16:03:53 +01:00
adam.harbas@a-net.ba
391b912eb2 Text button changed 2016-01-11 10:54:49 +01:00
adam.harbas@a-net.ba
c345e9f016 Last used btn 2016-01-11 10:52:18 +01:00
adam.harbas@a-net.ba
76ad0606b4 Removing duplicate migration 2016-01-11 10:23:26 +01:00
adam.harbas@a-net.ba
fbcde51ef0 Button order 2016-01-11 10:21:11 +01:00
adam.harbas@a-net.ba
f4884c4985 Fixing errors 2016-01-11 10:07:51 +01:00
adam.harbas@a-net.ba
3faa487ecd Fixing 2016-01-11 09:51:59 +01:00
Adam
7b2552c31c Pikpay button 2016-01-11 08:53:03 +01:00
adam.harbas@a-net.ba
7624a438b6 Last used button added 2016-01-08 10:21:33 +01:00
adam.harbas@a-net.ba
05c8f6ce88 Adding payment method 2016-01-08 09:36:28 +01:00
adam.harbas@a-net.ba
2a0e326862 Last used payment added 2016-01-07 15:00:59 +01:00
adam.harbas@a-net.ba
080255f229 Merge branch 'develop' of https://github.com/senaduka/ribica into develop 2016-01-07 13:58:45 +01:00
Adam
4cd1470674 Fix misspelled 2016-01-07 13:28:57 +01:00
Adam
8dd163da91 Exclude method from json parse in before 2016-01-07 13:14:49 +01:00
adam.harbas@a-net.ba
41fc327874 Payment confirmation changing from get to post 2016-01-07 10:51:43 +01:00
adam.harbas@a-net.ba
ea1c760e85 Oayment confirmation added to cart contoroller 2016-01-07 10:49:50 +01:00
adam.harbas@a-net.ba
0efb73e385 Use PaypalId and ApiUrl from globals 2016-01-07 09:19:25 +01:00
adam.harbas@a-net.ba
d0f02df738 Price and shipping to euro 2016-01-07 09:08:20 +01:00
adam.harbas@a-net.ba
81140de4d7 Price and shipping to euro 2016-01-07 09:06:54 +01:00
adam.harbas@a-net.ba
3a3ba87066 Conversion rate and CoonvertToEuro added to globals 2016-01-07 09:05:18 +01:00
adam.harbas@a-net.ba
e49b6b93d6 Custom field with deliveryDestination object added to paypal button 2016-01-07 08:58:37 +01:00
adam.harbas@a-net.ba
f092719ac7 Cart configuration action removed 2016-01-07 08:39:11 +01:00
adam.harbas@a-net.ba
1b3478707b CartConfiguration removed 2016-01-07 08:38:05 +01:00
adam.harbas@a-net.ba
ecd4d79ff2 Get configuration from server 2016-01-06 08:40:27 +01:00
adam.harbas@a-net.ba
2b51dfcdec Fix for helper 2016-01-06 08:27:18 +01:00
adam.harbas@a-net.ba
f92d54b142 Paypal button added to page 2016-01-06 08:26:19 +01:00
adam.harbas@a-net.ba
abb44757a5 Fix for paypalButton component 2016-01-05 09:31:51 +01:00
adam.harbas@a-net.ba
c2cd011ffb Add price conversion 2016-01-04 11:14:16 +01:00
adam.harbas@a-net.ba
6b61afebc0 Check if task succeeded and execute reindex task 2016-01-04 09:56:40 +01:00
adam.harbas@a-net.ba
3fc4a9edea Handle CSV::MalformedCSVError 2015-12-31 15:12:37 +01:00
adam.harbas@a-net.ba
83022beb5b don't do anything if csv_content is empty 2015-12-31 15:01:15 +01:00
adam.harbas@a-net.ba
a1fbc18856 Handle undefined method for nil object 2015-12-31 14:55:47 +01:00
Adam
a6a3d5937c Requiring gems 2015-12-31 14:53:20 +01:00
Adam
a41e997931 Execute task and getting output from rake 2015-12-31 14:17:38 +01:00
Adam
ca91c3d16f Create csv from csv content 2015-12-31 00:07:43 +01:00
Adam
d73b8ca8d8 Post action for export/import added 2015-12-30 22:58:14 +01:00
Adam
1dcd8d404e Export/import elements added to the page 2015-12-30 22:52:51 +01:00
Adam
93a315bbe1 Export/import link, route, action and page added 2015-12-30 22:48:42 +01:00
adam.harbas@a-net.ba
4ccfdb002e Option delete items added to check_availability page 2015-12-29 13:36:02 +01:00
Senad Uka
53ef339f5f added ringeraja ba advertisement 2015-12-16 08:49:42 +01:00
Senad Uka
1fa1c1c18e in the middle of the work 2015-12-16 08:39:54 +01:00
Senad Uka
924a675fa1 main css conflict 2015-11-29 10:02:57 +01:00
Senad Uka
8d4146acee letters are smaller now 2015-11-28 09:01:04 +01:00
Senad Uka
f93b127c2f big header is here 2015-11-28 08:46:09 +01:00
Senad Uka
bbd492467f size of the iframe is now bigger 2015-11-28 08:40:14 +01:00
Senad Uka
7d01bfb393 changed slugs to be nicer 2015-11-28 08:35:42 +01:00
Senad Uka
e993904d78 added newsletter signup form 2015-11-28 08:28:17 +01:00
Senad Uka
8d5067a567 fixed limit for number of items in group 2015-11-26 10:57:25 +01:00
Senad Uka
247d1c72c7 fixed limit for number of items in group 2015-11-26 10:49:25 +01:00
Senad Uka
b93660b264 added decoration support 2015-11-24 10:29:46 +01:00
Senad Uka
31876c0c1a deleted duplicate places 2015-11-23 12:59:13 +01:00
Senad Uka
3d0eff1c15 paypal button 2015-11-23 12:58:14 +01:00
Senad Uka
34bce252c5 Merge branch 'master' into develop 2015-11-15 06:31:48 +01:00
Senad Uka
d283204e88 fixed url on form 2015-11-14 07:26:47 +01:00
Senad Uka
a50d8339dd fixed url on form 2015-11-14 07:24:35 +01:00
Senad Uka
88c1ad4e92 fixed link 2015-11-14 07:18:02 +01:00
Senad Uka
a341137939 created item availability checking tool 2015-11-14 07:07:08 +01:00
Senad Uka
3e47672fb3 Merge branch 'develop' of github.com:senaduka/ribica into develop 2015-11-14 04:39:20 +01:00
Senad Uka
82d0195530 payments 2015-11-14 04:39:05 +01:00
harleyribic
f8915dd2d6 testiram 2015-11-11 19:46:23 +01:00
Senad Uka
c5fd654f5f deleted bundle 2015-11-07 09:52:50 +01:00
Senad Uka
0aab42fa67 fixed problem with adding delivery costs 2015-11-07 09:11:02 +01:00
Senad Uka
481f1122a7 .gitignore 2015-11-07 09:09:28 +01:00
Senad Uka
4955480e86 fixed problem with adding delivery costs 2015-11-07 09:03:50 +01:00
Senad Uka
c4b64a8839 Merge branch 'master' into develop 2015-11-07 08:10:44 +01:00
Senad Uka
ba3ae18669 delivery costs on cart page shown only when address is saved 2015-11-07 07:57:06 +01:00
Senad Uka
11b3669270 multiple cities supported / delivery costs shown only when place is selected 2015-11-07 07:41:08 +01:00
Senad Uka
d34c0c280c parametrized access 2015-10-31 08:24:44 +01:00
Senad Uka
3c5be78146 search problem 2015-10-11 06:37:17 +02:00
Senad Uka
b432ca58d5 fixed mistake in about page 2015-10-11 05:40:57 +02:00
Senad Uka
06e482685f changed phone number on about us / added viber and whatsapp 2015-10-10 09:19:55 +02:00
Senad Uka
8ec08804aa phone changed 2015-10-09 12:29:12 +02:00
Senad Uka
205c8352a1 update prices fails if list price is smaller than input price 2015-10-06 12:03:22 +02:00
Senad Uka
2c19f97971 random is now really rendom 2015-10-01 16:10:41 +02:00
Senad Uka
c75b112027 changed about you, changed price settings 2015-10-01 15:48:32 +02:00
Senad Uka
4470e85344 fixed import 2015-09-29 15:32:50 +02:00
Senad Uka
bb4fb665e3 changed some minor aesthetic issues 2015-09-29 14:22:27 +02:00
Senad Uka
f8ecebbc3d price is not reset when importing descriptions 2015-09-28 13:47:02 +02:00
Senad Uka
ae5b52165d finished price updating 2015-09-27 19:59:39 +02:00
Senad Uka
014416ca51 trello integration finished 2015-09-26 11:58:01 +02:00
Senad Uka
ae8e9b0081 converted time to CEST 2015-09-21 12:27:51 +02:00
Senad Uka
980ea36fc2 only sarajevo is supported now 2015-09-21 09:04:55 +02:00
Senad Uka
56f9c7b72b suggested articles / email is now sent 2015-09-21 06:31:52 +02:00
Senad Uka
2a4800fdab urgent delivery is hidden 2015-09-19 07:15:04 +02:00
Senad Uka
95e746c87c notification is on top 2015-09-19 07:12:09 +02:00
Senad Uka
5b9abe8a1c production database password change 2015-09-13 07:51:36 +02:00
Senad Uka
2ff5ceabd2 added email notification when order arives 2015-08-09 10:58:14 +02:00
Senad Uka
8b6a62b7e7 added order report to back office 2015-08-09 10:17:52 +02:00
Senad Uka
69f6c86aee added instant delivery to gui / added RUC as abbr tag and details tag on mobile 2015-08-02 18:47:48 +02:00
Senad Uka
68b25bf491 added migrations for instant delivery 2015-07-26 19:10:16 +02:00
Senad Uka
af6864954a added google analytics 2015-07-25 14:08:02 +02:00
Senad Uka
246a587c92 address entry looks bearable on mobile now 2015-07-05 13:44:38 +02:00
Senad Uka
96a2fba535 fixed conflicts 2015-07-05 13:24:09 +02:00
Senad Uka
f5664dee88 delivery costs are now shown 2015-07-05 13:22:47 +02:00
Edin Dazdarevic
94be4b0fbd updated .gitignore 2015-06-21 17:25:44 +02:00
Edin Dazdarevic
18d54ffea3 configuring the production build 2015-06-21 15:01:49 +02:00
Senad Uka
52a03f3334 cart page bearable on mobile 2015-06-21 12:24:18 +02:00
Senad Uka
a158c76789 modified item details page to look not horrible on mobile 2015-06-21 10:40:58 +02:00
Senad Uka
080d3e4497 it works on ipad, safari etc. / splash screen has a progress wheel 2015-06-21 09:22:41 +02:00
Senad Uka
f05b307fc0 homepage mobile experience became bearable by using questionable css and js practices 2015-06-20 07:05:16 +02:00
Senad Uka
7d74a3cc27 shortened supported towns 2015-06-20 05:19:52 +02:00
Senad Uka
afe0325f00 fixed bugs on menuitem etags 2015-06-20 05:08:22 +02:00
Senad Uka
baf1617053 added etags on prepare_for_display items and menuitems 2015-06-20 05:06:32 +02:00
Senad Uka
71c504cb5f added nicer splash screen to trick user into
thinking web site is faster :)
W
2015-06-20 04:43:08 +02:00
Senad Uka
393ec79a40 fixed bug in commission calculation 2015-06-14 07:14:00 +02:00
Senad Uka
4dfc2ffbd1 our commission is now shown on cart page 2015-06-14 07:11:46 +02:00
Senad Uka
ce733c8cd8 address is now collapsable 2015-06-14 06:18:10 +02:00
Senad Uka
6c5a21789a removed two files 2015-06-14 05:30:33 +02:00
Senad Uka
db796d5f82 moved list of cities to store / fixed bug with grunt watch on fast computers 2015-06-14 05:28:42 +02:00
Senad Uka
ee32d6f7c9 cart icon is hidden when ordering (cart and delivery) 2015-06-14 04:30:21 +02:00
Senad Uka
fb12ea613a fixed bug with phone number not validating correctly 2015-06-14 03:41:21 +02:00
Senad Uka
032dfc6d64 fixed bug with more than 10 items in cart 2015-06-14 03:38:44 +02:00
Senad Uka
e5cc98f934 zavrsi narudzbu is now 'nastavi narudzbu' 2015-06-13 06:27:48 +02:00
Senad Uka
9c01fac72d empty cart now contains suggestions 2015-06-13 06:21:04 +02:00
Senad Uka
1e32562aff changed button labeling to be consistent 'ubaci u korpu' and 'ukloni iz korpe' 2015-06-13 06:09:59 +02:00
Senad Uka
d92624bfc7 added toast notification that item is added to the cart 2015-06-13 05:35:05 +02:00
Senad Uka
0c305d3c40 floating price on item list 2015-06-13 03:53:42 +02:00
Senad Uka
c27e08a682 price on item page is now with two decimals 2015-06-13 03:28:16 +02:00
Senad Uka
771a71b42f fiddled around with css / numbers on two decimals now on product list 2015-06-06 21:50:41 +02:00
Edin Dazdarevic
763bc329fc fix for delivery destination not validating 2015-06-04 13:37:33 +02:00
Edin Dazdarevic
99492f137f link banner centering 2015-06-02 13:41:58 +02:00
Edin Dazdarevic
1db13308ef reverted change in Gruntfile.js and fixed CSS issues in firefox 2015-06-02 12:30:48 +02:00
Edin Dazdarevic
055f9ab3fa removing unneeded function call 2015-06-02 11:40:15 +02:00
Edin Dazdarevic
e35489a16e fixed link to registration on thank you page 2015-06-01 13:40:05 +02:00
99 changed files with 8916 additions and 1265 deletions

3
.gitignore vendored
View File

@@ -50,3 +50,6 @@
# everyone has its own configuration :)
config.rb
# bundle should be ignored
ribica.bundle.js

658
README.md
View File

@@ -1,3 +1,661 @@
# Ribica
One shop to rule them all!
## Unsuported towns
<option value="78255">Aleksandrovac</option>
<option value="89245">Avtovac</option>
<option value="76310">Balatun</option>
<option value="78000">Banja Luka</option>
<option value="75290">Banovici</option>
<option value="76312">Batkovic</option>
<option value="72233">Begov Han</option>
<option value="88363">Berkovici</option>
<option value="77000">Bihac</option>
<option value="76204">Bijela</option>
<option value="76300">Bijeljina</option>
<option value="73263">Bijelo Brdo</option>
<option value="89230">Bileca</option>
<option value="72248">Biljesevo</option>
<option value="88407">Bjelimici</option>
<option value="88201">Blagaj</option>
<option value="74275">Blatnica</option>
<option value="88263">Blatnica Kod Mostara</option>
<option value="71215">Blazuj</option>
<option value="76277">Bok</option>
<option value="74322">Boljanic</option>
<option value="77240">Bosanska Krupa</option>
<option value="77250">Bosanski Petrovac</option>
<option value="80270">Bosansko Grahovo</option>
<option value="88408">Bradina</option>
<option value="75420">Bratunac</option>
<option value="76000">Brcko</option>
<option value="76100">Brcko Distrikt</option>
<option value="77205">Brekovica</option>
<option value="74210">Brestovo</option>
<option value="71255">Brestovsko</option>
<option value="71370">Breza</option>
<option value="79208">Brezicani</option>
<option value="76109">Brezik</option>
<option value="76216">Brezovo Polje</option>
<option value="74206">Brijesnica</option>
<option value="74206">Brijesnica Kod Doboja</option>
<option value="74206">Brijesnica Mala</option>
<option value="74206">Brijesnica Velika</option>
<option value="76206">Brka</option>
<option value="72243">Brnjic</option>
<option value="88243">Brocanac</option>
<option value="88243">Brocanac</option>
<option value="73309">Brod Kod Foce</option>
<option value="76313">Brodac</option>
<option value="78204">Bronzani Majdan</option>
<option value="72290">Bucici</option>
<option value="70230">Bugojno</option>
<option value="75203">Bukinje</option>
<option value="74277">Buletic</option>
<option value="88202">Buna</option>
<option value="72260">Busovaca</option>
<option value="88409">Buturovicpolje</option>
<option value="77245">Buzim</option>
<option value="73280">Cajnice</option>
<option value="75405">Caparde</option>
<option value="88300">Capljina</option>
<option value="72224">Cardak</option>
<option value="71347">Careva Cuprija</option>
<option value="72246">Catici</option>
<option value="77220">Cazin</option>
<option value="74274">Cecava</option>
<option value="88404">Celebici</option>
<option value="80203">Celebici Lusnic</option>
<option value="73307">Celebici Kod Foce</option>
<option value="75246">Celic</option>
<option value="78240">Celinac</option>
<option value="89243">Cemerno</option>
<option value="88265">Cerin</option>
<option value="74211">Cerovica</option>
<option value="78403">Cerovljani</option>
<option value="88260">Citluk</option>
<option value="77226">Coralici</option>
<option value="76239">Crkvina</option>
<option value="88367">Crnici</option>
<option value="76328">Crnjelovo</option>
<option value="71221">Dejcici</option>
<option value="79243">Demirovac</option>
<option value="74400">Derventa</option>
<option value="75444">Derventa Kod Vlasenice</option>
<option value="89233">Divin</option>
<option value="74000">Doboj</option>
<option value="74206">Doboj Istok</option>
<option value="74203">Doboj Jug</option>
<option value="75328">Doborovci</option>
<option value="75308">Dobosnica</option>
<option value="77210">Dobretici</option>
<option value="71245">Dobrinja</option>
<option value="79223">Dobrljin</option>
<option value="77242">Dobro Selo</option>
<option value="73247">Dobrun</option>
<option value="75206">Dokanj</option>
<option value="72278">Dolac Na Lasvi</option>
<option value="76233">Domaljevac</option>
<option value="88305">Domanovici</option>
<option value="76274">Donja Mahala</option>
<option value="76257">Donja Medjidja</option>
<option value="76327">Donja Slatina</option>
<option value="71305">Donje Mostre</option>
<option value="79228">Donji Agici</option>
<option value="88343">Donji Memici</option>
<option value="70220">Donji Vakuf</option>
<option value="78432">Donji Vijacani</option>
<option value="79289">Donji Vrbljani</option>
<option value="76273">Donji Zabar</option>
<option value="76323">Dragaljevac</option>
<option value="74209">Dragalovci</option>
<option value="78215">Dragocaj</option>
<option value="88215">Dreznica</option>
<option value="75410">Drinjaca</option>
<option value="88344">Drinovci</option>
<option value="80260">Drvar</option>
<option value="75358">Duboki Potok</option>
<option value="75308">Dubostica</option>
<option value="78411">Dubrave</option>
<option value="75274">Dubrave Donje</option>
<option value="75273">Dubrave Gornje</option>
<option value="79227">Dubrovik</option>
<option value="74483">Dugo Polje</option>
<option value="75272">Djurdjevik</option>
<option value="75445">Dusanovo</option>
<option value="88342">Duzice</option>
<option value="76311">Dvorovi</option>
<option value="79264">Fajtovci</option>
<option value="73300">Foca</option>
<option value="71270">Fojnica</option>
<option value="89247">Fojnica Kod Gacka</option>
<option value="88306">Gabela Polje</option>
<option value="89240">Gacko</option>
<option value="80230">Glamoc</option>
<option value="76318">Glavicice</option>
<option value="72230">Globarica</option>
<option value="71275">Gojevici</option>
<option value="73000">Gorazde</option>
<option value="88340">Gorica</option>
<option value="77222">Gornja Koprivna</option>
<option value="76238">Gornja Slatina</option>
<option value="75208">Gornja Tuzla</option>
<option value="79266">Gornji Kamengrad</option>
<option value="78405">Gornji Podgradci</option>
<option value="76207">Gornji Rahic</option>
<option value="78439">Gornji Strpci</option>
<option value="74272">Gornji Teslic</option>
<option value="70240">Gornji Vakuf Uskoplje</option>
<option value="76207">Gornji Zovik</option>
<option value="89201">Grab</option>
<option value="78227">Grabovica</option>
<option value="75320">Gracanica</option>
<option value="70233">Gracanica Kod Bugojna</option>
<option value="75276">Gracanica Selo</option>
<option value="88392">Gradac</option>
<option value="76250">Gradacac</option>
<option value="78400">Gradiska</option>
<option value="88340">Grude</option>
<option value="80205">Guber</option>
<option value="80204">Listani</option>
<option value="72277">Guca Gora</option>
<option value="71240">Hadzici</option>
<option value="72225">Hajdarevici</option>
<option value="72245">Haljinici</option>
<option value="72281">Han Bila</option>
<option value="71360">Han Pijesak</option>
<option value="71212">Hrasnica</option>
<option value="88395">Hrasno</option>
<option value="71144">Hresa</option>
<option value="78436">Hrvacani</option>
<option value="75216">Husino</option>
<option value="88394">Hutovo</option>
<option value="71210">Ilidza</option>
<option value="71380">Ilijas</option>
<option value="73208">Ilovaca</option>
<option value="78234">Imljani</option>
<option value="77208">Izacic</option>
<option value="88420">Jablanica</option>
<option value="71423">Jahorina</option>
<option value="70101">Jajce</option>
<option value="76316">Janja</option>
<option value="72215">Janjici</option>
<option value="88224">Jare</option>
<option value="78233">Javorani</option>
<option value="74264">Jelah</option>
<option value="77241">Jezerski</option>
<option value="79244">Johova</option>
<option value="73319">Josanica</option>
<option value="78244">Josavka</option>
<option value="72264">Kacuni</option>
<option value="72240">Kakanj</option>
<option value="74413">Kalenderovci</option>
<option value="75260">Kalesija</option>
<option value="71230">Kalinovik</option>
<option value="77204">Kamenica</option>
<option value="72265">Kaonik</option>
<option value="71213">Kasindo</option>
<option value="80246">Kazaginac</option>
<option value="88283">Kifino Selo</option>
<option value="71250">Kiseljak</option>
<option value="75211">Kiseljak, Kod Tuzle</option>
<option value="75280">Kladanj</option>
<option value="74452">Klakar Donji</option>
<option value="79280">Kljuc</option>
<option value="88324">Klobuk</option>
<option value="74207">Klokotnica</option>
<option value="78230">Knezevo</option>
<option value="78230">Skender Vakuf</option>
<option value="79246">Knezica</option>
<option value="71356">Knezina</option>
<option value="78423">Kobas</option>
<option value="88226">Kocerin</option>
<option value="78409">Kocicevo</option>
<option value="78207">Kola</option>
<option value="80244">Kongora</option>
<option value="88400">Konjic</option>
<option value="77249">Konjoder</option>
<option value="74489">Koprivna</option>
<option value="75247">Koraj</option>
<option value="76236">Kornica</option>
<option value="74253">Kosova</option>
<option value="76276">Kostric</option>
<option value="78220">Kotor Varos</option>
<option value="74215">Kotorsko</option>
<option value="72226">Kovaci</option>
<option value="79002">Kozarac</option>
<option value="79240">Bosanska Dubica</option>
<option value="75413">Kozluk</option>
<option value="72244">Kraljeva Sutjeska</option>
<option value="71260">Kresevo</option>
<option value="78256">Kriskovci</option>
<option value="78206">Krupa Na Vrbasu</option>
<option value="88203">Krusevo</option>
<option value="78424">Kukulje</option>
<option value="71216">Kula</option>
<option value="78443">Kulasi</option>
<option value="77206">Kulen Vakuf</option>
<option value="80320">Kupres</option>
<option value="78250">Laktasi</option>
<option value="78407">Laminci Sredjani</option>
<option value="79204">Lamovita</option>
<option value="89208">Lastva</option>
<option value="72216">Lasva</option>
<option value="78222">Liplje</option>
<option value="75213">Lipnica</option>
<option value="78434">Lisnja</option>
<option value="80101">Livno</option>
<option value="75214">Ljubace</option>
<option value="79206">Ljubija</option>
<option value="88380">Ljubinje</option>
<option value="89209">Ljubomir</option>
<option value="88320">Ljubuski</option>
<option value="88223">Ljuti Dolac</option>
<option value="76278">Loncari</option>
<option value="75240">Lopare</option>
<option value="75300">Lukavac</option>
<option value="75301">Lukavac Mjesto</option>
<option value="75327">Lukavica</option>
<option value="79267">Lusci Palanka</option>
<option value="74250">Maglaj</option>
<option value="74216">Majevac</option>
<option value="77235">Mala Kladusa</option>
<option value="74418">Mala Socanica</option>
<option value="75320">Malesici</option>
<option value="76208">Maoca</option>
<option value="78410">Masici</option>
<option value="78223">Maslovare</option>
<option value="76271">Matici</option>
<option value="74203">Matuzici</option>
<option value="88266">Medjugorje</option>
<option value="79247">Medjuvodje</option>
<option value="72282">Mehurici</option>
<option value="80243">Mesihovina</option>
<option value="75446">Milici</option>
<option value="73283">Miljeno</option>
<option value="73313">Miljevina</option>
<option value="74485">Milosevac</option>
<option value="75329">Miricina</option>
<option value="74480">Modrica</option>
<option value="71428">Mokro</option>
<option value="89204">Mosko</option>
<option value="88000">Mostar</option>
<option value="88000">Mostar, Jug</option>
<option value="88000">Mostar, Jugozapad</option>
<option value="88000">Mostar, Sjever</option>
<option value="88000">Mostar, Zapad</option>
<option value="75212">Mramor</option>
<option value="73206">Mravinjac</option>
<option value="79544">Mrkodol</option>
<option value="70260">Mrkonjic Grad</option>
<option value="72212">Nemila</option>
<option value="88390">Neum</option>
<option value="88280">Nevesinje</option>
<option value="72276">Nova Bila</option>
<option value="78418">Nova Topola</option>
<option value="79220">Bosanski Novi</option>
<option value="74254">Novi Seher</option>
<option value="72290">Novi Travnik</option>
<option value="78428">Nozicko</option>
<option value="70225">Oborci</option>
<option value="76235">Obudovac</option>
<option value="76290">Odzak</option>
<option value="74470">Vukosavlje</option>
<option value="88285">Odzak Kod Nevesinja</option>
<option value="71340">Olovo</option>
<option value="79203">Omarska</option>
<option value="78406">Orahova</option>
<option value="75323">Orahovica Donja</option>
<option value="76270">Orasje</option>
<option value="74412">Osinja</option>
<option value="74225">Osjecani</option>
<option value="75406">Osmaci</option>
<option value="79263">Ostra Luka</option>
<option value="88423">Ostrozac</option>
<option value="77228">Ostrozac Kod Cazina</option>
<option value="77244">Otoka</option>
<option value="72238">Ozimica</option>
<option value="78437">Palackovci</option>
<option value="71420">Pale</option>
<option value="75435">Papraca</option>
<option value="71243">Pazaric</option>
<option value="77227">Pecigrad</option>
<option value="76256">Pelagicevo</option>
<option value="72112">Perin Han</option>
<option value="74317">Petrovo</option>
<option value="75412">Pilica</option>
<option value="78217">Piskavica</option>
<option value="88305">Pocitelj</option>
<option value="72252">Poculica</option>
<option value="71425">Podgrab</option>
<option value="80209">Podhum</option>
<option value="71387">Podlugovi</option>
<option value="74217">Podnovlje</option>
<option value="75355">Podorasje Kod Srebrenika</option>
<option value="77232">Podzvizd</option>
<option value="75303">Poljice</option>
<option value="75320">Popovi</option>
<option value="88240">Posusje</option>
<option value="78216">Potkozarje</option>
<option value="78435">Potocani</option>
<option value="88208">Potoci</option>
<option value="73290">Praca</option>
<option value="73245">Prelovo</option>
<option value="79287">Previja</option>
<option value="74276">Pribinic</option>
<option value="75249">Priboj Kod Lopara</option>
<option value="79101">Prijedor</option>
<option value="80202">Priluka</option>
<option value="80245">Prisoje</option>
<option value="78430">Prnjavor</option>
<option value="80206">Prolog</option>
<option value="88440">Prozor</option>
<option value="70223">Prusac</option>
<option value="71335">Przici</option>
<option value="72207">Puhovac</option>
<option value="75305">Puracic</option>
<option value="88325">Radisici</option>
<option value="75268">Rainci Gornji</option>
<option value="88245">Rakitno</option>
<option value="71217">Rakovica</option>
<option value="80247">Rasko Polje</option>
<option value="78429">Razboj Lijevce</option>
<option value="76218">Razljevo</option>
<option value="79288">Ribnik</option>
<option value="77215">Ripac</option>
<option value="73220">Rogatica</option>
<option value="79226">Rudice</option>
<option value="73260">Rudo</option>
<option value="88347">Ruzici</option>
<option value="76230">Bosanski Samac</option>
<option value="79285">Sanica Gornja</option>
<option value="79260">Sanski Most</option>
<option value="75411">Sapna</option>
<option value="78202">Saracica</option>
<option value="71000">Sarajevo</option>
<option value="71103">Sarajevo, Centar</option>
<option value="71160">Sarajevo, Novi Grad</option>
<option value="71120">Sarajevo, Novo Sarajevo</option>
<option value="71140">Sarajevo, Stari Grad</option>
<option value="76209">Satorovici</option>
<option value="73305">Sekovici</option>
<option value="75450">Sekovici</option>
<option value="71321">Semizovac</option>
<option value="76205">Seonjaci</option>
<option value="75275">Serici</option>
<option value="75245">Sibosnica</option>
<option value="78433">Sibovska</option>
<option value="75207">Simin Han</option>
<option value="78364">Sipovo</option>
<option value="78224">Siprage</option>
<option value="88220">Siroki Brijeg</option>
<option value="78422">Sitnesi</option>
<option value="79283">Sitnica</option>
<option value="75436">Skelani</option>
<option value="74261">Skugric</option>
<option value="75353">Sladna</option>
<option value="78253">Slatina Ilidza</option>
<option value="78438">Smrtici</option>
<option value="74279">Snjegotina Gornja</option>
<option value="74323">Sockovac</option>
<option value="71350">Sokolac</option>
<option value="71210">Sokolovici</option>
<option value="88345">Sovici</option>
<option value="75356">Spionica</option>
<option value="78420">Srbac</option>
<option value="75430">Srebrenica</option>
<option value="75350">Srebrenik</option>
<option value="71385">Srednje</option>
<option value="79249">Sreflije</option>
<option value="76258">Srnice</option>
<option value="79224">Bosanska Kostajnica</option>
<option value="74450">Bosanski Brod</option>
<option value="73110">Istocno Gorazde</option>
<option value="76278">Istocno Orasje</option>
<option value="71123">Istocno Sarajevo</option>
<option value="74208">Stanari</option>
<option value="78243">Stara Dubrava</option>
<option value="79268">Stari Majdan</option>
<option value="77224">Stijena</option>
<option value="73223">Stjenice</option>
<option value="75324">Stjepan Polje</option>
<option value="88360">Stolac</option>
<option value="72209">Stranjani</option>
<option value="78208">Stricici</option>
<option value="75305">Strpci</option>
<option value="88323">Studenci</option>
<option value="75283">Stupari</option>
<option value="77223">Sturlic</option>
<option value="75344">Suho Polje</option>
<option value="80249">Sujica</option>
<option value="79229">Svodna</option>
<option value="71244">Tarcin</option>
<option value="75414">Teocak</option>
<option value="74260">Tesanj</option>
<option value="74266">Tesanjka</option>
<option value="74270">Teslic</option>
<option value="88348">Tihaljina</option>
<option value="75357">Tinja</option>
<option value="75455">Tisca</option>
<option value="77233">Todorovo</option>
<option value="75265">Tojsici</option>
<option value="76272">Tolisa</option>
<option value="79265">Tomina</option>
<option value="80240">Tomislavgrad</option>
<option value="72213">Topcic Polje</option>
<option value="72270">Travnik</option>
<option value="88375">Trebinja</option>
<option value="89000">Trebinje</option>
<option value="78252">Trn</option>
<option value="76335">Trnava Donja</option>
<option value="76310">Trnjaci</option>
<option value="71220">Trnovo</option>
<option value="77225">Trzacka Rastela</option>
<option value="72283">Turbe</option>
<option value="75306">Turija</option>
<option value="78404">Turjak Kod Bosanske Gradiske</option>
<option value="75000">Tuzla</option>
<option value="76330">Ugljevik</option>
<option value="74278">Ugodnovici</option>
<option value="71233">Ulog</option>
<option value="74230">Usora</option>
<option value="73250">Ustikolina</option>
<option value="73265">Uvac</option>
<option value="73249">Vardiste</option>
<option value="71330">Vares</option>
<option value="77245">Varoska Rijeka</option>
<option value="74213">Velika Bukovica</option>
<option value="77207">Velika Gata</option>
<option value="77230">Velika Kladusa</option>
<option value="76329">Velika Obarska</option>
<option value="88208">Vidosi</option>
<option value="76275">Vidovice</option>
<option value="70202">Vinac</option>
<option value="88247">Vir Kod Posusja</option>
<option value="73240">Visegrad</option>
<option value="88307">Visici</option>
<option value="72250">Vitez</option>
<option value="88326">Vitina</option>
<option value="74265">Vitkovci Donji</option>
<option value="73205">Vitkovici</option>
<option value="75440">Vlasenica</option>
<option value="71320">Vogosca</option>
<option value="71123">Vojkovici</option>
<option value="70246">Voljevac</option>
<option value="72227">Vozuca</option>
<option value="74488">Vranjak</option>
<option value="88113">Vrapcici</option>
<option value="75248">Vrazici</option>
<option value="78211">Vrbanja Kod Banja Luke</option>
<option value="78225">Vrbanjci</option>
<option value="78408">Vrbaska</option>
<option value="77231">Vrnograc</option>
<option value="76325">Vrsani</option>
<option value="77203">Vrsta</option>
<option value="76254">Vuckovci</option>
<option value="74470">Vukosavlje</option>
<option value="76273">Zabar Donji</option>
<option value="73287">Zaborak</option>
<option value="72220">Zabrdje</option>
<option value="76333">Zabrdje</option>
<option value="78221">Zabrdje Kod Kotor Varosi</option>
<option value="78214">Zaluzani</option>
<option value="72220">Zavidovici</option>
<option value="74451">Zboriste</option>
<option value="76259">Zelinja</option>
<option value="72236">Zeljezno Polje</option>
<option value="72000">Zenica</option>
<option value="72230">Zepce</option>
<option value="88268">Zitomislici</option>
<option value="75270">Zivinice</option>
<option value="71370">Zupca</option>
<option value="75400">Zvornik</option>
deploy.sh
sudo pkill -f rails
sudo pkill -f puma
sudo git checkout -- front-api/db/schema.rb
sudo git pull
cd front-api
bundle install
rvmsudo rake db:migrate
rvmsudo bundle exec puma -e production -b unix:///var/www/ribica/ribica_app.sock -d
cd ..
cd back-office
bundle install
rvmsudo RAILS_ENV=production rails s -d
cd ..
cd front-ui
sudo npm install
sudo grunt build
cd ..
sudo service nginx restart
start_elastic_search.sh
sudo /opt/elasticsearch-1.4.4/bin/./elasticsearch -Xmx512m -Xms512m -d
ribica.conf
upstream ribica_app {
server unix:///var/www/ribica/ribica_app.sock;
}
server {
listen 443 ssl;
server_name ribica.ba www.ribica.ba;
location / {
root /var/www/ribica/front-ui/build;
try_files $uri $uri/ /index.html;
}
location /backoffice {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api {
proxy_pass http://ribica_app;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#listen 443;
ssl on;
ssl_certificate /etc/certifikati/SSL.crt;
ssl_certificate_key /etc/certifikati/www.ribica.ba.key;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
default
# You may add here your
# server {
# ...
# }
# statements for each of your virtual hosts to this file
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
#
# Generally, you will want to move this file somewhere, and start with a clean
# file but keep this around for reference. Or just disable in sites-enabled.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
server {
listen 80 default_server;
server_name ribica.ba www.ribica.ba;
return 301 https://$server_name$request_uri;
# Make site accessible from http://localhost/
}
default - dev server
upstream ribica_app {
server unix:///var/www/ribica/ribica_app.sock;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
location / {
root /var/www/ribica/front-ui/build;
try_files $uri $uri/ /index.html;
}
location /backoffice {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api {
proxy_pass http://ribica_app;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
~
~

View File

@@ -1,6 +1,5 @@
source 'https://rubygems.org'
gem 'rails_admin'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.0'
@@ -25,8 +24,11 @@ gem 'jbuilder', '~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', '~> 0.4.0', group: :doc
gem 'tzinfo-data'
gem "nokogiri", ">= 1.6.6"
# great server
gem 'puma'
gem 'puma', '~> 2.15.3'
# for uploading images
gem 'cloudinary'

View File

@@ -79,6 +79,7 @@ GEM
execjs (2.2.2)
faraday (0.9.1)
multipart-post (>= 1.2, < 3)
ffi (1.9.10-x64-mingw32)
font-awesome-rails (4.3.0.0)
railties (>= 3.2, < 5.0)
globalid (0.3.0)
@@ -111,11 +112,13 @@ GEM
multipart-post (2.0.0)
nested_form (0.3.2)
netrc (0.10.2)
nokogiri (1.6.5)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
nokogiri (1.6.6.2-x64-mingw32)
mini_portile (~> 0.6.0)
pg (0.18.1)
puma (2.10.2)
rack (>= 1.1, < 2.0)
pg (0.18.1-x64-mingw32)
puma (2.15.3)
rack (1.6.0)
rack-pjax (0.8.0)
nokogiri (~> 1.5)
@@ -167,6 +170,10 @@ GEM
rest-client (1.7.2)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
rest-client (1.7.2-x64-mingw32)
ffi (~> 1.9)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
safe_yaml (1.0.4)
sass (3.4.9)
sass-rails (5.0.1)
@@ -199,6 +206,8 @@ GEM
coffee-rails
tzinfo (1.2.2)
thread_safe (~> 0.1)
tzinfo-data (1.2015.7)
tzinfo (>= 1.0.0)
uglifier (2.7.0)
execjs (>= 0.3.0)
json (>= 1.8.0)
@@ -210,6 +219,7 @@ GEM
PLATFORMS
ruby
x64-mingw32
DEPENDENCIES
active_scaffold!
@@ -220,8 +230,9 @@ DEPENDENCIES
jbuilder (~> 2.0)
jquery-rails
jquery-ui-rails
nokogiri (>= 1.6.6)
pg
puma
puma (~> 2.15.3)
rails (= 4.2.0)
rails_admin
sass-rails (~> 5.0)
@@ -229,5 +240,9 @@ DEPENDENCIES
spring
tabulous
turbolinks
tzinfo-data
uglifier (>= 1.3.0)
web-console (~> 2.0)
BUNDLED WITH
1.10.6

View File

@@ -0,0 +1,5 @@
class CheckItemsConstraint
def self.matches?(request)
request.params[:commit] == 'Check'
end
end

View File

@@ -0,0 +1,5 @@
class DeleteItemsConstraint
def self.matches?(request)
request.params[:commit] == 'Delete Items'
end
end

View File

@@ -1,4 +1,5 @@
class CartsController < ApplicationController
active_scaffold :"cart" do |conf|
end
end

View File

@@ -3,4 +3,92 @@ class ItemsController < ApplicationController
conf.columns[:unit].form_ui = :select
conf.columns[:sub_category].form_ui = :select
end
def check_availability
@suppliers = Supplier.order(:name).all
@selected_supplier = Supplier.find_by_id(params[:supplier_id])
@items = @selected_supplier.try(:items) || []
@codes_to_check = params[:codes] || ""
@codes_to_check_array = @codes_to_check.split("\n").reject { |code| code.strip.blank? }.map(&:strip)
items_to_check = @items.map { |i| i.code.strip }
@missing_from_database = (@codes_to_check_array - items_to_check) || []
@missing_from_codes = (items_to_check - @codes_to_check_array) || []
end
def delete_items
@suppliers = Supplier.order(:name).all
@selected_supplier = Supplier.find_by_id(params[:supplier_id])
@items = @selected_supplier.try(:items) || []
@codes_to_check = params[:codes] || ""
@codes_to_check_array = @codes_to_check.split("\n").reject { |code| code.strip.blank? }.map(&:strip)
items_to_check = @items.map { |i| i.code.strip }
@items_for_delete = items_to_check & @codes_to_check_array
Item.where(:code => @items_for_delete).destroy_all
@not_deleted_items = (@codes_to_check_array - @items_for_delete) || []
render :template => "items/check_availability"
end
def export_import
@tasks = [ ["Validate items", "validate_items"],
["Import items", "import_items"],
["Update prices", "update_prices"] ]
@task = "validate_items"
end
def export_import_post
@tasks = [ ["Validate items", "validate_items"],
["Import items", "import_items"],
["Update prices", "update_prices"] ]
@task = params[:task]
@csv_content = params[:csv_content]
@error_message = ""
@output = []
if params[:csv_content] == ""
@error_message = "Format of CSV is wrong (CSV content is empty)"
else
begin
csv_parsed = CSV.parse(params[:csv_content])
rescue CSV::MalformedCSVError => er
@error_message = "Format of CSV is wrong (#{er.message})"
end
unless csv_parsed.nil?
csv_file = ItemsHelper::create_csv(csv_parsed)
begin
ENV["INPUT"] = csv_file.path
case @task
when 'validate_items'
@output = ItemsHelper::execute_command("rake ribica:validate_items -f #{Rails.root}/Rakefile")
@output.collect{|x| x.sub! "\n", "" }
when 'import_items'
@output = ItemsHelper::execute_command("rake ribica:import_items -f #{Rails.root}/Rakefile")
@output.collect{|x| x.sub! "\n", "" }
%x(rake ribica:reindex -f #{Rails.root}/Rakefile) unless RakeTasksHelper::is_error_occurred @output
when 'update_prices'
@output = ItemsHelper::execute_command("rake ribica:update_prices -f #{Rails.root}/Rakefile")
%x(rake ribica:reindex -f #{Rails.root}/Rakefile) unless RakeTasksHelper::is_error_occurred @output
else
@error_message = "There is no such task"
end
ensure
csv_file.unlink
end
end
@output = @output.join("<br/>")
end
render :template => "items/export_import"
end
end

View File

@@ -1,2 +1,6 @@
module CartsHelper
def money(amount)
sprintf('%.2f KM', amount.to_f)
end
end

View File

@@ -1,5 +1,27 @@
module ItemsHelper
def self.create_csv(data)
file = Tempfile.new([Rails.root.join('tmp/').to_s, ".csv"], "")
csv = CSV.new(file)
data.each do |row|
csv << row
end
file.rewind
file.close
file
end
def self.execute_command(command)
buffer = []
Open3.popen3(command) do |stdin, stdout, stderr|
begin
while line = stdout.readline
buffer << line
end
rescue
end
end
end
buffer
end
end

View File

@@ -0,0 +1,9 @@
module RakeTasksHelper
def self.task_error_message
"Rake task execution error"
end
def self.is_error_occurred(buffer)
buffer.include? self.task_error_message
end
end

View File

@@ -4,7 +4,23 @@ class Cart < ActiveRecord::Base
belongs_to :user
belongs_to :delivery_destination
def delivery_cost
place = delivery_destination.gift ? Place.by_code_or_default(delivery_destination.recipient_place)
: Place.by_code_or_default(delivery_destination.place)
if delivery_destination.instant_delivery
place.instant_delivery_price
else
place.delivery_price
end
end
def total
sum = item_in_carts.inject (0) { |sum, iic| sum + (iic.price * iic.count) }
sum += delivery_cost
end
def confirmed_at
delivery_destination.updated_at.in_time_zone('Europe/Sarajevo')
end
end

View File

@@ -1,2 +1,14 @@
class DeliveryDestination < ActiveRecord::Base
class DeliveryDestination < ActiveRecord::Base
def get_payment_string
case self.payment_method
when 'cash_on_delivery'
'Prilikom preuzimanja'
when 'paypal'
'Paypal'
when 'pikpay'
'Pikpay'
else
'Nepoznato'
end
end
end

View File

@@ -1,4 +1,12 @@
class Item < ActiveRecord::Base
CSV_COL = {
:code => 0,
:input_price => 1,
:list_price => 2,
:decoration => 3
}
belongs_to :unit
has_many :multi_media_descriptions
belongs_to :sub_category
@@ -8,6 +16,7 @@ class Item < ActiveRecord::Base
validates_presence_of :name, :description, :list_price, :current_input_price, :tags, :unit_id, :code, :sub_category_id, :weight, :supplier_id
validate :we_must_earn_money
def self.items_to_order
items_raw = Item.find_by_sql(%Q{
@@ -97,4 +106,33 @@ class Item < ActiveRecord::Base
return full_report
end
def self.update_prices(path)
nonexistent_codes = []
Item.transaction do
CSV.foreach(path) do |row|
code = row[CSV_COL[:code]].strip
item = Item.find_by_code(code)
if item.nil?
nonexistent_codes << code
else
item.current_input_price = row[CSV_COL[:input_price]]
item.list_price = row[CSV_COL[:list_price]]
item.decoration = row[CSV_COL[:decoration]]
item.save!
end
end
end
puts "Nonexistent codes: "
puts "#{nonexistent_codes.join("\n")}"
end
def we_must_earn_money
if list_price.to_f <= current_input_price.to_f
errors[:list_price] << "#{code} Ulazna cijena veca od izlazne"
puts "#{code} Ulazna cijena veca od izlazne"
end
end
end

View File

@@ -521,4 +521,13 @@ class Place < ActiveRecord::Base
return "<nepoznato mjesto>";
end
def self.by_code_or_default(code)
# removes garbage and converts whitespace prefixed codes correctly - like " 71000" -> 71000
valid_code = code.to_i.to_s
place = Place.where(postal_code: valid_code).first
place ||= Place.where("postal_code is null or postal_code = ''").first
return place
end
end

View File

@@ -0,0 +1,87 @@
<style type="text/css">
.tg { }
.tg td{ }
.tg th{ }
</style>
<%
dd = @record.delivery_destination
c = @record
%>
<p>
<p><strong>Ime:</strong> <%= dd.name %><br /><br /></p>
<p><strong>Adresa:</strong><br />
<%= dd.address %><br />
<%= dd.place.to_s.strip %> <%= Place.name_from_code(dd.place.to_s) %><br />
Bosna i Hercegovina<br /><br />
</p>
<p><strong>Email: </strong><%= dd.email %><br />
</p>
<p><strong>Telefon: </strong> +387 <%= dd.phone %><br /><br />
</p>
<p><strong>Plaćanje: </strong><%= dd.get_payment_string %></p><br />
<p><strong>Napomena: </strong><br />
<%= dd.note %><br /><br />
</p>
</p>
<% if dd.gift %>
<p><strong>Ovo je poklon</strong><br /><br />
<p><strong>Ime primaoca: </strong><%= dd.recipient_name %><br />
<p><strong>Adresa primaoca: </strong><%= dd.recipient_address %><br />
<p><strong>Postanski broj primaoca: </strong><%= dd.recipient_postal_code %><br />
<p><strong>Grad primaoca: </strong><%= dd.recipient_place %><br />
<p><strong>Email primaoca: </strong><%= dd.recipient_email %><br />
<p><strong>Telefon primaoca: </strong><%= dd.recipient_phone %><br /><br />
<% end %>
<p>
<strong>Naručeno:</strong> <%= c.confirmed_at.in_time_zone("Europe/Sarajevo").strftime("%A %d.%m.%Y. %H:%M") %>
<br />
<br />
</p>
<% if dd.instant_delivery %>
<p style="font-size: 120%;">
OVO JE NARUDŽBA ZA INSTANT DOSTAVU
</p>
<% end %>
<table class="tg">
<tr>
<th class="tg-031e">Code</th>
<th class="tg-031e">Item name</th>
<th style="text-align: right; ">Amount</th>
<th style="text-align: right; ">Price</th>
<th style="text-align: right; ">Total</th>
</tr>
<% @record.item_in_carts.each do |iic| %>
<tr>
<td class="tg-031e"><%= iic.item.code %></td>
<td class="tg-031e"><%= iic.item.name %></td>
<td style="text-align: right; "><%= iic.count %></td>
<td style="text-align: right; "><%= money(iic.price) %></td>
<td style="text-align: right; "><%= money(iic.count * iic.price) %></td>
</tr>
<% end %>
<tr>
<td class="tg-031e"></td>
<td class="tg-031e">Dostava</td>
<td style="text-align: right; ">1</td>
<td style="text-align: right; "><%= money(c.delivery_cost) %></td>
<td style="text-align: right; "><%= money(c.delivery_cost) %></td>
</tr>
<tr>
<td class="tg-031e"><strong>UKUPNO:</strong></td>
<td class="tg-031e"></td>
<td style="text-align: right; "></td>
<td style="text-align: right; "></td>
<td style="text-align: right; "><%= money(c.total) %></td>
</tr>
<tr><td><strong> </strong></td></tr>
</table>
<br /><br />

View File

@@ -0,0 +1,49 @@
<%= form_tag('./check_availability') do %>
<p><label for="supplier_id">Supplier: </label>
<%= select_tag "supplier_id", options_from_collection_for_select(@suppliers, "id", "name", @selected_supplier.try(:id)) %></p>
<p><label for="codes">
Codes to check:
</label><br />
<%= text_area_tag "codes", @codes_to_check, rows: 20, columns: 50 %></p>
<p><%= submit_tag "Check" %></p>
<p><%= submit_tag "Delete Items" %></p>
<% end %>
<% if controller.action_name == 'delete_items' %>
<h2> Delete successful </h2>
<% if @not_deleted_items.length > 0 %>
<p>Not deleted items</p>
<% @not_deleted_items.each do |code| %>
<%= code %><br />
<% end %>
<% end %>
<% else %>
<div>
<h2> In database: <%= @items.length %>, in file: <%= @codes_to_check_array.length %> </h2>
</div>
<div>
<h2>Not in database (<%= @missing_from_database.length %>): </h2>
<br />
<% @missing_from_database.each do |code| %>
<%= code %><br />
<% end %>
</div>
<div>
<h2>Not in file (<%= @missing_from_codes.length %>): </h2>
<br />
<% @missing_from_codes.each do |code| %>
<%= code %><br />
<% end %>
</div>
<% end %>

View File

@@ -0,0 +1,21 @@
<%= form_tag('./export_import') do %>
<p><label for="codes">
CSV content goes here:
</label><br />
<textarea id="csv_content" name="csv_content" rows=20 style="width: 100%"><%= @csv_content %></textarea></p>
<p><label for="task">Task: </label>
<%= select_tag "task", options_for_select(@tasks, @task) %></p>
<p><%= submit_tag "Run task" %></p>
<% if !@error_message.nil? && @error_message != "" %>
<div>Error message: <span style="color: red"><%= @error_message %></span></div>
<% end %>
<br />
<p><label for="output_area">Output area: </label>
<div id="output_area" style="width: 100%;height: 14em;outline: 1px solid #A9A9A9; overflow: scroll"><%= unless @output.nil?
@output.html_safe end %>
</div>
<% end %>

View File

@@ -0,0 +1,34 @@
<%= form_tag('./import') do %>
<p><label for="supplier_id">Supplier: </label>
<%= select_tag "supplier_id", options_from_collection_for_select(@suppliers, "id", "name", @selected_supplier.try(:id)) %></p>
<p><label for="codes">
Codes to check:
</label><br />
<%= text_area_tag "codes", @codes_to_check, rows: 20, columns: 50 %></p>
<p><%= submit_tag "Check" %></p>
<% end %>
<div>
<h2> In database: <%= @items.length %>, in file: <%= @codes_to_check_array.length %> </h2>
</div>
<div>
<h2>Not in database (<%= @missing_from_database.length %>): </h2>
<br />
<% @missing_from_database.each do |code| %>
<%= code %><br />
<% end %>
</div>
<div>
<h2>Not in file (<%= @missing_from_codes.length %>): </h2>
<br />
<% @missing_from_codes.each do |code| %>
<%= code %><br />
<% end %>
</div>

View File

@@ -1,6 +1,8 @@
require File.expand_path('../boot', __FILE__)
require 'rails/all'
require 'rake'
require 'open3'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
@@ -25,5 +27,3 @@ module Ribicabackoffice
config.assets.precompile += %w(*.svg *.eot *.woff *.ttf *.gif *.png *.ico)
end
end

View File

@@ -81,6 +81,6 @@ test:
production:
<<: *default
database: ribica
username: postgres
password: testni_hamo2
username: ribicadatabase
password: super*kicma*plazma*sunce
host: localhost

View File

@@ -33,7 +33,9 @@ RailsAdmin.config do |config|
end
config.navigation_static_links = {
'Orders to Confirm' => '../reports/orders_to_confirm',
'Items to Order' => '../reports/items_to_order'
'Orders to Confirm' => './reports/orders_to_confirm',
'Items to Order' => './backreports/items_to_order',
'Check availability' => './items/check_availability',
'Export/import' => './items/export_import'
}
end

View File

@@ -15,7 +15,16 @@ Rails.application.routes.draw do
resources :multi_media_descriptions do as_routes end
resources :sections do as_routes end
resources :media_types do as_routes end
resources :items do as_routes end
resources :items do
collection do
get 'check_availability'
post 'check_availability' => 'items#check_availability', constraints: CheckItemsConstraint
post 'check_availability' => 'items#delete_items', constraints: DeleteItemsConstraint
get 'export_import'
post 'export_import' => 'items#export_import_post'
end
as_routes
end
resources :units do as_routes end
resources :sub_categories do as_routes end
resources :categories do as_routes end

View File

@@ -7,3 +7,6 @@ I, [2015-05-13T06:59:46.065723 #35530] INFO -- : Import done
I, [2015-05-13T07:00:24.402397 #35642] INFO -- : Item import starting at 2015-05-13 07:00:24 +0200
I, [2015-05-13T07:00:24.402456 #35642] INFO -- : Will be importing items from /Users/senaduka/Downloads/items_for_import.csv
I, [2015-05-13T07:00:37.829898 #35642] INFO -- : Import done
I, [2015-06-06T08:06:52.737647 #7908] INFO -- : Item import starting at 2015-06-06 08:06:52 +0200
I, [2015-06-06T08:06:52.738405 #7908] INFO -- : Will be importing items from /home/senadu/Downloads/items_for_import - item_2015-03-21_06h31m33.csv.csv
I, [2015-06-06T08:06:59.910247 #7908] INFO -- : Import done

View File

@@ -182,6 +182,7 @@ def handle_multimedia(item, row, index, logger, lookup)
end
mmd.url = part
item.multi_media_descriptions.each(&:destroy)
item.multi_media_descriptions << mmd
end
end
@@ -195,10 +196,13 @@ def import_single_item(row, index, logger)
item = (Item.find_by code: code) || Item.new
item.name = row[lookup[:name_on_ribica]]
item.code = code
item.current_input_price = row[lookup[:current_input_price]] || 11.00
item.list_price = row[lookup[:list_price]] || 12.00
if item.new_record?
item.current_input_price = 0 #row[lookup[:current_input_price]] || 11.00
item.list_price = 0 #row[lookup[:list_price]] || 12.00
end
item.units_in_pack = row[lookup[:units_in_pack]]
item.description = row[lookup[:description]] || "default description"
item.description = row[lookup[:description]]
item.description ||= item.name
item.stock = row[lookup[:stock]]
item.on_display = row[lookup[:on_display]]
item.tags = row[lookup[:tags]]
@@ -219,6 +223,7 @@ def import_single_item(row, index, logger)
success = true
rescue Exception => e
logger.error "Could not import item on row number #{index} (#{row[lookup[:name_on_ribica]]}), reason: #{e}"
puts "Could not import item on row number #{index} (#{row[lookup[:name_on_ribica]]}), reason: #{e}"
success = false
end
@@ -238,14 +243,15 @@ def do_import(validate_only)
input_file = ENV['INPUT']
if input_file.to_s == ""
puts "Input file is missing! Please provide input file in form INPUT=somefile.csv"
puts RakeTasksHelper.task_error_message
return
end
lookup = get_column_lookup
path = Rails.root.join(input_file)
log_filename = "import.log"
log_filename = "import_validate.log" if validate_only
log_filename = Rails.root.join("import.log")
log_filename = Rails.root.join("import_validate.log") if validate_only
logger = Logger.new(log_filename)
@@ -270,6 +276,7 @@ def do_import(validate_only)
if should_rollback
puts "Import failed, please check the import log file for error details."
logger.info "Rolling back because of errors"
puts RakeTasksHelper.task_error_message
end
raise ActiveRecord::Rollback
@@ -277,12 +284,16 @@ def do_import(validate_only)
end
rescue Exception => e
puts "Import failed, please check the import log file for error details."
puts "Error while importing: #{e}"
logger.error "Error while importing: #{e}"
puts RakeTasksHelper.task_error_message
end
puts "Import done"
logger.info "Import done"
end
namespace :ribica do
desc "Creates menu structure using the sections and subsections from items."
task copy_sections_to_menu: :environment do
Section.transaction do
Section.all.each do |section|
@@ -313,6 +324,7 @@ namespace :ribica do
end
namespace :ribica do
desc "Clears database."
task clear_database: :environment do
conn = ActiveRecord::Base.connection
@@ -323,18 +335,29 @@ namespace :ribica do
end
namespace :ribica do
desc "Checks for errors in csv file. "
task validate_items: :environment do
do_import true
end
end
namespace :ribica do
desc "Imports items from csv if everything is ok. "
task import_items: :environment do
do_import false
end
end
namespace :ribica do
desc "Updates prices from csv. "
task update_prices: :environment do
Item.update_prices(ENV['INPUT'])
end
end
namespace :ribica do
desc "Imports items from csv if everything is ok. "
task reindex: :environment do
es_client = Elasticsearch::Client.new log: true
@@ -342,7 +365,7 @@ namespace :ribica do
begin
es_client.indices.delete index: 'ribica'
rescue
logger.warn "Ribica index could not be deleted. Continuing with indexing operation..."
Rails.logger.warn "Ribica index could not be deleted. Continuing with indexing operation..."
end
# now index items

View File

@@ -21,3 +21,6 @@ gem "json"
gem 'puma'
gem "sinatra-contrib"
gem 'rerun'
gem 'xxhash', '~> 0.3.0'
gem 'sendgrid-ruby'
gem 'ruby-trello'

View File

@@ -1,97 +1,120 @@
GEM
remote: https://rubygems.org/
specs:
activemodel (4.2.0)
activesupport (= 4.2.0)
activemodel (4.2.4)
activesupport (= 4.2.4)
builder (~> 3.1)
activerecord (4.2.0)
activemodel (= 4.2.0)
activesupport (= 4.2.0)
activerecord (4.2.4)
activemodel (= 4.2.4)
activesupport (= 4.2.4)
arel (~> 6.0)
activerecord-jdbc-adapter (1.3.13)
activerecord-jdbc-adapter (1.3.18)
activerecord (>= 2.2)
activerecord-jdbcpostgresql-adapter (1.3.13)
activerecord-jdbc-adapter (~> 1.3.13)
activerecord-jdbcpostgresql-adapter (1.3.18)
activerecord-jdbc-adapter (~> 1.3.18)
jdbc-postgres (>= 9.1)
activesupport (4.2.0)
activesupport (4.2.4)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
arel (6.0.0)
backports (3.6.4)
addressable (2.3.8)
arel (6.0.3)
backports (3.6.6)
bcrypt (3.1.10)
bcrypt (3.1.10-java)
bcrypt (3.1.10-x64-mingw32)
builder (3.2.2)
celluloid (0.16.0)
timers (~> 4.0.0)
elasticsearch (1.0.8)
elasticsearch-api (= 1.0.7)
elasticsearch-transport (= 1.0.7)
elasticsearch-api (1.0.7)
elasticsearch (1.0.13)
elasticsearch-api (= 1.0.13)
elasticsearch-transport (= 1.0.13)
elasticsearch-api (1.0.13)
multi_json
elasticsearch-transport (1.0.7)
elasticsearch-transport (1.0.13)
faraday
multi_json
faraday (0.9.1)
multipart-post (>= 1.2, < 3)
ffi (1.9.6)
ffi (1.9.6-java)
hitimes (1.2.2)
hitimes (1.2.2-java)
ffi (1.9.10)
ffi (1.9.10-java)
ffi (1.9.10-x64-mingw32)
hitimes (1.2.3)
hitimes (1.2.3-java)
i18n (0.7.0)
jdbc-postgres (9.3.1102)
jruby-openssl (0.9.6-java)
json (1.8.1)
json (1.8.1-java)
listen (2.8.5)
celluloid (>= 0.15.2)
jdbc-postgres (9.4.1200)
jruby-openssl (0.9.11-java)
json (1.8.3)
json (1.8.3-java)
listen (2.10.1)
celluloid (~> 0.16.0)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
minitest (5.5.0)
multi_json (1.10.1)
mime-types (2.6.2)
minitest (5.8.0)
multi_json (1.11.2)
multipart-post (2.0.0)
pg (0.17.1)
puma (2.10.2)
rack (>= 1.1, < 2.0)
puma (2.10.2-java)
rack (>= 1.1, < 2.0)
rack (1.6.0)
netrc (0.10.3)
oauth (0.4.7)
pg (0.18.3)
puma (2.14.0)
puma (2.14.0-java)
rack (1.6.4)
rack-protection (1.5.3)
rack
rack-test (0.6.3)
rack (>= 1.0)
rb-fsevent (0.9.4)
rb-fsevent (0.9.6)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
rerun (0.10.0)
listen (~> 2.7, >= 2.7.3)
sinatra (1.4.5)
rest-client (1.7.3)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
rest-client (1.7.3-x64-mingw32)
ffi (~> 1.9)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
ruby-trello (1.2.1)
activemodel (>= 3.2.0)
addressable (~> 2.3)
json
oauth (~> 0.4.5)
rest-client (~> 1.7.2)
sendgrid-ruby (0.0.3)
rest-client
smtpapi
sinatra (1.4.6)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
sinatra-activerecord (2.0.3)
tilt (>= 1.3, < 3)
sinatra-activerecord (2.0.8)
activerecord (>= 3.2)
sinatra (~> 1.0)
sinatra-contrib (1.4.2)
sinatra-contrib (1.4.6)
backports (>= 2.0)
multi_json
rack-protection
rack-test
sinatra (~> 1.4.0)
tilt (~> 1.3)
thread_safe (0.3.4)
thread_safe (0.3.4-java)
tilt (1.4.1)
timers (4.0.1)
tilt (>= 1.3, < 3)
smtpapi (0.1.0)
thread_safe (0.3.5)
thread_safe (0.3.5-java)
tilt (2.0.1)
timers (4.0.4)
hitimes
tzinfo (1.2.2)
thread_safe (~> 0.1)
xxhash (0.3.0)
PLATFORMS
java
ruby
x64-mingw32
DEPENDENCIES
activerecord
@@ -103,6 +126,12 @@ DEPENDENCIES
pg
puma
rerun
ruby-trello
sendgrid-ruby
sinatra
sinatra-activerecord
sinatra-contrib
xxhash (~> 0.3.0)
BUNDLED WITH
1.10.6

View File

@@ -1,11 +1,26 @@
require 'sinatra'
require 'sinatra/activerecord'
require './config'
require './helpers'
require 'json'
require 'sinatra/cookies'
require 'elasticsearch'
require 'xxhash'
require 'trello'
require 'sendgrid-ruby'
Trello.configure do |config|
# API key generated by visiting https://trello.com/1/appKey/generate
config.developer_public_key = RibicaConfig::TRELLO_DEVELOPER_PUBLIC_KEY
# Member token
# larry-price.com/blog/2014/03/18/connecting-to-the-trello-api/
config.member_token = RibicaConfig::TRELLO_MEMBER_TOKEN
end
Dir[File.dirname(__FILE__) + '/models/*.rb'].each {|file| require file }
set :bind, '0.0.0.0'
@@ -15,15 +30,18 @@ before do
content_type :json
# TODO: before running to production change this so that only specific
# domain is allowed
headers 'Access-Control-Allow-Origin' => 'http://localhost:3001',
'Access-Control-Allow-Methods' => ['OPTIONS', 'GET', 'POST','PUT'],
'Access-Control-Allow-Methods' => ['OPTIONS', 'GET', 'POST','PUT'].join(','),
'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type, Accept',
'Access-Control-Expose-Headers' => 'X-Total-Count',
'Access-Control-Allow-Credentials' => 'true'
request.body.rewind
json_string = request.body.read
@json_params = JSON.parse json_string if json_string.length > 1
unless Helper::do_not_parse_as_json.include? env['PATH_INFO']
request.body.rewind
json_string = request.body.read
@json_params = JSON.parse json_string if json_string.length > 1
end
if request.request_method == 'OPTIONS'
halt 200
@@ -31,11 +49,14 @@ before do
end
helpers Sinatra::Cookies
def xxhash(text)
seed = 31337
XXhash.xxh32(text, seed)
end
Dir[File.dirname(__FILE__) + '/controllers/*.rb'].each {|file| require file }

View File

@@ -10,3 +10,23 @@ ActiveRecord::Base.establish_connection(
:database => db.path[1..-1],
:encoding => 'utf8'
)
module RibicaConfig
ROOT_ADDRESS = "https://ribica.ba"
SENDGRID_API_USER = "ribica"
SENDGRID_API_KEY = "(enter key here)"
BACKOFFICE_ORDER_EMAIL_TO = "narudzbe@ribica.ba"
BACKOFFICE_ORDER_EMAIL_FROM = "draga@ribica.ba"
BACKOFFICE_ORDER_EMAIL_FROM_NAME = "Prodavnica Ribica"
# API key generated by visiting https://trello.com/1/appKey/generate
TRELLO_DEVELOPER_PUBLIC_KEY = "f13dd6c2dcc65f48b9a56c6d420e32e7"
# Member token
# larry-price.com/blog/2014/03/18/connecting-to-the-trello-api/
TRELLO_MEMBER_TOKEN = "(enter token here)"
TRELLO_BOARD_NAME = "FqDO1eFL"
BAM_TO_EURO_CONVERSION_RATE = 0.51
end

View File

@@ -1,9 +1,9 @@
create_the_cart = -> () {
create_the_cart = lambda do
# -1 is a placeholder for user id when we implement users
# auid will still be used in case user is not logged in
Cart.find_or_create(anonymous_id, logged_in_user_id).to_json
}
end
post '/cart', &create_the_cart
put '/cart', &create_the_cart
@@ -16,22 +16,19 @@ get '/cart/item' do
Cart.just_find(anonymous_id, logged_in_user_id).item_in_carts.to_json
end
update_cart_item = ->() {
update_cart_item = lambda do
cart_id = Cart.just_find(anonymous_id, logged_in_user_id).id
item_id = @json_params["item_id"].to_i
count = @json_params["count"].to_i
item_id = @json_params['item_id'].to_i
count = @json_params['count'].to_i
ItemInCart.update_state(cart_id, item_id, count).to_json
}
end
put '/cart/item', &update_cart_item
post '/cart/item', &update_cart_item
# gets list of items in cart without count
get '/cart/item/display' do
cart = Cart.just_find(anonymous_id, logged_in_user_id)
item_ids = cart.item_in_carts.map do |x|
x.item_id
end
item_ids = cart.item_in_carts.map(&:item_id)
items = []
items = Item.find(item_ids) if cart.item_in_carts.length > 0
prepare_items_for_mass_display(items)
@@ -39,20 +36,70 @@ end
get '/cart/delivery_destination' do
cart = Cart.just_find(anonymous_id, logged_in_user_id)
cart.delivery_destination.to_json(:except => [:created_at, :email_verification_code, :phone_verification_code])
cart.delivery_destination.to_json(except: [:created_at, :email_verification_code, :phone_verification_code])
end
update_delivery_destination = lambda do
cart = Cart.just_find(anonymous_id, logged_in_user_id)
allowed_keys = %w(name address place postal_code phone email note payment_method gift
recipient_name recipient_address recipient_place recipient_postal_code recipient_phone recipient_email)
params = @json_params.reject { |key, _| !allowed_keys.include?(key) }
cart.delivery_destination.update_attributes(params)
cart.delivery_destination.save!
cart.delivery_destination.to_json(except: [:created_at, :email_verification_code, :phone_verification_code])
end
put '/cart/delivery_destination', &update_delivery_destination
post '/cart/delivery_destination', &update_delivery_destination
def report_to_trello(cart)
Thread.new do
@cart = cart
board = Trello::Board.find(RibicaConfig::TRELLO_BOARD_NAME)
list = board.lists.first
card = Trello::Card.new
card.list_id = list.id
card.name = cart.title
card.pos = 'bottom'
card.desc = erb(:cart_trello, layout: false)
card.save
end
end
update_delivery_destination = ->() {
cart = Cart.just_find(anonymous_id, logged_in_user_id)
allowed_keys = ["name", "address", "place", "postal_code", "phone", "email", "note"]
allowed_keys = ["name", "address", "place", "postal_code", "phone", "email", "note", "payment_method", "gift",
"recipient_name", "recipient_address", "recipient_place", "recipient_postal_code", "recipient_phone", "recipient_email"]
params = @json_params.reject { |key,_| !allowed_keys.include?(key) }
cart.delivery_destination.update_attributes(params)
cart.delivery_destination.save!
report_to_trello(cart)
cart.delivery_destination.to_json(:except => [:created_at, :email_verification_code, :phone_verification_code])
}
put '/cart/delivery_destination', &update_delivery_destination
post '/cart/delivery_destination', &update_delivery_destination
def send_order_email(cart)
Thread.new do
client = SendGrid::Client.new(
api_user: RibicaConfig::SENDGRID_API_USER,
api_key: RibicaConfig::SENDGRID_API_KEY
)
email = SendGrid::Mail.new do |m|
m.to = RibicaConfig::BACKOFFICE_ORDER_EMAIL_TO
m.from = RibicaConfig::BACKOFFICE_ORDER_EMAIL_FROM
m.from_name = 'Prodavnica Ribica'
m.subject = "Nova Narudžba: #{cart.id}"
m.html = "Mušterija naručila nešto. <br /> Pogledati #{RibicaConfig::ROOT_ADDRESS}/backoffice/carts/#{cart.id}"
end
client.send(email)
end
end
post '/cart/confirmation' do
anonymous = anonymous_id
@@ -61,8 +108,51 @@ post '/cart/confirmation' do
cart.ordered = true
cart.save!
end
report_to_trello(cart)
send_order_email(cart)
# since there is no more ordered cart this needs to be done
# in order for next call of Cart#just_find to be ready
Cart.find_or_create(anonymous, logged_in_user_id)
"OK".to_json
'OK'.to_json
end
post '/payment/confirmation' do
data = JSON.parse params[:custom]
puts "Data #{data.inspect}"
anonymous = data['anonymous_id_string']
user = data['user_id']
user ||= -1
user = user.to_i
cart = Cart.just_find(anonymous, user)
if cart.item_in_carts.length > 0
cart.ordered = true
cart.save!
end
Cart.find_or_create(anonymous, user)
report_to_trello(cart)
send_order_email(cart)
'OK'.to_json
end
get '/pikpay/confirmation' do
anonymous = params['anonymous_id_string']
user = params['user_id']
user ||= -1
user = user.to_i
cart = Cart.just_find(anonymous, user)
if cart.item_in_carts.length > 0
cart.ordered = true
cart.save!
end
Cart.find_or_create(anonymous, user)
report_to_trello(cart)
send_order_email(cart)
redirect "#{RibicaConfig::ROOT_ADDRESS}/hvala"
end

View File

@@ -1,5 +1,5 @@
def prepare_items_for_mass_display(items)
items.to_json(
items_json = items.to_json(
:except => [:created_at, :current_input_price, :stock, :on_display],
:include => [
:unit ,
@@ -7,10 +7,12 @@ def prepare_items_for_mass_display(items)
:sub_category,
:brand
])
etag xxhash(items_json)
items_json
end
def offset_and_limit_invalid?(offset, limit)
offset < 0 or limit <= 0 or limit > 100
offset < 0 or limit <= 0 or limit > 300
end

View File

@@ -1,6 +1,8 @@
get '/menuitem' do
# list all menu items
MenuItem.eager_load(menu_sub_items: :menu_sub_sub_items)
menu_items_json = MenuItem.eager_load(menu_sub_items: :menu_sub_sub_items)
.order("menu_items.ordinal, menu_sub_items.ordinal, menu_sub_sub_items.ordinal")
.to_json(:include => [:menu_sub_items => {:include => :menu_sub_sub_items}])
etag xxhash(menu_items_json)
menu_items_json
end

View File

@@ -1,35 +1,45 @@
get '/search' do
es_client = Elasticsearch::Client.new log: true
q = params[:q]
# for now we do the basic query
results = es_client.search index: 'ribica', type: 'items', body: { query: { match: { _all: q } } }
ids = results["hits"]["hits"].map do |r|
r["_id"]
results = { }
begin
es_client = Elasticsearch::Client.new log: true
q = params[:q]
# for now we do the basic query
results = es_client.search index: 'ribica', type: 'items', body: { query: { match: { _all: q } } }
rescue Exception => error
puts error.inspect
results = { "hits" => {"hits" => [ {"_id" => Item.first.id, "_score" => 2 }, {"_id" => Item.last.id, "_score" => 1 } ]}}
end
ids = results["hits"]["hits"].map do |r|
r["_id"]
end
ids_with_score = {}
results["hits"]["hits"].each do |r|
ids_with_score[r["_id"].to_i] = {:score => r["_score"], :item => nil}
ids_with_score[r["_id"].to_i] = {:score => r["_score"], :item => nil}
end
if ids.length > 0
res = Item.where(:id => ids).to_a
# make sure we have correct relevance order, since `where in` does not guarantee order
res.each do |ii|
ids_with_score[ii.id][:item] = ii
end
final = []
ids_with_score.each do |k,v|
final << v
end
final.sort_by! {|v| -v[:score]}
res = Item.where(:id => ids).to_a
# make sure we have correct relevance order, since `where in` does not guarantee order
res.each do |ii|
ids_with_score[ii.id][:item] = ii
end
final = []
ids_with_score.each do |k,v|
final << v
end
final.sort_by! {|v| -v[:score]}
final = final.map do |f|
f[:item]
end
prepare_items_for_mass_display(final)
final = final.map do |f|
f[:item]
end
prepare_items_for_mass_display(final)
else
[].to_json
[].to_json
end
end

View File

@@ -0,0 +1,5 @@
class AddCommissionToItemInCart < ActiveRecord::Migration
def change
add_column :item_in_carts, :commision, :decimal
end
end

View File

@@ -0,0 +1,6 @@
class RenameCommissionInItemInCart < ActiveRecord::Migration
def change
remove_column :item_in_carts, :commision
add_column :item_in_carts, :commission, :decimal
end
end

View File

@@ -0,0 +1,5 @@
class AddInstantDeliveryPriceToPlaces < ActiveRecord::Migration
def change
add_column :places, :instant_delivery_price, :decimal , precision: 5, scale: 2
end
end

View File

@@ -0,0 +1,5 @@
class AddInstantDeliveryToDeliveryDestinations < ActiveRecord::Migration
def change
add_column :delivery_destinations, :instant_delivery, :boolean, default: false
end
end

View File

@@ -0,0 +1,5 @@
class AddDecorationToItem < ActiveRecord::Migration
def change
add_column :items, :decoration, :string
end
end

View File

@@ -0,0 +1,5 @@
class AddPaymentMethodToDeliveryDestination < ActiveRecord::Migration
def change
add_column :delivery_destinations, :payment_method, :string
end
end

View File

@@ -0,0 +1,5 @@
class AddGiftToDeliveryDestination < ActiveRecord::Migration
def change
add_column :delivery_destinations, :gift, :boolean, default: false
end
end

View File

@@ -0,0 +1,10 @@
class AddRecipientDestinationToDeliveryDestination < ActiveRecord::Migration
def change
add_column :delivery_destinations, :recipient_name, :string
add_column :delivery_destinations, :recipient_address, :string
add_column :delivery_destinations, :recipient_place, :string
add_column :delivery_destinations, :recipient_postal_code, :string
add_column :delivery_destinations, :recipient_phone, :string
add_column :delivery_destinations, :recipient_email, :string
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150420044444) do
ActiveRecord::Schema.define(version: 20160122145944) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -62,10 +62,19 @@ ActiveRecord::Schema.define(version: 20150420044444) do
t.boolean "phone_verified"
t.string "phone_verification_code"
t.string "email_verification_code"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "anonymous_id_string"
t.boolean "instant_delivery", default: false
t.string "payment_method"
t.boolean "gift", default: false
t.string "recipient_name"
t.string "recipient_address"
t.string "recipient_place"
t.string "recipient_postal_code"
t.string "recipient_phone"
t.string "recipient_email"
end
create_table "delivery_time_estimations", force: :cascade do |t|
@@ -104,6 +113,7 @@ ActiveRecord::Schema.define(version: 20150420044444) do
t.datetime "updated_at", null: false
t.integer "count"
t.decimal "price"
t.decimal "commission"
end
create_table "item_in_groups", force: :cascade do |t|
@@ -133,6 +143,7 @@ ActiveRecord::Schema.define(version: 20150420044444) do
t.decimal "weight", precision: 5, scale: 3
t.integer "delivery_time_estimation_id"
t.integer "brand_id"
t.string "decoration"
end
create_table "link_banners", force: :cascade do |t|
@@ -190,8 +201,9 @@ ActiveRecord::Schema.define(version: 20150420044444) do
t.string "postal_code"
t.decimal "delivery_price"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.decimal "instant_delivery_price", precision: 5, scale: 2
end
create_table "sections", force: :cascade do |t|

17
front-api/helpers.rb Normal file
View File

@@ -0,0 +1,17 @@
class Helper
def self.money(amount)
sprintf('%.2f KM', amount.to_f)
end
def self.get_bam_to_euro_conversion_rate
RibicaConfig::BAM_TO_EURO_CONVERSION_RATE
end
def self.bam_to_euro(amount)
self.get_bam_to_euro_conversion_rate * amount
end
def self.do_not_parse_as_json
['/payment/confirmation']
end
end

View File

@@ -1,5 +1,5 @@
class Cart < ActiveRecord::Base
has_many :item_in_carts, -> { order "created_at" }
has_many :item_in_carts, -> { order "created_at" }
belongs_to :delivery_destination
def self.find_or_create(anonymous_id, user_id)
@@ -53,4 +53,30 @@ class Cart < ActiveRecord::Base
cart ||= Cart.where(anonymous_id_string: anonymous_id).where(ordered: false).first
return cart
end
def confirmed_at
delivery_destination.updated_at.in_time_zone('Europe/Sarajevo')
end
def delivery_cost
place = Place.by_code_or_default(delivery_destination.place)
if delivery_destination.instant_delivery
place.instant_delivery_price
else
place.delivery_price
end
end
def total
sum = item_in_carts.inject (0) { |sum, iic| sum + (iic.price * iic.count) }
sum += delivery_cost
end
def title
number = id
name = delivery_destination.name
value = Helper.money(total)
phone = "0#{delivery_destination.phone}"
"BR: #{number} za #{name} (#{phone}) - #{value}"
end
end

View File

@@ -1,4 +1,4 @@
class DeliveryDestination < ActiveRecord::Base
class DeliveryDestination < ActiveRecord::Base
has_one :cart
belongs_to :user
@@ -6,9 +6,32 @@ class DeliveryDestination < ActiveRecord::Base
dd = DeliveryDestination.where(["user_id is not null and user_id = ?", user_id]).order("id desc").first
dd ||= DeliveryDestination.where(["anonymous_id_string is not null and anonymous_id_string = ?", anonymous_id]).order("id desc").first
dd ||= DeliveryDestination.new({user_id: user_id, anonymous_id_string: anonymous_id })
dd.payment_method ||= "cash_on_delivery"
dd.gift = false
dd.recipient_name = ""
dd.recipient_phone = ""
dd.recipient_email = ""
dd.recipient_address = ""
dd.recipient_place = ""
dd.recipient_postal_code = ""
attributes = dd.as_json
attributes.delete("id")
result = DeliveryDestination.create!(attributes)
return result
end
def get_payment_string
case self.payment_method
when 'cash_on_delivery'
'Prilikom preuzimanja'
when 'paypal'
'Paypal'
when 'pikpay'
'Pikpay'
else
'Nepoznato'
end
end
end

View File

@@ -15,7 +15,7 @@ class Item < ActiveRecord::Base
end
# TODO: change "best selling" algorithm when get some data - currently it's only sorted by earnings
scope :best_selling, -> (offset, limit) { where(:on_display => true).order("(list_price - current_input_price) DESC").limit(limit).offset(offset) }
scope :best_selling, -> (offset, limit) { where(:on_display => true).order("list_price ASC").limit(limit).offset(offset) }
scope :best_selling_in_sub_category, -> (sub_category_id, offset, limit) { best_selling(offset, limit).where(sub_category_id: sub_category_id) }

View File

@@ -13,6 +13,7 @@ class ItemInCart < ActiveRecord::Base
item_in_cart ||= ItemInCart.new(cart_id: cart_id, item_id: item_id)
item_in_cart.count = count
item_in_cart.price = item_in_cart.item.list_price
item_in_cart.commission = (((item_in_cart.item.list_price - item_in_cart.item.current_input_price) / (item_in_cart.item.current_input_price)) * 100).round(2)
item_in_cart.save!
item_in_cart.destroy! if count <= 0
end

View File

@@ -8,7 +8,8 @@ class MultiMediaDescription < ActiveRecord::Base
end
def resized_url
url.gsub('/upload/v','/upload/c_lpad,h_170,w_226/v')
decoration_slug = item.decoration.present? ? "/#{item.decoration}" : ""
url.gsub('/upload/v',"/upload/c_lpad,h_281,w_375#{decoration_slug}/v")
end
end

View File

@@ -8,4 +8,526 @@ class Place < ActiveRecord::Base
return place
end
def self.name_from_code(code)
places_bulk = %Q{Aleksandrovac 78255
Avtovac 89245
Balatun 76310
Banja Luka 78000
Banovici 75290
Batkovic 76312
Begov Han 72233
Berkovici 88363
Bihac 77000
Bijela 76204
Bijeljina 76300
Bijelo Brdo 73263
Bileca 89230
Biljesevo 72248
Bjelimici 88407
Blagaj 88201
Blatnica 74275
Blatnica Kod Mostara 88263
Blazuj 71215
Bok 76277
Boljanic 74322
Bosanska Krupa 77240
Bosanski Petrovac 77250
Bosansko Grahovo 80270
Bradina 88408
Bratunac 75420
Brcko 76000
Brcko Distrikt 76100
Brekovica 77205
Brestovo 74210
Brestovsko 71255
Breza 71370
Brezicani 79208
Brezik 76109
Brezovo Polje 76216
Brijesnica 74206
Brijesnica Kod Doboja 74206
Brijesnica Mala 74206
Brijesnica Velika 74206
Brka 76206
Brnjic 72243
Brocanac 88243
Brocanac 88243
Brod Kod Foce 73309
Brodac 76313
Bronzani Majdan 78204
Bucici 72290
Bugojno 70230
Bukinje 75203
Buletic 74277
Buna 88202
Busovaca 72260
Buturovicpolje 88409
Buzim 77245
Cajnice 73280
Caparde 75405
Capljina 88300
Cardak 72224
Careva Cuprija 71347
Catici 72246
Cazin 77220
Cecava 74274
Celebici 88404
Celebici Lusnic 80203
Celebici Kod Foce 73307
Celic 75246
Celinac 78240
Cemerno 89243
Cerin 88265
Cerovica 74211
Cerovljani 78403
Citluk 88260
Coralici 77226
Crkvina 76239
Crnici 88367
Crnjelovo 76328
Dejcici 71221
Demirovac 79243
Derventa 74400
Derventa Kod Vlasenice 75444
Divin 89233
Doboj 74000
Doboj Istok 74206
Doboj Jug 74203
Doborovci 75328
Dobosnica 75308
Dobretici 77210
Dobrinja 71245
Dobrljin 79223
Dobro Selo 77242
Dobrun 73247
Dokanj 75206
Dolac Na Lasvi 72278
Domaljevac 76233
Domanovici 88305
Donja Mahala 76274
Donja Medjidja 76257
Donja Slatina 76327
Donje Mostre 71305
Donji Agici 79228
Donji Memici 88343
Donji Vakuf 70220
Donji Vijacani 78432
Donji Vrbljani 79289
Donji Zabar 76273
Dragaljevac 76323
Dragalovci 74209
Dragocaj 78215
Dreznica 88215
Drinjaca 75410
Drinovci 88344
Drvar 80260
Duboki Potok 75358
Dubostica 75308
Dubrave 78411
Dubrave Donje 75274
Dubrave Gornje 75273
Dubrovik 79227
Dugo Polje 74483
Djurdjevik 75272
Dusanovo 75445
Duzice 88342
Dvorovi 76311
Fajtovci 79264
Foca 73300
Fojnica 71270
Fojnica Kod Gacka 89247
Gabela Polje 88306
Gacko 89240
Glamoc 80230
Glavicice 76318
Globarica 72230
Gojevici 71275
Gorazde 73000
Gorica 88340
Gornja Koprivna 77222
Gornja Slatina 76238
Gornja Tuzla 75208
Gornji Kamengrad 79266
Gornji Podgradci 78405
Gornji Rahic 76207
Gornji Strpci 78439
Gornji Teslic 74272
Gornji Vakuf Uskoplje 70240
Gornji Zovik 76207
Grab 89201
Grabovica 78227
Gracanica 75320
Gracanica Kod Bugojna 70233
Gracanica Selo 75276
Gradac 88392
Gradacac 76250
Gradiska 78400
Grude 88340
Guber 80205
Listani 80204
Guca Gora 72277
Hadzici 71240
Hajdarevici 72225
Haljinici 72245
Han Bila 72281
Han Pijesak 71360
Hrasnica 71212
Hrasno 88395
Hresa 71144
Hrvacani 78436
Husino 75216
Hutovo 88394
Ilidza 71210
Ilijas 71380
Ilovaca 73208
Imljani 78234
Izacic 77208
Jablanica 88420
Jahorina 71423
Jajce 70101
Janja 76316
Janjici 72215
Jare 88224
Javorani 78233
Jelah 74264
Jezerski 77241
Johova 79244
Josanica 73319
Josavka 78244
Kacuni 72264
Kakanj 72240
Kalenderovci 74413
Kalesija 75260
Kalinovik 71230
Kamenica 77204
Kaonik 72265
Kasindo 71213
Kazaginac 80246
Kifino Selo 88283
Kiseljak 71250
Kiseljak, Kod Tuzle 75211
Kladanj 75280
Klakar Donji 74452
Kljuc 79280
Klobuk 88324
Klokotnica 74207
Knezevo 78230
Skender Vakuf 78230
Knezica 79246
Knezina 71356
Kobas 78423
Kocerin 88226
Kocicevo 78409
Kola 78207
Kongora 80244
Konjic 88400
Konjoder 77249
Koprivna 74489
Koraj 75247
Kornica 76236
Kosova 74253
Kostric 76276
Kotor Varos 78220
Kotorsko 74215
Kovaci 72226
Kozarac 79002
Bosanska Dubica 79240
Kozluk 75413
Kraljeva Sutjeska 72244
Kresevo 71260
Kriskovci 78256
Krupa Na Vrbasu 78206
Krusevo 88203
Kukulje 78424
Kula 71216
Kulasi 78443
Kulen Vakuf 77206
Kupres 80320
Laktasi 78250
Laminci Sredjani 78407
Lamovita 79204
Lastva 89208
Lasva 72216
Liplje 78222
Lipnica 75213
Lisnja 78434
Livno 80101
Ljubace 75214
Ljubija 79206
Ljubinje 88380
Ljubomir 89209
Ljubuski 88320
Ljuti Dolac 88223
Loncari 76278
Lopare 75240
Lukavac 75300
Lukavac Mjesto 75301
Lukavica 75327
Lusci Palanka 79267
Maglaj 74250
Majevac 74216
Mala Kladusa 77235
Mala Socanica 74418
Malesici 75320
Maoca 76208
Masici 78410
Maslovare 78223
Matici 76271
Matuzici 74203
Medjugorje 88266
Medjuvodje 79247
Mehurici 72282
Mesihovina 80243
Milici 75446
Miljeno 73283
Miljevina 73313
Milosevac 74485
Miricina 75329
Modrica 74480
Mokro 71428
Mosko 89204
Mostar 88000
Mostar, Jug 88000
Mostar, Jugozapad 88000
Mostar, Sjever 88000
Mostar, Zapad 88000
Mramor 75212
Mravinjac 73206
Mrkodol 79544
Mrkonjic Grad 70260
Nemila 72212
Neum 88390
Nevesinje 88280
Nova Bila 72276
Nova Topola 78418
Bosanski Novi 79220
Novi Seher 74254
Novi Travnik 72290
Nozicko 78428
Oborci 70225
Obudovac 76235
Odzak 76290
Vukosavlje 74470
Odzak Kod Nevesinja 88285
Olovo 71340
Omarska 79203
Orahova 78406
Orahovica Donja 75323
Orasje 76270
Osinja 74412
Osjecani 74225
Osmaci 75406
Ostra Luka 79263
Ostrozac 88423
Ostrozac Kod Cazina 77228
Otoka 77244
Ozimica 72238
Palackovci 78437
Pale 71420
Papraca 75435
Pazaric 71243
Pecigrad 77227
Pelagicevo 76256
Perin Han 72112
Petrovo 74317
Pilica 75412
Piskavica 78217
Pocitelj 88305
Poculica 72252
Podgrab 71425
Podhum 80209
Podlugovi 71387
Podnovlje 74217
Podorasje Kod Srebrenika 75355
Podzvizd 77232
Poljice 75303
Popovi 75320
Posusje 88240
Potkozarje 78216
Potocani 78435
Potoci 88208
Praca 73290
Prelovo 73245
Previja 79287
Pribinic 74276
Priboj Kod Lopara 75249
Prijedor 79101
Priluka 80202
Prisoje 80245
Prnjavor 78430
Prolog 80206
Prozor 88440
Prusac 70223
Przici 71335
Puhovac 72207
Puracic 75305
Radisici 88325
Rainci Gornji 75268
Rakitno 88245
Rakovica 71217
Rasko Polje 80247
Razboj Lijevce 78429
Razljevo 76218
Ribnik 79288
Ripac 77215
Rogatica 73220
Rudice 79226
Rudo 73260
Ruzici 88347
Bosanski Samac 76230
Sanica Gornja 79285
Sanski Most 79260
Sapna 75411
Saracica 78202
Sarajevo 71000
Sarajevo, Centar 71103
Sarajevo, Novi Grad 71160
Sarajevo, Novo Sarajevo 71120
Sarajevo, Stari Grad 71140
Satorovici 76209
Sekovici 73305
Sekovici 75450
Semizovac 71321
Seonjaci 76205
Serici 75275
Sibosnica 75245
Sibovska 78433
Simin Han 75207
Sipovo 78364
Siprage 78224
Siroki Brijeg 88220
Sitnesi 78422
Sitnica 79283
Skelani 75436
Skugric 74261
Sladna 75353
Slatina Ilidza 78253
Smrtici 78438
Snjegotina Gornja 74279
Sockovac 74323
Sokolac 71350
Sokolovici 71210
Sovici 88345
Spionica 75356
Srbac 78420
Srebrenica 75430
Srebrenik 75350
Srednje 71385
Sreflije 79249
Srnice 76258
Bosanska Kostajnica 79224
Bosanski Brod 74450
Istocno Gorazde 73110
Istocno Orasje 76278
Istocno Sarajevo 71123
Stanari 74208
Stara Dubrava 78243
Stari Majdan 79268
Stijena 77224
Stjenice 73223
Stjepan Polje 75324
Stolac 88360
Stranjani 72209
Stricici 78208
Strpci 75305
Studenci 88323
Stupari 75283
Sturlic 77223
Suho Polje 75344
Sujica 80249
Svodna 79229
Tarcin 71244
Teocak 75414
Tesanj 74260
Tesanjka 74266
Teslic 74270
Tihaljina 88348
Tinja 75357
Tisca 75455
Todorovo 77233
Tojsici 75265
Tolisa 76272
Tomina 79265
Tomislavgrad 80240
Topcic Polje 72213
Travnik 72270
Trebinja 88375
Trebinje 89000
Trn 78252
Trnava Donja 76335
Trnjaci 76310
Trnovo 71220
Trzacka Rastela 77225
Turbe 72283
Turija 75306
Turjak Kod Bosanske Gradiske 78404
Tuzla 75000
Ugljevik 76330
Ugodnovici 74278
Ulog 71233
Usora 74230
Ustikolina 73250
Uvac 73265
Vardiste 73249
Vares 71330
Varoska Rijeka 77245
Velika Bukovica 74213
Velika Gata 77207
Velika Kladusa 77230
Velika Obarska 76329
Vidosi 88208
Vidovice 76275
Vinac 70202
Vir Kod Posusja 88247
Visegrad 73240
Visici 88307
Visoko 71300
Vitez 72250
Vitina 88326
Vitkovci Donji 74265
Vitkovici 73205
Vlasenica 75440
Vogosca 71320
Vojkovici 71123
Voljevac 70246
Vozuca 72227
Vranjak 74488
Vrapcici 88113
Vrazici 75248
Vrbanja Kod Banja Luke 78211
Vrbanjci 78225
Vrbaska 78408
Vrnograc 77231
Vrsani 76325
Vrsta 77203
Vuckovci 76254
Vukosavlje 74470
Zabar Donji 76273
Zaborak 73287
Zabrdje 72220
Zabrdje 76333
Zabrdje Kod Kotor Varosi 78221
Zaluzani 78214
Zavidovici 72220
Zboriste 74451
Zelinja 76259
Zeljezno Polje 72236
Zenica 72000
Zepce 72230
Zitomislici 88268
Zivinice 75270
Zupca 71370
Zvornik 75400}
separator = /(.*)\s(\d{5})/
places = places_bulk.split("\n")
places.each do |place|
matches = separator.match(place)
return matches[1].strip if matches and matches[2] === code.strip
end
return "<nepoznato mjesto>";
end
end

2544
front-api/tools/places.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -514,7 +514,13 @@ places_prepared = places.map &-> (p) {
matches = separator.match(p)
place = matches[1]
postal_code = matches[2]
%Q{ <option value="#{postal_code}">#{place}</option> }
# %Q{ <option value="#{postal_code}">#{place}</option> }
meho = <<HAMO
,{
"code": "#{postal_code}",
"placeLabel": "#{place}"
}
HAMO
}
puts places_prepared.join("\n")

View File

@@ -0,0 +1,50 @@
<%
dd = @cart.delivery_destination
c = @cart
%>
**Ime:** <%= dd.name %>
**Adresa:**
<%= dd.address %>
<%= dd.place.to_s.strip %> <%= Place.name_from_code(dd.place.to_s) %>
Bosna i Hercegovina
**Email:** <%= dd.email %>
**Telefon:** +387 <%= dd.phone %>
**Plaćanje:** <%= dd.get_payment_string %>
**Napomena:**
<%= dd.note %>
<% if dd.gift %>
**Poklon** **Name:** <%= dd.recipient_name %>
**Postal code:** <%= dd.recipient_postal_code %>
**Place:** <%= dd.recipient_place %>
**Address:** <%= dd.recipient_address %>
**Phone:** <%= dd.recipient_phone %>
**Email:** <%= dd.recipient_email %>
<% end %>
<% if dd.instant_delivery %>
**Naručeno:**
<%= @cart.updated_at.in_time_zone("Europe/Sarajevo").strftime("%A %d.%m.%Y. %H:%M") %>
<% end %>
<% @cart.item_in_carts.each do |iic| %>
**<%= iic.item.code %> <%= iic.item.name %>**
**<%= iic.count %>** x <%= Helper.money(iic.price) %> = <%= Helper.money(iic.count * iic.price) %>
<% end %>
Dostava
1 x <%= Helper.money(c.delivery_cost) %> = <%= Helper.money(c.delivery_cost) %>
**UKUPNO:** <%= Helper.money(c.total) %>

2
front-ui/.gitignore vendored
View File

@@ -18,6 +18,8 @@ coverage
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
build/**/*.js
build/**/*.css
build/*.js
build/*.css

View File

@@ -1,7 +1,7 @@
module.exports = function(grunt) {
grunt.initConfig({
browserify: {
basic: {
dev: {
src: ['app/ribica.js'],
dest: 'build/ribica.bundle.js',
options: {
@@ -12,7 +12,16 @@ module.exports = function(grunt) {
},
watch: true
}
},
prod: {
src: ['app/ribica.js'],
dest: 'build/ribica.bundle.js',
options: {
transform: ['reactify'],
browserifyOptions: {
standalone: 'RIBICA'
}
}
}
},
watch: {
@@ -39,7 +48,7 @@ module.exports = function(grunt) {
uglify: {
my_target: {
files: {
'build/ribica.min.js': ['build/ribica.js']
'build/ribica.js': ['build/ribica.js']
}
}
},
@@ -48,6 +57,7 @@ module.exports = function(grunt) {
options: {
variables: {
apiEndpoint: 'http://localhost:4567'
}
}
},
@@ -71,7 +81,7 @@ module.exports = function(grunt) {
expand: true,
flatten: true,
src: ['build/ribica.bundle.js'],
dest: 'build/'
dest: 'build/configured'
}]
}
},
@@ -84,7 +94,7 @@ module.exports = function(grunt) {
dest: 'build/ribica.css'
},
js: {
src: ['build/ribica.bundle.js'],
src: ['build/configured/ribica.bundle.js'],
dest: 'build/ribica.js'
}
}
@@ -103,7 +113,7 @@ module.exports = function(grunt) {
grunt.registerTask('default', []);
grunt.registerTask('config-dev', ['config:dev', 'replace']);
grunt.registerTask('config-prod', ['config:prod', 'replace']);
grunt.registerTask('dev', ['browserify', 'config-dev', 'concat:css', 'concat:js', 'connect:server:keepalive']);
grunt.registerTask('build', ['browserify', 'config-prod', 'concat:css', 'concat:js']);
grunt.registerTask('dev', ['browserify:dev', 'config-dev', 'concat:css', 'concat:js', 'connect:server:keepalive']);
grunt.registerTask('build', ['browserify:prod', 'config-prod', 'concat:css', 'concat:js', 'uglify']);
};

View File

@@ -1,5 +1,8 @@
var AppDispatcher = require('../dispatcher/appDispatcher');
var CartConstants = require('../constants/cartConstants');
var BurntToast = require('burnt-toast');
// Define action methods
var CartActions = {
@@ -11,6 +14,12 @@ var CartActions = {
},
addNItems: function(item, count) {
var burntToast = new BurntToast();7
var message = "Ubacili ste " + item.get('name') + " u korpu!";
burntToast.makeToast(message, {
duration: 1500,
position: "top"
});
AppDispatcher.handleAction({
actionType: CartConstants.ADD_N_ITEMS,
item: item,
@@ -45,17 +54,24 @@ var CartActions = {
},
takeItemOut: function(id) {
AppDispatcher.handleAction({
actionType: CartConstants.TAKE_ITEM_OUT,
itemId: id
});
AppDispatcher.handleAction({
actionType: CartConstants.TAKE_ITEM_OUT,
itemId: id
});
},
setItemCount: function(itemId, count) {
AppDispatcher.handleAction({
actionType: CartConstants.SET_ITEM_COUNT,
itemId: itemId,
count: count
});
AppDispatcher.handleAction({
actionType: CartConstants.SET_ITEM_COUNT,
itemId: itemId,
count: count
});
},
setAddressColapsed: function(isColapsed) {
AppDispatcher.handleAction({
actionType: CartConstants.SET_ADDRESS_COLAPSED,
isColapsed: isColapsed
});
}
};

View File

@@ -1,21 +1,23 @@
var AppDispatcher = require('../dispatcher/appDispatcher');
var NavigationConstants = require('../constants/navigationConstants');
var globals = require('../globals');
// Define action methods
var NavigationActions = {
// select item
goToItemDetails: function(item) {
AppDispatcher.handleAction({
actionType: NavigationConstants.CHANGE_URL,
url: '/artikal/' + item.get('id') +'/' + item.get('name')
url: '/artikal/' + item.get('id') +'/' + globals.Slugify(item.get('name'))
});
},
goToSection: function(section) {
AppDispatcher.handleAction({
actionType: NavigationConstants.CHANGE_URL,
url: '/sekcija/'+ section.get('id') + '/' + section.get('name')
url: '/sekcija/'+ section.get('id') + '/' + globals.Slugify(section.get('name'))
});
},
_getQueryStringPart: function(query, offset, limit) {
@@ -46,7 +48,7 @@ var NavigationActions = {
},
goToCategory: function(category,section, query, offset, limit) {
var url ='/sekcija/' + section.get('name') +'/kategorija/'+ category.get('id') + '/' + category.get('name');
var url ='/sekcija/' + section.get('name') +'/kategorija/'+ category.get('id') + '/' + globals.Slugify(category.get('name'));
var q = this._getQueryStringPart(query, offset, limit);
AppDispatcher.handleAction({
actionType: NavigationConstants.CHANGE_URL,
@@ -56,7 +58,7 @@ var NavigationActions = {
goToSubCategory: function(subCategory, offset, limit, query) {
var q = this._getQueryStringPart(query, offset, limit);
var url = '/podkategorija/' + subCategory.get('id') + '/' + subCategory.get('name');
var url = '/podkategorija/' + subCategory.get('id') + '/' + globals.Slugify(subCategory.get('name'));
AppDispatcher.handleAction({
actionType: NavigationConstants.CHANGE_URL,
url: (url + q)

View File

@@ -0,0 +1,68 @@
var React = require('react'),
CartStore = require('../../stores/cartStore'),
AddToCart = require('../cart/addToCart'),
CartActions = require('../../actions/cartActions'),
LinkBanner = require('../linkBanner/linkBanner'),
NavigationActions = require('../../actions/navigationActions'),
Globals = require('../../globals')
Router = require("react-router"),
Link = Router.Link;
var AboutUsPage = React.createClass({
render: function() {
return (
<div className="message center">
<p>Rođenje djeteta je sigurno jedan od najljepših trenutaka u životu svakog čovjeka, ali biti roditelj jedan je od najtežih poslova.
</p>
<p>Svjesni brzine života, želimo da Vam olakšamo pri nabavci opreme koja je neophodna u svakom domu koji krasi dijete.
Pažljivo biramo artikle, te štedimo vrijeme i novac
</p>
<p style={{"textAlign": "center"}}>
<img src="https://res.cloudinary.com/du5pdibul/image/upload/c_scale,w_700/v1443706682/download_eodxbl.jpg" style={{"margin": "10px"}} />
</p>
<p>Internet trgovina je dostupna uvijek! Jednostavnim pretraživanjem možete naći artikle i spremiti ih u korpu, a mi ćemo nastojati da ih dostavimo u dogovoreno vrijeme.</p>
<p>Potrudili smo se prilagoditi svima:
<ul>
<li>djevojkama i ženama koje tek postaju majke, a ne žele puno vremena trošiti na kupovinu;
</li>
<li>onima koje tek rode, a ne žele čekati rodbinu i prijatelje da im donesu mnoge stvari koje beba zahtijeva, a same nisu u mogućnosti izaći;
</li>
<li>majkama koje su kancelarijski radnici, a vrijeme poslije posla žele posvetiti djeci;
</li>
<li>očevima koji većinom ne vole ići u kupovinu, a trebaju im adekvatne igračke,
</li>
<li>onima koji nisu u mogućnosti prisustvovati slavljima, a željeli bi poslati znak pažnje u vidu poklona;
</li>
<li>svima ostalima kojima treba pomoć oko kupovine artikala namijenjenih djeci.
</li>
</ul>
</p>
<p>Ova e-trgovina je osmišljena u skladu sa željama i potrebama roditelja i djece, te Vam omogućava da uživate dok mi kupujemo za Vas , jer najbolje što možete pokloniti djeci je Vaše vrijeme.
</p>
<p>Sve narudžbe koje zaprimimo do 12:00h dostavljamo isti radni dan, dok narudžbe pristigle nakon toga dostavljamo sljedeći radni dan.
</p>
<p style={{"font-weight": "bold"}}>Plaćanje se vrši pouzećem prilikom dostave artikala! </p>
<p>Ukoliko želite da nas naružite ili pohvalite to možete uraditi putem Email adrese <a href="mailto:draga@ribica.ba">draga@ribica.ba</a> ili pozivom na broj +387 62 274 275.</p>
<p style={{textAlign: "right" }}>Vaša Ribica.ba</p>
</div>
);
}
});
module.exports = AboutUsPage;

View File

@@ -0,0 +1,40 @@
var React = require('react'),
CartStore = require('../../stores/cartStore'),
AddToCart = require('../cart/addToCart'),
CartActions = require('../../actions/cartActions'),
LinkBanner = require('../linkBanner/linkBanner'),
NavigationActions = require('../../actions/navigationActions'),
Globals = require('../../globals')
Router = require("react-router"),
Link = Router.Link;
var NewsletterSweepstake = React.createClass({
render: function() {
return (
<div className="center">
<h1 className="normal-message">Novosti i sniženja</h1>
<p className="normal-message">Prijavite se na naše novosti i saznajte sve o najnovijim sniženjima i ponudama na Ribici!</p>
<p className="normal-message"><strong>I ovaj put - najsretnije očekuju vrijedne Lego i Disney Frozen nagrade nagrade!</strong> <br /></p>
<p className="normal-message">Pobjednike objavljujemo u našim email novostima u Nedjelju, 7.2.2016. </p>
<p style={{"textAlign": "center"}}>
<iframe style={{"width": "100%", "height": "500px", "border": "0px", "overflow": "hidden"}} src="newsletter.html" />
</p>
</div>
);
}
});
module.exports = NewsletterSweepstake;

View File

@@ -90,8 +90,8 @@ var ByCategory = React.createClass({
locationName="category"
locationId={Number(this.getParams().id)} />
<h3> Kategorija - {this.state.category.get('name')}</h3>
Number of items in this category: {this.state.items.length}
<h3> {this.state.category.get('name')}</h3>
<div>
<AppliedFiltersList
filters={this.appliedCategoryFiltersArray()}
@@ -99,8 +99,6 @@ var ByCategory = React.createClass({
</div>
<div> total count is : {this.state.items.totalCount}</div>
<ItemList
items={this.state.items}
paginationEnabled={true}

View File

@@ -30,17 +30,14 @@ var BySubCategory = React.createClass({
return (<div>
<div className='col-md-2'>
<FilterCriteriaSelector filterCriterias={this.state.subCategory.get('filter_criterias')} onFCClick={this.onFCClick} />
<FilterCriteriaSelector filterCriterias={this.state.subCategory.get('filter_criterias')} onFCClick={this.onFCClick} />
</div>
<div classname='col-md-10'>
<h2>
Podkategorija {this.state.subCategory.get('name')}
{this.state.subCategory.get('name')}
</h2>
<div>Number of items in this subcategory: {this.state.items.length}</div>
<div>total count : {this.state.items.totalCount}</div>
<div>
<AppliedFiltersList filters={this.appliedSubCategoryFiltersArray()} onRemove={this.removeAppliedFilter} />

View File

@@ -1,6 +1,7 @@
var React = require('react');
var CartStore = require('../../stores/cartStore.js');
var CartActions = require('../../actions/cartActions.js');
var NavigationActions = require('../../actions/navigationActions.js');
var Globals = require('../../globals');
var buttonHolderStyle = {
@@ -13,13 +14,12 @@ var AddToCart = React.createClass({
var itemCount = this.state.count;
var amountAndAddButton = (
<div className="row-fluid add-to-cart">
<div className="span12">
<div style={buttonHolderStyle}><button className="btn white_button" onClick={this._onDecreaseClick}>-</button></div>
<div style={buttonHolderStyle}> <div className="add-to-cart-count">{ itemCount }</div> </div>
<div style={buttonHolderStyle}><button className="btn white_button" onClick={this._onIncreaseClick}>+</button></div>
<div className="col-lg-12">
<button className="btn white_button" onClick={this._onDecreaseClick}>-</button><span className='add-to-cart-count'>{itemCount}</span>
<button className="btn white_button" onClick={this._onIncreaseClick}>+</button>
</div>
<div>
<div style={buttonHolderStyle}><button className="btn add-to-cart-button" onClick={this._addToCartClick}>Dodaj na popis za kupovinu</button></div>
<div style={buttonHolderStyle}><button className="btn add-to-cart-button" onClick={this._addToCartClick}>Ubaci u korpu</button></div>
</div>
</div>
);
@@ -70,6 +70,10 @@ var AddToCart = React.createClass({
_addToCartClick: function () {
CartActions.addNItems(this.props.item, this.state.count);
setTimeout(function() {
NavigationActions.goToCart();
}, 500);
},
componentWillUnmount: function () {

View File

@@ -26,11 +26,11 @@ var CartIcon = React.createClass({
var textNotificationStyle = (this.state.count > 0) ? { display: 'inline-block'} : { display: 'none'} ;
return (
var large = (
<div>
<ul className="nav navbar-nav navbar-right hidden-md hidden-sm hidden-xs">
<li onClick={this._onClick} style={{borderTop: 'solid lightgray 1px', borderBottom: 'solid lightgray 1px', borderLeft: 'solid lightgray 1px', paddingBottom: 22}}><a ><div className="mycart"><span>{normalizeCount(this.state.count)}</span></div></a></li>
<li onClick={this._onClick} style={{borderTop: 'solid lightgray 1px', borderBottom: 'solid lightgray 1px', paddingBottom: 2}}><a href="#" style={{ paddingRight: '5px', backgroundColor: 'transparent' }}><CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts} justMerchandise={true}/> </a></li>
<li onClick={this._onClick} style={{borderTop: 'solid lightgray 1px', borderBottom: 'solid lightgray 1px', paddingBottom: 2}}><a href="#" style={{ paddingRight: '5px', backgroundColor: 'transparent' }}><CartTotal items={this.state.items} itemCounts={this.state.itemCounts}/> </a></li>
<li onClick={this._onClick} style={{borderTop: 'solid lightgray 1px', borderBottom: 'solid lightgray 1px', borderRight: 'solid lightgray 1px'}}>
<a style={{marginBottom: 10, marginRight: 10}} className="mybutton" href="#">Završi narudžbu</a></li>
@@ -38,10 +38,25 @@ var CartIcon = React.createClass({
<ul className="nav navbar-nav navbar-right hidden-lg">
<li onClick={this._onClick} style={{borderTop: 'solid lightgray 1px', borderBottom: 'solid lightgray 1px', borderLeft: 'solid lightgray 1px', paddingBottom: 22}}><a ><div className="mycart"><span>{normalizeCount(this.state.count)}</span></div></a></li>
<li onClick={this._onClick} style={{borderTop: 'solid lightgray 1px', borderBottom: 'solid lightgray 1px',borderRight: 'solid lightgray 1px', paddingBottom: 2}}><a href="#" style={{ paddingRight: '5px', backgroundColor: 'transparent'}}><CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts} justMerchandise={true}/> </a></li>
<li onClick={this._onClick} style={{borderTop: 'solid lightgray 1px', borderBottom: 'solid lightgray 1px',borderRight: 'solid lightgray 1px', paddingBottom: 2}}><a href="#" style={{ paddingRight: '5px', backgroundColor: 'transparent'}}><CartTotal items={this.state.items} itemCounts={this.state.itemCounts} /> </a></li>
</ul>
</div>
);
var small = (
<span>
<ul className="nav navbar-nav hidden-lg">
<li onClick={this._onClick} style={{paddingBottom: 22}}><a ><div className="mycart"><span>{normalizeCount(this.state.count)}</span></div></a></li>
</ul>
</span>
);
if (this.props.small === 'true') {
return small;
}
else {
return large;
}
},
// Add change listeners to stores

View File

@@ -7,6 +7,7 @@ var React = require('react'),
Globals = require('../../globals'),
LinkBanner = require('../linkBanner/linkBanner'),
CartTotal = require('./cartTotal');
AllItemsInGroup = require('../items/allItemsInGroup');
var Router = require('react-router');
@@ -15,6 +16,10 @@ var CartPage = React.createClass({
_onTakeItemOut: function(itemId) {
CartActions.takeItemOut(itemId);
},
_goToStartPage: function() {
NavigationActions.goToHome();
},
render: function() {
var counts = this.state.itemCounts;
var self = this;
@@ -22,24 +27,178 @@ var CartPage = React.createClass({
if(!counts) return false;
var count = counts[i.get('id')].get('count');
return count > 0 || count === "";
}).map(function (i) {
}); // tableDetails
var displayedItemsDesktop = displayedItems.map( this.desktopDetails );
var displayedItemsMobile = displayedItems.map( this.mobileDetails );
var deliveryDestination = (<span></span>);
if (this.state.destinationValid) {
deliveryDestination = (
<div>
Na adresu {this.state.deliveryDestination.name},
</div>
)
}
var cartTotal = (
<div>
<div className="row cart-total">
<div className="col-lg-6">Roba:<br />Dostava:<br />Ukupno:</div>
<div className="col-lg-6 text-right">
<CartTotal items={this.state.items} itemCounts={this.state.itemCounts} /><br />
<span className={this.state.deliveryDestinationErrors['place'] ? 'hidden' : 'shown'}>
<CartTotal deliveryCosts={this.state.deliveryCosts} instantDelivery={this.state.deliveryDestination.get('instant_delivery')}/><br />
<CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts} instantDelivery={this.state.deliveryDestination.get('instant_delivery')}/>
</span>
</div>
</div>
<div className="row">
<div className="col-lg-12 pull-left">
<button className="mybutton" onClick={this._onOrderClick}>Završi narudžbu</button> ili <button className="btn btn-default" onClick={this._goToStartPage}>Nastavi kupovati</button>
</div>
</div>
</div>
);
var instantDelivery = (<div className="text-center">
<input type="checkbox"
checked={this.state.deliveryDestination.get('instant_delivery')} onChange={this._onInstantDeliveryChange}/>
{" Hitna dostava (ako naručite toku radnog dana do 16h) ili ujutru (ako naručite poslije 16h)"}
</div>);
var buySomethingMessage = (<div></div>);
var content;
if (displayedItems.length <= 0) {
cartTotal = (<div></div>)
buySomethingMessage = (<div>
<div className="text-primary">Nemate ni jedan artikal u vašoj korpi. Kada vidite nešto što vam se sviđa - pritisnite dugme UBACI U KORPU pored artikla kako biste ga dodali u korpu.</div>
<div>Evo nekoliko artikala koje vam možemo preporučiti: </div>
<AllItemsInGroup groupId={Globals.ItemGroupIdOfEmptyCartPage} />
</div>)
content = buySomethingMessage;
} else {
content = (<div>
<table className="table">
<thead className="hidden-md hidden-sm hidden-xs">
<tr className="text-center">
<th className="col-lg-2"></th>
<th>Proizvod</th>
<th>Cijena</th>
<th className="col-lg-1">Količina</th>
<th className="hidden-md hidden-sm hidden-xs">Ukupna cijena</th>
<th></th>
</tr>
</thead>
<tbody>
{displayedItemsMobile}
{displayedItemsDesktop}
</tbody>
</table>
{buySomethingMessage}
{cartTotal}
</div>)
}
return (
<div className="col-lg-12">
<div className="row">
<div className="col-lg-12">
<LinkBanner locationName="checkoutPage"/>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<div className="cart-title">KORPA</div>
{content}
</div>
</div>
</div>
);
},
desktopDetails: function (i) {
var self = this;
var counts = this.state.itemCounts;
var count = counts[i.get('id')].get('count');
var commission = counts[i.get('id')].get('commission');
var price = i.get('list_price');
var firstImage = i.get('multi_media_descriptions')[0];
firstImage = firstImage || { resized_url: "https://res.cloudinary.com/lfvt7ps2n/image/upload/c_fit,h_172,w_226/v1421732950/http_www.asms.ru_bitrix_templates_main_images_nophoto_irnofq.png" } ;
return (
<tr key={i.get('id')}>
<td>
<tr key={i.get('id') } className="cart-table-row hidden-md hidden-xs hidden-sm">
<td className='text-center'>
<img style={{maxWidth: '90px', maxHeight: '90px'}} src={firstImage.url} alt="product image"/>
</td>
<td>
<td className="cart-price-bigger">
<p> {i.get('brand').name}</p>
<p>
{i.get('name')}
</p>
</td>
<td>{ Globals.FormatCurrency(price) }</td>
<td className="cart-price-bigger text-center"> <abbr title={ Globals.FormatPercentage(commission) + " RUC"}>{ Globals.FormatCurrency(price) } </abbr>
</td>
<td >
<select style={{textAlign: 'center'}} value={count} className="form-control"
onChange={self._onQuantityChange.bind(self, i.get('id'))}
>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
</td>
<td className="cart-price-bigger text-center">
{ Globals.FormatCurrency(count * price) }
</td>
<td>
<button className="btn btn-default" onClick={self._onTakeItemOut.bind(self, i.get('id'))}>Ukloni iz korpe</button>
</td>
</tr>)
},
mobileDetails: function (i) {
var self = this;
var counts = this.state.itemCounts;
var count = counts[i.get('id')].get('count');
var commission = counts[i.get('id')].get('commission');
var price = i.get('list_price');
var firstImage = i.get('multi_media_descriptions')[0];
firstImage = firstImage || { resized_url: "https://res.cloudinary.com/lfvt7ps2n/image/upload/c_fit,h_172,w_226/v1421732950/http_www.asms.ru_bitrix_templates_main_images_nophoto_irnofq.png" } ;
return (
<tr key={i.get('id') } className="cart-table-row hidden-lg">
<td className="cart-price-bigger">
<p> {i.get('brand').name}</p>
<p>
{i.get('name')}
</p>
<p>
<details>
<summary>
{ Globals.FormatCurrency(price) }
</summary>
<div className="cart-commision-tiny text-left">{ Globals.FormatPercentage(commission) } RUC</div>
</details>
</p>
</td>
<td className="col-sm-2 col-xs-2 col-md-2">
<select style={{textAlign: 'center'}} value={count}
onChange={self._onQuantityChange.bind(self, i.get('id'))}
>
@@ -57,80 +216,10 @@ var CartPage = React.createClass({
</select>
</td>
<td>
{ Globals.FormatCurrency(count * price) }
<button onClick={self._onTakeItemOut.bind(self, i.get('id'))}>X</button>
</td>
<td>
<button onClick={self._onTakeItemOut.bind(self, i.get('id'))}>Ukloni iz korpe</button>
</td>
</tr>)
});
var deliveryDestination = (<span></span>);
if (this.state.destinationValid) {
deliveryDestination = (
<div>
Na adresu {this.state.deliveryDestination.name},
</div>
)
}
var cartTotal = (
<div>
<div className="row cart-total">
<div className="col-lg-6">Ukupno</div>
<div className="col-lg-6">
<CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts}/>
</div>
</div>
<div className="row">
<div className="col-lg-12 pull-right">
<button className="btn btn-warning" onClick={this._onOrderClick}>Završi narudžbu</button>
</div>
</div>
</div>
);
var buySomethingMessage = (<div></div>);
var content;
if (displayedItems.length <= 0) {
cartTotal = (<div></div>)
buySomethingMessage = (<div>Nemate ni jedan artikal u vašoj korpi. Kada vidite nešto što vam se sviđa - pritisnite dugme KUPI pored artikla kako biste ga dodali u korpu. </div>)
content = buySomethingMessage;
} else {
content = (<div>
<table className="table">
<thead>
<tr>
<th className="col-lg-2"></th>
<th>Proizvod</th>
<th>Cijena</th>
<th className="col-lg-1">Količina</th>
<th>Ukupna cijena</th>
<th></th>
</tr>
</thead>
<tbody>
{displayedItems}
</tbody>
</table>
{buySomethingMessage}
{cartTotal}
</div>)
}
return (
<div className="col-lg-12">
<LinkBanner locationName="thankYouPage" />
<div className="cart-title">KORPA</div>
{content}
</div>
);
},
</tr>)
},
// Add change listeners to stores
componentDidMount: function() {
@@ -148,10 +237,14 @@ var CartPage = React.createClass({
}
},
_onInstantDeliveryChange: function () {
CartActions.changeDeliveryDestinationProperty("instant_delivery", !this.state.deliveryDestination.get('instant_delivery'));
},
_onOrderClick: function () {
NavigationActions.goToCheckout();
},
_onQuantityChange(itemId, e) {
_onQuantityChange: function(itemId, e) {
CartActions.setItemCount(itemId, e.target.value);
},
getInitialState: function () {

View File

@@ -1,6 +1,5 @@
var React = require('react'),
Globals = require('../../globals');
;
Globals = require('../../globals');;
var Router = require('react-router');
@@ -8,29 +7,34 @@ var Router = require('react-router');
var CartTotal = React.createClass({
render: function() {
render: function() {
var counts = this.props.itemCounts;
var total = 0;
if (counts && this.props.items) {
var items = this.props.items.models;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var count = counts[item.get('id')].get('count');
var price = item.get('list_price');
total += (price * count)
};
}
if (this.props.deliveryCosts) {
if (this.props.instantDelivery) {
total += Number(this.props.deliveryCosts.get('instant_delivery_price'))
} else {
total += Number(this.props.deliveryCosts.get('delivery_price'))
}
}
var counts = this.props.itemCounts;
var total = 0;
var items = this.props.items.models;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var count = counts[item.get('id')].get('count');
var price = item.get('list_price');
total += (price * count)
};
return ( <span>{Globals.FormatCurrency(total)}</span>);
return ( < span > {
Globals.FormatCurrency(total)
} < /span>);
}
});
module.exports = CartTotal;
module.exports = CartTotal;

View File

@@ -5,24 +5,55 @@ var React = require('react'),
NavigationActions = require('../../actions/navigationActions'),
SingleItem = require('../items/singleItem'),
Globals = require('../../globals'),
CartTotal = require('./cartTotal'),
CartTotal = require('./cartTotal'),
LinkBanner = require('../linkBanner/linkBanner'),
RibicaFormError = require('../shared/ribicaFormError');
PaymentSelect = require('../payment/paymentSelect'),
RibicaFormError = require('../shared/ribicaFormError'),
PaypalButton = require('../payment/paypalButton'),
PikpayButton = require('../payment/pikpayButton'),
CashOnDeliveryButton = require('../payment/cashOnDeliveryButton');
var Router = require('react-router');
var CheckoutPage = React.createClass({
render: function() {
return (
var choosePayment = (
<div className="payment-btn">
<PaymentSelect deliveryDestination={this.state.deliveryDestination}
amount={CartStore.getAmount()}
deliveryCost={CartStore.getDeliveryCost(false)}
disabled={!this.state.isDeliveryDestinationValid}
cashOnDeliveryDisabled={!this.state.isDeliveryDestinationValid || this.state.deliveryDestination.get('gift')}
onCashClick={this._onOrderClick}
cartId={this.state.deliveryDestination.get('id')}
/>
</div>
);
var last_used_payment;
if(this.state.deliveryDestination.get('payment_method') == 'paypal') {
last_used_payment = (
<PaypalButton disabled={!this.state.isDeliveryDestinationValid} onSubmitPaypal={this._handleOnSubmitPaypal} deliveryDestination={this.state.deliveryDestination} amount={CartStore.getAmount()} deliveryCost={CartStore.getDeliveryCost(false)} cartId={this.state.deliveryDestination.get('id')}/>
);
} else if(this.state.deliveryDestination.get('payment_method') == 'pikpay') {
last_used_payment = (
<PikpayButton amount={CartStore.getAmount()} deliveryCost={CartStore.getDeliveryCost(false)} disabled={!this.state.isDeliveryDestinationValid} deliveryDestination={this.state.deliveryDestination} cartId={this.state.deliveryDestination.get('id')}/>
);
} else {
last_used_payment = (
<CashOnDeliveryButton onCashClick={this._onOrderClick} disabled={!this.state.isDeliveryDestinationValid || this.state.deliveryDestination.get('gift')} cartId={this.state.deliveryDestination.get('id')}/>
);
}
var supportedPlaceOptions = CartStore.getSupportedPlaces().map ( function (p) { return (<option value={p.code}>{p.placeLabel}</option>)});
var content = (
<div className="checkout-page center">
<div className="form-horizontal">
<div className="form-horizontal checkout_form_margin">
<fieldset>
<legend>Dostava</legend>
<div className="form-group">
<legend>Podaci o naručiocu</legend>
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="name">Prezime i Ime</label>
<div className="col-md-4">
@@ -31,7 +62,7 @@ var CheckoutPage = React.createClass({
<span className="help-block">ime osobe koja prima pošiljku</span>
</div>
</div>
<div className="form-group">
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="name">Adresa</label>
<div className="col-md-4">
<RibicaFormError componentName="address" errorMessagesObject={this.state.deliveryDestinationErrors} />
@@ -39,541 +70,19 @@ var CheckoutPage = React.createClass({
<span className="help-block">adresa na koju će roba biti isporučena</span>
</div>
</div>
<div className="form-group">
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="place">Mjesto</label>
<div className="col-md-4">
<RibicaFormError componentName="place" errorMessagesObject={this.state.deliveryDestinationErrors} />
<select id="place" name="place" className="form-control" value={this.state.deliveryDestination.get('place')} onChange={this._onFieldChange} >
<option value="-12">Izaberite mjesto</option>
<option value="-13">-------------------------------</option>
<option value=" 71000">Sarajevo</option>
<option value=" 71103">Sarajevo, Centar</option>
<option value=" 71160">Sarajevo, Novi Grad</option>
<option value=" 71120">Sarajevo, Novo Sarajevo</option>
<option value=" 71140">Sarajevo, Stari Grad</option>
<option value=" 78000">Banja Luka</option>
<option value=" 75000">Tuzla</option>
<option value=" 72000">Zenica</option>
<option value=" 88000">Mostar</option>
<option value=" 88000">Mostar, Jug</option>
<option value=" 88000">Mostar, Jugozapad</option>
<option value=" 88000">Mostar, Sjever</option>
<option value=" 88000">Mostar, Zapad</option>
<option value="-1">-------------------------------</option>
<option value="78255">Aleksandrovac</option>
<option value="89245">Avtovac</option>
<option value="76310">Balatun</option>
<option value="78000">Banja Luka</option>
<option value="75290">Banovici</option>
<option value="76312">Batkovic</option>
<option value="72233">Begov Han</option>
<option value="88363">Berkovici</option>
<option value="77000">Bihac</option>
<option value="76204">Bijela</option>
<option value="76300">Bijeljina</option>
<option value="73263">Bijelo Brdo</option>
<option value="89230">Bileca</option>
<option value="72248">Biljesevo</option>
<option value="88407">Bjelimici</option>
<option value="88201">Blagaj</option>
<option value="74275">Blatnica</option>
<option value="88263">Blatnica Kod Mostara</option>
<option value="71215">Blazuj</option>
<option value="76277">Bok</option>
<option value="74322">Boljanic</option>
<option value="77240">Bosanska Krupa</option>
<option value="77250">Bosanski Petrovac</option>
<option value="80270">Bosansko Grahovo</option>
<option value="88408">Bradina</option>
<option value="75420">Bratunac</option>
<option value="76000">Brcko</option>
<option value="76100">Brcko Distrikt</option>
<option value="77205">Brekovica</option>
<option value="74210">Brestovo</option>
<option value="71255">Brestovsko</option>
<option value="71370">Breza</option>
<option value="79208">Brezicani</option>
<option value="76109">Brezik</option>
<option value="76216">Brezovo Polje</option>
<option value="74206">Brijesnica</option>
<option value="74206">Brijesnica Kod Doboja</option>
<option value="74206">Brijesnica Mala</option>
<option value="74206">Brijesnica Velika</option>
<option value="76206">Brka</option>
<option value="72243">Brnjic</option>
<option value="88243">Brocanac</option>
<option value="88243">Brocanac</option>
<option value="73309">Brod Kod Foce</option>
<option value="76313">Brodac</option>
<option value="78204">Bronzani Majdan</option>
<option value="72290">Bucici</option>
<option value="70230">Bugojno</option>
<option value="75203">Bukinje</option>
<option value="74277">Buletic</option>
<option value="88202">Buna</option>
<option value="72260">Busovaca</option>
<option value="88409">Buturovicpolje</option>
<option value="77245">Buzim</option>
<option value="73280">Cajnice</option>
<option value="75405">Caparde</option>
<option value="88300">Capljina</option>
<option value="72224">Cardak</option>
<option value="71347">Careva Cuprija</option>
<option value="72246">Catici</option>
<option value="77220">Cazin</option>
<option value="74274">Cecava</option>
<option value="88404">Celebici</option>
<option value="80203">Celebici Lusnic</option>
<option value="73307">Celebici Kod Foce</option>
<option value="75246">Celic</option>
<option value="78240">Celinac</option>
<option value="89243">Cemerno</option>
<option value="88265">Cerin</option>
<option value="74211">Cerovica</option>
<option value="78403">Cerovljani</option>
<option value="88260">Citluk</option>
<option value="77226">Coralici</option>
<option value="76239">Crkvina</option>
<option value="88367">Crnici</option>
<option value="76328">Crnjelovo</option>
<option value="71221">Dejcici</option>
<option value="79243">Demirovac</option>
<option value="74400">Derventa</option>
<option value="75444">Derventa Kod Vlasenice</option>
<option value="89233">Divin</option>
<option value="74000">Doboj</option>
<option value="74206">Doboj Istok</option>
<option value="74203">Doboj Jug</option>
<option value="75328">Doborovci</option>
<option value="75308">Dobosnica</option>
<option value="77210">Dobretici</option>
<option value="71245">Dobrinja</option>
<option value="79223">Dobrljin</option>
<option value="77242">Dobro Selo</option>
<option value="73247">Dobrun</option>
<option value="75206">Dokanj</option>
<option value="72278">Dolac Na Lasvi</option>
<option value="76233">Domaljevac</option>
<option value="88305">Domanovici</option>
<option value="76274">Donja Mahala</option>
<option value="76257">Donja Medjidja</option>
<option value="76327">Donja Slatina</option>
<option value="71305">Donje Mostre</option>
<option value="79228">Donji Agici</option>
<option value="88343">Donji Memici</option>
<option value="70220">Donji Vakuf</option>
<option value="78432">Donji Vijacani</option>
<option value="79289">Donji Vrbljani</option>
<option value="76273">Donji Zabar</option>
<option value="76323">Dragaljevac</option>
<option value="74209">Dragalovci</option>
<option value="78215">Dragocaj</option>
<option value="88215">Dreznica</option>
<option value="75410">Drinjaca</option>
<option value="88344">Drinovci</option>
<option value="80260">Drvar</option>
<option value="75358">Duboki Potok</option>
<option value="75308">Dubostica</option>
<option value="78411">Dubrave</option>
<option value="75274">Dubrave Donje</option>
<option value="75273">Dubrave Gornje</option>
<option value="79227">Dubrovik</option>
<option value="74483">Dugo Polje</option>
<option value="75272">Djurdjevik</option>
<option value="75445">Dusanovo</option>
<option value="88342">Duzice</option>
<option value="76311">Dvorovi</option>
<option value="79264">Fajtovci</option>
<option value="73300">Foca</option>
<option value="71270">Fojnica</option>
<option value="89247">Fojnica Kod Gacka</option>
<option value="88306">Gabela Polje</option>
<option value="89240">Gacko</option>
<option value="80230">Glamoc</option>
<option value="76318">Glavicice</option>
<option value="72230">Globarica</option>
<option value="71275">Gojevici</option>
<option value="73000">Gorazde</option>
<option value="88340">Gorica</option>
<option value="77222">Gornja Koprivna</option>
<option value="76238">Gornja Slatina</option>
<option value="75208">Gornja Tuzla</option>
<option value="79266">Gornji Kamengrad</option>
<option value="78405">Gornji Podgradci</option>
<option value="76207">Gornji Rahic</option>
<option value="78439">Gornji Strpci</option>
<option value="74272">Gornji Teslic</option>
<option value="70240">Gornji Vakuf Uskoplje</option>
<option value="76207">Gornji Zovik</option>
<option value="89201">Grab</option>
<option value="78227">Grabovica</option>
<option value="75320">Gracanica</option>
<option value="70233">Gracanica Kod Bugojna</option>
<option value="75276">Gracanica Selo</option>
<option value="88392">Gradac</option>
<option value="76250">Gradacac</option>
<option value="78400">Gradiska</option>
<option value="88340">Grude</option>
<option value="80205">Guber</option>
<option value="80204">Listani</option>
<option value="72277">Guca Gora</option>
<option value="71240">Hadzici</option>
<option value="72225">Hajdarevici</option>
<option value="72245">Haljinici</option>
<option value="72281">Han Bila</option>
<option value="71360">Han Pijesak</option>
<option value="71212">Hrasnica</option>
<option value="88395">Hrasno</option>
<option value="71144">Hresa</option>
<option value="78436">Hrvacani</option>
<option value="75216">Husino</option>
<option value="88394">Hutovo</option>
<option value="71210">Ilidza</option>
<option value="71380">Ilijas</option>
<option value="73208">Ilovaca</option>
<option value="78234">Imljani</option>
<option value="77208">Izacic</option>
<option value="88420">Jablanica</option>
<option value="71423">Jahorina</option>
<option value="70101">Jajce</option>
<option value="76316">Janja</option>
<option value="72215">Janjici</option>
<option value="88224">Jare</option>
<option value="78233">Javorani</option>
<option value="74264">Jelah</option>
<option value="77241">Jezerski</option>
<option value="79244">Johova</option>
<option value="73319">Josanica</option>
<option value="78244">Josavka</option>
<option value="72264">Kacuni</option>
<option value="72240">Kakanj</option>
<option value="74413">Kalenderovci</option>
<option value="75260">Kalesija</option>
<option value="71230">Kalinovik</option>
<option value="77204">Kamenica</option>
<option value="72265">Kaonik</option>
<option value="71213">Kasindo</option>
<option value="80246">Kazaginac</option>
<option value="88283">Kifino Selo</option>
<option value="71250">Kiseljak</option>
<option value="75211">Kiseljak, Kod Tuzle</option>
<option value="75280">Kladanj</option>
<option value="74452">Klakar Donji</option>
<option value="79280">Kljuc</option>
<option value="88324">Klobuk</option>
<option value="74207">Klokotnica</option>
<option value="78230">Knezevo</option>
<option value="78230">Skender Vakuf</option>
<option value="79246">Knezica</option>
<option value="71356">Knezina</option>
<option value="78423">Kobas</option>
<option value="88226">Kocerin</option>
<option value="78409">Kocicevo</option>
<option value="78207">Kola</option>
<option value="80244">Kongora</option>
<option value="88400">Konjic</option>
<option value="77249">Konjoder</option>
<option value="74489">Koprivna</option>
<option value="75247">Koraj</option>
<option value="76236">Kornica</option>
<option value="74253">Kosova</option>
<option value="76276">Kostric</option>
<option value="78220">Kotor Varos</option>
<option value="74215">Kotorsko</option>
<option value="72226">Kovaci</option>
<option value="79002">Kozarac</option>
<option value="79240">Bosanska Dubica</option>
<option value="75413">Kozluk</option>
<option value="72244">Kraljeva Sutjeska</option>
<option value="71260">Kresevo</option>
<option value="78256">Kriskovci</option>
<option value="78206">Krupa Na Vrbasu</option>
<option value="88203">Krusevo</option>
<option value="78424">Kukulje</option>
<option value="71216">Kula</option>
<option value="78443">Kulasi</option>
<option value="77206">Kulen Vakuf</option>
<option value="80320">Kupres</option>
<option value="78250">Laktasi</option>
<option value="78407">Laminci Sredjani</option>
<option value="79204">Lamovita</option>
<option value="89208">Lastva</option>
<option value="72216">Lasva</option>
<option value="78222">Liplje</option>
<option value="75213">Lipnica</option>
<option value="78434">Lisnja</option>
<option value="80101">Livno</option>
<option value="75214">Ljubace</option>
<option value="79206">Ljubija</option>
<option value="88380">Ljubinje</option>
<option value="89209">Ljubomir</option>
<option value="88320">Ljubuski</option>
<option value="88223">Ljuti Dolac</option>
<option value="76278">Loncari</option>
<option value="75240">Lopare</option>
<option value="75300">Lukavac</option>
<option value="75301">Lukavac Mjesto</option>
<option value="75327">Lukavica</option>
<option value="79267">Lusci Palanka</option>
<option value="74250">Maglaj</option>
<option value="74216">Majevac</option>
<option value="77235">Mala Kladusa</option>
<option value="74418">Mala Socanica</option>
<option value="75320">Malesici</option>
<option value="76208">Maoca</option>
<option value="78410">Masici</option>
<option value="78223">Maslovare</option>
<option value="76271">Matici</option>
<option value="74203">Matuzici</option>
<option value="88266">Medjugorje</option>
<option value="79247">Medjuvodje</option>
<option value="72282">Mehurici</option>
<option value="80243">Mesihovina</option>
<option value="75446">Milici</option>
<option value="73283">Miljeno</option>
<option value="73313">Miljevina</option>
<option value="74485">Milosevac</option>
<option value="75329">Miricina</option>
<option value="74480">Modrica</option>
<option value="71428">Mokro</option>
<option value="89204">Mosko</option>
<option value="88000">Mostar</option>
<option value="88000">Mostar, Jug</option>
<option value="88000">Mostar, Jugozapad</option>
<option value="88000">Mostar, Sjever</option>
<option value="88000">Mostar, Zapad</option>
<option value="75212">Mramor</option>
<option value="73206">Mravinjac</option>
<option value="79544">Mrkodol</option>
<option value="70260">Mrkonjic Grad</option>
<option value="72212">Nemila</option>
<option value="88390">Neum</option>
<option value="88280">Nevesinje</option>
<option value="72276">Nova Bila</option>
<option value="78418">Nova Topola</option>
<option value="79220">Bosanski Novi</option>
<option value="74254">Novi Seher</option>
<option value="72290">Novi Travnik</option>
<option value="78428">Nozicko</option>
<option value="70225">Oborci</option>
<option value="76235">Obudovac</option>
<option value="76290">Odzak</option>
<option value="74470">Vukosavlje</option>
<option value="88285">Odzak Kod Nevesinja</option>
<option value="71340">Olovo</option>
<option value="79203">Omarska</option>
<option value="78406">Orahova</option>
<option value="75323">Orahovica Donja</option>
<option value="76270">Orasje</option>
<option value="74412">Osinja</option>
<option value="74225">Osjecani</option>
<option value="75406">Osmaci</option>
<option value="79263">Ostra Luka</option>
<option value="88423">Ostrozac</option>
<option value="77228">Ostrozac Kod Cazina</option>
<option value="77244">Otoka</option>
<option value="72238">Ozimica</option>
<option value="78437">Palackovci</option>
<option value="71420">Pale</option>
<option value="75435">Papraca</option>
<option value="71243">Pazaric</option>
<option value="77227">Pecigrad</option>
<option value="76256">Pelagicevo</option>
<option value="72112">Perin Han</option>
<option value="74317">Petrovo</option>
<option value="75412">Pilica</option>
<option value="78217">Piskavica</option>
<option value="88305">Pocitelj</option>
<option value="72252">Poculica</option>
<option value="71425">Podgrab</option>
<option value="80209">Podhum</option>
<option value="71387">Podlugovi</option>
<option value="74217">Podnovlje</option>
<option value="75355">Podorasje Kod Srebrenika</option>
<option value="77232">Podzvizd</option>
<option value="75303">Poljice</option>
<option value="75320">Popovi</option>
<option value="88240">Posusje</option>
<option value="78216">Potkozarje</option>
<option value="78435">Potocani</option>
<option value="88208">Potoci</option>
<option value="73290">Praca</option>
<option value="73245">Prelovo</option>
<option value="79287">Previja</option>
<option value="74276">Pribinic</option>
<option value="75249">Priboj Kod Lopara</option>
<option value="79101">Prijedor</option>
<option value="80202">Priluka</option>
<option value="80245">Prisoje</option>
<option value="78430">Prnjavor</option>
<option value="80206">Prolog</option>
<option value="88440">Prozor</option>
<option value="70223">Prusac</option>
<option value="71335">Przici</option>
<option value="72207">Puhovac</option>
<option value="75305">Puracic</option>
<option value="88325">Radisici</option>
<option value="75268">Rainci Gornji</option>
<option value="88245">Rakitno</option>
<option value="71217">Rakovica</option>
<option value="80247">Rasko Polje</option>
<option value="78429">Razboj Lijevce</option>
<option value="76218">Razljevo</option>
<option value="79288">Ribnik</option>
<option value="77215">Ripac</option>
<option value="73220">Rogatica</option>
<option value="79226">Rudice</option>
<option value="73260">Rudo</option>
<option value="88347">Ruzici</option>
<option value="76230">Bosanski Samac</option>
<option value="79285">Sanica Gornja</option>
<option value="79260">Sanski Most</option>
<option value="75411">Sapna</option>
<option value="78202">Saracica</option>
<option value="71000">Sarajevo</option>
<option value="71103">Sarajevo, Centar</option>
<option value="71160">Sarajevo, Novi Grad</option>
<option value="71120">Sarajevo, Novo Sarajevo</option>
<option value="71140">Sarajevo, Stari Grad</option>
<option value="76209">Satorovici</option>
<option value="73305">Sekovici</option>
<option value="75450">Sekovici</option>
<option value="71321">Semizovac</option>
<option value="76205">Seonjaci</option>
<option value="75275">Serici</option>
<option value="75245">Sibosnica</option>
<option value="78433">Sibovska</option>
<option value="75207">Simin Han</option>
<option value="78364">Sipovo</option>
<option value="78224">Siprage</option>
<option value="88220">Siroki Brijeg</option>
<option value="78422">Sitnesi</option>
<option value="79283">Sitnica</option>
<option value="75436">Skelani</option>
<option value="74261">Skugric</option>
<option value="75353">Sladna</option>
<option value="78253">Slatina Ilidza</option>
<option value="78438">Smrtici</option>
<option value="74279">Snjegotina Gornja</option>
<option value="74323">Sockovac</option>
<option value="71350">Sokolac</option>
<option value="71210">Sokolovici</option>
<option value="88345">Sovici</option>
<option value="75356">Spionica</option>
<option value="78420">Srbac</option>
<option value="75430">Srebrenica</option>
<option value="75350">Srebrenik</option>
<option value="71385">Srednje</option>
<option value="79249">Sreflije</option>
<option value="76258">Srnice</option>
<option value="79224">Bosanska Kostajnica</option>
<option value="74450">Bosanski Brod</option>
<option value="73110">Istocno Gorazde</option>
<option value="76278">Istocno Orasje</option>
<option value="71123">Istocno Sarajevo</option>
<option value="74208">Stanari</option>
<option value="78243">Stara Dubrava</option>
<option value="79268">Stari Majdan</option>
<option value="77224">Stijena</option>
<option value="73223">Stjenice</option>
<option value="75324">Stjepan Polje</option>
<option value="88360">Stolac</option>
<option value="72209">Stranjani</option>
<option value="78208">Stricici</option>
<option value="75305">Strpci</option>
<option value="88323">Studenci</option>
<option value="75283">Stupari</option>
<option value="77223">Sturlic</option>
<option value="75344">Suho Polje</option>
<option value="80249">Sujica</option>
<option value="79229">Svodna</option>
<option value="71244">Tarcin</option>
<option value="75414">Teocak</option>
<option value="74260">Tesanj</option>
<option value="74266">Tesanjka</option>
<option value="74270">Teslic</option>
<option value="88348">Tihaljina</option>
<option value="75357">Tinja</option>
<option value="75455">Tisca</option>
<option value="77233">Todorovo</option>
<option value="75265">Tojsici</option>
<option value="76272">Tolisa</option>
<option value="79265">Tomina</option>
<option value="80240">Tomislavgrad</option>
<option value="72213">Topcic Polje</option>
<option value="72270">Travnik</option>
<option value="88375">Trebinja</option>
<option value="89000">Trebinje</option>
<option value="78252">Trn</option>
<option value="76335">Trnava Donja</option>
<option value="76310">Trnjaci</option>
<option value="71220">Trnovo</option>
<option value="77225">Trzacka Rastela</option>
<option value="72283">Turbe</option>
<option value="75306">Turija</option>
<option value="78404">Turjak Kod Bosanske Gradiske</option>
<option value="75000">Tuzla</option>
<option value="76330">Ugljevik</option>
<option value="74278">Ugodnovici</option>
<option value="71233">Ulog</option>
<option value="74230">Usora</option>
<option value="73250">Ustikolina</option>
<option value="73265">Uvac</option>
<option value="73249">Vardiste</option>
<option value="71330">Vares</option>
<option value="77245">Varoska Rijeka</option>
<option value="74213">Velika Bukovica</option>
<option value="77207">Velika Gata</option>
<option value="77230">Velika Kladusa</option>
<option value="76329">Velika Obarska</option>
<option value="88208">Vidosi</option>
<option value="76275">Vidovice</option>
<option value="70202">Vinac</option>
<option value="88247">Vir Kod Posusja</option>
<option value="73240">Visegrad</option>
<option value="88307">Visici</option>
<option value="71300">Visoko</option>
<option value="72250">Vitez</option>
<option value="88326">Vitina</option>
<option value="74265">Vitkovci Donji</option>
<option value="73205">Vitkovici</option>
<option value="75440">Vlasenica</option>
<option value="71320">Vogosca</option>
<option value="71123">Vojkovici</option>
<option value="70246">Voljevac</option>
<option value="72227">Vozuca</option>
<option value="74488">Vranjak</option>
<option value="88113">Vrapcici</option>
<option value="75248">Vrazici</option>
<option value="78211">Vrbanja Kod Banja Luke</option>
<option value="78225">Vrbanjci</option>
<option value="78408">Vrbaska</option>
<option value="77231">Vrnograc</option>
<option value="76325">Vrsani</option>
<option value="77203">Vrsta</option>
<option value="76254">Vuckovci</option>
<option value="74470">Vukosavlje</option>
<option value="76273">Zabar Donji</option>
<option value="73287">Zaborak</option>
<option value="72220">Zabrdje</option>
<option value="76333">Zabrdje</option>
<option value="78221">Zabrdje Kod Kotor Varosi</option>
<option value="78214">Zaluzani</option>
<option value="72220">Zavidovici</option>
<option value="74451">Zboriste</option>
<option value="76259">Zelinja</option>
<option value="72236">Zeljezno Polje</option>
<option value="72000">Zenica</option>
<option value="72230">Zepce</option>
<option value="88268">Zitomislici</option>
<option value="75270">Zivinice</option>
<option value="71370">Zupca</option>
<option value="75400">Zvornik</option>
{supportedPlaceOptions}
</select>
</div>
</div>
<div className="form-group">
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="phone">Telefon</label>
<div className="col-md-4">
<RibicaFormError componentName="phone" errorMessagesObject={this.state.deliveryDestinationErrors} />
@@ -584,7 +93,7 @@ var CheckoutPage = React.createClass({
<p className="help-block">broj mobitela - mora biti sa jedne od mreža u BiH</p>
</div>
</div>
<div className="form-group">
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="email">E - mail</label>
<div className="col-md-4">
<RibicaFormError componentName="email" errorMessagesObject={this.state.deliveryDestinationErrors} />
@@ -592,22 +101,135 @@ var CheckoutPage = React.createClass({
<span className="help-block">E - mail adresa na koju će vam biti poslano obavještenje o narudžbi</span>
</div>
</div>
<div className="form-group">
<label className="col-md-4 control-label" htmlFor="note">Napomena</label>
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="note">Napomena (poruka, ili broj nagradnog kupona)</label>
<div className="col-md-4">
<textarea className="form-control" id="note" name="note" value={this.state.deliveryDestination.get('note')} onChange={this._onFieldChange} ></textarea>
</div>
</div>
<div className="form-group">
<div className="form-group">
<label className="col-md-4 control-label" htmlFor="order"></label>
<div className="col-md-8">
<div> Roba: <CartTotal items={this.state.items} itemCounts={this.state.itemCounts} /><br />
<span className={this.state.deliveryDestinationErrors[this.state.deliveryCostsTarget] ? 'hidden' : 'shown'}>
Dostava: <CartTotal deliveryCosts={this.state.deliveryCosts} /><br />
Ukupno: <CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts} />
</span>
</div>
<br />
<div className="checkbox">
<label><input type="checkbox" name="gift" checked={this.state.deliveryDestination.get('gift')} onChange={this._onFieldChange} />Poklon (plaćanje samo preko Paypal-a)</label>
</div>
</div>
</div>
</fieldset>
<fieldset className={this.state.deliveryDestination.get('gift') ? 'shown' : 'hidden'}>
<legend>Podaci o dostavi</legend>
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="recipient_name">Prezime i Ime</label>
<div className="col-md-4">
<RibicaFormError componentName="recipient_name" errorMessagesObject={this.state.deliveryDestinationErrors} />
<input id="recipient_name" name="recipient_name" type="text" placeholder="Prezime Ime" className="form-control input-md" required="" value={this.state.deliveryDestination.get('recipient_name')} onChange={this._onFieldChange} />
<span className="help-block">ime osobe koja prima pošiljku</span>
</div>
</div>
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="recipient_address">Adresa</label>
<div className="col-md-4">
<RibicaFormError componentName="recipient_address" errorMessagesObject={this.state.deliveryDestinationErrors} />
<input id="recipient_address" name="recipient_address" type="text" placeholder="Ulica i broj" className="form-control input-md" required="" value={this.state.deliveryDestination.get('recipient_address')} onChange={this._onFieldChange} />
<span className="help-block">adresa na koju će roba biti isporučena</span>
</div>
</div>
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="recipient_place">Mjesto</label>
<div className="col-md-4">
<RibicaFormError componentName="recipient_place" errorMessagesObject={this.state.deliveryDestinationErrors} />
<select id="recipient_place" name="recipient_place" className="form-control" value={this.state.deliveryDestination.get('recipient_place')} onChange={this._onFieldChange} >
{supportedPlaceOptions}
</select>
</div>
</div>
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="recipient_phone">Telefon</label>
<div className="col-md-4">
<RibicaFormError componentName="recipient_phone" errorMessagesObject={this.state.deliveryDestinationErrors} />
<div className="input-group">
<span className="input-group-addon">+387 </span>
<input id="recipient_phone" name="recipient_phone" className="form-control" placeholder="061 222 333" type="text" required="" value={this.state.deliveryDestination.get('recipient_phone')} onChange={this._onFieldChange} />
</div>
<p className="help-block">broj mobitela - mora biti sa jedne od mreža u BiH</p>
</div>
</div>
<div className="form-group ">
<label className="col-md-4 control-label" htmlFor="recipient_email">E - mail</label>
<div className="col-md-4">
<RibicaFormError componentName="recipient_email" errorMessagesObject={this.state.deliveryDestinationErrors} />
<input id="recipient_email" name="recipient_email" type="text" placeholder="ime@nekimail.com" className="form-control input-md" required="" value={this.state.deliveryDestination.get('recipient_email')} onChange={this._onFieldChange} />
<span className="help-block">E - mail adresa na koju će vam biti poslano obavještenje o narudžbi</span>
</div>
</div>
</fieldset>
<div className="payment-select-container">
{choosePayment}
</div>
</div>
</div>
);
if(CartStore.isAddressColapsed()) {
var address = CartStore.getHumanReadableAddress().map(function (a) { return (<span>{a}<br /></span>)});
content = (
<div className="checkout-page center text-center" >
<h2> Roba će biti dostavljena na adresu: </h2>
<p className="lead">
{address}
<br />
Roba: <CartTotal items={this.state.items} itemCounts={this.state.itemCounts} /><br />
Dostava: <CartTotal deliveryCosts={this.state.deliveryCosts} /><br />
Ukupno: <CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts} />
</p>
<div className="hidden-xs">
<button className="btn btn-default gift-btn" onClick={this._onGiftBtnClicked}>Poklon</button>
<br />
<p className="collapsed-address-container">{last_used_payment} ili <button className="btn btn-default" onClick={this._onUncolapseClick}>Promijeni način plaćanja ili adresu</button></p>
</div>
<div className="collapsed-address-container-mobile btn-group-vertical visible-xs">
<button className="btn btn-default" onClick={this._onGiftBtnClicked}>Poklon</button>
{last_used_payment}
<button className="btn btn-default" onClick={this._onUncolapseClick}>Promijeni način plaćanja ili adresu</button>
</div>
<div className="form-group">
<label className="col-md-4 control-label" htmlFor="order"></label>
<div className="col-md-8">
<CartTotal items={this.state.items} itemCounts={this.state.itemCounts} deliveryCosts={this.state.deliveryCosts} /> <button id="order" name="order" className="btn btn-success" disabled={!this.state.isDeliveryDestinationValid} onClick={this._onOrderClick}>Naruči</button>
<div> </div>
<div></div>
</div>
</div>
</fieldset>
</div>
</div>
);
</div>
</div>);
}
return content;
},
@@ -628,13 +250,32 @@ var CheckoutPage = React.createClass({
},
_onFieldChange: function (event) {
CartActions.changeDeliveryDestinationProperty(event.target.name, event.target.value);
if(event.target.name === "gift") {
CartActions.changeDeliveryDestinationProperty(event.target.name, $(event.target).is(':checked'));
} else {
CartActions.changeDeliveryDestinationProperty(event.target.name, event.target.value);
}
},
_onOrderClick: function (event) {
CartActions.changeDeliveryDestinationProperty("payment_method", "cash_on_delivery");
CartActions.confirmDelivery();
},
_onUncolapseClick: function (event) {
CartActions.setAddressColapsed(false);
},
_onGiftBtnClicked: function (event) {
CartActions.changeDeliveryDestinationProperty('gift', true);
CartActions.setAddressColapsed(false);
},
_handleOnSubmitPaypal: function(event) {
CartActions.changeDeliveryDestinationProperty('payment_method', 'paypal');
return false;
},
getInitialState: function () {
return CartStore.getWholeCartState();
}
@@ -642,4 +283,4 @@ var CheckoutPage = React.createClass({
});
module.exports = CheckoutPage;
module.exports = CheckoutPage;

View File

@@ -15,7 +15,8 @@ var AllItemsInGroup = React.createClass({
// Add change listeners to stores
componentDidMount: function() {
ItemActions.loadBestSellingItemsForGroup(NavigationStore.getGroupIdFromUrl());
var groupId = this.props.groupId || NavigationStore.getGroupIdFromUrl();
ItemActions.loadBestSellingItemsForGroup(groupId);
ItemStore.addChangeListener(this._onChange);
},

View File

@@ -61,6 +61,7 @@ var ItemList = React.createClass({
return (
<nav>
<ul className="pagination">
{pages}
</ul>

View File

@@ -7,6 +7,7 @@ var React = require('react'),
AddToCart = require('../cart/addToCart');
var Router = require('react-router');
var Globals = require('../../globals');
var ItemWithDetailsPage = React.createClass({
@@ -16,29 +17,36 @@ var ItemWithDetailsPage = React.createClass({
return (
<div className="item-with-details row-fluid center">
<div className="col-md-5 col-md-offset-2 item_image">
<div className="col-md-5 col-md-offset-2 item_image col-sm-offset-2 col-sm-3">
<img src={this.state.firstImage} width="100%" />
</div>
<div className="col-md-5">
<div className="col-md-5 col-sm-7 center">
<div className='item_brand_name'> {this.state.item.get('brand').name}</div>
<div className='item_name'> {this.state.item.get('name')}</div>
<div>
<div className='item_price'> {this.state.item.get('list_price')} KM</div>
<div className='item_price'> {Globals.FormatCurrency(this.state.item.get('list_price'))}</div>
<div>Količina</div>
<div> <AddToCart item={this.state.item} /></div>
</div>
<div>
</div>
<div className="item-with-details row-fluid center">
<div className="col-lg-offset-2 col-lg-10 hidden-md hidden-sm hidden-xs">
<div className="item_description_tab">Opis proizvoda</div>
<div className="item_description_tab_area"> </div>
<div className="item_description_text">{this.state.item.get('description')}</div>
</div>
</div>
<Traits traits={this.state.item.get('traits')} />
<div className="col-xs-offset-2 col-xs-10 hidden-lg">
<h2>Opis proizvoda: </h2>
<div> </div>
<p className="lead">{this.state.item.get('description')}</p>
</div>
<div className="col-xs-offset-2 col-xs-10">
<Traits traits={this.state.item.get('traits')} />
</div>
</div>
</div>
);

View File

@@ -0,0 +1,47 @@
var React = require('react');
var ItemList = require('./itemList');
var ItemStore = require('../../stores/itemStore.js');
var ItemActions = require('../../actions/itemActions.js');
var ItemCollection = require('../../models/itemCollection');
var RandomItems = React.createClass({
render: function() {
return (
<ItemList items={this.state.items} />
);
},
// Add change listeners to stores
componentDidMount: function() {
ItemActions.loadFrontPageItems();
ItemStore.addChangeListener(this._onChange);
},
componentWillUnmount: function () {
ItemStore.removeChangeListener(this._onChange);
},
getInitialState: function() {
return {
items: this.getRandomItems()
}
},
getRandomItems: function() {
var NUMBER_OF_SUGGESTED_ITEMS = 8;
var allItems = ItemStore.getItems();
allItems.reset(allItems.shuffle(), {silent:true});
allItems.reset(allItems.first(NUMBER_OF_SUGGESTED_ITEMS), {silent:true});
return allItems;
},
_onChange: function () {
this.setState({
items: this.getRandomItems()
});
},
});
module.exports = RandomItems;

View File

@@ -1,8 +1,9 @@
var React = require('react');
var ItemActions = require('../../actions/itemActions');
var NavigationActions = require('../../actions/navigationActions');
var NavigationStore = require('../../stores/navigationStore')
var NavigationStore = require('../../stores/navigationStore');
var Globals = require('../../globals');
var Router = require('react-router');
var SingleItem = React.createClass({
@@ -10,6 +11,8 @@ var SingleItem = React.createClass({
var hidePrice = this.props.hidePrice || false;
var self = this;
var itemClick = this.itemClick;
var stopPropagation = this.stopPropagation;
var itemUrl = NavigationStore.getUrlForItem(this.props.item);
var firstImage = this.props.item.get('multi_media_descriptions')[0];
firstImage = firstImage || { resized_url: "https://res.cloudinary.com/lfvt7ps2n/image/upload/c_fit,h_172,w_226/v1421732950/http_www.asms.ru_bitrix_templates_main_images_nophoto_irnofq.png" } ;
if (hidePrice) {
@@ -19,7 +22,7 @@ var SingleItem = React.createClass({
<img className="img-responsive" src={firstImage.url} alt="product image" />
<div>
<p>{ this.props.item.get('brand') ? this.props.item.get('brand').name : '' }{ this.props.item.get('name') }</p>
<p> <p className="productbox item_brand_name">{ this.props.item.get('brand') ? this.props.item.get('brand').name : '' }</p>{ this.props.item.get('name') }</p>
</div>
</div>
@@ -28,24 +31,33 @@ var SingleItem = React.createClass({
}
else {
return (
<a href={itemUrl} onClick={stopPropagation}>
<div className="col-lg-3 col-md-3 col-sm-4 col-xs-6" onClick={itemClick}>
<div className="productbox">
<img className="img-responsive" src={firstImage.resized_url} alt="product image" />
<div style={{height: "110px"}}>
<p>{ this.props.item.get('brand') ? this.props.item.get('brand').name : '' }{ this.props.item.get('name') }</p>
<h4>{ this.props.item.get('list_price') } KM </h4>
<div style={{height: "90px"}} className="item_name_and_price">
<p><span className="text-uppercase">{ this.props.item.get('brand') ? this.props.item.get('brand').name : '' }</span>
<br /><span className="text-capitalize">{ this.props.item.get('name') }</span></p>
<h4 className="item_floating_price">{ Globals.FormatCurrency(this.props.item.get('list_price')) }</h4>
</div>
</div>
</div>
</a>
);
}
},
stopPropagation: function(e){
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
return false;
},
itemClick: function(e) {
NavigationActions.goToItemDetails(this.props.item);
}
});

View File

@@ -8,16 +8,16 @@ var Router = require('react-router');
var LinkBanner = React.createClass({
propTypes: {
locationName: React.PropTypes.string.isRequired,
locationId: React.PropTypes.number,
locationId: React.PropTypes.number,
},
render: function() {
var banners = this.state.banners.map(function (banner) {
return <div key={"banner" + banner.get('id')}><a href={banner.get('link_url')}><img src={banner.get('image_url')} /></a></div>
var banners = this.state.banners.map(function (banner) {
return <div key={"banner" + banner.get('id')}><a href={banner.get('link_url')}><img className='img-responsive center-block' src={banner.get('image_url')} /></a></div>
});
return (<div>{ banners }</div>);
@@ -25,27 +25,27 @@ var LinkBanner = React.createClass({
},
getInitialState: function () {
var allBanners = InitializationStore.getBanners();
var locationId = this.props.locationId;
var locationName = this.props.locationName;
var locationId = this.props.locationId;
var locationName = this.props.locationName;
var bannersToShow = [];
if (locationId) {
bannersToShow = allBanners[locationName][locationId];
} else {
bannersToShow = allBanners[locationName];
}
bannersToShow = bannersToShow || [];
return { banners: bannersToShow };
return { banners: bannersToShow };
}
});
module.exports = LinkBanner;
module.exports = LinkBanner;

View File

@@ -0,0 +1,17 @@
var React = require('react');
var ItemActions = require('../../actions/itemActions');
var NavigationActions = require('../../actions/navigationActions');
var NavigationStore = require('../../stores/navigationStore');
var Globals = require('../../globals');
var Router = require('react-router');
var CashOnDeliveryButton = React.createClass({
render: function() {
return (
<button type="button" className="btn mybutton payment-btn" disabled={this.props.disabled} onClick={this.props.onCashClick}>Plati prilikom preuzimanja</button>
);
}
});
module.exports = CashOnDeliveryButton;

View File

@@ -0,0 +1,36 @@
var React = require('react');
var ItemActions = require('../../actions/itemActions');
var NavigationActions = require('../../actions/navigationActions');
var NavigationStore = require('../../stores/navigationStore');
var Globals = require('../../globals');
var Router = require('react-router');
var PaypalButton = require('./paypalButton');
var PikpaylButton = require('./pikpayButton');
var CashOnDeliveryButton = require('./cashOnDeliveryButton');
var PaymentSelect = React.createClass({
render: function() {
var cashOnDeliveryBtn = ( <CashOnDeliveryButton onCashClick={this.props.onCashClick} disabled={this.props.cashOnDeliveryDisabled} cartId={this.props.cartId}/> );
var pikpayBtn = ( <PikpaylButton amount={this.props.amount} deliveryCost={this.props.deliveryCost} disabled={this.props.disabled} deliveryDestination={this.props.deliveryDestination} cartId={this.props.cartId}/> );
var paypalBtn = ( <PaypalButton disabled={this.props.disabled} deliveryDestination={this.props.deliveryDestination} amount={this.props.amount} deliveryCost={this.props.deliveryCost} cartId={this.props.cartId}/> );
return (
<div>
<div className="payment_select btn-group payment-select-desktop hidden-xs">
{cashOnDeliveryBtn}
{pikpayBtn}
{paypalBtn}
</div>
<div className="payment_select btn-group-vertical payment-select-mobile visible-xs">
{cashOnDeliveryBtn}
{pikpayBtn}
{paypalBtn}
</div>
</div>);
}
});
module.exports = PaymentSelect;

View File

@@ -0,0 +1,62 @@
var React = require('react');
var ItemActions = require('../../actions/itemActions');
var NavigationActions = require('../../actions/navigationActions');
var NavigationStore = require('../../stores/navigationStore');
var CartStore = require('../../stores/cartStore');
var CartActions = require('../../actions/cartActions');
var Globals = require('../../globals');
var Router = require('react-router');
var PaypalButton = React.createClass({
render: function() {
var deliveryDestination = JSON.stringify(
{
'anonymous_id_string': this.props.deliveryDestination.get('anonymous_id_string'),
'user_id': this.props.deliveryDestination.get('user_id')
});
var amount = Globals.ConvertToEuro(this.props.amount);
var deliveryCost = Globals.ConvertToEuro(this.props.deliveryCost);
var root = location.protocol + '//' + location.host;
var return_url = root + "/hvala";
var cancel_return_url = root + "/dostava";
var notifyUrl = Globals.IsUrlAbsolute(Globals.ApiUrl) ? Globals.ApiUrl : root + Globals.ApiUrl;
notifyUrl += "/payment/confirmation";
return (
<button onClick={this._onPaypalClick} disabled={this.props.disabled} type="button" className="btn mybutton payment-btn">Paypal
<form id="paypal_form" className="hidden" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_xclick" />
<input type="hidden" name="business" value={Globals.PaypalId} />
<input type="hidden" name="lc" value="BA" />
<input type="hidden" name="item_name" value="Item" />
<input type="hidden" name="item_number" value={this.props.cartId} />
<input type="hidden" name="amount" value={amount} />
<input type="hidden" name="currency_code" value="EUR" />
<input type="hidden" name="button_subtype" value="services" />
<input type="hidden" name="no_note" value="0" />
<input type="hidden" name="cn" value="Napomena:" />
<input type="hidden" name="no_shipping" value="1" />
<input type="hidden" name="rm" value="1" />
<input type="hidden" name="return" value={return_url} />
<input type="hidden" name="cancel_return" value={cancel_return_url} />
<input type="hidden" name="shipping" value={deliveryCost} />
<input type="hidden" name="bn" value="PP-BuyNowBF:btn_buynowCC_LG.gif:NonHosted" />
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1" />
<input name="notify_url" value={notifyUrl} type="hidden" />
<input name="custom" value={deliveryDestination} type="hidden" />
</form>
</button>
);
},
_onPaypalClick: function(e) {
CartActions.changeDeliveryDestinationProperty('payment_method', 'paypal');
CartStore.saveDeliveryDestinationAnd(function() {
$("#paypal_form").submit();
});
}
});
module.exports = PaypalButton;

View File

@@ -0,0 +1,64 @@
var React = require('react');
var ItemActions = require('../../actions/itemActions');
var NavigationActions = require('../../actions/navigationActions');
var NavigationStore = require('../../stores/navigationStore');
var CartStore = require('../../stores/cartStore');
var CartActions = require('../../actions/cartActions');
var Globals = require('../../globals');
var Router = require('react-router');
var sha1 = require('sha1');
var PikpayButton = React.createClass({
render: function() {
var total = this.props.amount + this.props.deliveryCost;
total = total.toFixed(2) * 100;
var order_info = "Info";
var order_number = this.props.cartId;
var key = Globals.PikpayKey;
var authenticity_token = Globals.PikpayAuthenticityToken;
var digest = sha1(key + order_number + total + "BAM");
var rebate_digest = sha1(key + order_number + total + total + "BAM");
var deliveryDestination = this.props.deliveryDestination;
var city = CartStore.getNameOfThePlace(deliveryDestination.get('place'));
var custom = JSON.stringify(
{
'anonymous_id_string': deliveryDestination.get('anonymous_id_string'),
'user_id': deliveryDestination.get('user_id')
});
return (<span></span> /*
<button type="button" onClick={this._onPikpayClick} disabled={this.props.disabled} className="btn mybutton payment-btn">Plati karticom
<form id="pikpay_form" action={Globals.PikpayFormUrl} method="post" target="_top">
<input type="hidden" name="ch_full_name" value={deliveryDestination.get('name')} />
<input type="hidden" name="ch_address" value={deliveryDestination.get('address')} />
<input type="hidden" name="ch_city" value={city} />
<input type="hidden" name="ch_zip" value={deliveryDestination.get('place')} />
<input type="hidden" name="ch_country" value="Bosna i Hercegovina" />
<input type="hidden" name="ch_phone" value={deliveryDestination.get('phone')} />
<input type="hidden" name="ch_email" value={deliveryDestination.get('email')} />
<input type="hidden" name="order_info" value={order_info} />
<input type="hidden" name="order_number" value={order_number} />
<input type="hidden" name="amount" value={total} />
<input type="hidden" name="currency" value="BAM" />
<input type="hidden" name="original_amount" value={total} />
<input type="hidden" name="language" value="hr" />
<input type="hidden" name="transaction_type" value="authorize" />
<input type="hidden" name="authenticity_token" value={authenticity_token} />
<input type="hidden" name="digest" value={digest} />
<input type="hidden" name="rebate_digest" value={rebate_digest} />
<input type="hidden" name="custom_params" value={custom} />
</form>
</button> */
);
},
_onPikpayClick: function(e) {
CartActions.changeDeliveryDestinationProperty('payment_method', 'pikpay');
CartStore.saveDeliveryDestinationAnd(function() {
$("#pikpay_form").submit();
});
}
});
module.exports = PikpayButton;

View File

@@ -7,6 +7,7 @@ var React = require('react'),
RouteHandler = Router.RouteHandler,
LoginStatus = require('./shared/loginStatus'),
InitializationStore = require('../stores/initializationStore'),
NavigationStore = require('../stores/navigationStore'),
InitializationActions = require('../actions/initializationActions');
var CartIcon = require('./cart/cartIcon');
@@ -36,29 +37,42 @@ var RootApp = React.createClass({
InitializationStore.removeChangeListener(this._onChange);
},
shouldShowCart: function () {
return !NavigationStore.hideCart();
},
render: function() {
if (!this.state.isEverythingReadyToStartTheShow) {
return (<div>loading...</div>);
return (<div> <img height={50} src="https://res.cloudinary.com/du5pdibul/image/upload/v1428813560/logo_h5f9yp.png" /><div> se učitava. Budite strpljivi! </div>
<img height={50} src="https://res.cloudinary.com/du5pdibul/image/upload/v1434870249/progress_bar_idgtad.gif" />
</div>);
}
var cart = "";
if(this.shouldShowCart()) {
cart = (<CartIcon />);
}
return (
<div className="container">
<div>
<div className="col-lg-12 hidden-sm hidden-xs " style={{height: 80, background: 'none', marginBottom: '0px !important'}} id="mybody">
<div className="col-lg-12 hidden-sm hidden-xs" style={{height: 80, background: 'none', marginBottom: '0px !important'}} id="mybody">
<div style={{padding: '15px 15px'}} className="col-lg-2 col-md-2 col-sm-2 col-xs-2">
<Link to="app"><img height={50} src="https://res.cloudinary.com/du5pdibul/image/upload/v1428813560/logo_h5f9yp.png" /></Link>
</div>
<div style={{padding: '30px 15px'}} className="col-lg-5 col-md-5 col-sm-4 col-xs-4">
<SearchBox />
<div style={{padding: '30px 15px'}} className="col-lg-6 col-md-6 col-sm-6 col-xs-6">
<SearchBox small="false" />
</div>
<div style={{padding: '15px 15px'}} className="col-lg-5 col-md-5 hidden-sm hidden-xs">
<div style={{padding: '15px 15px'}} className="col-lg-4 col-md-4 hidden-sm hidden-xs">
<nav style={{background: 'none', border: 'none', marginBottom: '0px !important'}} className="navbar mytopnav">
<div className="container-fluid">
<CartIcon />
<div>
{cart}
</div>
</nav>
</div>
@@ -73,6 +87,16 @@ var RootApp = React.createClass({
<div className='row'>
<RouteHandler />
</div>
<div>
&nbsp;
</div>
<div className="footer">
© Ribica.ba 2015. Uka Ventura, Dolina 15, 71000 Sarajevo,
<img src="https://res.cloudinary.com/du5pdibul/image/upload/c_scale,w_32/v1444446944/jWKeAZUzPtUqgZdcLDSa7vd3iwdan8QX4nlYlHBMkJHcxwr-uZU40vDmrMKczCmiCBRf_w300_w8b7bj.png" />
<img src="https://res.cloudinary.com/du5pdibul/image/upload/c_scale,w_32/v1444448923/whatsapp-providna_ti2wlg.png" />
+387 62 274 275 , ID: 4302548210004
</div>
</div>
);

View File

@@ -6,7 +6,9 @@ var React = require('react'),
MenuItemStore = require('../../stores/menuItemStore'),
CartStore = require('../../stores/cartStore.js');
MenuItemActions = require('../../actions/menuItemActions'),
NavigationActions = require('../../actions/navigationActions');
NavigationActions = require('../../actions/navigationActions'),
SearchBox = require('./searchBox'),
CartIcon = require('../cart/cartIcon');
Backbone.$ = $;
@@ -18,6 +20,16 @@ var MenuItemListComponent = React.createClass({
}
},
menuItemUrl: function(menuItem) {
var url = "#"
if (menuItem.get) {
url = menuItem.get('url');
} else {
url = menuItem.url;
}
return url;
},
getInitialState: function() {
var state = MenuItemStore.getState();
var cartState = CartStore.getWholeCartState();
@@ -42,11 +54,11 @@ var MenuItemListComponent = React.createClass({
MenuItemActions.unsetMenuItemHover();
NavigationActions.goToMenuItem(menuItem);
e.preventDefault();
return false;
},
_onCartClick: function(e) {
NavigationActions.goToCart();
e.preventDefault();
e.cancelImmediatePropagation();
},
render: function() {
@@ -59,19 +71,21 @@ var MenuItemListComponent = React.createClass({
};
return (
<div className="navbar navbar-default navbar-static-top">
<div className="container">
<div>
<span style={{ float: "left" }}className="hidden-lg hidden-md">
<CartIcon small="true" />
</span>
<span style={{ position:'absolute', width: "60%" , marginLeft: "5%", marginRight: "22%", marginTop: "7px" }} className="hidden-lg hidden-md">
<SearchBox small="true" />
</span>
<ul className='nav nav-pills'>
<div className="navbar-header">
<button type="button" className="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<button type="button" className="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span className="icon-bar" />
<span className="icon-bar" />
<span className="icon-bar" />
</button>
<a className="navbar-brand hidden-lg hidden-md" href="#"><img style={{marginTop: '-4px'}} height={24} src="https://res.cloudinary.com/du5pdibul/image/upload/v1428813560/logo_h5f9yp.png" /></a>
<ul className="mynav hidden-lg hidden-md " style={{listStyle: 'none'}}>
<li><a href="#">0,00 KM</a></li>
<li onClick={this._onCartClick}><a style={{marginLeft: 10}} className="mybutton" href="#" onClick={this._onCartClick}>Završi narudžbu</a></li>
</ul>
</div>
<div className="navbar-collapse collapse">
<ul className="nav navbar-nav hidden-sm hidden-xs ">
@@ -118,16 +132,16 @@ var MenuItemListComponent = React.createClass({
{this.state.menuItems.map(function(menuItem) {
return <li className="mydropdown menu-large" onMouseLeave={self.onMouseOut} onMouseOver={self.onMouseOver.bind(self, menuItem)}>
<a href="#" className="dropdown-toggle " id={menuItem.get('title').toLowerCase()} onClick={self.onMenuItemClick.bind(self, menuItem)} >{menuItem.get('title')}</a>
<a href={self.menuItemUrl(menuItem)} className="dropdown-toggle " id={menuItem.get('title').toLowerCase().replace(/\s+/g, '')} onClick={self.onMenuItemClick.bind(self, menuItem)} >{menuItem.get('title')}</a>
<ul className={menuItem.get('id') !== self.state.hoveredMenuItem ? "dropdown-menu megamenu row hide": "dropdown-menu megamenu row"}>
{menuItem.get('menu_sub_items').map(function(menuSubItem) {
return (
<li className="col-sm-3" key={menuSubItem.id}>
<ul>
<li className="dropdown-header"><a href="#" onClick={self.onMenuItemClick.bind(self, menuSubItem)}><p>{menuSubItem.title}</p></a></li>
<li className="dropdown-header"><a href={self.menuItemUrl(menuSubItem)} onClick={self.onMenuItemClick.bind(self, menuSubItem)}><p>{menuSubItem.title}</p></a></li>
{menuSubItem.menu_sub_sub_items.map(function(menuSubSubItem) {
return (<li><a href="#" onClick={self.onMenuItemClick.bind(self, menuSubSubItem)}>{menuSubSubItem.title}</a></li>)
return (<li><a href={self.menuItemUrl(menuSubSubItem)} onClick={self.onMenuItemClick.bind(self, menuSubSubItem)}>{menuSubSubItem.title}</a></li>)
})}
</ul>
</li>
@@ -186,7 +200,7 @@ var MenuItemListComponent = React.createClass({
*/}
{this.state.menuItems.map(function(menuItem) {
return <li className="dropdown menu-large">
<a href="#" className="dropdown-toggle " data-toggle="dropdown" role="button" aria-expanded="false">
<a href={self.menuItemUrl(menuItem)} className="dropdown-toggle " data-toggle="dropdown" role="button" aria-expanded="false">
{menuItem.get('title')} <b className="caret" /> </a>
<ul className="dropdown-menu megamenu row">
@@ -195,9 +209,9 @@ var MenuItemListComponent = React.createClass({
<li className="col-sm-3" key={menuSubItem.id}>
<ul>
<li className="dropdown-header"><a href="#" onClick={self.onMenuItemClick.bind(self, menuSubItem)}><p>{menuSubItem.title}</p></a></li>
<li className="dropdown-header"><a href={self.menuItemUrl(menuSubItem)} onClick={self.onMenuItemClick.bind(self, menuSubItem)}><p>{menuSubItem.title}</p></a></li>
{menuSubItem.menu_sub_sub_items.map(function(menuSubSubItem) {
return (<li><a href="#" onClick={self.onMenuItemClick.bind(self, menuSubSubItem)}>{menuSubSubItem.title}</a></li>)
return (<li><a href={self.menuItemUrl(menuSubSubItem)} onClick={self.onMenuItemClick.bind(self, menuSubSubItem)}>{menuSubSubItem.title}</a></li>)
})}
</ul>
</li>

View File

@@ -34,13 +34,14 @@ var SearchBox = React.createClass({
},
onKeyPress: function(e) {
var enterKeyCode = 13;
if(e.which == enterKeyCode) {
var whichKey = e.key || e.which;
if(whichKey == enterKeyCode) {
this.doSearch();
e.preventDefault();
}
},
render: function() {
return (<form style={ {marginLeft: '60px', width: '100%'} } className="form-inline">
var large = (<form onSubmit={this.onSearchClick} style={ {marginLeft: '60px', width: '100%'} } className="form-inline">
<div className="left-inner-addon">
<i className="glyphicon glyphicon-search"></i>
<input style={{width: '75%'}} type="search" onKeyPress={this.onKeyPress}
@@ -54,8 +55,28 @@ var SearchBox = React.createClass({
onClick={this.onSearchClick}>Traži</button>
</div>
</form>)
</form>);
var small = (<form onSubmit={this.onSearchClick} className="form-inline">
<span className="left-inner-addon">
<i style={{positon: "absolute", zIndex: "600" }} className="glyphicon glyphicon-search"></i>
<input style={{ width: "100%"}} type="search" onKeyPress={this.onKeyPress}
className="search-box form-control"
value={this.state.q} onChange={this.onSearchBoxChange}
aria-hidden="true">
</input>
</span>
</form>);
if (this.props.small === 'true') {
return small;
}
else {
return large;
}
}
});
}
);
module.exports = SearchBox;

View File

@@ -2,9 +2,13 @@ var React = require('react'),
Router = require('react-router'),
RouteHandler = Router.RouteHandler,
AllItems = require('../items/allItems'),
RandomItems = require('../items/randomItems'),
LinkBanner = require('../linkBanner/linkBanner'),
AllItemsInGroup = require('../items/allItemsInGroup');
var ga = require('react-google-analytics');
var GAInitiailizer = ga.Initializer;
var StartPage = React.createClass({
render : function() {
return (
@@ -13,8 +17,10 @@ var StartPage = React.createClass({
<div className='col-md-12'>
<LinkBanner locationName="startPage" />
<AllItemsInGroup />
<RouteHandler />
<h2>Preporučeno</h2>
<RandomItems />
</div>
<GAInitiailizer />
</div>
)
}

View File

@@ -7,33 +7,35 @@ var React = require('react'),
Globals = require('../../globals')
Router = require("react-router"),
Link = Router.Link;
var ThankYouPage = React.createClass({
render: function() {
return (
<div className="thank-you-page center">
<div className="message center">
<h1>Roba je naručena!</h1>
<p>Hvala na narudžbi. Naša zaposlenica će vas kontaktirati da ugovori detalje o preuzimanju. </p>
<p><LinkBanner locationName="thankYouPage" /></p>
<p>Registrujte se kako biste dobili informaciju o popustima, imali pregled svih vaših narudžbi,
{/*
<p>Registrujte se kako biste dobili informaciju o popustima, imali pregled svih vaših narudžbi,
koristili Baby Shower, Predlagač poklona te kalendar događaja.</p>
<p>
Registracija je jednostavna - samo jedan klik. <Link to="registracija">Klikni ovdje</Link>.
</p>
*/}
</div>
);
);
}
});
module.exports = ThankYouPage;
module.exports = ThankYouPage;

View File

@@ -9,5 +9,6 @@ module.exports = keyMirror({
CONFIRM_DELIVERY: null,
SET_ITEM_COUNT: null,
ADD_N_ITEMS: null,
REMOVE_ITEM: null
REMOVE_ITEM: null,
SET_ADDRESS_COLAPSED: null
});

View File

@@ -1,13 +1,16 @@
.add-to-cart {
padding-top: 20px;
padding-top: 20px;
}
.add-to-cart-count {
font-size: 20px;
margin-top-top: 10px;
width: 50px;
text-align: center;
vertical-align: bottom;
padding-left: 10px;
padding-right: 10px;
vertical-align: middle;
/*margin-top-top: 10px;*/
/*width: 50px;*/
/*text-align: center;*/
/*vertical-align: bottom; */
}
.shopping-cart-icon {
@@ -19,7 +22,7 @@
.shopping-cart-notification-text {
display: inline-block;
background:red;
background:red;
color: white;
font-size: 9px;
position: absolute;
@@ -42,4 +45,17 @@
font-size: 17px;
padding-top: 10px;
padding-bottom: 10px;
}
.cart-table-row {
vertical-align: middle;
}
.cart-price-bigger {
font-size: 21px;
}
.cart-commision-tiny {
font-size: 45%;
}

View File

@@ -0,0 +1,33 @@
.checkout_form_margin {
margin-right: 10px !important;
margin-left: 10px !important;
}
.payment-select-container {
text-align: center;
}
.payment-select-desktop .payment-btn {
margin-right: 10px !important;;
}
.payment-select-mobile .payment-btn {
margin-bottom: 6px !important;
}
.collapsed-address-container .mybutton {
margin-top: 0px !important;
width: 240px;
}
.collapsed-address-container-mobile button {
margin-bottom: 6px !important;
margin-left: 10% !important;
margin-right: 10% !important;
width: 80% !important;
}
.gift-btn {
width: 200px;
margin-bottom: 10px;
}

View File

@@ -48,8 +48,6 @@
font-size: 36px;
padding-top: 36px;
padding-bottom: 36px;
}
.item_image {
@@ -57,8 +55,38 @@
border-color: #cccccc;
}
@media only screen and (min-width: 500px) {
.item_name_and_price {
text-align: center;
vertical-align: center;
}
.item_floating_price {
position: absolute;
bottom: 8%;
right: 8%;
text-align: right;
width: 100%;
}
}
.item_name_and_price {
text-align: center;
vertical-align: center;
font-size: 13px;
}
.item_floating_price {
position: absolute;
bottom: 12%;
right: 12%;
text-align: right;
width: 100%;
font-size: 13px;
}
/*

View File

@@ -1,94 +1,77 @@
@import url(http://fonts.googleapis.com/css?family=Roboto:400,300italic,300,100italic,100,400italic,500,500italic,700italic,700,900,900italic);
/* site */
html {
overflow-y: scroll;
overflow-y: scroll;
}
body{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813560/bg_prp6pz.jpg") fixed 100% 100% ;
background-size: cover ;
background-position: center ;
background-repeat: no-repeat;
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813560/bg_prp6pz.jpg") fixed 100% 100% ;
background-size: cover ;
background-position: center ;
background-repeat: no-repeat;
font-family: 'Roboto', sans-serif;
}
#left-nave {
background: blue;
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 250px;
}
.beba{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/beba-bg_wip7kj.png");
background-size: cover;
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/beba-bg_wip7kj.png");
background-size: cover;
}
html, body { height: 100%; width: 100%; margin: 0; }
h1 h2 h3 h4 h5{
font-family: 'Roboto', sans-serif;
font-family: 'Roboto', sans-serif;
}
.col-centered{
float: none;
margin: 5% auto;
float: none;
margin: 5% auto;
}
.mybutton{
background-color: #00a8a8;
padding: 6px 11px !important;
border-radius: 3px;
margin-top: 8px;
border: solid #06c3c3 1px;
color: #ffffff !important;
background-color: #00a8a8;
padding: 6px 11px !important;
border-radius: 3px;
margin-top: 8px;
border: solid #06c3c3 1px;
color: #ffffff !important;
}
.mybutton:hover {
background-color: #06c3c3 !important;
padding: 6px 11px !important;
color: black !important;
border-radius: 3px;
margin-top: 8px;
border: solid #06c3c3 1px;
color: #ffffff !important;
background-color: #06c3c3 !important;
padding: 6px 11px !important;
color: black !important;
border-radius: 3px;
margin-top: 8px;
border: solid #06c3c3 1px;
color: #ffffff !important;
}
.add-to-cart-button {
background-color: #00a8a8;
padding: 6px 11px !important;
border-radius: 3px;
border: solid #06c3c3 1px;
color: #ffffff !important;
background-color: #00a8a8;
padding: 6px 11px !important;
border-radius: 3px;
border: solid #06c3c3 1px;
color: #ffffff !important;
}
.add-to-cart-button:hover {
background-color: #06c3c3 !important;
padding: 6px 11px !important;
color: black !important;
border-radius: 3px;
border: solid #06c3c3 1px;
color: #ffffff !important;
background-color: #06c3c3 !important;
padding: 6px 11px !important;
color: black !important;
border-radius: 3px;
border: solid #06c3c3 1px;
color: #ffffff !important;
}
a{
font-family: 'Roboto', sans-serif;
font-weight: bold;
font-family: 'Roboto', sans-serif;
font-weight: bold;
}
.mynav li{
display: inline-block;
display: inline-block;
}
.mynav {
margin-top: 6px;
text-align: center;
text-align: center;
}
.mynav a{
text-decoration: none;
@@ -107,27 +90,24 @@ text-decoration: none;
color: #06c3c3;
background: none;
}
.mynav li .mybutton {
background-color: #00a8a8;
padding: 4px 10px !important;
margin-top: 6px;
display: block;
border-radius: 3px;
border: solid #06c3c3 1px;
color: #ffffff;
background-color: #00a8a8;
padding: 4px 10px !important;
margin-top: 6px;
display: block;
border-radius: 3px;
border: solid #06c3c3 1px;
color: #ffffff;
}
.mynav li .mybutton:hover {
background-color: #06c3c3;
padding: 4px 10px !important;
margin-top: 6px;
display: block;
border-radius: 3px;
border: solid #06c3c3 1px;
color: #ffffff;
background-color: #06c3c3;
padding: 4px 10px !important;
margin-top: 6px;
display: block;
border-radius: 3px;
border: solid #06c3c3 1px;
color: #ffffff;
}
.productbox{
cursor: pointer;
-moz-box-shadow: 1px 2px 2px #cccccc;
@@ -142,205 +122,181 @@ margin-bottom: 35px;
height: auto;
border-bottom: solid gray 1px;
width: 100%;
}
.productbox div{
padding: 4px 10px;
}
.productbox a{
color: black;
text-decoration: none;
text-decoration: none;
}
.productbox a:hover{
color: black;
text-decoration: none;
text-decoration: none;
text-decoration: underline;
}
.productbox div span{
color: black;
margin-left: 8px;
}
#beba{
padding-left: 45px;
padding-right: 45px;
margin-left: -23px;
margin-right: -23px;
padding-left: 45px;
padding-right: 45px;
margin-left: -23px;
margin-right: -23px;
}
#beba:hover{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/beba-bg_wip7kj.png");
background-position: center ;
background-size: 100% 100%;
color: white;
background-repeat: no-repeat;
background-repeat: no-repeat;
}
#dijete:hover{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/dijete-bg_gff80o.png");
background-position: center ;
background-size: 100% 100%;
color: white;
background-repeat: no-repeat;
background-repeat: no-repeat;
}
#dijete:hover a{
color: white;
}
#mama:hover{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/mama-bg_rqbnuo.png");
background-position: center ;
background-size: 100% 100%;
color: white;
background-repeat: no-repeat;
background-repeat: no-repeat;
}
#mama:hover a{
color: white;
}
#madeinbih:hover{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/made-in-bih-bg_uusqpm.png");
background-position: center ;
background-size: 100% 100%;
color: white;
background-repeat: no-repeat;
background-repeat: no-repeat;
}
#madeinbih:hover a{
color: white;
}
#onama:hover{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/made-in-bih-bg_uusqpm.png");
background-position: center ;
background-size: 100% 100%;
color: white;
background-repeat: no-repeat;
}
#onama:hover a{
color: white;
}
#marka:hover{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/marka-bg_cy8hpe.png");
background-position: center ;
background-size: 100% 100%;
color: white;
background-repeat: no-repeat;
background-repeat: no-repeat;
}
#marka:hover a{
color: white;
}
#akcija:hover{
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/akcija-bg_xtawus.png");
background-position: center ;
background-size: 100% 100%;
color: white;
background-repeat: no-repeat;
background-repeat: no-repeat;
}
#akcija:hover a{
color: white;
}
.mydropdown li:hover .sub-menu {
visibility: visible;
display: block;
visibility: visible;
display: block;
}
.mydropdown:hover .dropdown-menu {
display: block;
visibility: visible;
display: block;
visibility: visible;
}
.navbar-default{
color: black;
background-color: white;
border-color: lightgray;
}
.navbar-default .navbar-nav > li > a{
color:black;
color:black;
}
.navbar-default .navbar-nav > li{
padding-left: 16px;
padding-right: 16px;
}
.navbar-default .navbar-nav > li > a:focus{
background-color: rgba(255, 255, 255, 0.6) !important;
-moz-box-shadow: 0px 0px 2px #999;
background-color: rgba(255, 255, 255, 0.6) !important;
-moz-box-shadow: 0px 0px 2px #999;
-webkit-box-shadow: 0px 0px 2px #999;
box-shadow: 0px 0px 2px #999;
border: none !important;
border-radius: 2px;
}
.navbar-default .navbar-nav > .dropdown > a .caret{
border-top-color: #fff;
border-bottom-color: #fff;
border-top-color: #fff;
border-bottom-color: #fff;
}
.navbar-default .navbar-brand{
color:#fff;
color:#fff;
}
.menu-large {
position: static !important;
position: static !important;
}
.megamenu{
padding: 20px 0px;
width:94%;
margin-left: 3%;
border-top: none !important;
-moz-box-shadow: 0px 1px 2px #999;
padding: 20px 0px;
width:94%;
margin-left: 3%;
border-top: none !important;
-moz-box-shadow: 0px 1px 2px #999;
-webkit-box-shadow: 0px 1px 2px #999;
box-shadow: 0px 1px 2px #999;
}
.megamenu> li > ul {
padding: 0;
margin: 0;
padding: 0;
margin: 0;
}
.megamenu> li > ul > li {
list-style: none;
padding-left: 10px;
list-style: none;
padding-left: 10px;
}
.megamenu> li > ul > li > a {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.428571429;
color: black;
white-space: normal;
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.428571429;
color: black;
white-space: normal;
}
.megamenu> li ul > li > a:hover,
.megamenu> li ul > li > a:focus {
text-decoration: none;
color: #262626;
background-color: #f5f5f5;
text-decoration: none;
color: #262626;
background-color: #f5f5f5;
}
.megamenu.disabled > a,
.megamenu.disabled > a:hover,
.megamenu.disabled > a:focus {
color: #999999;
color: #999999;
}
.megamenu.disabled > a:hover,
.megamenu.disabled > a:focus {
text-decoration: none;
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
cursor: not-allowed;
text-decoration: none;
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
cursor: not-allowed;
}
.megamenu.dropdown-header {
color: #428bca;
font-size: 18px;
color: #428bca;
font-size: 18px;
}
.dropdown-header p {
border-bottom: solid 1.5px lightgray !important;
@@ -348,184 +304,204 @@ margin-bottom: 2px;
text-transform: uppercase;
color: black;
font-family: 'Roboto', sans-serif;
font-weight: 500;
font-size: 16px;
font-weight: 500;
font-size: 16px;
}
.mycart{
position: absolute;
position: absolute;
background: url("https://res.cloudinary.com/du5pdibul/image/upload/v1428813559/cart-icon_p9u63r.png");
background-position: center center;
background-size: cover;
background-repeat: no-repeat;
width: 42px;
height: 44px;
padding-bottom: 0px;
right:-14px;
bottom: -15px;
text-align: right;
background-repeat: no-repeat;
width: 42px;
height: 44px;
padding-bottom: 0px;
right:-14px;
bottom: -15px;
text-align: right;
}
.mycart div {
text-align: right;
text-align: right;
}
.mycart span{
position: absolute;
left: 13.4px;
top: 4px;
color: #ffffff;
}
.mycart span:hover{
color: white;
}
#myhome{
border-bottom: solid #06c3c3 1px;
border-bottom: solid #06c3c3 1px;
}
#myhome-hover:hover {
border-bottom: solid #06c3c3 1px;
#myhome-hover:hover {
border-bottom: solid #06c3c3 1px;
}
.sidebar ul{
list-style: none;
list-style: none;
}
.sidebar li{
margin-left:10px;
margin-bottom: 5px;
color:black;
padding: 10px;
border-bottom: solid 1px lightgray;
margin-left:10px;
margin-bottom: 5px;
color:black;
padding: 10px;
border-bottom: solid 1px lightgray;
}
.sidebar li a{
text-decoration: none;
color:black;
color:black;
}
.sidebar li a:hover{
color:#06c3c3;
color:#06c3c3;
}
@media (max-width: 768px) {
.megamenu{
margin-left: 0 ;
margin-right: 0 ;
}
.megamenu> li {
margin-bottom: 30px;
}
.megamenu> li:last-child {
margin-bottom: 0;
}
.megamenu.dropdown-header {
padding: 3px 15px !important;
}
.navbar-nav .open .dropdown-menu .dropdown-header{
color:#fff;
}
.megamenu{
margin-left: 0 ;
margin-right: 0 ;
}
.megamenu> li {
margin-bottom: 30px;
}
.megamenu> li:last-child {
margin-bottom: 0;
}
.megamenu.dropdown-header {
padding: 3px 15px !important;
}
.navbar-nav .open .dropdown-menu .dropdown-header{
color:#fff;
}
}
@media (max-width: 1000px) {
.navbar-header {
float: none;
}
.navbar-toggle {
display: block;
}
.navbar-collapse {
box-shadow: inset 0 1px 0 rgba(255,255,255,0.1);
border-top:none !important;
}
.navbar-collapse.collapse {
display: none!important;
}
.navbar-nav>li {
float: none;
}
.navbar-nav>ul {
margin-bottom: 0px;
}
.navbar-nav>li>a {
padding-top: 10px;
padding-bottom: 10px;
}
.navbar-text {
float: none;
margin: 15px 0;
}
/* since 3.1.0 */
.navbar-collapse.collapse.in {
display: block!important;
}
.collapsing {
overflow: hidden!important;
}
.navbar-header {
float: none;
}
.navbar-toggle {
display: block;
}
.navbar-collapse {
box-shadow: inset 0 1px 0 rgba(255,255,255,0.1);
border-top:none !important;
}
.navbar-collapse.collapse {
display: none!important;
}
.navbar-nav>li {
float: none;
}
.navbar-nav>ul {
margin-bottom: 0px;
}
.navbar-nav>li>a {
padding-top: 10px;
padding-bottom: 10px;
}
.navbar-text {
float: none;
margin: 15px 0;
}
/* since 3.1.0 */
.navbar-collapse.collapse.in {
display: block!important;
}
.collapsing {
overflow: hidden!important;
}
}
.error-message {
color: red;
color: red;
}
.white_button{
background-color: #f9f9f9;
width: 40px;
height: 40px;
padding-bottom: 7px;
display: block;
border-radius: 1px;
margin-top: 8px;
border: solid #cccccc 1px;
color: #4d4d4d !important;
font-size: 16px;
background-color: #f9f9f9;
width: 40px;
height: 40px;
padding-bottom: 7px;
/*display: block;*/
border-radius: 1px;
margin-top: 8px;
border: solid #cccccc 1px;
color: #4d4d4d !important;
font-size: 16px;
}
.search-box {
background-color: #efefef;
border-radius: 0px;
border: 1px solid #cccccc;
padding-left: 10px;
padding-right: 10px;
background-color: #efefef;
border-radius: 0px;
border: 1px solid #cccccc;
padding-left: 10px;
padding-right: 10px;
}
.search-button {
background-color: #f9f9f9;
color: #31b8b8;
border: 1px solid #cccccc;
border-radius: 0px;
margin-left: 5px;
background-color: #f9f9f9;
color: #31b8b8;
border: 1px solid #cccccc;
border-radius: 0px;
margin-left: 5px;
}
.qty-box {
background-color: #efefef;
border-radius: 0px;
border: 1px solid #cccccc;
background-color: #efefef;
border-radius: 0px;
border: 1px solid #cccccc;
}
.left-inner-addon {
position: relative;
position: relative;
}
.left-inner-addon input {
padding-left: 30px;
padding-left: 30px;
}
.left-inner-addon i {
position: absolute;
padding: 10px 12px;
pointer-events: none;
position: absolute;
padding: 10px 12px;
pointer-events: none;
}
.cart-title {
font-size: 29px;
color: #06c3c3;
}
.cart-title {
font-size: 29px;
color: #06c3c3;
.footer {
text-align: center;
width: 100%;
bottom: 20px;
margin-bottom: 20px;
}
.message {
font-size: 130%;
text-align: left;
margin-left: 20px;
margin-right: 20px;
}
.payment {
box-sizing: border-box;
color: rgb(102, 102, 102);
display: block;
font-family: Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol';
font-size: 13px;
font-style: normal;
font-variant: normal;
font-weight: normal;
height: 36px;
line-height: 18.2px;
width: 708px;
word-wrap: break-word;
}
.message {
font-size: 130%;
text-align: left;
margin-left: 20px;
margin-right: 20px;
}
.normal-message {
text-align: left;
margin-left: 20px;
margin-right: 20px;
}

View File

@@ -0,0 +1,71 @@
#burnttoast {
width: auto;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
position: fixed;
visibility: hidden;
border-radius: 2px;
top: 1em;
left: 50vw;
opacity: 0;
filter: alpha(opacity=0);
z-index: 3000;
margin: 4px;
padding: 0.75em 1.25em;
cursor: pointer;
font-family: sans-serif;
font-size: 0.9em;
font-weight: 300;
transition-property: bottom, opacity;
transition: 0.4s cubic-bezier(0.44,0,0.2,0.8);
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
}
#burnttoast.top {
bottom: auto;
top: -5em;
}
#burnttoast.left {
left: 0;
-webkit-transform: translateX(0);
transform: translateX(0);
}
#burnttoast.top.show {
bottom: auto;
top: 0;
}
#burnttoast.show {
transition-property: top, opacity;
visibility: visible;
opacity: 1;
filter: alpha(opacity=100);
top: 0;
}
.burnt-toast {
max-width: 100%;
width: 100%;
height: auto;
padding: 1em 2em;
color: rgb(240,240,240);
background: rgba(0,0,0,0.8);
}
.burnt-toast p {
margin: 0;
}
@media (max-width: 410px) {
#burnttoast {
width: 100%;
margin: 0;
left: 0;
-webkit-transform: translateX(0);
transform: translateX(0);
}
}

View File

@@ -1,10 +1,45 @@
var conversionRate = 0.51;
module.exports = {
ApiUrl: '@@apiEndpoint',
DefaultPageSize: 24,
ItemGroupIdOfStartPage: "1",
ItemGroupIdOfEmptyCartPage: "1",
BamToEuroConversionRate: conversionRate,
FormatCurrency: function(amount_s) {
var amount = parseFloat(amount_s);
return ( amount.toFixed(2) + " KM" )
var amount = parseFloat(amount_s);
return (amount.toFixed(2) + " KM")
},
MaxNumberOfItemsToBeAdded: 1000
FormatPercentage: function(amount_s) {
var amount = parseFloat(amount_s);
return (amount.toFixed(2) + "%")
},
ConvertToEuro: function(amount_s) {
amount = amount_s * conversionRate;
return amount.toFixed(2);
},
IsUrlAbsolute: function(url) {
var r = new RegExp('^(?:[a-z]+:)?//', 'i');
return r.test(url);
},
MaxNumberOfItemsToBeAdded: 1000,
PaypalId: "W7GKS2Q9ZGLGY",
PikpayFormUrl: "https://ipgtest.pikpay.ba/form",
PikpayAuthenticityToken: "1bb1eea16bd6492c01262636897c0c2e3291a1ab",
PikpayKey: "Ribica",
Slugify: function(text) {
if(!text) {
text = "";
}
return text.toString().toLowerCase()
.replace(/š/g,'s')
.replace(/[čć]/g,'c')
.replace(/[ž]/g,'z')
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
}
};

View File

@@ -14,9 +14,9 @@ var DeliveryDestination = Backbone.Model.extend({
},
url: Globals.ApiUrl + '/cart/delivery_destination',
defaults: {
defaults: {
count: 0
}
});
module.exports = DeliveryDestination;
module.exports = DeliveryDestination;

View File

@@ -13,7 +13,7 @@ var ItemWithDetails = Backbone.Model.extend({
var price = parseFloat(this.get('list_price')).toFixed(2)
var pricePerUnit = (price / unitsInPack).toFixed(2);
var descriptionSuffix = this.get('unit').description_suffix;
return (+pricePerUnit).toString() + " KM " + descriptionSuffix;
return Globals.FormatCurrency(pricePerUnit);
}
},
defaults : {

View File

@@ -6,16 +6,15 @@ var FREE_SHIPPING_LIMIT = 50;
var Place = Backbone.Model.extend({
initialize: function(options) {
options || (options = {});
this.postalCode = options.postalCode;
initialize: function(options) {
options || (options = {});
this.postalCode = options.postalCode;
},
url: function() {
var postalCode = this.postalCode || "00000";
return Globals.ApiUrl + '/place/' + postalCode.trim();
}
url: function() {
var postalCode = this.postalCode || "00000";
return Globals.ApiUrl + '/place/' + postalCode.trim();
}
});

View File

@@ -3,6 +3,10 @@ var React = require('react');
var ExternalApi = require('./externalApi');
var Router = require('./router');
Backbone.$ = $;
var ga = require('react-google-analytics');
ga('create', 'UA-65612001-1', 'auto');
Router.run(function(Handler, state) {
React.render(<Handler />, document.body);
@@ -12,3 +16,4 @@ module.exports = {
App: ExternalApi,
Router: Router
}

View File

@@ -17,6 +17,8 @@ var ByCategory = require('./components/browsing/byCategory');
var BySubCategory = require('./components/browsing/bySubCategory');
var BySection = require('./components/browsing/bySection');
var ThankYouPage = require('./components/thankyou/thankYouPage');
var AboutUsPage = require('./components/about/aboutUsPage');
var NewsletterSweepstake = require('./components/about/newsletterSweepstake');
// var Register = require('./components/account/register');
// var Login = require('./components/account/login');
var SearchResultsPage = require('./components/search/searchResultsPage');
@@ -34,6 +36,8 @@ var routes = (
<Route name='podkategorija' path="/podkategorija/:id/*" handler={BySubCategory} />
<Route name='byCat' path="sekcija/:sekcijaName/kategorija/:id/*" handler={ByCategory} />
<Route name='hvala' path="/hvala" handler={ThankYouPage} />
<Route name='onama' path="/o-nama" handler={AboutUsPage} />
<Route name='newsletter' path="/email-novosti" handler={NewsletterSweepstake} />
<Route name='pretraga' path="/pretraga" handler={SearchResultsPage} />
<DefaultRoute handler={StartPage}/>
</Route>
@@ -44,4 +48,9 @@ var router = Router.create({
location: Router.HistoryLocation
});
router.run(function (Handler, state) {
});
module.exports = router;

File diff suppressed because it is too large Load Diff

View File

@@ -102,7 +102,7 @@ var fetchBestSellingItemsForGroup = function(groupId) {
var items = _bestSellingForGroup;
items.setClassificationType(4);
items.setClassificationId(groupId);
items.setLimit(30);
items.setLimit(200);
items.setOffset(0);
items.fetch({

View File

@@ -15,33 +15,50 @@ var getGroupIdFromUrl = function() {
var url = document.URL;
var itemIdRegex = /grupa\/(\d+)\//g;
var match = itemIdRegex.exec(url);
var result = Globals.ItemGroupIdOfStartPage;
if(match) {
result = match[1]
if (match) {
result = match[1]
}
return result;
return result;
};
// Extend ItemStore with EventEmitter to add eventing capabilities
var NavigationStore = _.extend({}, EventEmitter.prototype, {
getGroupIdFromUrl: getGroupIdFromUrl,
getGroupIdFromUrl: getGroupIdFromUrl,
// Emit Change event
emitChange: function() {
this.emit('change');
},
// Emit Change event
emitChange: function() {
this.emit('change');
},
// Add change listener
addChangeListener: function(callback) {
this.on('change', callback);
},
// Add change listener
addChangeListener: function(callback) {
this.on('change', callback);
},
// Remove change listener
removeChangeListener: function(callback) {
this.removeListener('change', callback);
},
hideCart: function() {
// TODO: figure out how to find this out using Router
var url = document.URL;
var itemIdRegex = /\/(korpa|dostava)/g;
var match = itemIdRegex.exec(url);
if (match) {
return true;
} else {
return false;
}
},
getUrlForItem: function(item) {
return '/artikal/' + item.get('id') +'/' + Globals.Slugify(item.get('name'))
}
// Remove change listener
removeChangeListener: function(callback) {
this.removeListener('change', callback);
}
});
@@ -50,24 +67,25 @@ var NavigationStore = _.extend({}, EventEmitter.prototype, {
// Register callback with AppDispatcher
NavigationStore.dispatchToken = AppDispatcher.register(function(payload) {
var action = payload.action;
var action = payload.action;
switch(action.actionType) {
switch (action.actionType) {
case NavigationConstants.CHANGE_URL:
var router = require('../router');
setTimeout(function(){
router.transitionTo(action.url);
}, 0);
break;
case NavigationConstants.CHANGE_URL:
ga('send', 'pageview', action.url);
var router = require('../router');
setTimeout(function() {
router.transitionTo(action.url);
}, 0);
break;
default:
return true;
}
default:
return true;
}
// If action was responded to, emit change event
NavigationStore.emitChange();
return true;
// If action was responded to, emit change event
NavigationStore.emitChange();
return true;
});

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>ribica.ba - ispunjava zelje</title>
<title>ribica.ba - prvi online shop sa dostavom u BiH za mame i bebe - kupite igracke, pelene i ostalo</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -17,9 +17,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimum-scale=1" />
</head>
<body>
<div> <img height={50} src="https://res.cloudinary.com/du5pdibul/image/upload/v1428813560/logo_h5f9yp.png" /><div> se učitava. Budite strpljivi! </div>
<img height={50} src="https://res.cloudinary.com/du5pdibul/image/upload/v1434870249/progress_bar_idgtad.gif" />
</div>
<script src='/ribica.js' type='text/javascript'></script>
<script type='text/javascript'>
RIBICA.App.bootstrap();
</script>
</body>
</html>

View File

@@ -0,0 +1,56 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script>window.html5 || document.write('<script src="js/vendor/html5shiv.js"><\/script>')</script>
<![endif]-->
</head>
<body>
<!-- Begin MailChimp Signup Form -->
<link href="//cdn-images.mailchimp.com/embedcode/classic-081711.css" rel="stylesheet" type="text/css">
<style type="text/css">
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
/* Add your own MailChimp form style overrides in your site stylesheet or in this style block.
We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
</style>
<div id="mc_embed_signup">
<form action="//ribica.us11.list-manage.com/subscribe/post?u=e7326b7fcff6f5faa9c098b0a&amp;id=d252ea09a5" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<h2>Ribica novosti i sniženja</h2>
<div class="indicates-required"><span class="asterisk">*</span> obavezno</div>
<div class="mc-field-group">
<label for="mce-EMAIL">Email Adresa <span class="asterisk">*</span>
</label>
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
</div>
<div class="mc-field-group">
<label for="mce-FNAME">Ime </label>
<input type="text" value="" name="FNAME" class="" id="mce-FNAME">
</div>
<div class="mc-field-group">
<label for="mce-LNAME">Prezime </label>
<input type="text" value="" name="LNAME" class="" id="mce-LNAME">
</div>
<div class="mc-field-group">
<label for="mce-GRAD">Grad </label>
<input type="text" value="" name="GRAD" class="" id="mce-GRAD">
</div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_e7326b7fcff6f5faa9c098b0a_d252ea09a5" tabindex="-1" value=""></div>
<div class="clear"><input type="submit" value="Prijavi se" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</div>
</form>
</div>
<script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';fnames[1]='FNAME';ftypes[1]='text';fnames[2]='LNAME';ftypes[2]='text';fnames[3]='GRAD';ftypes[3]='text'; }(jQuery));var $mcj = jQuery.noConflict(true);</script>
<!--End mc_embed_signup-->
</body>
</html>

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

View File

@@ -26,9 +26,12 @@
"underscore": "^1.7.0"
},
"dependencies": {
"react": "~0.12.2",
"backbone": "~1.1.2",
"burnt-toast": "^0.1.6",
"react": "~0.12.2",
"react-google-analytics": "^0.2.0",
"react-router": "~0.11.6",
"sha1": "^1.1.1",
"superagent": "~0.21.0"
}
}