Compare commits

...

444 Commits

Author SHA1 Message Date
A1Gard 86cc39f7fe fixed migrate bug 3 weeks ago
A1Gard 0e5867e29b updated yarn lock 3 weeks ago
A1Gard 6bf6ed1211 optimize vite again 3 weeks ago
A1Gard 39beb3ef34 optimize vite 3 weeks ago
A1Gard 69144725c8 updated read me 3 weeks ago
A1Gard 329f52926e fixed live edit theme parts 3 weeks ago
A1Gard 87d620d3c6 optimize theme images 3 weeks ago
A1Gard 30c067c12c fixed api bug 3 weeks ago
A1Gard 48b6e24cee fixed ui bugs mobile mode 3 weeks ago
A1Gard f0e5354311 fixed scroll bug mobile mode 4 weeks ago
A1Gard dbf22107dc improve recet menu ui/ux 4 weeks ago
A1Gard d5e041090a added modern slider theme 4 weeks ago
A1Gard 6e525eb140 fixed slider bug 4 weeks ago
A1Gard e128e3edb8 improved ui 4 weeks ago
A1Gard c0d68fca14 added nested list control 4 weeks ago
A1Gard ae9ebdbd9c fixed contact form data 4 weeks ago
A1Gard 8792e85889 fixed ui bg post modren post 4 weeks ago
A1Gard 38df54b04f improve js quality 1 month ago
A1Gard 97bc53c46c improve js quality 1 month ago
A1Gard 0d3df2ed2c added autoplay video to groups
added submenu expandable to homayon-menu repsonosive
1 month ago
A1Gard 60b25eefc3 added link to sub [group & category] 1 month ago
A1Gard 37590097a9 added group & category description
added sub-menu repsonsive homayon
2 months ago
A1Gard 26ff1ea516 fixed hide menu with sidebar 2 months ago
A1Gard ff735b6654 fixed hide menu with sidebar 2 months ago
A1Gard db9cec77b2 added sub menu to homayon 2 months ago
A1Gard 8f3b7cf402 fixed 5 rate bug 2 months ago
A1Gard 9cf561d163 fixed responsive ui 2 months ago
A1Gard 7b689b839b fixed new product bug 2 months ago
A1Gard 07fd4c9ec6 optimized decription theme part 2 months ago
A1Gard 8adbf04b8f fixed publish bug 2 months ago
A1Gard cbfde34f6d fixed bug theme part 2 months ago
A1Gard fd72cc8bae added pedram theme part 2 months ago
A1Gard fbeb98c19f added tal theme part 2 months ago
A1Gard d704442a6b added Sina theme part 2 months ago
A1Gard ed7c5350f1 added under construction timer theme part 3 months ago
A1Gard 6b9689ad56 added under construction logic and one theme part 3 months ago
A1Gard 037e65ba47 added timer event theme part 3 months ago
A1Gard 23e14fad39 added version 3 months ago
A1Gard cbcd93e278 fixed bug props name change error [safe name change] 3 months ago
A1Gard 752a307363 fixed bug props remove error [safe remove] 3 months ago
A1Gard e07bfdfb22 fixed bug props restore 3 months ago
A1Gard fac7ec12ae updated navbar style 3 months ago
A1Gard e500639457 added price sort 3 months ago
A1Gard c2f76e2987 added swagger for project api 3 months ago
A1Gard 7942a5dc1c added posts and groups api 3 months ago
A1Gard be97c636a6 improved APIs 3 months ago
A1Gard 0502d923ec improved helper function 3 months ago
A1Gard c110b8b340 added fast attachment upload to improve system ux
update detach word mistake fixed
3 months ago
A1Gard eda081e5d4 added hide menu category and group 3 months ago
A1Gard b8c2d71934 fixed persian date bug persian2Gregorian 3 months ago
A1Gard 7036d5e051 added auto complete for tags 3 months ago
A1Gard 5925446bc8 added main group filter to post list 3 months ago
A1Gard 2562545009 added fast edit group ux feature
added persian translate
added some comments to code
3 months ago
A1Gard 2e086355bc added fast category edit ux feature 3 months ago
A1Gard 326ba890ec optimize theme parts 3 months ago
A1Gard 53a482ed16 fixed some bug setting query type 3 months ago
A1Gard 2e0d601d28 added products slider theme part 3 months ago
A1Gard e2fe49132c fixed some bugs and optimize codes 3 months ago
A1Gard d97d661f68 added product and post query setting 3 months ago
A1Gard a2a12eadfc fixed test error bug 4 months ago
A1Gard 169df5f2db updated user seeder 4 months ago
A1Gard 737637e4f1 added rss feed to project 4 months ago
A1Gard 1a072400e5 updated read me 4 months ago
A1Gard 40ed5166ac added safety remove for category and group 4 months ago
A1Gard 6d96a5f0a6 added documentation to project 4 months ago
A1Gard 44f20acf36 fixed bug 4 months ago
A1Gard 7cd2a4a8e6 optimize homayon menu 4 months ago
A1Gard 8054aafeb2 added curve footer theme part
fixed live edit bugs
4 months ago
A1Gard 379cb1abfe added grid clip theme part
fixed autoplay limit problem
4 months ago
A1Gard 021566438d added button checkbox to homayon menu 4 months ago
A1Gard c564c5f490 added cache to design page 4 months ago
A1Gard 5c4721bc5f improved responsive theme parts 4 months ago
A1Gard 1c2997d99a add curve posts slider theme part
fixed bug curve cat theme part
4 months ago
A1Gard 5be09856a5 fixed ara bug added sub groups function 4 months ago
A1Gard e1a50fd787 improved ui homayon & autoplay theme part 4 months ago
A1Gard 5dbce6050f added ara group 4 months ago
A1Gard 7703bca26d fixed js bug 4 months ago
A1Gard e408d42712 fixed bug darkmode other footer 4 months ago
A1Gard a680d19efe fixed bug parent 4 months ago
A1Gard 0e79938a65 fixed vido loop 4 months ago
A1Gard 30aaca779f added optimize setting images
fixed upload file bug
4 months ago
A1Gard 72cb61958b added optimize image ckeditor uploader 4 months ago
A1Gard abb049decb fixed parent bug 4 months ago
A1Gard c3d83c955c added live edit setting 4 months ago
A1Gard 4e6016013a added gisoo theme part 4 months ago
A1Gard 01d6df06e4 added homayon menu theme part 4 months ago
A1Gard 8372ad3803 Merge remote-tracking branch 'origin/master' 4 months ago
A1Gard a719f8f703 fixed bug setting first time by observer 4 months ago
A1Gard 910570a53a fixed bug setting observer 4 months ago
A1Gard f3cf36fbbf added other footer 4 months ago
A1Gard e818e7e220 fixed bug post modern bg 4 months ago
A1Gard 3c62650b0f added port modern posts theme part 4 months ago
A1Gard 16a8eb5713 translate update 4 months ago
A1Gard e44ecffc94 added developer guide area 4 months ago
A1Gard be504d1cbd optimize panel buttons
improved ui
4 months ago
A1Gard 6d6cce4a84 fixed add card removed test code 4 months ago
A1Gard 74d90e0b0b fixed add to card bug for yasamin theme part 4 months ago
A1Gard 5f0a59acb0 fix invoice bug 4 months ago
A1Gard 6c697831de updated composer 4 months ago
A1Gard 4874063332 fixed bug for windows area design 4 months ago
A1Gard 4e22e134c4 update readme and env 4 months ago
A1Gard a43d845075 update read me 4 months ago
A1Gard c5388731ff added svg support to image seeder 5 months ago
A1Gard 243aa3911e added Main categories icon theme part
optimize other parts
5 months ago
A1Gard 199f289359 added Hod header theme part 5 months ago
A1Gard b3b4f83f39 added curve category theme part 5 months ago
A1Gard 110c710f90 added samira theme part 5 months ago
A1Gard d28305d0c0 fixed comments send bugs 5 months ago
A1Gard 3dcb82d875 improve ux datepicker for persian dates 5 months ago
A1Gard 1ff040bd19 added date, datetime & time picker to setting 5 months ago
A1Gard 29f6538e24 added seeding all model 5 months ago
A1Gard bbe6ea2953 fixed bug 5 months ago
A1Gard f00d5b7c87 improved ui/ux by stock quantity 5 months ago
A1Gard 6bf5a08d2e upated readme 5 months ago
A1Gard 0da9dea3e4 added assets build command to use by site 5 months ago
A1Gard 4e9e3c6543 fixed some user bug 5 months ago
A1Gard 4f4d4aaec3 fixed multi-lang problem 6 months ago
A1Gard 77402f4bfe optimized ui/ux yasamin theme part 6 months ago
A1Gard c78e51874c added Farhad theme part 6 months ago
A1Gard 5b1762c22f added Yasamin theme part 6 months ago
A1Gard b6ebfd0d52 added hidden sidebar theme part
optimized pages thumb image
6 months ago
A1Gard e2d00a283a added theme part Maryam
fixed ui responsive bug natalia
6 months ago
A1Gard 231ff84192 fixed sitemap bug 6 months ago
A1Gard c53aac1694 fixed ios ui bug horizontal scroll 6 months ago
A1Gard d7f17c1f52 added little footer 6 months ago
A1Gard beffa8b410 added text splitter 6 months ago
A1Gard d22b4ade83 added natalia theme part 6 months ago
A1Gard 8f0affcedc fixed ui bugs
added Vickushka theme part
6 months ago
A1Gard 9f06be9c98 fixed ui bugs
improve translates
6 months ago
A1Gard f010f263df translates update 6 months ago
A1Gard 4db967a2ca added seo content analyzer 6 months ago
A1Gard ff0770d0d8 added parallel categories grid theme part 6 months ago
A1Gard 83c9b7d0e8 added shiva theme part 6 months ago
A1Gard 5e7c29a64a added main categories theme part 6 months ago
A1Gard 290ace2fdb added minoo theme part 6 months ago
A1Gard a083d1b8f6 added product grid template
added default product grid theme part
added sort to area
6 months ago
A1Gard 0bd19ec838 updated translate persian 6 months ago
A1Gard 4615b6db44 optimized sitemap
added group and categories to sitemap
6 months ago
A1Gard a1a6efbd88 fixed sitemap bug 6 months ago
A1Gard c922aaa670 improved sitemap 6 months ago
A1Gard df18c15d57 updated translate 6 months ago
A1Gard 60a549f38c optimized panel responsive to show better in mobile 6 months ago
A1Gard 8bf8d02ad6 added blade cache system to Page Speed Optimization
added clear cache to settings
6 months ago
A1Gard ab41f99b22 update robots.txt 6 months ago
A1Gard e67fe3b201 added lazy load to images 6 months ago
A1Gard 158578a5ff added minify output 6 months ago
A1Gard 6d53cfc8a4 fixed error 6 months ago
A1Gard b880173db2 optimized canonical for filters category 6 months ago
A1Gard 9cf354cde7 added persian readme 6 months ago
A1Gard 3ab6d7f1d5 updated readme 6 months ago
A1Gard 757aa18ccf optimized gitignore 6 months ago
A1Gard 7899db771c optimized client assets generator 6 months ago
farazdy 1a2abbb692 added custom theme each post, category, group, product 6 months ago
A1Gard a745e49cc1 improved product seeder 6 months ago
A1Gard 442dfe7d36 added canonical tag to post, product, group, category 6 months ago
A1Gard 24f0423c81 added theme and promote fields 6 months ago
A1Gard 19e6b7a980 optimized time picker 6 months ago
A1Gard 0442658688 improved ui/ux 6 months ago
A1Gard 07011a2fc8 added date & time to meta properties 6 months ago
A1Gard 22f4154fc1 added time picker 6 months ago
A1Gard 9e8d552ecc added attempts to rate 6 months ago
A1Gard 7b59932ecd fixed navbar title mistake 6 months ago
A1Gard 6dbd61c815 added mail register & reset password
fixed some bugs
added simple register theme part
6 months ago
A1Gard 189235fc1b fixed test unit 6 months ago
SadeghPM de3c40a354 Update app.php 6 months ago
SadeghPM d04ed717f3 Create Kernel.php 6 months ago
SadeghPM 93fa61afa5 Update laravel.yml 6 months ago
A1Gard 86c9da6b33 fixe client default 6 months ago
A1Gard d3ad0b2571 added avatar to user 6 months ago
A1Gard 092fc9a65f added avatar & etc. to customer 6 months ago
A1Gard 5730a89298 added rate list to panel
added last rate history
improved security of rates
6 months ago
A1Gard 3dfb0f085f added rate system 6 months ago
A1Gard 8b4ba518c0 added evaluationables relations to models 6 months ago
A1Gard f43bf77bb3 added evaluation to project 6 months ago
A1Gard be9339cfed added rate input vue component 6 months ago
A1Gard 85e8e2f2a7 fixed no comment bug 6 months ago
A1Gard 825dac7fbd added table of content [SEO] 7 months ago
A1Gard 2b63541938 added online demo
fixed menu link bug
7 months ago
A1Gard 3a1cc136ad added demo version 7 months ago
A1Gard fac7d55fb3 added author slider theme part 7 months ago
A1Gard 87d1305e20 added theme part post slider 7 months ago
A1Gard 022528f90c fixed ui bugs 7 months ago
A1Gard 105a369112 fixed use default 7 months ago
A1Gard dedbb2694f added category group 7 months ago
A1Gard 1bce26bd75 updated read me 7 months ago
A1Gard 2474a8a281 added sms auth system 7 months ago
A1Gard f7f4b50554 added simple footer theme part 7 months ago
A1Gard 3245470104 fixed some bugs 7 months ago
A1Gard 523b787805 fixed template list bug 7 months ago
A1Gard d78ac7ebca fixed some bug ui/ux & translate mistakes 7 months ago
A1Gard b5fc5df31d optimized ui/ux
fixed seeder
7 months ago
A1Gard 59aaa7b326 favicon changed 7 months ago
A1Gard 9e848f9f1d fixed some bugs
added version
7 months ago
A1Gard 5f8e1d567b added follow us theme part 7 months ago
A1Gard c69e907093 updated developer access role 7 months ago
A1Gard bc4c505378 updated persian translate 7 months ago
A1Gard 0f37f021b9 updated theme part seeder 7 months ago
A1Gard 561ed29bf6 added Liana theme part to invoice 7 months ago
A1Gard d292f374f2 fixed avisa theme part
fixed home links
7 months ago
A1Gard f120123958 added invoice controller
added order relations
fixed ticket bug
7 months ago
A1Gard 589689a593 optimized ui/ux of menus and customer 7 months ago
A1Gard bdb801f5e5 optimized ui/ux 7 months ago
A1Gard c3f92cf4b3 added persian translate 7 months ago
A1Gard 07ea625b36 optimized ux of area designer 7 months ago
A1Gard 4075b06798 fixed read me
update version
7 months ago
A1Gard d4bf269fea fixed search apl-menu 7 months ago
A1Gard 1132788e1f added full screen button to mp4 player 7 months ago
A1Gard 6cdb844ba2 added search
improved ui of tag & search page
7 months ago
A1Gard be7c088b42 added tag page
fixed readme
fixed map bugs (inline map & icons)
7 months ago
farazdy e9b7648eda Added contact area 7 months ago
A1Gard 9b7e4960a0 Merge remote-tracking branch 'origin/master' 7 months ago
A1Gard 09b4ac404e added dark-mode 7 months ago
A1Gard 2c96c13b51 fixed font ui button 7 months ago
sadeghpm bf9d2a0344 Merge remote-tracking branch 'origin/master' 7 months ago
A1Gard 37d1269e3c fixed error 7 months ago
A1Gard 47bc2b7ada added node to workflow test 7 months ago
A1Gard a9e27ff6d5 fixed visitor counter middleware 7 months ago
A1Gard 11bb2df3ab added sample test unit 7 months ago
A1Gard 02c620967d fixed bug dena theme 7 months ago
A1Gard 20fa8feb3b fixed test 7 months ago
A1Gard 5f7cb0df3e updated github test 7 months ago
A1Gard 54369c533d added github test 7 months ago
A1Gard f4619f8b9a added siemap and customer test 7 months ago
A1Gard b1a891689c added client web page test 7 months ago
A1Gard bcf776856a update readme 7 months ago
A1Gard 00420aff5e fixed bug ui apl menu 8 months ago
A1Gard 3f17a11b15 fixed bug 8 months ago
A1Gard 55955f06b9 added default header
optimized area and part seeder
8 months ago
A1Gard cf7945ab49 fixed no parent bugs group & category 8 months ago
sadeghpm d4370e9260 Payment gateway added 8 months ago
A1Gard c49c71bfd0 added multi lang items into sitemap 8 months ago
A1Gard f0b8ae1e30 added Deeba menu part
fixed menu item lang bug url
optimized menu item url function
fixed some ui bug other theme part [responsive]
update composer description
added generator
8 months ago
A1Gard 2ebe36e98d optimized translate code 8 months ago
A1Gard 68be86dd68 added tag translate name and model 8 months ago
A1Gard e0aff07b07 added Translation to setting 8 months ago
A1Gard 5e56382c18 added ticket control customer profile 8 months ago
A1Gard f2095dcc26 added address control to customer profile
added comment list
added product favorites control
8 months ago
A1Gard cf27800e5b improved code quality
fixed some bugs
8 months ago
A1Gard f28965ffca improved user experience tab control 8 months ago
A1Gard 9bd2d8bf33 improved user experience multi langs 8 months ago
A1Gard 66b0a4e2c5 added multi-lang support client
fixed rtl bootstrap file for rtl lang
WIP: need fixed for messages maybe sessions
8 months ago
sadeghpm 8b0399832f Bug fix 8 months ago
A1Gard e297d66f98 fixed synatax error sitemap 8 months ago
A1Gard 6bec06438e added sitemap [seo]
WIP: multi-lang website sitemaps
8 months ago
A1Gard a01811b915 added meta description [seo]
added twitter [x] preview
8 months ago
A1Gard 3edd772380 added Open Graph Tags to models
updated markup
8 months ago
A1Gard eb0d939040 added markup json to product and post 8 months ago
A1Gard a3b8155cb4 added markup json breadcrumb [seo] 8 months ago
A1Gard b2b4eb39d7 added screenshot to readme 8 months ago
A1Gard 070f8b34f4 updated screenshot 8 months ago
A1Gard ddaba8e9f3 fixed word mistake 8 months ago
A1Gard 967df9c3bd added some translate 8 months ago
A1Gard c9b7f63efc updated translate [persian] 8 months ago
A1Gard 8a70dae42f remove duplicated assets
added latest products theme part
fix some bugs
updated part seeder
8 months ago
A1Gard 0ef97636ca added data seeder 8 months ago
A1Gard b8ee68a682 added slider seeder
added slider image seeder
8 months ago
A1Gard dc3ec891e8 added Avisa theme part
added customer controller to profile
WIP: complete
8 months ago
A1Gard 729f043158 added theme part ns card
added CardController
added transport seeder
fixed meta input ui bug
8 months ago
A1Gard b3db68ec77 added autoplay video clips 8 months ago
A1Gard b460bb9ae4 removed group and category from area seeder 8 months ago
A1Gard 5afa248303 added clips list theme part
added dor clip theme part
fixed some clip upload bug
fixe product preview permission
optimized mp3 & mp4 player
8 months ago
A1Gard 2c090427c5 added attachment list theme part
added attach with preview theme part
8 months ago
A1Gard 777a81d6f2 added video player
added mp3 player
8 months ago
A1Gard 145dce02ea added Karen theme part
improved ux of preview QuantitiesAddToCard
8 months ago
A1Gard 847752d469 added single product route
added aria product theme part
added tooltip to compare add to card
fixed has discount bug
fixed toggle card bugs [quantity]
improved ui different color for secondary color
fixed tag url
8 months ago
A1Gard d4e971c4b9 fixed bug discount expire time 8 months ago
A1Gard 83a1717f00 added login pattern bg background
added simple pattern
removed card from theme part
8 months ago
A1Gard ffbf0c07e2 improved sub category - sub group ui 8 months ago
A1Gard 5edc0b61f5 added login big bg
added login js
added customer sign-in process
8 months ago
A1Gard 7bcca79732 added go to top theme part
added background color to compare
8 months ago
A1Gard 25926a9059 added compare product theme part
fixed bugs & improved ui/ux in product fullMeta
added compare route
improved select filter category client
8 months ago
A1Gard 20d5fde215 complete meta filter 8 months ago
A1Gard f45ac25f9a added first page ignore middleware [seo]
improved fix css switch
added basic meta filter [WIP]
8 months ago
A1Gard afe5df8b4e added subcategories grid theme part
changed directory some theme part
8 months ago
A1Gard 905134ecf9 added product grid side theme part [WIP: filter category] 8 months ago
A1Gard 05ef81dec3 added product grid theme part 8 months ago
A1Gard 3f3c934d83 added card toggle
added card storage to customer
8 months ago
A1Gard 1e71cc704e added vue & vuex to client js
added vue-toast to client
added image seeder guide to readme
added fav toggle to client
8 months ago
A1Gard bee0b8ea69 added image seeding command 8 months ago
A1Gard 4d8d1c0226 added seeding prepare command 8 months ago
A1Gard 47990d98ee added seeder image git ignore 8 months ago
A1Gard dec3e0b16b added attachment temp download link
added simple attachs list
fixed area seeder
8 months ago
A1Gard edecafe6c5 added galleries list theme part 8 months ago
A1Gard 66df032556 added grid gallery to theme part 8 months ago
A1Gard 68be630c1c added gallery grid theme part
added aparat gallery theme part
8 months ago
A1Gard 8eca666671 added sub-group theme part
optimized theme parts
improved group seeder
change screenshots
8 months ago
farazdy f6a6d3ea00 Added grid post list sidebar 8 months ago
farazdy 3bb26bcd45 Added grid post list 8 months ago
farazdy 7853e633fa Added simple post list sidebar 8 months ago
A1Gard 2ea5b673d8 fixed pagination ul 8 months ago
A1Gard 8c0ae50046 added simple post list theme part
fixed some bugs
8 months ago
A1Gard 912044d965 updated area seeder
updated remix icon version
8 months ago
A1Gard 779b8a7074 fixed bug setting duplicate 8 months ago
A1Gard 5fa5b70d49 fixed width setting 8 months ago
A1Gard cf9514e3dc added post sidebar theme part 8 months ago
A1Gard 1f95373c2f updated git ignore upload folder 8 months ago
A1Gard e0c7dc3570 added simple comment theme part
added safe form
added comment submit method
8 months ago
A1Gard 77aa183a30 fixd bug & added approved comment 8 months ago
A1Gard 00b7d19f09 added comment area & post view & time-spend 8 months ago
A1Gard c9510fe2b1 added post view increment 8 months ago
A1Gard 742c75b75c added simple post 8 months ago
A1Gard 468d9f60aa optimized xcontroller & route
added client controller
8 months ago
A1Gard 527cf862a9 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	resources/views/segments/header/SimpleHeader/SimpleHeader.blade.php
8 months ago
A1Gard b39b4a43d2 added ParallaxHeaderPin theme part
fixed some ui bugs
8 months ago
A1Gard c947b35bf1 added simple part
added slider
8 months ago
A1Gard ce19cd3d77 added simple part 8 months ago
A1Gard d9e8ab919a added inline map theme part 8 months ago
A1Gard f2c039e23c added location picker to setting 8 months ago
A1Gard a16ff6461f added counter theme part 8 months ago
A1Gard 29a66fab5e added icon field to setting 8 months ago
A1Gard 584a062867 added grid category 8 months ago
A1Gard be6a666d54 added categories fav image 8 months ago
A1Gard 5fbc036017 added parallax footer part 8 months ago
A1Gard 2e75682b35 added long height posts
added sample images
8 months ago
A1Gard c3711bc322 added ParallaxSlider part
fix slider auto problem
8 months ago
A1Gard 9cfd799e59 optimize new slider data match by theme 9 months ago
A1Gard b2490089ca added add data to slider
added remove data to slider
9 months ago
A1Gard 480a363e37 added theme controller
optimized website gtmetrix [removed inline styles]
9 months ago
A1Gard 1482829b32 added side menu 9 months ago
A1Gard 40e0201cc6 added main to website 9 months ago
A1Gard fdc54ef515 Merge pull request #45 from CyberAli1989/master
feat: Implement product listing API with caching and sorting
9 months ago
Ali 3a0d622c16 Merge branch '4xmen:master' into master 9 months ago
cyberali d09a7f0afb feat: Implement product listing API with caching , sorting , filters and search
- Add ProductController with index method to provide product listings
- Implement caching for product listings based on request URI
- Add sorting functionality for products by various criteria (new, old, most_view, less_view, most_buy, less_buy)
- Implement filtering by category using slug
- Add search functionality to filter products by name
- Implement price range filtering using min_price and max_price parameters
- Include related category data in the product resource response
- Set default pagination to 20 items per page with optional customization via per_page parameter
9 months ago
cyberali 01ecec0b6c Roll back Category model 9 months ago
cyberali 09be9d65ca feat: Implement home page API endpoint
- Add HomeController with index method to provide home page data
- Fetch and include menu with items, limiting selected fields for optimization
- Fetch and include latest 6 sliders using SliderResource
- Fetch and include top 8 parent categories with their products using CategoryResource
- Fetch and include active advertisements with available clicks using AdvResource
- Fetch and include latest 8 posts using PostResource
- Return all collected data as a successful JSON response
9 months ago
A1Gard 063fb11f1d fixed dir problem 9 months ago
A1Gard a89143c974 added Third Grid product 9 months ago
A1Gard 1258652234 added index image post theme part 9 months ago
A1Gard 1c1f9b4cab added no link post theme part 9 months ago
A1Gard e096035c09 optimized firefooter 9 months ago
A1Gard a4e20ba17e added fire footer 9 months ago
A1Gard 0b5eb7697b added wave footer 9 months ago
A1Gard 790e8dc445 Merge pull request #44 from CyberAli1989/master
Add helper functions for standard JSON responses
9 months ago
cyberali 001f200613 Add helper functions for standard JSON responses
- Added `success` function to format successful JSON responses with customizable meta tags, Open Graph data, Twitter card data, and canonical URL.
- Added `errors` function to format error JSON responses with customizable status codes.
9 months ago
A1Gard 1a4ac2783c added Apl Menu theme part
added published to group and category
9 months ago
A1Gard 7daa9f4be4 added recet menu 9 months ago
A1Gard 4ebcecf646 added menu to setting
added menu seeder
9 months ago
A1Gard d299c9e38e Merge remote-tracking branch 'origin/master' 9 months ago
A1Gard e8ab808839 optimized panel sidebar with acl 9 months ago
A1Gard 49b4904a5c added acl to project 9 months ago
A1Gard 3e4a189db3 added sort to menu items 9 months ago
A1Gard 34c42c42ef added menu controller [WIP: sort]
added menu item input vue component
added v-model(s) & name support to MorphSelector
9 months ago
YasinDehfuli 12f7223ebd Update README.md 9 months ago
YasinDehfuli cb978d2d7e Update README.md 9 months ago
YasinDehfuli 48172c4572 Update README.md 9 months ago
A1Gard f36b32f1c6 removed old code comment 9 months ago
A1Gard e63aecd310 added live preview 9 months ago
A1Gard 99476b87cb improved seeder customer, area & post 9 months ago
A1Gard 1bf9992cdb fixed bug gfx vue component 9 months ago
A1Gard ed0dd558b1 added preloader to panel 9 months ago
A1Gard 5e18f1e800 improved Seeders 9 months ago
A1Gard 8b8d5454fb added last week orders to home 9 months ago
A1Gard 470ab8e922 added invoice seeder
added device pie chart
optimized order & invoice table struct
9 months ago
A1Gard f4bf290756 added status to invoice
added user relations
added invoice and ticket to index
9 months ago
A1Gard a8ce5ab623 added last month visits to home 9 months ago
A1Gard 6254bb1989 added referer website to visitor 9 months ago
A1Gard 02556e0e53 optimized visitor model, migration, middleware, seeder 9 months ago
A1Gard b6d1a114f8 added visitor statistics 9 months ago
A1Gard 8f871174c4 added fixed bug 9 months ago
A1Gard 805ee1563f fixed Increment.vue component inc bug 9 months ago
A1Gard 0e62221c2a added gradient text 9 months ago
A1Gard d51373ef95 added simple text
added invert outline btn
9 months ago
A1Gard 7baedd0a95 fixed readme 9 months ago
A1Gard a9c2d8a2ba change readme 9 months ago
A1Gard c7a0a3261a Merge branch 'master' of https://github.com/4xmen/xshop 9 months ago
A1Gard 7cce5589b8 remove laravel README.md 9 months ago
A1Gard c62d66f7f4 added some feature for theme and setting
fixed some bugs
optimized make part
9 months ago
A1Gard c0e3ed96a0 added parallax short theme part 9 months ago
A1Gard e8219bc878 added fav product with meta theme part 9 months ago
A1Gard 7a91972487 added git ignore to media library 9 months ago
A1Gard e15dca711c optimized client command
added post icon simple
9 months ago
A1Gard 926bbb9b56 added multi language support 9 months ago
A1Gard d49bff4b8f added sort button to category group
added sort to prop
9 months ago
A1Gard f858bb323a added panel rtl theme 9 months ago
A1Gard 2d1946389e added persian lang 9 months ago
A1Gard a4f87d6f5f optimized png for slider group category 9 months ago
A1Gard 7ad9538fa3 added cities and states controller 9 months ago
A1Gard 9493f94aab added open file by click on image [UX] 9 months ago
A1Gard cfc8c55a5a added watermark size and opacity to app setting 9 months ago
A1Gard 32a04fd070 added optimize for category group slider
fixed bug part seeder
added watermark2 to SettingSeeder
added upload gitignore
9 months ago
A1Gard 97403be70b added up-down button to areea 9 months ago
A1Gard 87a89acfbd added remove to part area
added sort part area
added topSimple part
added bootstrap to vendor assets
fixed slider simple duplicate bug
added setting group
fixed setting social group
9 months ago
A1Gard e20334dfb5 optimized client assets generator
completed two preloader sample
9 months ago
A1Gard 07b48881a1 added Area and Part
added two preloader sample
9 months ago
A1Gard 687fba4806 added client asset generator [WIP] 9 months ago
A1Gard c80c5a660b added client assets 9 months ago
A1Gard 4450a69908 optimized 9 months ago
A1Gard 2bf17918b2 added theme part command 9 months ago
A1Gard d7ae44512f added color to setting 9 months ago
A1Gard 7d3b1500d8 optimized editor with site template 9 months ago
A1Gard bafd039343 added GFX to panel 9 months ago
A1Gard 9843fb043c added admin log to setting 9 months ago
A1Gard d63d1f079b fixed some fatal errors 9 months ago
A1Gard b85b9bdba1 added sort save to groups 9 months ago
A1Gard 61c3acb25a added sort save category 9 months ago
A1Gard b078f83e0e added setting to panel 9 months ago
A1Gard d0ceab5d64 added comment controller 9 months ago
A1Gard 9b95e1fa5e added contact controller 9 months ago
A1Gard dc9b6beb48 added hash to contact and invoice 9 months ago
A1Gard cd3887f0b9 optimized list 9 months ago
A1Gard dea838a4bc added ticket controller 9 months ago
A1Gard 5a3245ea5c added question controller 9 months ago
A1Gard be41e769ff optimized attachment and list template 9 months ago
A1Gard 374165b094 added attachment relations
added deattach
9 months ago
A1Gard 02a8ad5177 added attachment controller 9 months ago
A1Gard 4a857afb6b added morph selector 9 months ago
A1Gard 7650908732 fixed navbar ui bugs 9 months ago
A1Gard f074423cfe added transports 9 months ago
A1Gard 3c186f1bf4 added discounts to product
added located date to Carbon
9 months ago
A1Gard 6577adabb6 added discount controller 9 months ago
A1Gard e1445a6281 added product seeder WIP: need picture and category 9 months ago
A1Gard 9dc22a0f9d fixed toggle bulk action bug 9 months ago
A1Gard 352508117a fixed restore annd trashed bug for product 9 months ago
A1Gard f661371f88 save quantities
fixed some bugs for update and edit page
9 months ago
A1Gard 66a9b45bde fixed currency input bug 9 months ago
A1Gard a43098693e added vuex compatible to SearchableSelect
added meta input
10 months ago
A1Gard 323ae11dd8 added vuex to project 10 months ago
A1Gard d9a249cd58 fixed ckeditor change bug 10 months ago
A1Gard 55cf393101 added buy price
fixed redirect for insert category_id in list
10 months ago
A1Gard 6b672e6ca7 added slug unique fixer to xcontroller 10 months ago
A1Gard 9d7fac82e7 added prop controller
added category api [props]
10 months ago
A1Gard c19b3bf3e9 added product controller [WIP: discount & meta & quantity] 10 months ago
A1Gard c353645f66 added customer controller
added state and city
installed api route
added address input
10 months ago
A1Gard 0cade105f0 added adv xcontroller
added disable date picker
10 months ago
A1Gard 08121212bc added category controller 10 months ago
A1Gard daf37c669f added dataz attribute 10 months ago
A1Gard a3d392e719 added slider with meta data values 10 months ago
A1Gard 36b7fe8858 added clip controller 10 months ago
A1Gard de3c79e1c7 added custom slug to gallery 10 months ago
A1Gard b7acaa0610 added galley xController
optimize ui
added imgUrl to list template
10 months ago
A1Gard b44cd1183b added admin logs controller
added log to users
10 months ago
A1Gard aaab1aa309 added ckeditor to panel
added post xController
10 months ago
A1Gard 182fd6eb38 fixed breadcrumb component 10 months ago
A1Gard 08c18bec45 added Group
added some essential function to helper and xcontoller
10 months ago
A1Gard 72aecf103b added xcontroller commands 10 months ago
A1Gard ee2d417770 added xController store ad update
form template
edited menu and order migration
10 months ago
A1Gard e271017973 xController list + template-list 10 months ago
A1Gard ba8c4e9fe0 added panel list component
fixed post group id
10 months ago
A1Gard 0b814b2395 modified starterkit models
added helper
11 months ago
A1Gard 9b1cbb6ad0 added migrations 11 months ago
A1Gard 49ca8ac6bb added user roles 11 months ago
A1Gard 3bdf044a41 fixed style for panel 11 months ago
A1Gard 12a1a9beb6 added panel navbar
fixed env
11 months ago
A1Gard 82fb5f0499 Initial commit 2 years ago

@ -1,10 +1,10 @@
APP_NAME=Laravel
APP_NAME=xShop2
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_DEPLOYED=false
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_TIMEZONE=ASIA/TEHRAN
APP_URL=http://127.0.0.1:8000
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
@ -20,12 +20,13 @@ LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
#DB_CONNECTION=sqlite
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=/project-directory/xshop/storage/logs/open.db
#DB_USERNAME=root
#DB_PASSWORD=
SESSION_DRIVER=database
SESSION_LIFETIME=9999999
@ -42,6 +43,9 @@ CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
PANEL_PREFIX=dashboard
PANEL_PAGE_COUNT=30
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
@ -56,6 +60,9 @@ MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
MEDIA_WATERMARK_SIZE=15
MEDIA_WATERMARK_OPACITY=50
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
@ -63,3 +70,23 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
XLANG_ACTIVE=false
XLANG_MAIN=en
XLANG_API_URL="http://5.255.98.77:3001"
CURRENCY_SYMBOL="$"
CURRENCY_FACTOR=1
CURRENCY_CODE=USD
SMS_SING=true
SMS_DRIVER=Kavenegar
SMS_TOKEN=
SMS_USER=
SMS_PASSWORD=
SMS_URL="https://api.kavenegar.com/v1/TOKEN/verify/lookup.json"
SMS_NUMBER=
ZARINPAL_MERCHANT=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ZIBAL_MERCHANT=zibal
PAY_GATEWAY=zibal

@ -0,0 +1,55 @@
name: Laravel
on:
push:
tags:
- "*"
pull_request:
branches: [ "master" ]
jobs:
laravel-tests:
runs-on: ubuntu-latest
steps:
- uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e
with:
php-version: '8.2'
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
- name: remove composer lock
run: rm composer.lock
- name: Install Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --ignore-platform-reqs
- name: Generate key
run: php artisan key:generate
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
- name: Create Database
run: |
mkdir -p database
touch database/database.sqlite
- name: Dataseeder
env:
DB_CONNECTION: sqlite
DB_DATABASE: database/database.sqlite
run: php artisan migrate --seed
- name: Npm install
run: npm install
- name: client build
env:
DB_CONNECTION: sqlite
DB_DATABASE: database/database.sqlite
run: php artisan client -vvv
- name: npm build
run: npm run build
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite
DB_DATABASE: database/database.sqlite
run: php artisan test

1
.gitignore vendored

@ -17,3 +17,4 @@ yarn-error.log
/.fleet
/.idea
/.vscode
package-lock.json

@ -0,0 +1,625 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for software and
other kinds of works.
The licenses for most software and other practical works are designed to take
away your freedom to share and change the works. By contrast, the GNU General
Public License is intended to guarantee your freedom to share and change all
versions of a program--to make sure it remains free software for all its users.
We, the Free Software Foundation, use the GNU General Public License for most
of our software; it applies also to any other work released this way by its
authors. You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our
General Public Licenses are designed to make sure that you have the freedom
to distribute copies of free software (and charge for them if you wish), that
you receive source code or can get it if you want it, that you can change
the software or use pieces of it in new free programs, and that you know you
can do these things.
To protect your rights, we need to prevent others from denying you these rights
or asking you to surrender the rights. Therefore, you have certain responsibilities
if you distribute copies of the software, or if you modify it: responsibilities
to respect the freedom of others.
For example, if you distribute copies of such a program, whether gratis or
for a fee, you must pass on to the recipients the same freedoms that you received.
You must make sure that they, too, receive or can get the source code. And
you must show them these terms so they know their rights.
Developers that use the GNU GPL protect your rights with two steps: (1) assert
copyright on the software, and (2) offer you this License giving you legal
permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains that
there is no warranty for this free software. For both users' and authors'
sake, the GPL requires that modified versions be marked as changed, so that
their problems will not be attributed erroneously to authors of previous versions.
Some devices are designed to deny users access to install or run modified
versions of the software inside them, although the manufacturer can do so.
This is fundamentally incompatible with the aim of protecting users' freedom
to change the software. The systematic pattern of such abuse occurs in the
area of products for individuals to use, which is precisely where it is most
unacceptable. Therefore, we have designed this version of the GPL to prohibit
the practice for those products. If such problems arise substantially in other
domains, we stand ready to extend this provision to those domains in future
versions of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents. States
should not allow patents to restrict development and use of software on general-purpose
computers, but in those that do, we wish to avoid the special danger that
patents applied to a free program could make it effectively proprietary. To
prevent this, the GPL assures that patents cannot be used to render the program
non-free.
The precise terms and conditions for copying, distribution and modification
follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of works,
such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this License.
Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals
or organizations.
To "modify" a work means to copy from or adapt all or part of the work in
a fashion requiring copyright permission, other than the making of an exact
copy. The resulting work is called a "modified version" of the earlier work
or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based on the
Program.
To "propagate" a work means to do anything with it that, without permission,
would make you directly or secondarily liable for infringement under applicable
copyright law, except executing it on a computer or modifying a private copy.
Propagation includes copying, distribution (with or without modification),
making available to the public, and in some countries other activities as
well.
To "convey" a work means any kind of propagation that enables other parties
to make or receive copies. Mere interaction with a user through a computer
network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices" to the
extent that it includes a convenient and prominently visible feature that
(1) displays an appropriate copyright notice, and (2) tells the user that
there is no warranty for the work (except to the extent that warranties are
provided), that licensees may convey the work under this License, and how
to view a copy of this License. If the interface presents a list of user commands
or options, such as a menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work for making
modifications to it. "Object code" means any non-source form of a work.
A "Standard Interface" means an interface that either is an official standard
defined by a recognized standards body, or, in the case of interfaces specified
for a particular programming language, one that is widely used among developers
working in that language.
The "System Libraries" of an executable work include anything, other than
the work as a whole, that (a) is included in the normal form of packaging
a Major Component, but which is not part of that Major Component, and (b)
serves only to enable use of the work with that Major Component, or to implement
a Standard Interface for which an implementation is available to the public
in source code form. A "Major Component", in this context, means a major essential
component (kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to produce
the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all the source
code needed to generate, install, and (for an executable work) run the object
code and to modify the work, including scripts to control those activities.
However, it does not include the work's System Libraries, or general-purpose
tools or generally available free programs which are used unmodified in performing
those activities but which are not part of the work. For example, Corresponding
Source includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically linked
subprograms that the work is specifically designed to require, such as by
intimate data communication or control flow between those subprograms and
other parts of the work.
The Corresponding Source need not include anything that users can regenerate
automatically from other parts of the Corresponding Source.
The Corresponding Source for a work in source code form is that same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of copyright
on the Program, and are irrevocable provided the stated conditions are met.
This License explicitly affirms your unlimited permission to run the unmodified
Program. The output from running a covered work is covered by this License
only if the output, given its content, constitutes a covered work. This License
acknowledges your rights of fair use or other equivalent, as provided by copyright
law.
You may make, run and propagate covered works that you do not convey, without
conditions so long as your license otherwise remains in force. You may convey
covered works to others for the sole purpose of having them make modifications
exclusively for you, or provide you with facilities for running those works,
provided that you comply with the terms of this License in conveying all material
for which you do not control copyright. Those thus making or running the covered
works for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of your copyrighted
material outside their relationship with you.
Conveying under any other circumstances is permitted solely under the conditions
stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological measure
under any applicable law fulfilling obligations under article 11 of the WIPO
copyright treaty adopted on 20 December 1996, or similar laws prohibiting
or restricting circumvention of such measures.
When you convey a covered work, you waive any legal power to forbid circumvention
of technological measures to the extent such circumvention is effected by
exercising rights under this License with respect to the covered work, and
you disclaim any intention to limit operation or modification of the work
as a means of enforcing, against the work's users, your or third parties'
legal rights to forbid circumvention of technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you receive
it, in any medium, provided that you conspicuously and appropriately publish
on each copy an appropriate copyright notice; keep intact all notices stating
that this License and any non-permissive terms added in accord with section
7 apply to the code; keep intact all notices of the absence of any warranty;
and give all recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey, and you
may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to produce
it from the Program, in the form of source code under the terms of section
4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified it, and
giving a relevant date.
b) The work must carry prominent notices stating that it is released under
this License and any conditions added under section 7. This requirement modifies
the requirement in section 4 to "keep intact all notices".
c) You must license the entire work, as a whole, under this License to anyone
who comes into possession of a copy. This License will therefore apply, along
with any applicable section 7 additional terms, to the whole of the work,
and all its parts, regardless of how they are packaged. This License gives
no permission to license the work in any other way, but it does not invalidate
such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display Appropriate
Legal Notices; however, if the Program has interactive interfaces that do
not display Appropriate Legal Notices, your work need not make them do so.
A compilation of a covered work with other separate and independent works,
which are not by their nature extensions of the covered work, and which are
not combined with it such as to form a larger program, in or on a volume of
a storage or distribution medium, is called an "aggregate" if the compilation
and its resulting copyright are not used to limit the access or legal rights
of the compilation's users beyond what the individual works permit. Inclusion
of a covered work in an aggregate does not cause this License to apply to
the other parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms of sections
4 and 5, provided that you also convey the machine-readable Corresponding
Source under the terms of this License, in one of these ways:
a) Convey the object code in, or embodied in, a physical product (including
a physical distribution medium), accompanied by the Corresponding Source fixed
on a durable physical medium customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product (including
a physical distribution medium), accompanied by a written offer, valid for
at least three years and valid for as long as you offer spare parts or customer
support for that product model, to give anyone who possesses the object code
either (1) a copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical medium customarily
used for software interchange, for a price no more than your reasonable cost
of physically performing this conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the written
offer to provide the Corresponding Source. This alternative is allowed only
occasionally and noncommercially, and only if you received the object code
with such an offer, in accord with subsection 6b.
d) Convey the object code by offering access from a designated place (gratis
or for a charge), and offer equivalent access to the Corresponding Source
in the same way through the same place at no further charge. You need not
require recipients to copy the Corresponding Source along with the object
code. If the place to copy the object code is a network server, the Corresponding
Source may be on a different server (operated by you or a third party) that
supports equivalent copying facilities, provided you maintain clear directions
next to the object code saying where to find the Corresponding Source. Regardless
of what server hosts the Corresponding Source, you remain obligated to ensure
that it is available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided you inform
other peers where the object code and Corresponding Source of the work are
being offered to the general public at no charge under subsection 6d.
A separable portion of the object code, whose source code is excluded from
the Corresponding Source as a System Library, need not be included in conveying
the object code work.
A "User Product" is either (1) a "consumer product", which means any tangible
personal property which is normally used for personal, family, or household
purposes, or (2) anything designed or sold for incorporation into a dwelling.
In determining whether a product is a consumer product, doubtful cases shall
be resolved in favor of coverage. For a particular product received by a particular
user, "normally used" refers to a typical or common use of that class of product,
regardless of the status of the particular user or of the way in which the
particular user actually uses, or expects or is expected to use, the product.
A product is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent the
only significant mode of use of the product.
"Installation Information" for a User Product means any methods, procedures,
authorization keys, or other information required to install and execute modified
versions of a covered work in that User Product from a modified version of
its Corresponding Source. The information must suffice to ensure that the
continued functioning of the modified object code is in no case prevented
or interfered with solely because modification has been made.
If you convey an object code work under this section in, or with, or specifically
for use in, a User Product, and the conveying occurs as part of a transaction
in which the right of possession and use of the User Product is transferred
to the recipient in perpetuity or for a fixed term (regardless of how the
transaction is characterized), the Corresponding Source conveyed under this
section must be accompanied by the Installation Information. But this requirement
does not apply if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has been installed
in ROM).
The requirement to provide Installation Information does not include a requirement
to continue to provide support service, warranty, or updates for a work that
has been modified or installed by the recipient, or for the User Product in
which it has been modified or installed. Access to a network may be denied
when the modification itself materially and adversely affects the operation
of the network or violates the rules and protocols for communication across
the network.
Corresponding Source conveyed, and Installation Information provided, in accord
with this section must be in a format that is publicly documented (and with
an implementation available to the public in source code form), and must require
no special password or key for unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this License
by making exceptions from one or more of its conditions. Additional permissions
that are applicable to the entire Program shall be treated as though they
were included in this License, to the extent that they are valid under applicable
law. If additional permissions apply only to part of the Program, that part
may be used separately under those permissions, but the entire Program remains
governed by this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option remove any
additional permissions from that copy, or from any part of it. (Additional
permissions may be written to require their own removal in certain cases when
you modify the work.) You may place additional permissions on material, added
by you to a covered work, for which you have or can give appropriate copyright
permission.
Notwithstanding any other provision of this License, for material you add
to a covered work, you may (if authorized by the copyright holders of that
material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the terms of
sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or author
attributions in that material or in the Appropriate Legal Notices displayed
by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or requiring
that modified versions of such material be marked in reasonable ways as different
from the original version; or
d) Limiting the use for publicity purposes of names of licensors or authors
of the material; or
e) Declining to grant rights under trademark law for use of some trade names,
trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that material by
anyone who conveys the material (or modified versions of it) with contractual
assumptions of liability to the recipient, for any liability that these contractual
assumptions directly impose on those licensors and authors.
All other non-permissive additional terms are considered "further restrictions"
within the meaning of section 10. If the Program as you received it, or any
part of it, contains a notice stating that it is governed by this License
along with a term that is a further restriction, you may remove that term.
If a license document contains a further restriction but permits relicensing
or conveying under this License, you may add to a covered work material governed
by the terms of that license document, provided that the further restriction
does not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you must place,
in the relevant source files, a statement of the additional terms that apply
to those files, or a notice indicating where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the form
of a separately written license, or stated as exceptions; the above requirements
apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly provided
under this License. Any attempt otherwise to propagate or modify it is void,
and will automatically terminate your rights under this License (including
any patent licenses granted under the third paragraph of section 11).
However, if you cease all violation of this License, then your license from
a particular copyright holder is reinstated (a) provisionally, unless and
until the copyright holder explicitly and finally terminates your license,
and (b) permanently, if the copyright holder fails to notify you of the violation
by some reasonable means prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated permanently
if the copyright holder notifies you of the violation by some reasonable means,
this is the first time you have received notice of violation of this License
(for any work) from that copyright holder, and you cure the violation prior
to 30 days after your receipt of the notice.
Termination of your rights under this section does not terminate the licenses
of parties who have received copies or rights from you under this License.
If your rights have been terminated and not permanently reinstated, you do
not qualify to receive new licenses for the same material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or run a copy
of the Program. Ancillary propagation of a covered work occurring solely as
a consequence of using peer-to-peer transmission to receive a copy likewise
does not require acceptance. However, nothing other than this License grants
you permission to propagate or modify any covered work. These actions infringe
copyright if you do not accept this License. Therefore, by modifying or propagating
a covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically receives
a license from the original licensors, to run, modify and propagate that work,
subject to this License. You are not responsible for enforcing compliance
by third parties with this License.
An "entity transaction" is a transaction transferring control of an organization,
or substantially all assets of one, or subdividing an organization, or merging
organizations. If propagation of a covered work results from an entity transaction,
each party to that transaction who receives a copy of the work also receives
whatever licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the Corresponding
Source of the work from the predecessor in interest, if the predecessor has
it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the rights
granted or affirmed under this License. For example, you may not impose a
license fee, royalty, or other charge for exercise of rights granted under
this License, and you may not initiate litigation (including a cross-claim
or counterclaim in a lawsuit) alleging that any patent claim is infringed
by making, using, selling, offering for sale, or importing the Program or
any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this License
of the Program or a work on which the Program is based. The work thus licensed
is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims owned or controlled
by the contributor, whether already acquired or hereafter acquired, that would
be infringed by some manner, permitted by this License, of making, using,
or selling its contributor version, but do not include claims that would be
infringed only as a consequence of further modification of the contributor
version. For purposes of this definition, "control" includes the right to
grant patent sublicenses in a manner consistent with the requirements of this
License.
Each contributor grants you a non-exclusive, worldwide, royalty-free patent
license under the contributor's essential patent claims, to make, use, sell,
offer for sale, import and otherwise run, modify and propagate the contents
of its contributor version.
In the following three paragraphs, a "patent license" is any express agreement
or commitment, however denominated, not to enforce a patent (such as an express
permission to practice a patent or covenant not to sue for patent infringement).
To "grant" such a patent license to a party means to make such an agreement
or commitment not to enforce a patent against the party.
If you convey a covered work, knowingly relying on a patent license, and the
Corresponding Source of the work is not available for anyone to copy, free
of charge and under the terms of this License, through a publicly available
network server or other readily accessible means, then you must either (1)
cause the Corresponding Source to be so available, or (2) arrange to deprive
yourself of the benefit of the patent license for this particular work, or
(3) arrange, in a manner consistent with the requirements of this License,
to extend the patent license to downstream recipients. "Knowingly relying"
means you have actual knowledge that, but for the patent license, your conveying
the covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that country
that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or arrangement,
you convey, or propagate by procuring conveyance of, a covered work, and grant
a patent license to some of the parties receiving the covered work authorizing
them to use, propagate, modify or convey a specific copy of the covered work,
then the patent license you grant is automatically extended to all recipients
of the covered work and works based on it.
A patent license is "discriminatory" if it does not include within the scope
of its coverage, prohibits the exercise of, or is conditioned on the non-exercise
of one or more of the rights that are specifically granted under this License.
You may not convey a covered work if you are a party to an arrangement with
a third party that is in the business of distributing software, under which
you make payment to the third party based on the extent of your activity of
conveying the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory patent
license (a) in connection with copies of the covered work conveyed by you
(or copies made from those copies), or (b) primarily for and in connection
with specific products or compilations that contain the covered work, unless
you entered into that arrangement, or that patent license was granted, prior
to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting any implied
license or other defenses to infringement that may otherwise be available
to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or otherwise)
that contradict the conditions of this License, they do not excuse you from
the conditions of this License. If you cannot convey a covered work so as
to satisfy simultaneously your obligations under this License and any other
pertinent obligations, then as a consequence you may not convey it at all.
For example, if you agree to terms that obligate you to collect a royalty
for further conveying from those to whom you convey the Program, the only
way you could satisfy both those terms and this License would be to refrain
entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have permission to
link or combine any covered work with a work licensed under version 3 of the
GNU Affero General Public License into a single combined work, and to convey
the resulting work. The terms of this License will continue to apply to the
part which is the covered work, but the special requirements of the GNU Affero
General Public License, section 13, concerning interaction through a network
will apply to the combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of the
GNU General Public License from time to time. Such new versions will be similar
in spirit to the present version, but may differ in detail to address new
problems or concerns.
Each version is given a distinguishing version number. If the Program specifies
that a certain numbered version of the GNU General Public License "or any
later version" applies to it, you have the option of following the terms and
conditions either of that numbered version or of any later version published
by the Free Software Foundation. If the Program does not specify a version
number of the GNU General Public License, you may choose any version ever
published by the Free Software Foundation.
If the Program specifies that a proxy can decide which future versions of
the GNU General Public License can be used, that proxy's public statement
of acceptance of a version permanently authorizes you to choose that version
for the Program.
Later license versions may give you additional or different permissions. However,
no additional obligations are imposed on any author or copyright holder as
a result of your choosing to follow a later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM
AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO
USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided above cannot
be given local legal effect according to their terms, reviewing courts shall
apply local law that most closely approximates an absolute waiver of all civil
liability in connection with the Program, unless a warranty or assumption
of liability accompanies a copy of the Program in return for a fee. END OF
TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible
use to the public, the best way to achieve this is to make it free software
which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach
them to the start of each source file to most effectively state the exclusion
of warranty; and each file should have at least the "copyright" line and a
pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like
this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it under certain
conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands might
be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary. For
more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General Public
License instead of this License. But first, please read <https://www.gnu.org/
licenses /why-not-lgpl.html>.

@ -0,0 +1,227 @@
<div dir="rtl">
<div align="center">
<img width="250" src="resources/images/xshop-logo.svg" alt="xShop logo">
</div>
# xShop/v2
> [!NOTE]
> xShop یک سیستم فروشگاه نوشته شده در لاراول با قابلیت سفارشی‌سازی فراوان
## امکانات نگارش جدید
- تغییرات اساسی در کنترل پنل
- یکپارچه‌سازی Laravel & vujs.js
- نمودارهای پیشرفته
- بهینه‌سازی سایت‌های چند زبانه و افزایش بهره‌وری هوش مصنوعی ترجمان
- اصلاح مشکلات تکنیکی
- کاهش سایز پروژه
- UI/UX شخصی‌سازی شده
- دوستانه تر شدن محیط توسعه
- شخصی سازی قالب وبسایت
## مستندات
- [📄 مستندات کامل 📄](https://4xmen.github.io/xshop/#/)
- [🇺🇸 English read me](README.md)
## نحوه نصب [ حالت توسعه دهنده ]
> [!مهم]
> ابتدا یک دیتابیس درست کنیدد و سپس `.env.example` را به `.env` تغییر دهید و `.env` کارهای زیر به ترتیب مراحل اجرای
> پروژه روی لوکال هاست می‌باشد :
<div dir="ltr">
```bash
git clone https://github.com/4xmen/xshop.git
cd xshop
cp .env.example .env
composer install
php artisan migrate:fresh --seed
php artisan storage:link
php artisan key:generate
php artisan serv
# to develop front-end
npm install -g yarn
yarn install
php artisan client
yarn dev
```
</div>
> [!TIP]
> اطلاعات ورود اولیه : `developer@example.com` (به عنوان توسعه دهنده) یا `admin@example.com` (به عنوان مدیر) و
> گذرواژه : `password`
## افزودن تصاویر نمونه
- دانلود و آماده‌سازی تصاویر
<div dir="ltr">
```bash
php artisan seeding:prepare
```
</div>
- ویا تصاویر خود را با پوشه مورد نظر در این مسیر کپی کنید: `database/seeders/images/`
- سپس یکی از مدل ها دلخواه را seed image کنید: [Group, Category, Post, Product, Slider]
<div dir="ltr">
```bash
php artisan seeding:image Product digital
```
</div>
یا برای همه مدل‌ها یک‌جا از دستور زیر استفاده کنید:
<div dir="ltr">
```bash
php artisan seeding:all digital
```
</div>
> شما ابتدا باید مدل را نوشته و سپس فولدر مورد نظر برای تصاویر را وارد کنید[bag, clothe, digital, sport, posts, makeup]
> همچنین می‌توانید یک پوشه درخواه پر از تصاویر jpg دلخواه را در آن پر کنید
## ملزومات
- php 8.2.x یا بالاتر با همراه افزونه‌ها [ `php-gd`, `sqlite3`, `php-soap`]
- mysql یا mariadb یا sqlite
- composer
- پیشنهاد می‌شود imagemagick برای افزایش راندمان تصاویر نصب کنید
## راهنمای انتشار
قویا پیشنهاد می‌کنیم از vps به جا میزبان اشتراکی استفاده کنید و بعد از ساختن دیتابیس دستورات زیر را اجرا کنید:
<div dir="ltr">
```bash
cd /home/[yourUsername]/[pathOfYourWebsitePublicHTML]
git clone https://github.com/4xmen/xshop.git . # if this command not work make empty this folder
cp .env.example .env
nano .env # edit your config db, url, etc.
composer install
php artisan migrate:fresh --seed
php artisan storage:link
php key:generate
npm install
php artisan client
npm run build
```
</div>
## بهینه سازی سایت و آماده سازی نسخه نهایی
<div dir="ltr">
```bash
nano .env # make APP_DEBUG false, APP_ENV production
php artisan optimize
composer install --optimize-autoloader --no-dev
```
</div>
## اضافه کردن cron job
جهت اجرا کامل برنامه ها زمان‌دار فروشگاه باید یک دستور زیر رو بزنید:
<div dir="ltr">
```bash
crontab -e
```
</div>
و این خط رو اضافه کنید:
<div dir="ltr">
```bash
* * * * * cd /home/[yourusername]/[your-public-html-project-root] && php artisan schedule:run >> /dev/null 2>&1
```
</div>
## ساختن xController
درواقع xController یک کنترولر بسیار پیشرفته با همراه لاگ و CRUD برای توسعه آسان است با فرض زیر:
User [`model`]
<div dir="ltr">
```bash
php artisan make:xcontroller User
```
</div>
## make theme part
Theme part usable in area
PartName [`theme aprt name`]
segmentName [`group`, `category`, `preloader`, ...],
<div dir="ltr">
```bash
php artisan make:part PartName segmentName
```
</div>
## client بهینه‌سازی فضای
برا بهینه سازی کلیه دارای‌های سایت `scss`,`js`,`css`
<div dir="ltr">
```bash
php artisan client
php artisan build
```
</div>
### توضیحات پرونده‌های قسمت قالب
- PartName.php: `onCreate`, `onRemove`, `onMount` برای انجام اعمال قسمت
- PartName.blade.php: برای قرارگرفتن کد‌های blade code
- PartName.scss:برای افزودن ویژگی‌های scss
- PartName.js: برای افزودن javascript
- screenshot.png: و یک پیش‌نمایش از قسمت قالب
## Demo / دمو
> برای دیدن یک دموی آنلاین : <a href="https://xshop.xstack.ir/login">https://xshop.xstack.ir/</a>
### تصاویر محیطی
![1](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot1.png)
![2](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot2.png)
![3](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot3.jpg)
![4](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot4.png)
![5](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot5.jpg)
## دسترسی به نگارش xShop/v1
> [!هشدار]
> xShop/v1 قابل دسترس در اینجا: <a href="https://github.com/4xmen/xshop.v1">https://github.com/4xmen/xshop.v1</a>
<p align="center">
توسعه داده شده با محبت ! ❤️
</p>
</div>

@ -1,66 +1,196 @@
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
<div align="center">
<img width="250" src="resources/images/xshop-logo.svg" alt="xShop logo">
</div>
<p align="center">
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
# xShop/v2
> [!NOTE]
> xShop is an open source shop developed in laravel, very customizable!
## New Features:
- Dashboard panel changes
- Integration of Vue.js and laravel
- Advanced charts
- Better customizable with AI & languages
- Fixed Technical issues
- Project size compression
- UI/UX is more specific
- Developer Friendlier
- Custom theme design
## Documentation
- [ 📄 **Full document** 📄 ](https://4xmen.github.io/xshop/#/)
- [🇮🇷 Persian read me](README-fa.md)
## Installation [ Development mode ]
> [!IMPORTANT]
> Create new database and rename `.env.example` to `.env` then update you `.env` configs so run this commands:
```bash
git clone https://github.com/4xmen/xshop.git
cd xshop
cp .env.example .env
composer install
php artisan migrate:fresh --seed
php artisan storage:link
php artisan key:generate
php artisan serv
# to develop front-end
npm i
php artisan client
npm install @rollup/rollup-win32-x64-msvc # just for windows if the below line dose not work
npm run dev
# or with yarn
yarn install
php artisan client
yarn add @rollup/rollup-win32-x64-msvc # just for windows if the below line dose not work
yarn dev
```
> [!TIP]
> Default admin email is : `developer@example.com` (developer) or `admin@example.com` (admin) and default password is: `password`
## image seeding
- Download & prepare images
```bash
php artisan seeding:prepare
```
- nor copy your image folder to `database/seeders/images/`
- then: Seeding image for models: [Group, Category, Post, Product, Slider]
```bash
php artisan seeding:image Product digital
```
Or to seed all models:
```bash
php artisan seeding:all digital
```
## About Laravel
> First parameter is Model, Second is image seeder directory available [bag, clothe, digital, sport, posts, makeup]
> You can create your directory and put your image into new directory then use image seeder
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
## Requirement
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
- php 8.2.x or above [ `php-gd`, `sqlite3`, `php-soap`]
- mysql or mariadb or sqlite
- composer
- recommends install imagemagick on server to more image performance
Laravel is accessible, powerful, and provides tools required for large, robust applications.
## Deploy guide
## Learning Laravel
We recommend deploy xShop on VPS, so create database and run this commands:
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
```bash
cd /home/[yourUsername]/[pathOfYourWebsitePublicHTML]
git clone https://github.com/4xmen/xshop.git . # if this command not work make empty this folder
cp .env.example .env
nano .env # edit your config db, url, etc.
composer install
php artisan migrate:fresh --seed
php artisan storage:link
php key:generate
npm install
php artisan client
npm run build
```
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
## Make your site optimize & production mode
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Laravel Sponsors
```bash
nano .env # make APP_DEBUG false, APP_ENV production
php artisan optimize
composer install --optimize-autoloader --no-dev
```
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
## Add cron job
### Premium Partners
You must add crontab for your project:
- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[WebReinvent](https://webreinvent.com/)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
- **[Jump24](https://jump24.co.uk)**
- **[Redberry](https://redberry.international/laravel/)**
- **[Active Logic](https://activelogic.com)**
- **[byte5](https://byte5.de)**
- **[OP.GG](https://op.gg)**
```bash
crontab -e
```
## Contributing
Add this line:
```bash
* * * * * cd /home/[yourusername]/[your-public-html-project-root] && php artisan schedule:run >> /dev/null 2>&1
```
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
## make xController
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
Controller with log and semi-automatic CURD with logs
User [`model`]
## Security Vulnerabilities
```bash
php artisan make:xcontroller User
```
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## make theme part
## License
Theme part usable in area
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
PartName [`theme aprt name`]
segmentName [`group`, `category`, `preloader`, ...],
```bash
php artisan make:part PartName segmentName
```
## client optimize
Optimize client assets, `scss`,`js`,`css`
```bash
php artisan client
php artisan build
```
### theme parts file
- PartName.php: `onCreate`, `onRemove`, `onMount` actions of theme part
- PartName.blade.php: your theme part blade code
- PartName.scss: your theme part scss
- PartName.js: your theme part javascript
- screenshot.png: screenshot preview of theme part
## Demo
> Online demo available here: <a href="https://xshop.xstack.ir/login">https://xshop.xstack.ir/</a>
### Screenshots
![1](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot1.png)
![2](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot2.png)
![3](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot3.jpg)
![4](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot4.png)
![5](https://raw.githubusercontent.com/A1Gard/xshop-installer-assets/master/screenshots/xshop-screenshot5.jpg)
## Access to xShop/v1
> [!WARNING]
> xShop/v1 available here: <a href="https://github.com/4xmen/xshop.v1">https://github.com/4xmen/xshop.v1</a>
<p align="center">
Developed With Love ! ❤️
</p>

@ -0,0 +1,130 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
class AssetsBuild extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'build';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Safely run npm build process ';
/**
* Store command output
*/
private $commandOutput = '';
/**
* Get Laravel root path
*/
private function getLaravelRoot()
{
return base_path();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
try {
// Set working directory to Laravel root
chdir($this->getLaravelRoot());
// Check if npm is installed
$npmVersion = new Process(['npm', '-v']);
$npmVersion->setWorkingDirectory($this->getLaravelRoot());
$npmVersion->run();
if (!$npmVersion->isSuccessful()) {
$this->addOutput('npm is not installed. Please install npm first.');
return Command::FAILURE;
}
// Check if package.json exists
if (!file_exists($this->getLaravelRoot() . '/package.json')) {
$this->addOutput('package.json not found in ' . $this->getLaravelRoot());
return Command::FAILURE;
}
// Check if node_modules exists
if (!file_exists($this->getLaravelRoot() . '/node_modules')) {
$this->addOutput('node_modules not found. Installing dependencies...');
$installProcess = new Process(['npm', 'install']);
$installProcess->setWorkingDirectory($this->getLaravelRoot());
$installProcess->setTimeout(3600); // 1 hour timeout
$installProcess->run(function ($type, $buffer) {
$this->addOutput($buffer);
});
if (!$installProcess->isSuccessful()) {
throw new ProcessFailedException($installProcess);
}
}
// Run npm build
$this->addOutput('Starting build process in: ' . $this->getLaravelRoot());
$buildProcess = new Process(['./node_modules/.bin/vite', 'build']);
$buildProcess->setWorkingDirectory($this->getLaravelRoot());
// $this->addOutput($this->getLaravelRoot());
$buildProcess->setTimeout(3600); // 1 hour timeout
$buildProcess->run(function ($type, $buffer) {
$this->addOutput($buffer);
});
if (!$buildProcess->isSuccessful()) {
throw new ProcessFailedException($buildProcess);
}
$this->addOutput('Build completed successfully!');
// Store output in cache for retrieval
cache()->put('build_command_output', $this->commandOutput, now()->addMinutes(5));
return Command::SUCCESS;
} catch (\Exception $e) {
$errorMessage = 'Build process failed: ' . $e->getMessage();
$this->addOutput($errorMessage);
cache()->put('build_command_output', $this->commandOutput, now()->addMinutes(5));
return Command::FAILURE;
}
}
/**
* Add output to both console and stored output
*/
private function addOutput($output)
{
$this->commandOutput .= $output . PHP_EOL;
$this->info($output); // This will only show in CLI
}
/**
* Get the command output
*/
public function getOutput()
{
return $this->commandOutput;
}
}

@ -0,0 +1,48 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class SeedImageAll extends Command
{
public $models = ['Category', 'Group', 'Slider', 'Post', 'Product'];
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'seeding:all {directory}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
//
foreach ($this->models as $model) {
// Call the seeder command
$exitCode = \Artisan::call('seeding:image', ['directory' => $this->argument('directory'), 'model' => $model]);
// Get the output
$output = \Artisan::output();
// Handle the exit code and output as needed
if ($exitCode === 0) {
$this->info( "Seeding was successful: [$model] \n");
} else {
$this->error("Seeding failed with exit code {$exitCode}:\n");
}
$this->info( $output);
}
}
}

@ -0,0 +1,141 @@
<?php
namespace App\Console\Commands;
use App\Models\Category;
use App\Models\Group;
use App\Models\Post;
use App\Models\Product;
use App\Models\Slider;
use Illuminate\Console\Command;
use Spatie\Image\Enums\AlignPosition;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Enums\Unit;
use Spatie\Image\Image;
class SeedingImage extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'seeding:image {model} {directory}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Seeding image model';
/**
* Execute the console command.
*/
public function handle()
{
if (!\File::exists(__DIR__ . '/../../../database/seeders/images/' . $this->argument('directory'))) {
$this->error('Directory not found');
}
$images = \File::files(__DIR__ . '/../../../database/seeders/images/' . $this->argument('directory'));
switch ($this->argument('model')) {
case 'Product':
foreach (Product::all() as $item) {
$this->info('Product: ' . $item->id . ' adding image...');
shuffle($images);
if ($item->media()->count() == 0){
$name = $images[0]->getFilename();
$tempName = explode('.', $name);
$item->name = readable($tempName[0]) . ' model ' . $item->id;
}
$item->status = 1;
$item->addMedia($images[0]->getRealPath())
->preservingOriginal() //middle method
->toMediaCollection(); //finishing method
$item->save();
}
break;
case 'Category':
$svgs = \File::files(__DIR__ . '/../../../database/seeders/images/svg');
foreach (Category::all() as $item) {
$this->info('Category: ' . $item->name . ' adding image...');
shuffle($images);
if (!\File::exists(storage_path().'/app/public/categories/')){
mkdir(storage_path().'/app/public/categories/', 0755, true);
}
\File::copy($images[0]->getRealPath(),storage_path().'/app/public/categories/' . $images[0]->getFilename());
$item->image = $images[0]->getFilename();
$i = Image::load($images[0]->getRealPath())
->optimize()
->format('webp');
$i->save(storage_path() . '/app/public/categories/optimized-'. $item->image);
shuffle($images);
\File::copy($images[0]->getRealPath(),storage_path().'/app/public/categories/' . $images[0]->getFilename());
$item->bg = $images[0]->getFilename();
$i = Image::load($images[0]->getRealPath())
->optimize()
->format('webp');
$i->save(storage_path() . '/app/public/categories/optimized-'. $item->bg);
shuffle($svgs);
\File::copy($svgs[0]->getRealPath(),storage_path().'/app/public/categories/' . $svgs[0]->getFilename());
$item->svg = $svgs[0]->getFilename();
$item->save();
}
break;
case 'Group':
foreach (Group::all() as $item) {
$this->info('Group: ' . $item->name . ' adding image...');
shuffle($images);
if (!\File::exists(storage_path().'/app/public/groups/')){
mkdir(storage_path().'/app/public/groups/', 0755, true);
}
\File::copy($images[0]->getRealPath(),storage_path().'/app/public/groups/' . $images[0]->getFilename());
$item->image = $images[0]->getFilename();
$i = Image::load($images[0]->getRealPath())
->optimize()
->format('webp');
$i->save(storage_path() . '/app/public/groups/optimized-'. $item->image);
shuffle($images);
\File::copy($images[0]->getRealPath(),storage_path().'/app/public/groups/' . $images[0]->getFilename());
$item->bg = $images[0]->getFilename();
$i = Image::load($images[0]->getRealPath())
->optimize()
->format('webp');
$i->save(storage_path() . '/app/public/groups/optimized-'. $item->bg);
$item->save();
}
break;
case 'Slider':
foreach (Slider::all() as $item) {
$this->info('Slider: ' . $item->name . ' adding image...');
shuffle($images);
if (!\File::exists(storage_path().'/app/public/sliders/')){
mkdir(storage_path().'/app/public/sliders/', 0755, true);
}
\File::copy($images[0]->getRealPath(),storage_path().'/app/public/sliders/' . $images[0]->getFilename());
$item->image = $images[0]->getFilename();
$i = Image::load($images[0]->getRealPath())
->optimize()
->format('webp');
$i->save(storage_path() . '/app/public/sliders/optimized-'. $item->image);
$item->status = 1;
$item->save();
}
break;
case 'Post':
foreach (Post::all() as $item) {
$this->info('post: ' . $item->id . ' adding image...');
shuffle($images);
$item->addMedia($images[0]->getRealPath())
->preservingOriginal() //middle method
->toMediaCollection(); //finishing method
$item->save();
$item->status = 1;
}
break;
default:
$this->error('Model not valid');
}
}
}

@ -0,0 +1,65 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class SeedingPrepare extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'seeding:prepare';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Download and unpack zip file for seeding.';
/**
* Execute the console command.
*/
public function handle()
{
// URL of the zip file
$zipUrl = 'https://github.com/A1Gard/xshop-installer-assets/raw/master/seeder-image.zip';
$localZipPath =__DIR__.'/../../../database/seeders/images/seeder-image.zip'; // Path where the zip will be saved
$extractPath = __DIR__.'/../../../database/seeders/images'; // Directory where the zip will be extracted
// Downloading the ZIP file
$this->info('Downloading ZIP file...');
$zipContent = file_get_contents($zipUrl);
file_put_contents($localZipPath, $zipContent);
// Check if the ZIP file was successfully downloaded
if (!file_exists($localZipPath)) {
$this->error('Failed to download the ZIP file.');
return;
}
// Unzipping the file
$this->info('Unzipping the file...');
$zip = new \ZipArchive();
if ($zip->open($localZipPath) === TRUE) {
// Create the extraction directory if it doesn't exist
if (!file_exists($extractPath)) {
mkdir($extractPath, 0777, true);
}
// Extract the ZIP file to the specified directory
$zip->extractTo($extractPath);
$zip->close();
$this->info('File unzipped successfully.');
} else {
$this->error('Failed to unzip the file.');
}
// Optionally, delete the zip file after extraction
unlink($localZipPath);
}
}

@ -0,0 +1,103 @@
<?php
namespace App\Console\Commands;
use App\Models\Part;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
class clientAssetGenerator extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'client';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
// make gfx variable
$gfxes = gfx();
$vars['xshop-background'] = $gfxes['background'] ?? '#000000';
$vars['xshop-primary'] = $gfxes['primary'] ?? '#6e0000';
$vars['xshop-diff'] = getGrayscaleTextColor($gfxes['primary']) ?? '#6e0000';
$vars['xshop-diff2'] = getGrayscaleTextColor($gfxes['secondary']) ?? '#6e0000';
$vars['xshop-secondary'] = $gfxes['secondary'] ?? '#ff0000';
$vars['xshop-text'] = $gfxes['text'] ?? '#111111';
$vars['xshop-border-radius'] = $gfxes['border-radius'] ?? '7px';
$vars['xshop-shadow'] = $gfxes['shadow'] ?? '2px 2px 4px #777777';
// prepare client.scss and add gfx variable
$js = "// PLEASE DO NOT EDIT THIS FILE, \n// IF YOU WANT ADD ANY CODE CREATE NEW JS INTO client-custom \n import axios from 'axios'; \n window.axios = axios; \n \n window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';" . PHP_EOL;
$variables = "// PLEASE DO NOT EDIT THIS FILE, \n// IF YOU WANT ADD ANY CODE CREATE NEW SCSS INTO client-custom" . PHP_EOL;
$appVariables = '';
foreach ($vars as $k => $var) {
$appVariables .= '$' . "$k:$var;" . PHP_EOL;
}
$appVariables .= ":root{" . PHP_EOL;
foreach ($vars as $k => $var) {
$appVariables .= "--$k:$var;" . PHP_EOL;
}
$appVariables .= "}" . PHP_EOL . PHP_EOL;
file_put_contents(resource_path() . '/sass/client-custom/_app_variables.scss', $appVariables);
// add custom scss and js
$files = File::allFiles(resource_path() . '/sass/client-custom');
foreach ($files as $file) {
if ($file->getType() == 'file' && $file->getExtension() == 'scss') {
$variables .= '@use "client-custom/' .
substr(trim($file->getBasename(), '_'), 0, -5)
. '";' . PHP_EOL;
}
}
$files = File::allFiles(resource_path() . '/js/client-custom');
foreach ($files as $file) {
if ($file->getType() == 'file' && $file->getExtension() == 'js') {
$js .= 'import "./client-custom/' . $file->getBasename() . '";' . PHP_EOL;
}
}
// add parts scss & js
foreach (Part::distinct()->get() as $part) {
if (filesize(__DIR__ . '/../../../resources/views/segments/' . $part->segment . '/' . $part->part . '/' . $part->part . '.scss') > 10) {
$variables .= '@use "../views/segments/' . $part->segment . '/'
. $part->part . '/' . $part->part . '";' . PHP_EOL;
}
if (filesize(__DIR__ . '/../../../resources/views/segments/' . $part->segment . '/' . $part->part . '/' . $part->part . '.js') > 10) {
$js .= 'import "../views/segments/' . $part->segment . '/'
. $part->part . '/' . $part->part . '.js";' . PHP_EOL;
}
}
// save scss
file_put_contents(resource_path() . '/sass/client.scss', $this->removeDuplicateLines($variables));
file_put_contents(resource_path() . '/js/client.js', $this->removeDuplicateLines( $js ));
}
// remove duplicate lines
private function removeDuplicateLines($text)
{
$lines = explode("\n", $text);
$uniqueLines = array_unique($lines);
$uniqueText = implode("\n", $uniqueLines);
return $uniqueText;
}
}

@ -0,0 +1,42 @@
@extends('admin.templates.panel-form-template')
@section('title')
@if(isset($item))
{{__("Edit user")}} [{{$item->id}}]
@else
{{__("Add new user")}}
@endif -
@endsection
@section('form')
<div class="row">
<div class="col-lg-3">
@include('components.err')
<div class="item-list mb-3">
<h3 class="p-3">
<i class="ri-message-3-line"></i>
{{__("Tips")}}
</h3>
<ul>
<li>
{{__("Recommends")}}
</li>
</ul>
</div>
</div>
<div class="col-lg-9 ps-xl-1 ps-xxl-1">
<div class="general-form ">
<h1>
@if(isset($item))
{{__("Edit user")}} [{{$item->id}}]
@else
{{__("Add new user")}}
@endif
</h1>
</div>
</div>
</div>
@endsection

@ -0,0 +1,21 @@
<?php
namespace Resources\Views\Segments;
use App\Models\Part;
class Handle
{
public static function onAdd(Part $part = null)
{
}
public static function onRemove(Part $part = null)
{
}
public static function onMount(Part $part = null)
{
return $part;
}
}

@ -0,0 +1,15 @@
@extends('admin.templates.panel-list-template')
@section('list-title')
<i class="ri-user-3-line"></i>
{{__("Users list")}}
@endsection
@section('title')
{{__("Users list")}} -
@endsection
@section('filter')
{{-- Other filters --}}
@endsection
@section('bulk')
{{-- <option value="-"> - </option> --}}
@endsection

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,120 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\UserSaveRequest;
use App\Models\Access;
use App\Models\User;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class UserController extends XController
{
// protected $_MODEL_ = User::class;
// protected $SAVE_REQUEST = UserSaveRequest::class;
protected $cols = [];
protected $extra_cols = [];
protected $searchable = [];
protected $listView = 'admin.users.user-list';
protected $formView = 'admin.users.user-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(User::class, UserSaveRequest::class);
}
/**
* @param $user User
* @param $request UserSaveRequest
* @return User
*/
public function save($user, $request)
{
$user->save();
return $user;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(User $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(User $item)
{
return parent::delete($item);
}
public function update(Request $request, User $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(User::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,97 @@
<?php
namespace App\Console\Commands;
use App\Models\Area;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use Symfony\Component\Process\Process;
class makePart extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'make:part {part} {segment}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'make segment part';
/**
* Execute the console command.
*/
public function handle()
{
//
$part = $this->argument('part');
$segment = strtolower($this->argument('segment'));
// make detail
$detail = [
'name' => $part,
'version' => '1.0',
'author' => 'xStack',
'email' => 'xshop@xstack.ir',
'license' => 'GPL-3.0-or-later',
'url' => 'https://xstack.ir',
'author_url' => 'https://4xmen.ir',
'packages' => [],
];
// check section
if (!in_array($segment, Area::$allSegments)) {
$this->error(__('Invalid area segment'));
return -1;
}
$folderPath = resource_path() . '/views/segments/' . $segment . '/' . ucfirst($part);
// check is exists
if (File::exists($folderPath)) {
$this->warn(__('Command ignored, segment part exists!'));
return -1;
}
// create folder
File::makeDirectory($folderPath, 0755, true);
File::makeDirectory($folderPath.'/assets', 0755, true);
$this->info('Directory created as: /segments/' . $segment . '/' . ucfirst($part));
$handler = file_get_contents(__DIR__.'/data/handle.dat');
$handler = str_replace('Handle',ucfirst($part), $handler);
$scss = <<<DOC
.$part {
// scss
}
DOC;
file_put_contents($folderPath . '/' . $part . '.blade.php', str_replace('THEME_PART_NAME',$part,'<section class="THEME_PART_NAME live-setting" data-live="{{$data->area_name.\'_\'.$data->part}}" ></section>'));
file_put_contents($folderPath . '/' . $part . '.js', '');
file_put_contents($folderPath . '/' . $part . '.json', json_encode($detail,JSON_PRETTY_PRINT));
file_put_contents($folderPath . '/' . ucfirst($part) . '.php', $handler);
file_put_contents($folderPath . '/' . $part . '.scss', $scss);
File::copy(__DIR__.'/data/screen.webp',$folderPath .'/screenshot.webp');
$process = new Process(['composer', 'dump-autoload']);
$process->setWorkingDirectory(base_path())->run();
$this->info(__("Theme part created successfully: [blade, js, json, scss, php, assets, screenshot]"));
return 0;
}
}

@ -0,0 +1,94 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\File;
class makeXcontroller extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'make:xcontroller {model}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'create new xController';
/**
* Execute the console command.
*/
public function handle()
{
//
$model = ucfirst($this->argument('model'));
$var = '$' . strtolower($this->argument('model'));
$plural = Str::plural($model);
// check model exists
if (!file_exists(__DIR__.'/../../Models/'.$model.'.php')){
$this->error("Model not found!");
return ;
}
// get controller content
$content = file_get_contents(__DIR__ . '/data/xcontroller.dat');
// replace variables
$content = str_replace('User', $model, $content);
$content = str_replace('users', strtolower($plural), $content);
$content = str_replace('user', strtolower($model), $content);
$content = str_replace('$user', $var, $content);
Artisan::call('make:request', ['name' => $model.'SaveRequest']);
Artisan::call('make:controller', ['name' => 'Admin/' . $model . 'Controller']);
$model_content = file_get_contents(__DIR__.'/../../Models/'.$model.'.php');
// check soft delete for restore
if (!strpos($model_content,'SoftDeletes')){
$pattern = '/\/\*\*restore\*\/(.*?)\/\*restore\*\*\//s';
$replacement = '';
$content = preg_replace($pattern, $replacement, $content);
}
file_put_contents(__DIR__.'/../../Http/Controllers/Admin/' . $model . 'Controller.php',$content);
$this->info('Admin/' . $model . 'Controller created');
$this->info( $model.'SaveRequest created');
$folderPath = resource_path('views/admin/'.strtolower($plural));
// create view folder
if (!File::exists($folderPath)) {
File::makeDirectory($folderPath);
$this->info('Folder created successfully.');
} else {
$this->info('Folder already exists.');
}
// make list blade
$model = strtolower($model);
$content = file_get_contents(__DIR__ . '/data/listblade.dat');
$content = str_replace('Users',$plural,$content);
file_put_contents($folderPath.'/'.$model.'-list.blade.php',$content);
$this->info($model.'-list.blade.php created');
// make form blade
$content = file_get_contents(__DIR__ . '/data/formblade.dat');
$content = str_replace('Users',$plural,$content);
$content = str_replace('user',strtolower($model),$content);
file_put_contents($folderPath.'/'.$model.'-form.blade.php',$content);
$this->info($model.'-form.blade.php created');
}
}

@ -0,0 +1,67 @@
<?php
namespace App\Contracts;
interface Payment
{
/**
* Register Payment Service Provider
*
* @return self
*/
public static function registerService();
/**
* Get Payment name
*
* @return string
*/
public static function getName(): string;
/**
* Get payment type must be one of: ONLINE, CHEQUE, CARD, CASH, CASH_ON_DELIVERY
*
* @return string
*/
public static function getType(): string;
/**
* Is Active To Show user
*
* @return bool
*/
public static function isActive(): bool;
/**
* Gateway Logo
*
* @return string
*/
public static function getLogo();
/**
* Request online payment
*
* @param int $amount transaction amount
* @param string $callbackUrl a url that callback user after transaction
* @param array $additionalData additional data to send back
*
* @return array request data like token and order id
* @throws \Throwable
*/
public function request(int $amount, string $callbackUrl, array $additionalData = []): array;
/**
* Redirect customer to bank payment page
*/
public function goToBank();
/**
* Verify payment
*
* @return array successful payment have two keys: reference_id , card_number
* @throws \Throwable if payment fail
*/
public function verify(): array;
}

@ -0,0 +1,40 @@
<?php
namespace App\Contracts;
interface PaymentStore
{
/**
* Store payment request
*
* @param int $orderId Payment unique order id
* @param null $token
* @param string $type One of 'ONLINE', 'CHEQUE', 'CASH', 'CARD', 'CASH_ON_DELIVERY'
*
* @return \App\Models\Payment
*/
public function storePaymentRequest($orderId,$amount, $token = null, $type = 'ONLINE',$bank=null): \App\Models\Payment;
/**
* Store success payment and update invoice status
*
* @param int $paymentId Payment unique order id
* @param string|int $referenceId Transaction reference id
* @param null $cardNumber
*
* @return \App\Models\Payment
*/
public function storeSuccessPayment($paymentId, $referenceId, $cardNumber = null): \App\Models\Payment;
/**
* Store failed payment and update invoice status
*
* @param int $orderId Payment unique order id
* @param null $message Fail reason text to store
*
* @return \App\Models\Payment
*/
public function storeFailPayment($orderId, $message = null): \App\Models\Payment;
}

@ -0,0 +1,21 @@
<?php
namespace App\Events;
use App\Models\Invoice;
use Illuminate\Queue\SerializesModels;
class InvoiceCompleted
{
use SerializesModels;
/**
* @var Invoice
*/
public $invoice;
public function __construct(Invoice $invoice)
{
$this->invoice = $invoice;
}
}

@ -0,0 +1,27 @@
<?php
namespace App\Events;
use App\Models\Invoice;
use App\Models\Payment;
use Illuminate\Queue\SerializesModels;
class InvoiceFailed
{
use SerializesModels;
/**
* @var Invoice
*/
public $invoice;
/**
* @var Payment
*/
public $payment;
public function __construct(Invoice $invoice,Payment $payment)
{
$this->invoice = $invoice;
$this->payment = $payment;
}
}

@ -0,0 +1,27 @@
<?php
namespace App\Events;
use App\Models\Invoice;
use App\Models\Payment;
use Illuminate\Queue\SerializesModels;
class InvoiceSucceed
{
use SerializesModels;
/**
* @var Invoice
*/
public $invoice;
/**
* @var Payment
*/
public $payment;
public function __construct(Invoice $invoice,Payment $payment)
{
$this->invoice = $invoice;
$this->payment = $payment;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,78 @@
<?php
namespace App\Helpers;
class PersianFaker
{
private static $mobile_prefix = ['0912', '0919', '0935', '0936', '0937', '0933', '0938', '0915'];
private static $card_prefix = ['6037', '6104', '6391', '6280', '6273', '6287', '6280', '5022'];
private static $colors = [
'red' => ['name' => 'قرمز', 'code' => '#ff0000'],
'blue' => ['name' => 'آبی', 'code' => '#0000ff'],
'green' => ['name' => 'سبز', 'code' => '#008000'],
'yellow' => ['name' => 'زرد', 'code' => '#ffff00'],
'purple' => ['name' => 'بنفش', 'code' => '#800080'],
'orange' => ['name' => 'نارنجی', 'code' => '#ffa500'],
'pink' => ['name' => 'صورتی', 'code' => '#ffc0cb'],
'white' => ['name' => 'سفید', 'code' => '#ffffff'],
'black' => ['name' => 'سیاه', 'code' => '#000000'],
'grey' => ['name' => 'خاکستری', 'code' => '#808080'],
'brown' => ['name' => 'قهوه‌ای', 'code' => '#a52a2a'],
'silver' => ['name' => 'نقره‌ای', 'code' => '#c0c0c0'],
'gold' => ['name' => 'طلایی', 'code' => '#ffd700'],
'turquoise' => ['name' => 'فیروزه ای', 'code' => '#40e0d0'],
'magenta' => ['name' => 'بنفش روشن', 'code' => '#ff00ff'],
'cyan' => ['name' => 'فیروزی', 'code' => '#00ffff'],
'maroon' => ['name' => 'آبی کمرنگ', 'code' => '#800000'],
'navy' => ['name' => 'نیرویی', 'code' => '#000080'],
'teal' => ['name' => 'نیلی', 'code' => '#008080'],
'olive' => ['name' => 'زیتونی', 'code' => '#808000'],
];
static public function mobile()
{
return self::$mobile_prefix[rand(0, count(self::$mobile_prefix) - 1)] . rand(1000000, 9999999);
}
static public function shetabCard()
{
return self::$card_prefix[rand(0, count(self::$card_prefix) - 1)] . '-'
. rand(1000, 9999) . '-' . rand(1000, 9999) . '-' . rand(1000, 9999);
}
static function validCodeMeli()
{
do {
$randomNumber = str_pad(mt_rand(1, 99999999), 8, '0', STR_PAD_LEFT);
$code = '0000' . $randomNumber;
$code = substr($code, strlen($code) - 10, 10);
if (intval(substr($code, 3, 6)) == 0) {
continue;
}
$checksum = intval(substr($code, 9, 1));
$s = 0;
for ($i = 0; $i < 9; $i++) {
$s += intval(substr($code, $i, 1)) * (10 - $i);
}
$s = $s % 11;
if (($s < 2 && $checksum == $s) || ($s >= 2 && $checksum == (11 - $s))) {
return $code;
}
} while (true);
}
static public function color(){
$colors = self::$colors;
shuffle($colors);
return $colors[0];
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,319 @@
<?php
namespace App\Helpers;
use App\Models\Visitor;
/**
* @package Helpers
* @author A1Gard <a1gard@4xmen.ir>
* @date : 3-April-2013 (14-1-1392)
* @time : 20:32
* @subpackage TVisitor
* @version 1.0
* @todo : TVisitor class for get visitor info
*/
class TVisitor {
function __construct() {
}
/**
* @todo Detect visitor OS
* @return int os number
*/
public static function DetectOSI() {
if (!isset($_SERVER['HTTP_USER_AGENT']))
return 0;
$os_list = array_values(Visitor::$osList);
foreach ($os_list as $index => $match) {
if (preg_match("/$match/i", $_SERVER['HTTP_USER_AGENT'])) {
return $index + 1;
}
}
return null;
}
/**
* @todo Detect visitor OS
* @return string OS name
*/
public static function DetectOS() {
if (!isset($_SERVER['HTTP_USER_AGENT']))
return null;
$os_list = Visitor::$osList;
foreach ($os_list as $os => $pattern) {
if (preg_match("/$pattern/i", $_SERVER['HTTP_USER_AGENT'])) {
return $os;
}
}
return null;
}
/**
* @todo Detect if visitor is using a mobile device
* @return bool
*/
public static function IsMobile() {
if (!isset($_SERVER['HTTP_USER_AGENT'])) {
return false;
}
$user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
// List of mobile devices and operating systems
$mobile_agents = [
'mobile', 'android', 'iphone', 'ipod', 'ipad', 'windows phone', 'blackberry', 'kindle', 'silk',
'opera mini', 'opera mobi', 'webos', 'symbian', 'nokia', 'samsung', 'lg', 'htc', 'mot', 'tablet',
'rim tablet', 'meego', 'netfront', 'bolt', 'fennec', 'series60', 'maemo', 'midp', 'cldc', 'up.browser',
'up.link', 'mmp', 'symbian', 'smartphone', 'wap',
];
// Check if user agent contains any mobile keywords
foreach ($mobile_agents as $agent) {
if (strpos($user_agent, $agent) !== false) {
return true;
}
}
// Check for mobile-specific headers
if (isset($_SERVER['HTTP_ACCEPT'])) {
if (strpos(strtolower($_SERVER['HTTP_ACCEPT']), 'application/vnd.wap.xhtml+xml') !== false) {
return true;
}
}
if (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE'])) {
return true;
}
// Check for Opera Mini
if (isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA'])) {
return true;
}
// Use PHP's built-in mobile detection (if available)
if (function_exists('http_negotiate_language')) {
$accept = http_negotiate_language(['wap', 'html']);
if ($accept === 'wap') {
return true;
}
}
return false;
}
/**
* @todo Get browser name only
* @return string browser name
*/
public static function DetectBrowser() {
if (!isset($_SERVER['HTTP_USER_AGENT']))
return null;
$browser_list = Visitor::$browserList;
foreach ($browser_list as $browser => $pattern) {
if (preg_match("/$pattern/i", $_SERVER['HTTP_USER_AGENT'])) {
return $browser;
}
}
return null;
}
/**
* @todo Get browser name only
* @return int browser num
*/
public static function DetectBrowserI() {
if (!isset($_SERVER['HTTP_USER_AGENT']))
return 0;
$browser_list = array_values(Visitor::$browserList);
foreach ($browser_list as $index => $pattern) {
if (preg_match("/$pattern/i", $_SERVER['HTTP_USER_AGENT'])) {
return $index + 1;
}
}
return 0;
}
/**
* @todo Find browser version
* @return string version
*/
public static function BrowserVersion() {
if (!isset($_SERVER['HTTP_USER_AGENT'])) {
return '0';
}
$ua = $_SERVER['HTTP_USER_AGENT'];
$browser = self::DetectBrowser();
$version = null;
switch ($browser) {
case 'Edge':
if (preg_match('/(Edge|Edg)\/(\d+(\.\d+)*)/', $ua, $matches)) {
$version = $matches[2];
}
break;
case 'Chrome':
if (preg_match('/Chrome\/(\d+(\.\d+)*)/', $ua, $matches)) {
$version = $matches[1];
}
break;
case 'Firefox':
if (preg_match('/Firefox\/(\d+(\.\d+)*)/', $ua, $matches)) {
$version = $matches[1];
}
break;
case 'Safari':
if (preg_match('/Version\/(\d+(\.\d+)*)/', $ua, $matches)) {
$version = $matches[1];
}
break;
case 'Opera':
if (preg_match('/(OPR|Opera)\/(\d+(\.\d+)*)/', $ua, $matches)) {
$version = $matches[2];
}
break;
case 'Internet Explorer':
if (preg_match('/MSIE (\d+(\.\d+)*)/', $ua, $matches)) {
$version = $matches[1];
} elseif (preg_match('/rv:(\d+(\.\d+)*)/', $ua, $matches)) {
$version = $matches[1]; // For IE 11
}
break;
default:
// Generic version detection for other browsers
if (preg_match('/' . preg_quote($browser, '/') . '\/(\d+(\.\d+)*)/', $ua, $matches)) {
$version = $matches[1];
}
break;
}
return $version;
}
/**
* Get searched keywords from referrer URL
* @param string $referer
* @return array|null
*/
public static function GetKeyword($referer = null) {
if ($referer === null) {
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
}
if (empty($referer)) {
return null;
}
$engines = Visitor::$engines;
$parsed_url = parse_url($referer);
$host = isset($parsed_url['host']) ? strtolower($parsed_url['host']) : '';
$query = isset($parsed_url['query']) ? $parsed_url['query'] : '';
parse_str($query, $query_params);
foreach ($engines as $engine => $params) {
if (strpos($host, $engine) !== false) {
foreach ($params as $param) {
if (isset($query_params[$param]) && !empty($query_params[$param])) {
return [
'engine' => $engine,
'keyword' => urldecode($query_params[$param])
];
}
}
}
}
return null;
}
// /**
// * @param string $class alternative class
// * return vistor os icon
// * @uses awesome font
// */
// static public function GetOSIcon($class = '') {
// // get os
// $os_int = self::DetectOSI();
//
// $win = range(1, 16);
// $linux = range(17, 19);
// $osx = array(20, 21);
// $android = array(25);
// $searchbot = array(27);
// $other = array(0, 22, 23, 24, 26);
//
// switch (true) {
// case in_array($os_int, $win):
// $icon = 'windows';
// break;
// case in_array($os_int, $linux):
// $icon = 'linux';
// break;
// case in_array($os_int, $osx):
// $icon = 'apple';
// break;
//
// case in_array($os_int, $android):
// $icon = 'android';
// break;
//
// case in_array($os_int, $searchbot):
// $icon = 'google';
// break;
// default:
// $icon = 'question';
// break;
// }
//
// $result = '<span class="fa fa-' . $icon . ' ' . $class . '" title="'
// . self::DetectOS() . '" ></span>';
//
// return $result;
// }
// /**
// * @param string $class alternative class
// * return vistor browser icon
// * @uses awesome font
// */
// static public function GetBrowerIcon($class = '') {
// // get os
// $bowser = self::DetectBrowser();
//
//
//
// $result = '<span class="fa fa-' . strtolower($bowser) . ' ' . $class . '" title="'
// . self::DetectBrowser() . '" ></span>';
// return $result;
// }
public static function getRefererDomain() {
if(isset($_SERVER['HTTP_REFERER'])) {
$referer = parse_url($_SERVER['HTTP_REFERER']);
$domain = isset($referer['host']) ? $referer['host'] : null;
return $domain;
} else {
return null;
}
}
}

@ -0,0 +1,118 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Address;
use App\Models\Customer;
use Illuminate\Http\Request;
class AddressController extends Controller
{
public function save(Address $address, Request $request)
{
$address->address = $request->input('address');
$address->lat = $request->input('lat');
$address->lng = $request->input('lng');
$address->state_id = $request->input('state_id')??null;
$address->city_id = $request->input('city_id')??null;
$address->zip = $request->input('zip');
$address->save();
return $address;
}
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request, Customer $item)
{
//
$request->validate([
'address' => ['required', 'string', 'min:10'],
'zip' => ['required', 'string', 'min:5'],
'state_id' => ['required', 'exists:states,id'],
'city_id' => ['required', 'exists:cities,id'],
'lat' => ['nullable'],
'lng' => ['nullable'],
]);
$address = new Address();
$address->customer_id = $item->id;
$address = $this->save($address, $request);
logAdmin(__METHOD__,Address::class,$address->id);
return ['OK' => true,'message' => __("Address added to :CUSTOMER",['CUSTOMER'=>$item->name]), 'list'=> $item->addresses];
}
/**
* Display the specified resource.
*/
public function show(Address $address)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Address $address)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Address $item)
{
//
$request->validate([
'address' => ['required', 'string', 'min:10'],
'zip' => ['required', 'string', 'min:5'],
'state_id' => ['required', 'exists:states,id'],
'city_id' => ['required', 'exists:cities,id'],
'lat' => ['nullable'],
'lng' => ['nullable'],
]);
$this->save($item, $request);
logAdmin(__METHOD__,Address::class,$item->id);
return ['OK' => true, "message" => __("address updated")];
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Address $item)
{
//
$add = $item->address ;
logAdmin(__METHOD__,Address::class,$item->id);
$item->delete();
return ['OK' => true, "message" => __(":ADDRESS removed",['ADDRESS' => $add])];
}
public function customer(Customer $item)
{
return $item->addresses;
}
}

@ -0,0 +1,43 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\AdminLogSaveRequest;
use App\Models\Access;
use App\Models\AdminLog;
use App\Models\User;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class AdminLogController extends XController
{
// protected $_MODEL_ = AdminLog::class;
// protected $SAVE_REQUEST = AdminLogSaveRequest::class;
protected $cols = ['action','user_id'];
protected $extra_cols = ['id','loggable_type','loggable_id'];
protected $searchable = ['action'];
protected $listView = 'admin.commons.adminlogs';
protected $buttons = [];
public function __construct()
{
parent::__construct(AdminLog::class);
}
public function log(User $item){
return redirect()->route('admin.adminlog.index',['filter[user_id]'=> '['.$item->id.']']);
}
}

@ -0,0 +1,132 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\AdvSaveRequest;
use App\Models\Access;
use App\Models\Adv;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class AdvController extends XController
{
// protected $_MODEL_ = Adv::class;
// protected $SAVE_REQUEST = AdvSaveRequest::class;
protected $cols = ['title','link'];
protected $extra_cols = ['id','image'];
protected $searchable = ['title','link'];
protected $listView = 'admin.advs.adv-list';
protected $formView = 'admin.advs.adv-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Adv::class, AdvSaveRequest::class);
}
/**
* @param $adv Adv
* @param $request AdvSaveRequest
* @return Adv
*/
public function save($adv, $request)
{
$adv->title = $request->input('title');
$adv->max_click = $request->input('max_click');
$adv->link = $request->input('link');
$adv->expire = date('Y-m-d',$request->input('expire'));
$adv->user_id = auth()->id();
$adv->status = $request->input('status');
if ($request->has('image')){
$adv->image = $this->storeFile('image',$adv, 'ad');
}
$adv->save();
return $adv;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Adv $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Adv $item)
{
return parent::delete($item);
}
public function update(Request $request, Adv $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Adv::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,256 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Area;
use App\Models\Category;
use App\Models\Group;
use App\Models\Part;
use App\Models\Post;
use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
class AreaController extends Controller
{
//
public function index()
{
$areas = Area::orderByDesc('sort')->orderBy('name')->get();
return view('admin.areas.area-list', compact('areas'));
}
public function design(Area $area)
{
$valids = [];
foreach ($area->segment as $seg) {
if (File::exists(resource_path() . '/views/segments/' . $seg)) {
$dirs = File::directories(resource_path() . '/views/segments/' . $seg);
foreach ($dirs as $dir) {
$tmp = str_replace(DIRECTORY_SEPARATOR,'/',$dir);
$temp = explode('/', $tmp);
$valids[] = [
'segment' => $temp[count($temp) - 2],
'part' => $temp[count($temp) - 1],
'data' => json_decode(file_get_contents($dir . DIRECTORY_SEPARATOR. $temp[count($temp) - 1] . '.json'), true)
];
}
}
}
return response()->view('admin.areas.area-design', compact('area', 'valids'))
->header('Cache-Control','public, max-age=31536000, immutable');
}
public function designModel(Area $area, $model, $id)
{
switch ($model) {
case 'Group':
$m = Group::whereId($id)->first();
break;
case 'Category':
$m = Category::whereId($id)->first();
break;
case 'Post':
$m = Post::whereId($id)->first();
break;
case 'Product':
$m = Product::whereId($id)->first();
break;
default:
return abort(404);
}
$valids = [];
foreach ($area->segment as $seg) {
if (File::exists(resource_path() . '/views/segments/' . $seg)) {
$dirs = File::directories(resource_path() . '/views/segments/' . $seg);
foreach ($dirs as $dir) {
$tmp = str_replace(DIRECTORY_SEPARATOR,'/',$dir);
$temp = explode('/', $tmp);
$valids[] = [
'segment' => $temp[count($temp) - 2],
'part' => $temp[count($temp) - 1],
'data' => json_decode(file_get_contents($dir . DIRECTORY_SEPARATOR. $temp[count($temp) - 1] . '.json'), true)
];
}
}
}
$parts = $area->parts;
foreach ($parts as $part) {
$part->id = null;
}
if ($m->theme == null) {
$data = ['parts' => $parts, 'use_default' => $area->use_default,'max' => 10];
} else {
$data = json_decode($m->theme, true);
}
return view('admin.areas.model-design', compact('m', 'valids', 'data', 'model'));
}
/**
* screenshot segment
* @param $segment
* @param $part
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function image($segment, $part)
{
return response()->file(resource_path() . '/views/segments/' . $segment . '/' . $part . '/screenshot.webp', ['Content-Type' => 'image/png']);
}
public function update(Request $request, Area $area)
{
// return $request->all();
foreach ($request->input('parts', []) as $i => $item) {
$data = json_decode($item);
if ($data == null) {
continue;
}
if ($data->id == null) {
// create
$part = new Part();
$part->area_id = $area->id;
$part->segment = $data->segment;
$part->part = $data->part;
$part->sort = $i;
$part->save();
} else {
$part = Part::whereId($data->id)->first();
$part->segment = $data->segment;
$part->part = $data->part;
$part->sort = $i;
$part->save();
}
}
foreach (json_decode($request->input('removed')) as $id) {
Part::where('id', $id)->first()->delete();
}
\Artisan::call('client');
logAdmin(__METHOD__, __CLASS__, $area->id);
if ($request->has('use_default')) {
$area->use_default = 1;
} else {
$area->use_default = 0;
}
$area->save();
return redirect()->back()->with(['message' => __('area :NAME of website updated', ['NAME' => $area->name])]);
}
public function updateModel(Request $request, $model, $id)
{
// return $request->all();
switch ($model) {
case 'Group':
$m = Group::whereId($id)->first();
break;
case 'Category':
$m = Category::whereId($id)->first();
break;
case 'Post':
$m = Post::whereId($id)->first();
break;
case 'Product':
$m = Product::whereId($id)->first();
break;
default:
return abort(404);
}
foreach ($request->input('parts', []) as $i => $item) {
$data = json_decode($item);
if ($data == null) {
continue;
}
if ($data->id == null) {
// create
$part = new Part();
$part->area_id = null;
$part->segment = $data->segment;
$part->part = $data->part;
$part->sort = $i;
$part->custom = $model.$m->id;
$part->save();
} else {
$part = Part::whereId($data->id)->first();
$part->segment = $data->segment;
$part->part = $data->part;
$part->sort = $i;
$part->save();
}
}
foreach (json_decode($request->input('removed')) as $id) {
Part::where('id', $id)->first()->delete();
}
\Artisan::call('client');
logAdmin(__METHOD__, __CLASS__, $m->id);
$m->theme = [
'parts' => Part::where('custom',$model.$m->id)->get(),
'use_default' => ($request->has('use_default')),
'max' => 10,
];
$m->save();
return redirect()->back()->with(['message' => __('area :NAME of website updated', ['NAME' => $model.$m->id ])]);
}
public function sort(Area $area)
{
return view('admin.areas.area-sort', compact('area'));
}
public function sortSave(Request $request)
{
foreach ($request->input('items') as $key => $v) {
$p = Part::whereId($v['id'])->first();
$p->sort = $key;
$p->save();
}
logAdmin(__METHOD__, __CLASS__, $p->area_id);
return ['OK' => true, 'message' => __("As you wished sort saved")];
}
public function build(){
$exitCode = \Artisan::call('client');
$exitCode = \Artisan::call('build');
// Get the command output from cache
$output = cache()->get('build_command_output', 'No output available');
// return response()->json([
// 'success' => $exitCode === 0,
// 'exit_code' => $exitCode,
// 'output' => $output
// ]);
logAdmin(__METHOD__, __CLASS__, null);
if ($exitCode == 0){
\Log::info($output);
return redirect()->back()->with(['message' => __('Assets build successfully')]);
}else{
\Log::error($output);
return redirect()->back()->with(['message' => __('Assets build failed')]);
}
}
public function guide(){
$areas = Area::all();
return view('admin.areas.area-guide', compact('areas'));
}
}

@ -0,0 +1,146 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\AttachmentSaveRequest;
use App\Models\Access;
use App\Models\Attachment;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class AttachmentController extends XController
{
// protected $_MODEL_ = Attachment::class;
// protected $SAVE_REQUEST = AttachmentSaveRequest::class;
protected $cols = ['title','ext','is_fillable'];
protected $extra_cols = ['slug','id'];
protected $searchable = ['title', 'subtitle', 'body'];
protected $listView = 'admin.attachments.attachment-list';
protected $formView = 'admin.attachments.attachment-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Attachment::class, AttachmentSaveRequest::class);
}
/**
* @param $attachment Attachment
* @param $request AttachmentSaveRequest
* @return Attachment
*/
public function save($attachment, $request)
{
$attachment->title = $request->input('title');
$attachment->slug = $this->getSlug($attachment,'slug','title');
$attachment->body = $request->input('body');
$attachment->subtitle = $request->input('subtitle');
$attachment->is_fillable = $request->has('is_fillable');
if ($request->has('file')){
$attachment->file = $this->storeFile('file',$attachment, 'attachments');
$attachment->size = $request->file('file')->getSize();
$attachment->ext = $request->file('file')->getClientOriginalExtension();
}
if ($request->has('attachable_id') && $request->has('attachable_id')){
$attachment->attachable_type = $request->input('attachable_type');
$attachment->attachable_id = $request->input('attachable_id');
}else{
$attachment->attachable_type = null;
$attachment->attachable_id = null;
}
$attachment->save();
return $attachment;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Attachment $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Attachment $item)
{
return parent::delete($item);
}
public function detach(Attachment $item)
{
$item->attachable_id = null;
$item->attachable_type = null;
$item->save();
logAdmin(__METHOD__,__CLASS__,$item->id);
if (request()->ajax()) {
return ['OK' => true , 'message' => __('As you wished detached successfully')];
}
return redirect()->back()
->with(['message' => __('As you wished detached successfully')]);
}
public function update(Request $request, Attachment $item)
{
return $this->bringUp($request, $item);
}
public function attaching(Request $request){
$item = new Attachment();
$item = $this->save($item, $request);
logAdmin(__METHOD__,__CLASS__,$item->id);
return ['OK' => true,'data'=> $item,'message' => __('File uploaded successfully')];
}
}

@ -0,0 +1,222 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\CategorySaveRequest;
use App\Models\Access;
use App\Models\Category;
use App\Models\Item;
use App\Models\Setting;
use Illuminate\Http\Request;
use App\Helper;
use Spatie\Image\Enums\AlignPosition;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Enums\Unit;
use Spatie\Image\Image;
use function App\Helpers\hasCreateRoute;
class CategoryController extends XController
{
// protected $_MODEL_ = Category::class;
// protected $SAVE_REQUEST = CategorySaveRequest::class;
protected $cols = ['name', 'subtitle', 'parent_id'];
protected $extra_cols = ['id', 'slug', 'image'];
protected $searchable = ['name', 'subtitle', 'description'];
protected $listView = 'admin.categories.category-list';
protected $formView = 'admin.categories.category-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Category::class, CategorySaveRequest::class);
}
/**
* @param $category Category
* @param $request CategorySaveRequest
* @return Category
*/
public function save($category, $request)
{
$category->name = $request->input('name');
$category->subtitle = $request->input('subtitle');
$category->icon = $request->input('icon');
$category->description = $request->input('description');
$category->hide = $request->has('hide');
if ($request->input('parent_id') == ''){
$category->parent_id = null;
}else{
$category->parent_id = $request->input('parent_id',null);
}
if ($request->has('canonical') && trim($request->input('canonical')) != ''){
$category->canonical = $request->input('canonical');
}
$category->slug = $this->getSlug($category);
if ($request->has('image')) {
$category->image = $this->storeFile('image', $category, 'categories');
$key = 'image';
$format = $request->file($key)->guessExtension();
if (strtolower($format) == 'png'){
$format = 'webp';
}
$i = Image::load($request->file($key)->getPathname())
->optimize()
// ->nonQueued()
->format($format);
if (getSetting('watermark2')) {
$i->watermark(public_path('upload/images/logo.png'),
AlignPosition::BottomLeft, 5, 5, Unit::Percent,
config('app.media.watermark_size'), Unit::Percent,
config('app.media.watermark_size'), Unit::Percent, Fit::Contain,
config('app.media.watermark_opacity'));
}
$i->save(storage_path() . '/app/public/categories/optimized-'. $category->$key);
}
if ($request->has('bg')) {
$category->bg = $this->storeFile('bg', $category, 'categories');
$key = 'bg';
$format = $request->file($key)->guessExtension();
if (strtolower($format) == 'png'){
$format = 'webp';
}
$i = Image::load($request->file($key)->getPathname())
->optimize()
// ->nonQueued()
->format($format);
if (getSetting('watermark2')) {
$i->watermark(public_path('upload/images/logo.png'),
AlignPosition::BottomLeft, 5, 5, Unit::Percent,
config('app.media.watermark_size'), Unit::Percent,
config('app.media.watermark_size'), Unit::Percent, Fit::Contain,
config('app.media.watermark_opacity'));
}
$i->save(storage_path() . '/app/public/categories/optimized-'. $category->$key);
}
if ($request->has('svg')){
$category->svg = $this->storeFile('svg',$category, 'categories');
}
$category->save();
return $category;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
$cats = Category::all();
return view($this->formView, compact('cats'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Category $item)
{
//
$cats = Category::all();
return view($this->formView, compact('item', 'cats'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Category $item)
{
if (Setting::where('type','CATEGORY')->where('raw',$item->id)->count() > 0){
$msg = __("You can't delete this item while using it in setting.");
return redirect()->back()->withErrors($msg);
}
if (Item::where('menuable_type',Category::class)->where('menuable_type',$item->id)->count() > 0){
$msg = __("You can't delete this item while using it in menu.");
return redirect()->back()->withErrors($msg);
}
return parent::delete($item);
}
public function update(Request $request, Category $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Category::withTrashed()->where('id', $item)->first());
}
/*restore**/
/**sort*/
public function sort()
{
$items = Category::orderBy('sort')
->get(['id', 'name', 'parent_id']);
return view('admin.commons.sort', compact('items'));
}
public function sortSave(Request $request)
{
// return $request->items;
foreach ($request->items as $key => $item) {
$i = Category::whereId($item['id'])->first();
$i->sort = $key;
$i->parent_id = $item['parentId'] ?? null;
$i->save();
}
logAdmin(__METHOD__, __CLASS__, null);
return ['OK' => true, 'message' => __("As you wished sort saved")];
}
/*sort**/
}

@ -0,0 +1,127 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\CitySaveRequest;
use App\Models\Access;
use App\Models\City;
use App\Models\State;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class CityController extends XController
{
// protected $_MODEL_ = City::class;
// protected $SAVE_REQUEST = CitySaveRequest::class;
protected $cols = ['name','state_id'];
protected $extra_cols = ['id'];
protected $searchable = [];
protected $listView = 'admin.cities.city-list';
protected $formView = 'admin.cities.city-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(City::class, CitySaveRequest::class);
}
/**
* @param $city City
* @param $request CitySaveRequest
* @return City
*/
public function save($city, $request)
{
$city->name = $request->name;
$city->state_id = $request->state_id;
$city->lat = $request->lat;
$city->lng = $request->lng;
$city->save();
return $city;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
$states = State::orderBy('name')->get(['id', 'name']);
return view($this->formView,compact('states'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(City $item)
{
//
$states = State::orderBy('name')->get(['id', 'name']);
return view($this->formView, compact('item','states'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(City $item)
{
return parent::delete($item);
}
public function update(Request $request, City $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(City::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Spatie\Image\Enums\AlignPosition;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Enums\Unit;
use Spatie\Image\Image;
use function PHPUnit\Framework\fileExists;
class CkeditorController extends Controller
{
public function upload(Request $request)
{
if ($request->hasFile('upload')) {
$key = 'upload';
$format = getSetting('optimize');
$i = Image::load($request->file($key)->getRealPath())
->optimize()
// ->sharpen(10)
// ->quality(90)
// ->nonQueued()
->format($format);
$filename = 'optimized-' . $request->file($key)->getClientOriginalName() . '_' . time() . '.webp';
if (getSetting('watermark')) {
$i->watermark(public_path('upload/images/logo.png'),
AlignPosition::BottomLeft, 5, 5, Unit::Percent,
config('app.media.watermark_size'), Unit::Percent,
config('app.media.watermark_size'), Unit::Percent, Fit::Contain,
config('app.media.watermark_opacity'));
}
$directoryPath = storage_path('app/public/upload');
if (!file_exists($directoryPath)) {
if (!mkdir($directoryPath, 0777, true) && !is_dir($directoryPath)) {
// Handle error - directory creation failed
throw new \RuntimeException(sprintf('Directory "%s" was not created', $directoryPath));
}
}
$i->save(storage_path() . '/app/public/upload/'.$filename);
// $originName = $request->file('upload')->getClientOriginalName();
// $fileName = pathinfo($originName, PATHINFO_FILENAME);
// $extension = $request->file('upload')->getClientOriginalExtension();
// $fileName = $fileName . '_' . time() . '.' . $extension;
//
// $request->file('upload')->move(public_path('upload/images'), $fileName);
$CKEditorFuncNum = $request->input('CKEditorFuncNum');
$url =\Storage::url('upload/' . $filename);
$msg = __('Image uploaded successfully');
$response = "<script>window.parent.CKEDITOR.tools.callFunction($CKEditorFuncNum, '$url', '$msg')</script>";
@header('Content-type: text/html; charset=utf-8');
echo $response;
}
}
}

@ -0,0 +1,183 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\ClipSaveRequest;
use App\Models\Access;
use App\Models\Clip;
use Illuminate\Http\Request;
use App\Helper;
use Spatie\Image\Enums\AlignPosition;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Enums\Unit;
use Spatie\Image\Image;
use function App\Helpers\hasCreateRoute;
class ClipController extends XController
{
// protected $_MODEL_ = Clip::class;
// protected $SAVE_REQUEST = ClipSaveRequest::class;
protected $cols = ['title','status'];
protected $extra_cols = ['id','slug','cover'];
protected $searchable = ['title','body'];
protected $listView = 'admin.clips.clip-list';
protected $formView = 'admin.clips.clip-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Clip::class, ClipSaveRequest::class);
}
/**
* @param $clip Clip
* @param $request ClipSaveRequest
* @return Clip
*/
public function save($clip, $request)
{
$clip->title = $request->input('title');
$clip->slug = $this->getSlug($clip,'slug','title');
$clip->body = $request->input('body');
$clip->user_id = auth()->id();
$clip->status = $request->input('status');
// if ($request->hasFile('clip')) {
// $name = $clip->slug . '.' . request()->clip->getClientOriginalExtension();
// $clip->file = $name;
// $request->file('clip')->storeAs('public/clips', $name);
// }
// if ($request->hasFile('cover')) {
// $name = $clip->slug . '.' . request()->cover->getClientOriginalExtension();
// $clip->cover = $name;
// $request->file('cover')->storeAs('public/clips', $name);
// }
if ($request->has('cover')){
$clip->cover = $this->storeFile('cover',$clip, 'clips');
$key = 'cover';
$format = $request->file($key)->guessExtension();
if (strtolower($format) == 'png'){
$format = 'webp';
}
$i = Image::load($request->file($key)->getPathname())
->optimize()
// ->nonQueued()
->format($format);
if (getSetting('watermark2')) {
$i->watermark(public_path('upload/images/logo.png'),
AlignPosition::BottomLeft, 5, 5, Unit::Percent,
config('app.media.watermark_size'), Unit::Percent,
config('app.media.watermark_size'), Unit::Percent, Fit::Contain,
config('app.media.watermark_opacity'));
}
if (!file_exists(storage_path() . '/app/public/cover')){
mkdir(storage_path() . '/app/public/cover/');
}
$i->save(storage_path() . '/app/public/cover/optimized-'. $clip->$key);
}
if ($request->has('clip')){
$clip->file = $this->storeFile('clip',$clip, 'clips');
}
$clip->save();
$tags = array_filter(explode(',,', $request->input('tags')));
if (count($tags) > 0){
$clip->syncTags($tags);
}
return $clip;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Clip $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
case 'publish':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 1]);
$msg = __(':COUNT items published successfully', ['COUNT' => count($ids)]);
break;
case 'draft':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 0]);
$msg = __(':COUNT items drafted successfully', ['COUNT' => count($ids)]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Clip $item)
{
return parent::delete($item);
}
public function update(Request $request, Clip $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Clip::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,142 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\CommentSaveRequest;
use App\Models\Access;
use App\Models\Comment;
use App\Models\User;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class CommentController extends XController
{
// protected $_MODEL_ = Comment::class;
// protected $SAVE_REQUEST = CommentSaveRequest::class;
protected $cols = ['*'];
protected $extra_cols = [];
protected $searchable = ['body','name','email','ip'];
protected $listView = 'admin.comments.comment-list';
protected $formView = 'admin.comments.comment-form';
protected $buttons = [
// 'edit' =>
// ['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
// 'destroy' =>
// ['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Comment::class, CommentSaveRequest::class);
}
/**
* @param $comment Comment
* @param $request CommentSaveRequest
* @return Comment
*/
public function save($comment, $request)
{
$comment->save();
return $comment;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Comment $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
case 'status':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => $data[1]]);
$msg = __(':COUNT items changed status successfully', ['COUNT' => count($ids)]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Comment $item)
{
return parent::delete($item);
}
public function update(Request $request, Comment $item)
{
return $this->bringUp($request, $item);
}
public function status( Comment $item, $status)
{
$item->status = $status;
$item->save();
$statuses = [
-1 => __('rejected'),
0 => __('pending'),
1 => __('approved')
];
return redirect()->back()->with(['message'=> __('Comment :STATUS',['STATUS' => $statuses[$status]]) ]);
}
public function reply(Comment $item){
return view('admin.comments.comment-reply',compact('item'));
}
public function replying(Comment $item){
$c = new Comment();
$c->ip = \request()->ip();
$c->commentator_type = User::class;
$c->commentator_id = auth()->id();
$c->commentable_type = $item->commentable_type;
$c->commentable_id = $item->commentable_id;
$c->parent_id = $item->id;
$c->status = 1;
$c->body = \request()->input('body');
$c->save();
return redirect()->route('admin.comment.index')->with(['message'=> __('Comment replay')]);
}
}

@ -0,0 +1,132 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\ContactSaveRequest;
use App\Models\Access;
use App\Models\Contact;
use Illuminate\Http\Request;
use App\Helper;
use Illuminate\Support\Facades\Mail;
use function App\Helpers\hasCreateRoute;
class ContactController extends XController
{
// protected $_MODEL_ = Contact::class;
// protected $SAVE_REQUEST = ContactSaveRequest::class;
protected $cols = ['name','subject','mobile','email',"created_at",'is_answered'];
protected $extra_cols = ['id','hash'];
protected $searchable = ['name','subject','mobile','email','body'];
protected $listView = 'admin.contacts.contact-list';
protected $formView = 'admin.contacts.contact-form';
protected $buttons = [
// 'edit' =>
// ['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Contact::class, ContactSaveRequest::class);
}
/**
* @param $contact Contact
* @param $request ContactSaveRequest
* @return Contact
*/
public function save($contact, $request)
{
$contact->save();
return $contact;
}
public function show( $hash){
$item = Contact::whereHash($hash)->firstOrFail();
return view('admin.contacts.contact-show',compact('item'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Contact $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Contact $item)
{
return parent::delete($item);
}
public function update(Request $request, Contact $item)
{
return $this->bringUp($request, $item);
}
public function reply(Request $request, Contact $item)
{
$body = $request->bodya;
$item->is_answered = true;
$item->body .= '<hr>'. __("Answer: <br>").$body;
$item->save();
Mail::raw($body, function ($message) use ($item){
$message->from(getSetting('email'),config('app.name'));
$message->to($item->email);
$message->subject('reply:',config('app.name', 'xshop') .' پاسخ تماس با ');
});
logAdmin(__METHOD__,Contact::class,$item->id);
return redirect()->back()->with(['message' => __('Your Email sent')]);
}
}

@ -0,0 +1,182 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\CustomerSaveRequest;
use App\Models\Access;
use App\Models\Credit;
use App\Models\Customer;
use Illuminate\Http\Request;
use App\Helper;
use Spatie\Image\Image;
use function App\Helpers\hasCreateRoute;
class CustomerController extends XController
{
// protected $_MODEL_ = Customer::class;
// protected $SAVE_REQUEST = CustomerSaveRequest::class;
protected $cols = ['name','mobile','email'];
protected $extra_cols = ['id'];
protected $searchable = ['name','mobile','email'];
protected $listView = 'admin.customers.customer-list';
protected $formView = 'admin.customers.customer-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Customer::class, CustomerSaveRequest::class);
}
/**
* @param $customer Customer
* @param $request CustomerSaveRequest
* @return Customer
*/
public function save($customer, $request)
{
// dd($request->all());
$customer->name = $request->input('name');
if ($customer->credit != $request->input('credit') && $customer->id != null){
$diff = $request->input('credit') - $customer->credit;
$customer->credit = $request->input('credit')??0 ;
$cr = new Credit();
$cr->customer_id = $customer->id;
$cr->amount = $diff;
$cr->data = json_encode([
'user_id' => auth()->user()->id,
'message' => __("Increase / decrease by Admin"),
]);
$cr->save();
}
if ($request->has('email')) {
$customer->email = $request->input('email');
}
$customer->mobile = $request->input('mobile');
$customer->sex = $request->input('sex');
if ($request->has('height') && trim($request->input('height')) != '') {
$customer->height = $request->input('height',null);
}
if ($request->has('weight') && trim($request->input('weight')) != '') {
$customer->weight = $request->input('weight', null);
}
$customer->description = $request->input('description');
if (trim($request->input('password')) != '') {
$customer->password = bcrypt($request->input('password'));
}
if ($request->has('dob') && $request->dob != ''){
$customer->dob = date('Y-m-d',floor($request->dob));
}else{
$customer->dob = null;
}
if ($request->hasFile('avatar')) {
$name = time() . '.' . request()->avatar->getClientOriginalExtension();
$customer->avatar = $name;
$request->file('avatar')->storeAs('public/customers', $name);
$format = $request->file('avatar')->guessExtension();
$format = 'webp';
$key = 'avatar';
$i = Image::load($request->file($key)->getPathname())
->optimize()
->width(500)
->height(500)
->crop(500, 500)
// ->nonQueued()
->format($format);
$i->save(storage_path() . '/app/public/customers/'. $customer->avatar);
}
$customer->colleague = $request->has('colleague');
$customer->save();
return $customer;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Customer $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Customer $item)
{
return parent::delete($item);
}
public function update(Request $request, Customer $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Customer::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,133 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\DiscountSaveRequest;
use App\Models\Access;
use App\Models\Discount;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class DiscountController extends XController
{
// protected $_MODEL_ = Discount::class;
// protected $SAVE_REQUEST = DiscountSaveRequest::class;
protected $cols = ['title','code','expire','product_id'];
protected $extra_cols = ['id'];
protected $searchable = ['title','code','body'];
protected $listView = 'admin.discounts.discount-list';
protected $formView = 'admin.discounts.discount-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Discount::class, DiscountSaveRequest::class);
}
/**
* @param $discount Discount
* @param $request DiscountSaveRequest
* @return Discount
*/
public function save($discount, $request)
{
if ($request->product_id != ''){
$discount->product_id = $request->product_id;
}
$discount->title = $request->title;
$discount->body = $request->body;
$discount->amount = $request->amount;
if ($request->has('expire') && $request->expire != ''){
$discount->expire = date('Y-m-d H:i:s',floor($request->expire));
}else{
$discount->expire = null;
}
$discount->code = $request->code;
$discount->type = $request->type;
$discount->save();
return $discount;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Discount $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Discount $item)
{
return parent::delete($item);
}
public function update(Request $request, Discount $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Discount::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,132 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\EvaluationSaveRequest;
use App\Models\Access;
use App\Models\Evaluation;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class EvaluationController extends XController
{
// protected $_MODEL_ = Evaluation::class;
// protected $SAVE_REQUEST = EvaluationSaveRequest::class;
protected $cols = ['title'];
protected $extra_cols = ['id'];
protected $searchable = ['title'];
protected $listView = 'admin.evaluations.evaluation-list';
protected $formView = 'admin.evaluations.evaluation-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Evaluation::class, EvaluationSaveRequest::class);
}
/**
* @param $evaluation Evaluation
* @param $request EvaluationSaveRequest
* @return Evaluation
*/
public function save($evaluation, $request)
{
$evaluation->title = $request->title;
if ($request->evaluationable_type == null || $request->evaluationable_type == '') {
$evaluation->evaluationable_type = null;
}else{
$evaluation->evaluationable_type = $request->evaluationable_type ;
}
if ($request->evaluationable_id == null || $request->evaluationable_id == '') {
$evaluation->evaluationable_id = null;
}else{
$evaluation->evaluationable_id = $request->evaluationable_id ;
}
$evaluation->save();
return $evaluation;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Evaluation $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Evaluation $item)
{
return parent::delete($item);
}
public function update(Request $request, Evaluation $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Evaluation::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,141 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\GallerySaveRequest;
use App\Models\Access;
use App\Models\Gallery;
use App\Models\Image;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class GalleryController extends XController
{
// protected $_MODEL_ = Gallery::class;
// protected $SAVE_REQUEST = GallerySaveRequest::class;
protected $cols = ['title','status'];
protected $extra_cols = ['id','slug'];
protected $searchable = ['title','description'];
protected $listView = 'admin.galleries.gallery-list';
protected $formView = 'admin.galleries.gallery-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Gallery::class, GallerySaveRequest::class);
}
/**
* @param $gallery Gallery
* @param $request GallerySaveRequest
* @return Gallery
*/
public function save($gallery, $request)
{
$gallery->title = $request->input('title');
$gallery->slug = $this->getSlug($gallery,'slug','title');
$gallery->description = $request->input('description');
$gallery->status = $request->input('status');
$gallery->user_id = auth()->id();
$gallery->save();
if ($request->hasFile('image')) {
$gallery->media()->delete();
$gallery->addMedia($request->file('image'))
->preservingOriginal() //middle method
->toMediaCollection();
}
$gallery->save();
return $gallery;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Gallery $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
case 'publish':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 1]);
$msg = __(':COUNT items published successfully', ['COUNT' => count($ids)]);
break;
case 'draft':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 0]);
$msg = __(':COUNT items drafted successfully', ['COUNT' => count($ids)]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Gallery $item)
{
return parent::delete($item);
}
public function update(Request $request, Gallery $item)
{
return $this->bringUp($request, $item);
}
public function updateTitle(Request $request){
foreach ($request->titles as $k => $title) {
$image = Image::whereId($k)->first();
$image->title = $title;
$image->save();
}
return redirect()->back()->with(['message' => __("Titles updated")]);
}
}

@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Area;
use App\Models\Gfx;
use Illuminate\Http\Request;
use mysql_xdevapi\Exception;
class GfxController extends Controller
{
//
public function index()
{
$previews = Area::whereNotNull('preview')
->pluck('preview', 'name')->toArray();
array_walk($previews, function ($value, $key) use (&$previews) {
try {
$previews[$key] = route($value);
}catch (Exception $exception){
}
});
return view('admin.commons.gfx',compact('previews'));
}
public function update(Request $request)
{
foreach ($request->input('gfx', []) as $key => $gfx) {
$g = Gfx::where('key', $key)->first();
if ($g != null) {
$g->value = $gfx;
$g->save();
}
}
logAdmin(__METHOD__, __CLASS__, null);
\Artisan::call('client');
return redirect()->back()->with(['message' => __('GFX of website updated')]);
}
}

@ -0,0 +1,211 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\GroupSaveRequest;
use App\Models\Access;
use App\Models\Group;
use App\Models\Item;
use App\Models\Setting;
use Illuminate\Http\Request;
use App\Helper;
use Spatie\Image\Enums\AlignPosition;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Enums\Unit;
use Spatie\Image\Image;
use function App\Helpers\hasCreateRoute;
class GroupController extends XController
{
// protected $_MODEL_ = Group::class;
// protected $SAVE_REQUEST = GroupSaveRequest::class;
protected $cols = ['name','subtitle','parent_id'];
protected $extra_cols = ['id','slug','image'];
protected $searchable = ['name','subtitle','description'];
protected $listView = 'admin.groups.group-list';
protected $formView = 'admin.groups.group-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Group::class, GroupSaveRequest::class);
}
/**
* @param $group Group
* @param $request GroupSaveRequest
* @return Group
*/
public function save($group, $request)
{
$group->name = $request->input('name');
$group->subtitle = $request->input('subtitle');
$group->description = $request->input('description');
$group->hide = $request->has('hide');
if ($request->input('parent_id') == ''){
$group->parent_id = null;
}else{
$group->parent_id = $request->input('parent_id',null);
}
if ($request->has('canonical') && trim($request->input('canonical')) != ''){
$group->canonical = $request->input('canonical');
}
$group->slug = $this->getSlug($group);
if ($request->has('image')){
$group->image = $this->storeFile('image',$group, 'groups');
$key = 'image';
$format = $request->file($key)->guessExtension();
if (strtolower($format) == 'png'){
$format = 'webp';
}
$i = Image::load($request->file($key)->getPathname())
->optimize()
// ->nonQueued()
->format($format);
if (getSetting('watermark2')) {
$i->watermark(public_path('upload/images/logo.png'),
AlignPosition::BottomLeft, 5, 5, Unit::Percent,
config('app.media.watermark_size'), Unit::Percent,
config('app.media.watermark_size'), Unit::Percent, Fit::Contain,
config('app.media.watermark_opacity'));
}
$i->save(storage_path() . '/app/public/groups/optimized-'. $group->$key);
}
if ($request->has('bg')){
$group->bg = $this->storeFile('bg',$group, 'groups');
$key = 'bg';
$format = $request->file($key)->guessExtension();
if (strtolower($format) == 'png'){
$format = 'webp';
}
$i = Image::load($request->file($key)->getPathname())
->optimize()
// ->nonQueued()
->format($format);
if (getSetting('watermark2')) {
$i->watermark(public_path('upload/images/logo.png'),
AlignPosition::BottomLeft, 5, 5, Unit::Percent,
config('app.media.watermark_size'), Unit::Percent,
config('app.media.watermark_size'), Unit::Percent, Fit::Contain,
config('app.media.watermark_opacity'));
}
$i->save(storage_path() . '/app/public/groups/optimized-'. $group->$key);
}
$group->save();
return $group;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
$cats = Group::all();
return view($this->formView,compact('cats'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Group $item)
{
//
$cats = Group::all();
return view($this->formView, compact('item','cats'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Group $item)
{
if (Setting::where('type','GROUP')->where('raw',$item->id)->count() > 0){
$msg = __("You can't delete this item while using it in setting.");
return redirect()->back()->withErrors($msg);
}
if (Item::where('menuable_type',Group::class)->where('menuable_type',$item->id)->count() > 0){
$msg = __("You can't delete this item while using it in menu.");
return redirect()->back()->withErrors($msg);
}
return parent::delete($item);
}
public function update(Request $request, Group $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Group::withTrashed()->where('id', $item)->first());
}
/*restore**/
/**sort*/
public function sort(){
$items = Group::orderBy('sort')
->get(['id','name','parent_id']);
return view('admin.commons.sort',compact('items'));
}
public function sortSave(Request $request){
// return $request->items;
foreach ($request->items as $key => $item){
$i = Group::whereId($item['id'])->first();
$i->sort = $key;
$i->parent_id = $item['parentId']??null;
$i->save();
}
logAdmin(__METHOD__,__CLASS__,null);
return ['OK' => true,'message' => __("As you wished sort saved")];
}
/*sort**/
}

@ -0,0 +1,108 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\GuestLogSaveRequest;
use App\Models\Access;
use App\Models\GuestLog;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class GuestLogController extends XController
{
// protected $_MODEL_ = GuestLog::class;
// protected $SAVE_REQUEST = GuestLogSaveRequest::class;
protected $cols = ['ip','action','created_at','loggable_type','loggable_id'];
protected $extra_cols = ['id'];
protected $searchable = ['action','ip'];
protected $listView = 'admin.guestlogs.guestlog-list';
protected $formView = 'admin.guestlogs.guestlog-form';
protected $buttons = [
// 'edit' =>
// ['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
// 'destroy' =>
// ['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(GuestLog::class, GuestLogSaveRequest::class);
}
/**
* @param $guestlog GuestLog
* @param $request GuestLogSaveRequest
* @return GuestLog
*/
public function save($guestlog, $request)
{
$guestlog->save();
return $guestlog;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(GuestLog $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(GuestLog $item)
{
return parent::delete($item);
}
public function update(Request $request, GuestLog $item)
{
return $this->bringUp($request, $item);
}
}

@ -0,0 +1,90 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Gallery;
use App\Models\Image;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class ImageController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request, Gallery $gallery)
{
$request->validate([
'image' => ['required']
]);
foreach ($request->file('image') as $k => $item) {
DB::transaction(function () use ($gallery, $item, $request): void {
$newimage = $gallery->images()->create([
'title' => $gallery->title . '-' . ($gallery->images()->count() + 1),
'user_id' => auth()->id(),
]);
$newimage->addMedia($item)
->toMediaCollection();
});
}
logAdmin(__METHOD__, Gallery::class, $gallery->id);
return redirect()->back()->with(['message' => __(':COUNT Images uploaded successfully', ['COUNT' => count($request->file('image'))] )]);
}
/**
* Display the specified resource.
*/
public function show(Image $image)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Image $image)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Image $image)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Image $image)
{
//
logAdmin(__METHOD__, Image::class, $image->id);
$image->delete();
return redirect()->back()->with(['message' => __('Image deleted successfully')]);
}
}

@ -0,0 +1,184 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\InvoiceSaveRequest;
use App\Models\Access;
use App\Models\Credit;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\Order;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class InvoiceController extends XController
{
// protected $_MODEL_ = Invoice::class;
// protected $SAVE_REQUEST = InvoiceSaveRequest::class;
protected $cols = ['hash', 'customer_id', 'count', 'total_price', 'status'];
protected $extra_cols = ['id'];
protected $searchable = ['desc'];
protected $listView = 'admin.invoices.invoice-list';
protected $formView = 'admin.invoices.invoice-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Invoice::class, InvoiceSaveRequest::class);
}
/**
* @param $invoice Invoice
* @param $request InvoiceSaveRequest
* @return Invoice
*/
public function save($invoice, $request)
{
if($invoice->tracking_code != $request->get('tracking_code') && strlen(trim($request->tracking_code)) == 24){
if (config('app.sms.driver') == 'Kavenegar'){
$args = [
'receptor' => $invoice->customer->mobile,
'template' => trim(getSetting('sent')),
'token' => trim($request->tracking_code)
];
}else{
$args = [
'code' => trim($request->tracking_code),
];
}
sendingSMS(getSetting('sent'),$invoice->customer->mobile,$args);
}
$invoice->transport_id = $request->input('transport_id', null);
$invoice->address_id = $request->input('address_id', null);
$invoice->tracking_code = $request->tracking_code;
$invoice->status = $request->status;
$invoice->save();
return $invoice;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Invoice $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Invoice $item)
{
return parent::delete($item);
}
public function update(Request $request, Invoice $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Invoice::withTrashed()->where('id', $item)->first());
}
public function removeOrder(Order $order)
{
$customer = Customer::whereId($order->invoice->customer_id)->first();
if ($order->price_total > 0) {
$diff = $order->price_total;
$customer->credit += $diff;
$customer->save();
$cr = new Credit();
$cr->customer_id = $customer->id;
$cr->amount = $diff;
$cr->data = json_encode([
'user_id' => auth()->user()->id,
'message' => __("Increase by Admin removed:") . ' ' . $order->product->name . __("Invoice") . ' : ' . $order->invoice->hash,
]);
$cr->save();
$order->delete();
}
return redirect()->back()->with('message', __('Order removed successfully'));
}
/*restore**/
public function show($hash){
$invoice = Invoice::where('hash', $hash)->firstOrFail();
$area = 'invoice';
$title = __("Invoice");
$subtitle = __("Invoice ID:") . ' ' . $invoice->hash;
$options = new QROptions([
'version' => 5,
'outputType' => QRCode::OUTPUT_MARKUP_SVG,
'eccLevel' => QRCode::ECC_L,
// 'imageTransparent' => true,
]);
$qr = new QRCode($options);
return view('client.invoice', compact('area', 'title', 'subtitle', 'invoice', 'qr'));
}
}

@ -0,0 +1,164 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\MenuSaveRequest;
use App\Models\Access;
use App\Models\Item;
use App\Models\Menu;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class MenuController extends XController
{
// protected $_MODEL_ = Menu::class;
// protected $SAVE_REQUEST = MenuSaveRequest::class;
protected $cols = ['name'];
protected $extra_cols = ['id'];
protected $searchable = ['name'];
protected $listView = 'admin.menus.menu-list';
protected $formView = 'admin.menus.menu-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Menu::class, MenuSaveRequest::class);
}
/**
* @param $menu Menu
* @param $request MenuSaveRequest
* @return Menu
*/
public function save($menu, $request)
{
$menu->name = $request->input('name');
if ($menu->user_id == null){
$menu->user_id = auth()->user()->id;
}
$menu->save();
$items = json_decode($request->input('items','[]'));
foreach ($items as $item) {
if ($item->id == null){
$i = new Item();
}else{
$i = Item::whereId($item->id)->first();
}
$i->user_id = auth()->user()->id;
$i->menu_id = $menu->id;
$i->meta = $item->meta??null;
$i->sort = $item->sort;
$i->parent = $item->parent;
$i->kind = $item->kind;
$i->title = $item->title;
$i->menuable_id = $item->menuable_id??null;
$i->menuable_type = $item->menuable_type??null;
$i->save();
}
Item::whereIn('id',json_decode($request->input('removed','[]')))->delete();
return $menu;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Menu $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Menu $item)
{
return parent::delete($item);
}
public function update(Request $request, Menu $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Menu::withTrashed()->where('id', $item)->first());
}
public function sort(Menu $item){
return view('admin.menus.menu-sort', compact('item'));
}
public function sortSave(Request $request){
foreach ($request->input('items') as $key => $v){
$p = Item::whereId($v['id'])->first();
$p->sort = $key;
$p->save();
}
logAdmin(__METHOD__,__CLASS__,null);
return ['OK' => true,'message' => __("As you wished sort saved")];
}
/*restore**/
}

@ -0,0 +1,199 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\PostSaveRequest;
use App\Models\Access;
use App\Models\Group;
use App\Models\Post;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class PostController extends XController
{
// protected $_MODEL_ = Post::class;
// protected $SAVE_REQUEST = PostSaveRequest::class;
protected $cols = ['title','hash','view','status'];
protected $extra_cols = ['id', 'slug'];
protected $searchable = ['title','subtitle','body'];
protected $listView = 'admin.posts.post-list';
protected $formView = 'admin.posts.post-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
'group' =>
['title' => "Edit group", 'class' => 'btn-outline-light edit-group-btn', 'icon' => 'ri-list-check-3'],
];
public function __construct()
{
parent::__construct(Post::class, PostSaveRequest::class);
}
/**
* @param $post Post
* @param $request PostSaveRequest
* @return Post
*/
public function save($post, $request)
{
$post->title = $request->input('title');
$post->slug = $this->getSlug($post,'slug','title');
$post->body = $request->input('body');
$post->subtitle = $request->input('subtitle');
$post->status = $request->input('status');
$post->group_id = $request->input('group_id');
$post->user_id = auth()->id();
$post->is_pinned = $request->has('is_pin');
$post->table_of_contents = $request->has('table_of_contents');
$post->icon = $request->input('icon');
$post->keyword = $request->input('keyword');
if ($request->has('canonical') && trim($request->input('canonical')) != ''){
$post->canonical = $request->input('canonical');
}
if ($post->hash == null) {
$post->hash = date('Ym') . str_pad(dechex(crc32($post->slug)), 8, '0', STR_PAD_LEFT);
}
$post->save();
$post->groups()->sync($request->input('cat'));
$tags = array_filter(explode(',,', $request->input('tags')));
if (count($tags) > 0){
$post->syncTags($tags);
}
if ($request->hasFile('image')) {
$post->media()->delete();
$post->addMedia($request->file('image'))
->preservingOriginal() //middle method
->toMediaCollection(); //finishing method
}
return $post;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
$cats = Group::all(['name','id','parent_id']);
return view($this->formView, compact('cats'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Post $item)
{
//
$cats = Group::all(['name','id','parent_id']);
return view($this->formView, compact('item', 'cats'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
case 'publish':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 1]);
$msg = __(':COUNT items published successfully', ['COUNT' => count($ids)]);
break;
case 'draft':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 0]);
$msg = __(':COUNT items drafted successfully', ['COUNT' => count($ids)]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Post $item)
{
return parent::delete($item);
}
public function update(Request $request, Post $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Post::withTrashed()->where('id', $item)->first());
}
/*restore**/
/**
* @param $id Post's id
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\View\View
*/
public function groupEdit($id)
{
$post = Post::find($id);
$groups = Group::all(['id', 'name', 'parent_id']);
return view('admin.posts.group-edit', compact('post', 'groups'));
}
/**
* @param Post $item
* @param Request $request
* @return array|\Illuminate\Http\RedirectResponse
*/
public function groupSave(Post $item, Request $request)
{
$item->groups()->sync($request->input('cat'));
logAdmin(__METHOD__, __CLASS__, $item->id);
if ($request->ajax()) {
return ['OK' => true, 'message' => __('Groups saved successfully')];
} else {
return redirect()->back()->with(['message' => __('Groups saved successfully')]);
}
}
}

@ -0,0 +1,244 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\ProductSaveRequest;
use App\Models\Access;
use App\Models\Category;
use App\Models\Product;
use App\Models\Quantity;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class ProductController extends XController
{
// protected $_MODEL_ = Product::class;
// protected $SAVE_REQUEST = ProductSaveRequest::class;
protected $cols = ['name', 'category_id', 'view', 'sell', 'status'];
protected $extra_cols = ['id', 'slug', 'image_index'];
protected $searchable = ['name', 'slug', 'description', 'excerpt', 'sku', 'table'];
protected $listView = 'admin.products.product-list';
protected $formView = 'admin.products.product-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
'category' =>
['title' => "Edit category", 'class' => 'btn-outline-light edit-category-btn', 'icon' => 'ri-list-check-3'],
];
public function __construct()
{
parent::__construct(Product::class, ProductSaveRequest::class);
}
/**
* @param $product Product
* @param $request ProductSaveRequest
* @return Product
*/
public function save($product, $request)
{
// dd($request->all());
$product->name = $request->input('name');
$product->slug = $this->getSlug($product, 'slug', 'name');
$product->table = $request->input('table');
$product->description = $request->input('desc');
$product->excerpt = $request->input('excerpt');
$product->keyword = $request->input('keyword');
$product->stock_status = $request->input('stock_status');
$product->price = $request->input('price', 0);
$product->buy_price = $request->input('buy_price', 0);
if (!$request->has('quantity')) {
$product->price = $request->input('price', 0);
$product->stock_quantity = $request->input('stock_quantity');
}
$product->average_rating = $request->input('average_rating', 0);
$product->average_rating = $request->input('average_rating', 0);
$product->rating_count = $request->input('rating_count', 0);
$product->on_sale = $request->input('on_sale', 1);
$product->sku = $request->input('sku', null);
$product->virtual = $request->input('virtual', false);
$product->downloadable = $request->input('downloadable', false);
$product->category_id = $request->input('category_id');
$product->image_index = $request->input('index_image', 0);
$product->user_id = auth()->id();
$product->status = $request->input('status');
$tags = array_filter(explode(',,', $request->input('tags')));
if ($request->has('canonical') && trim($request->input('canonical')) != '') {
$product->canonical = $request->input('canonical');
}
$product->save();
$product->categories()->sync($request->input('cat'));
if (count($tags) > 0) {
$product->syncTags($tags);
}
foreach ($product->getMedia() as $media) {
in_array($media->id, request('medias', [])) ?: $media->delete();
}
foreach ($request->file('image', []) as $image) {
try {
$product->addMedia($image)
->preservingOriginal() //middle method
->toMediaCollection(); //finishing method
} catch (FileDoesNotExist $e) {
} catch (FileIsTooBig $e) {
}
}
if ($request->has('meta')) {
// dd($request->input('meta'));
$product->syncMeta(json_decode($request->get('meta', '[]'), true));
}
$toRemoveQ = $product->quantities()->pluck('id')->toArray();
if ($request->has('q')) {
$qz = json_decode($request->input('q'));
foreach ($qz as $qi) {
if ($qi->id == null) {
$q = new Quantity();
} else {
$q = Quantity::whereId($qi->id)->first();
unset($toRemoveQ[array_search($q->id, $toRemoveQ)]); // remove for to remove IDz
}
$q->image = $qi->image;
$q->count = $qi->count;
$q->price = $qi->price;
$q->product_id = $product->id;
$q->data = json_encode($qi->data);
$q->save();
}
$product->quantities()->whereIn('id', $toRemoveQ)->delete();
if ($product->quantities()->count() > 0) {
$product->stock_quantity = $product->quantities()->sum('count');
$product->price = $product->quantities()->min('price');
}
$product->save();
}
return $product;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
$cats = Category::all(['id', 'name', 'parent_id']);
return view($this->formView, compact('cats'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Product $item)
{
//
$cats = Category::all(['id', 'name', 'parent_id']);
return view($this->formView, compact('item', 'cats'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
case 'publish':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 1]);
$msg = __(':COUNT items published successfully', ['COUNT' => count($ids)]);
break;
case 'draft':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 0]);
$msg = __(':COUNT items drafted successfully', ['COUNT' => count($ids)]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Product $item)
{
return parent::delete($item);
}
public function update(Request $request, Product $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Product::withTrashed()->where('id', $item)->first());
}
/*restore**/
/**
* @param $id Product's id
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\View\View
*/
public function categoryEdit($id)
{
$product = Product::find($id);
$cats = Category::all(['id', 'name', 'parent_id']);
return view('admin.products.category-edit', compact('product', 'cats'));
}
/**
* @param Product $item
* @param Request $request
* @return array|\Illuminate\Http\RedirectResponse
*/
public function categorySave(Product $item, Request $request)
{
$item->categories()->sync($request->input('cat'));
logAdmin(__METHOD__, __CLASS__, $item->id);
if ($request->ajax()) {
return ['OK' => true, 'message' => __('Categories saved successfully')];
} else {
return redirect()->back()->with(['message' => __('Categories saved successfully')]);
}
}
}

@ -0,0 +1,172 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\PropSaveRequest;
use App\Models\Access;
use App\Models\Category;
use App\Models\Product;
use App\Models\Prop;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class PropController extends XController
{
// protected $_MODEL_ = Prop::class;
// protected $SAVE_REQUEST = PropSaveRequest::class;
protected $cols = ['name','label','icon'];
protected $extra_cols = ['id'];
protected $searchable = ['name','label'];
protected $listView = 'admin.props.prop-list';
protected $formView = 'admin.props.prop-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Prop::class, PropSaveRequest::class);
}
/**
* @param $prop Prop
* @param $request PropSaveRequest
* @return Prop
*/
public function save($prop, $request)
{
// dd($request->all());
$prop->name = $request->input('name');
$prop->type = $request->input('type');
$prop->required = $request->input('required');
$prop->searchable = $request->input('searchable');
$prop->width = $request->input('width');
$prop->label = $request->input('label');
$prop->unit = $request->input('unit');
$prop->priceable = $request->has('priceable');
$prop->icon = $request->input('icon');
$data = [];
if (($request->has('options')) && $request->input('options') != null && $request->input('options') != ''){
$data = $request->input('options');
}
$prop->options = $data;
$prop->save();
$prop->categories()->sync($request->input('cat'));
return $prop;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
$cats = Category::all(['id','name','parent_id']);
return view($this->formView,compact('cats'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Prop $item)
{
//
$cats = Category::all(['id','name','parent_id']);
return view($this->formView, compact('item','cats'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Prop $item)
{
foreach (Product::whereHasMeta($item->name)->get() as $product){
$product->removeMeta($item->name);
}
return parent::delete($item);
}
private function updateName($item, $request)
{
if ($item->name != $request->input('name') && $request->input('name') != ''){
foreach (Product::whereHasMeta($item->name)->get() as $product){
$product->setMeta($request->input('name'),$product->getMeta($item->name));
$product->removeMeta($item->name);
}
}
}
public function update(Request $request, Prop $item)
{
$this->updateName($item, $request);
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Prop::withTrashed()->where('id', $item)->first());
}
/*restore**/
public function sort(){
return view('admin.props.prop-sort');
}
public function sortSave(Request $request){
foreach ($request->input('items') as $key => $v){
$p = Prop::whereId($v['id'])->first();
$p->sort = $key;
$p->save();
}
logAdmin(__METHOD__,__CLASS__,null);
return ['OK' => true,'message' => __("As you wished sort saved")];
}
}

@ -0,0 +1,118 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\QuestionSaveRequest;
use App\Models\Access;
use App\Models\Question;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class QuestionController extends XController
{
// protected $_MODEL_ = Question::class;
// protected $SAVE_REQUEST = QuestionSaveRequest::class;
protected $cols = ['body','product_id','status'];
protected $extra_cols = ['id'];
protected $searchable = ['body','answer'];
protected $listView = 'admin.questions.question-list';
protected $formView = 'admin.questions.question-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Question::class, QuestionSaveRequest::class);
}
/**
* @param $question Question
* @param $request QuestionSaveRequest
* @return Question
*/
public function save($question, $request)
{
$question->body = $request->input('body');
$question->answer = $request->input('answer');
$question->status = $request->input('status');
$question->save();
return $question;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Question $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
case 'publish':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 1]);
$msg = __(':COUNT items published successfully', ['COUNT' => count($ids)]);
break;
case 'draft':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 0]);
$msg = __(':COUNT items drafted successfully', ['COUNT' => count($ids)]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Question $item)
{
return parent::delete($item);
}
public function update(Request $request, Question $item)
{
return $this->bringUp($request, $item);
}
}

@ -0,0 +1,108 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\RateSaveRequest;
use App\Models\Access;
use App\Models\Rate;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class RateController extends XController
{
// protected $_MODEL_ = Rate::class;
// protected $SAVE_REQUEST = RateSaveRequest::class;
protected $cols = ['rateable_type', 'rateable_id', 'rater_type', 'rater_id', 'rate','evaluation_id'];
protected $extra_cols = ['id'];
protected $searchable = ['rate','rateable_type','rateable_id', 'rater_type', 'rater_id'];
protected $listView = 'admin.rates.rate-list';
protected $formView = 'admin.rates.rate-form';
protected $buttons = [
// 'edit' =>
// ['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
// 'destroy' =>
// ['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Rate::class, RateSaveRequest::class);
}
/**
* @param $rate Rate
* @param $request RateSaveRequest
* @return Rate
*/
public function save($rate, $request)
{
$rate->save();
return $rate;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Rate $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Rate $item)
{
return parent::delete($item);
}
public function update(Request $request, Rate $item)
{
return $this->bringUp($request, $item);
}
}

@ -0,0 +1,165 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\SettingSaveRequest;
use App\Models\Category;
use App\Models\Group;
use App\Models\Menu;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Spatie\Image\Image;
class SettingController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
$settings = Setting::where('active', true)
->orderBy('section')->get(); //ESH// just active setting`s show
$cats = Category::all(['id', 'name'])->toArray();
$menus = Menu::all(['id', 'name']);
$groups = Group::all(['id', 'name'])->toArray();
$catz = array_merge([['id' => 0, 'name' => __('All')]], $cats);
$groupz = array_merge([['id' => 0, 'name' => __('All')]], $groups);
return view('admin.commons.setting',
compact('settings', 'cats', 'groups', 'menus', 'catz', 'groupz'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(SettingSaveRequest $request)
{
//
$set = new Setting();
$set->title = $request->title;
$set->key = $request->key;
$set->section = $request->section;
$set->type = $request->type;
$set->size = $request->size;
$set->save();
logAdmin(__METHOD__, __CLASS__, $set->id);
return redirect()->back()->with(['message' => __('Setting added to website')]);
}
/**
* Display the specified resource.
*/
public function show(Setting $setting)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Setting $setting)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request)
{
$all = $request->all();
foreach ($all as $key => $val) {
$set = Setting::where('key', $key)->first();
if ($set == null) {
continue;
}
if ($set->type == 'PRODUCT_QUERY' || $set->type == 'POST_QUERY') {
$set->value = implode(',', $val);
$set->raw = implode(',', $val);
$set->save();
} elseif ($set != null && !$request->hasFile($key)) {
$set->value = validateSettingRequest($set, $val);
$set->raw = validateSettingRequest($set, $val);
// need to test
if (config('app.xlang.active') && config('app.xlang.main') != 'en' && (
$set->type != 'TEXT' && $set->type != 'EDITOR' && $set->type != 'LONGTEXT')) {
$set->setTranslation('value', 'en', $val);
}
$set->save();
}
}
$files = $request->allFiles();
if (isset($files['file'])) {
$format = getSetting('optimize');
foreach ($files['file'] as $index => $file) {
if (($file->guessExtension() == 'jpg' || $file->guessExtension() == 'png') && ($index != 'site_image')) {
$i = Image::load($file->getRealPath())
->optimize()
->format($format);
$file->move(public_path('upload/images/'), str_replace('_', '.', $index));//store('/images/'.,['disk' => 'public']);
$optimizedFile = public_path('upload/images/optimized-') . str_replace('_', '.', $index);
$optimizedFile = str_replace(['jpg', 'png', 'gif'], 'webp', $optimizedFile);
$i->save($optimizedFile);
} else
if ($file->guessExtension() == 'mp4' || $file->guessExtension() == 'mp3') {
$file->move(public_path('upload/media/'), str_replace('_', '.', $index));//store('/images/'.,['disk' => 'public']);
} else {
$file->move(public_path('upload/file/'), str_replace('_', '.', $index));//store('/images/'.,['disk' => 'public']);
}
}
}
if ($request->has('build')) {
Artisan::call('build');
}
logAdmin(__METHOD__, __CLASS__, null);
return redirect()->back()->with(['message' => __('Setting of website updated')]);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Setting $setting)
{
//
}
public function cacheClear()
{
$f = Setting::where('key', 'cache_number')->first();
$f->value += 1;
$f->save();
Artisan::call('cache:clear');
Artisan::call('config:clear');
Artisan::call('view:clear');
Artisan::call('route:clear');
return redirect()->back()->with(['message' => __('Cache cleared')]);
}
public function liveEdit($slug)
{
$settings = Setting::where('active', true)->where('key', 'LIKE', $slug . '%')
->orderBy('section')->get();
$cats = Category::all(['id', 'name'])->toArray();
$catz = array_merge([['id' => 0, 'name' => __('All')]], $cats);
$menus = Menu::all(['id', 'name']);
$groups = Group::all(['id', 'name'])->toArray();
$groupz = array_merge([['id' => 0, 'name' => __('All')]], $groups);
return view('admin.commons.live',
compact('settings', 'cats', 'groups', 'menus', 'catz', 'groupz'));
}
}

@ -0,0 +1,150 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\SliderSaveRequest;
use App\Models\Access;
use App\Models\Slider;
use Illuminate\Http\Request;
use App\Helper;
use Spatie\Image\Enums\AlignPosition;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Enums\Unit;
use Spatie\Image\Image;
use function App\Helpers\hasCreateRoute;
class SliderController extends XController
{
// protected $_MODEL_ = Slider::class;
// protected $SAVE_REQUEST = SliderSaveRequest::class;
protected $cols = ['body','status'];
protected $extra_cols = ['id','image'];
protected $searchable = ['body'];
protected $listView = 'admin.sliders.slider-list';
protected $formView = 'admin.sliders.slider-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Slider::class, SliderSaveRequest::class);
}
/**
* @param $slider Slider
* @param $request SliderSaveRequest
* @return Slider
*/
public function save($slider, $request)
{
$slider->body = $request->input('body', '');
$slider->status = $request->input('status');
$slider->data = $request->input('data');
$slider->user_id = auth()->id();
if ($request->hasFile('cover')) {
$name = time() . '.' . request()->cover->getClientOriginalExtension();
$slider->image = $name;
$request->file('cover')->storeAs('public/sliders', $name);
$format = $request->file('cover')->guessExtension();
if (strtolower($format) == 'png'){
$format = 'webp';
}
$key = 'cover';
$i = Image::load($request->file($key)->getPathname())
->optimize()
// ->nonQueued()
->format($format);
if (getSetting('watermark2')) {
$i->watermark(public_path('upload/images/logo.png'),
AlignPosition::BottomLeft, 5, 5, Unit::Percent,
config('app.media.watermark_size'), Unit::Percent,
config('app.media.watermark_size'), Unit::Percent, Fit::Contain,
config('app.media.watermark_opacity'));
}
$i->save(storage_path() . '/app/public/sliders/optimized-'. $slider->image);
}
if ($slider->id == null && Slider::count() > 0) {
$slider->data = Slider::first()->data;
}
$slider->save();
return $slider;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Slider $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
case 'publish':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 1]);
$msg = __(':COUNT items published successfully', ['COUNT' => count($ids)]);
break;
case 'draft':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 0]);
$msg = __(':COUNT items drafted successfully', ['COUNT' => count($ids)]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Slider $item)
{
return parent::delete($item);
}
public function update(Request $request, Slider $item)
{
return $this->bringUp($request, $item);
}
}

@ -0,0 +1,125 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\StateSaveRequest;
use App\Models\Access;
use App\Models\State;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class StateController extends XController
{
// protected $_MODEL_ = State::class;
// protected $SAVE_REQUEST = StateSaveRequest::class;
protected $cols = ['name','country'];
protected $extra_cols = ['id'];
protected $searchable = [];
protected $listView = 'admin.states.state-list';
protected $formView = 'admin.states.state-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(State::class, StateSaveRequest::class);
}
/**
* @param $state State
* @param $request StateSaveRequest
* @return State
*/
public function save($state, $request)
{
$state->name = $request->name;
$state->country = $request->country;
$state->lat = $request->lat;
$state->lng = $request->lng;
$state->save();
return $state;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(State $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(State $item)
{
return parent::delete($item);
}
public function update(Request $request, State $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(State::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,71 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\TagSaveRequest;
use Illuminate\Http\Request;
use Spatie\Tags\Tag;
class TagController extends XController
{
protected $cols = ['name', 'slug'];
protected $extra_cols = ['id'];
protected $searchable = ['name', 'slug'];
protected $listView = 'admin.tags.tag-list';
protected $buttons = [
];
public function __construct()
{
parent::__construct(Tag::class, TagSaveRequest::class);
}
protected function makeSortAndFilter()
{
if (!\request()->has('sort') || !in_array(\request('sort'), $this->cols)) {
$query = $this->_MODEL_::orderByDesc('id');
} else {
$query = $this->_MODEL_::orderBy(\request('sort'), \request('sortType', 'asc'));
}
foreach (\request()->input('filter', []) as $col => $filter) {
if (isJson($filter)) {
$vals = json_decode($filter);
if (count($vals) != 0) {
$query->whereIn($col, $vals);
}
} else {
$query->where($col, $filter);
}
}
if (mb_strlen(trim(\request()->input('q', ''))) > 0) {
$q = trim(json_encode(\request()->input('q', '')), ' "');
$q = str_replace('\\', '\\\\', $q);
foreach ($this->searchable as $col) {
$query->where(function ($query) use ($q) {
foreach ($this->searchable as $key => $col) {
if ($key === 0) {
$query->where($col, 'LIKE', '%' . $q . '%');
} else {
$query->orWhere($col, 'LIKE', '%' . $q . '%');
}
}
});
}
}
return $query;
}
}

@ -0,0 +1,143 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\TicketSaveRequest;
use App\Models\Access;
use App\Models\Ticket;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class TicketController extends XController
{
// protected $_MODEL_ = Ticket::class;
// protected $SAVE_REQUEST = TicketSaveRequest::class;
protected $cols = ['title','status','customer_id'];
protected $extra_cols = ['id'];
protected $searchable = [];
protected $listView = 'admin.tickets.ticket-list';
protected $formView = 'admin.tickets.ticket-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Ticket::class, TicketSaveRequest::class);
}
/**
* @param $ticket Ticket
* @param $request TicketSaveRequest
* @return Ticket
*/
public function save($ticket, $request)
{
$ticket->save();
return $ticket;
}
public function index()
{
$query = $this->makeSortAndFilter();
$query = $query->whereNull('parent_id');
return $this->showList($query);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Ticket $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
case 'close':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 'CLOSED']);
$msg = __(':COUNT items closed successfully', ['COUNT' => count($ids)]);
break;
case 'pending':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 'PENDING']);
$msg = __(':COUNT items pending successfully', ['COUNT' => count($ids)]);
break;
case 'answered':
$this->_MODEL_::whereIn('id', $request->input('id'))->update(['status' => 'ANSWERED']);
$msg = __(':COUNT items answered successfully', ['COUNT' => count($ids)]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Ticket $item)
{
return parent::delete($item);
}
public function update(Request $request, Ticket $item)
{
$item->answer = $request->answer;
$item->status = $request->status;
$item->user_id = auth()->id();
$item->save();
if ($request->has('answers')){
foreach ($request->answers as $id => $answer) {
Ticket::whereId($id)->update(['answer' => $answer]);
}
}
logAdmin(__METHOD__,Ticket::class,$item->id);
if ($request->ajax()) {
return ['OK' => true,
"data" => modelWithCustomAttrs($item) ,
"message" => __('As you wished updated successfully'), "id" => $item->id];
} else {
return redirect(getRoute('edit', $item->{$item->getRouteKeyName()}))
->with(['message' => __('As you wished updated successfully')]);
}
}
}

@ -0,0 +1,131 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\TransportSaveRequest;
use App\Models\Access;
use App\Models\Transport;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class TransportController extends XController
{
// protected $_MODEL_ = Transport::class;
// protected $SAVE_REQUEST = TransportSaveRequest::class;
protected $cols = ['title','price','is_default','icon'];
protected $extra_cols = ['id'];
protected $searchable = ['title','description'];
protected $listView = 'admin.transports.transport-list';
protected $formView = 'admin.transports.transport-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Transport::class, TransportSaveRequest::class);
}
/**
* @param $transport Transport
* @param $request TransportSaveRequest
* @return Transport
*/
public function save($transport, $request)
{
$transport->price = $request->price;
$transport->title = $request->title;
$transport->icon = $request->icon;
$transport->description = $request->description;
$transport->is_default = $request->has('is_default');
if ($request->has('is_default')){
Transport::where('is_default',1)->where('id','<>',$transport->id)->update([
'is_default' => 0,
]);
$transport->is_default = 1;
}
$transport->save();
return $transport;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Transport $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Transport $item)
{
return parent::delete($item);
}
public function update(Request $request, Transport $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Transport::withTrashed()->where('id', $item)->first());
}
/*restore**/
}

@ -0,0 +1,185 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\UserSaveRequest;
use App\Models\Access;
use App\Models\User;
use Illuminate\Http\Request;
use App\Helper;
use Spatie\Image\Image;
use function App\Helpers\hasCreateRoute;
class UserController extends XController
{
protected $cols = ['name', 'email', 'role', 'mobile'];
protected $searchable = ['name', 'mobile', 'email'];
protected const request = UserSaveRequest::class;
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'log' =>
['title' => "Logs", 'class' => 'btn-outline-light', 'icon' => 'ri-file-list-2-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function save($user, $request)
{
// dd($request->all());
if ($user->role == 'DEVELOPER' && !auth()->user()->hasRole('developer')) {
abort(403);
}
if (!auth()->user()->hasRole('developer') && $request->role == 'DEVELOPER') {
abort(403);
}
$user->name = $request->input('name');
if (!config('app.demo')) {
$user->email = $request->input('email');
if (trim($request->input('password')) != '') {
$user->password = bcrypt($request->input('password'));
}
}
$user->mobile = $request->input('mobile');
$user->role = $request->input('role');
$user->syncRoles($request->input('role'));
$user->save();
if ($request->has('acl')) {
$user->accesses()->delete();
foreach ($request->input('acl', []) as $route) {
$a = new Access();
$a->route = $route;
$a->user_id = $user->id;
$a->save();
$routes = explode('.', $route);
if ($routes[2] == 'store' || $routes[2] == 'update') {
$routes[2] = $routes[2] == 'store' ? 'create' : 'edit';
$a = new Access();
$a->route = implode('.', $routes);
$a->user_id = $user->id;
$a->save();
}
}
}
if ($request->hasFile('avatar')) {
$name = time() . '.' . request()->avatar->getClientOriginalExtension();
$user->avatar = $name;
$request->file('avatar')->storeAs('public/users', $name);
$format = $request->file('avatar')->guessExtension();
$format = 'webp';
$key = 'avatar';
$i = Image::load($request->file($key)->getPathname())
->optimize()
->width(500)
->height(500)
->crop(500, 500)
// ->nonQueued()
->format($format);
$i->save(storage_path() . '/app/public/users/'. $user->avatar);
$user->save();
}
return $user;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(User $item)
{
$routes = [];
foreach (\Route::getRoutes()->getRoutes() as $route) {
$action = $route->getAction();
if (array_key_exists('as', $action)) {
$routeName = explode('.', $action['as']);
if (isset($routeName[2]) && $routeName[0] == 'admin') {
if (!isset($routes[$routeName[1]])) {
$routes[$routeName[1]] = [];
if ($routeName[2] != 'edit' && $routeName[2] != 'create')
$routes[$routeName[1]][] = $routeName[2];
} else {
if ($routeName[2] != 'edit' && $routeName[2] != 'create')
$routes[$routeName[1]][] = $routeName[2];
}
}
}
}
unset($routes['home'], $routes['user'], $routes['ckeditor'], $routes['area'], $routes['lang'], $routes['gfx']);
//
return view($this->formView, compact('item', 'routes'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
case 'role':
foreach ($ids as $id) {
$user = User::where('id', $id)->first();
$user->role = $data[1];
$user->syncRoles([strtolower($data[1])]);
$user->save();
}
$msg = __(':COUNT users role changed to :NEWROLE successfully', ['COUNT' => count($ids), 'NEWROLE' => __($data[1])]);
break;
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(User $item)
{
return parent::delete($item);
}
public function update(Request $request, User $item)
{
return $this->bringUp($request, $item);
}
public function restore($item)
{
return parent::restoreing(User::withTrashed()->where('email', $item)->first());
}
}

@ -0,0 +1,330 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\XLangSaveRequest;
use App\Models\Access;
use App\Models\Attachment;
use App\Models\Category;
use App\Models\City;
use App\Models\Clip;
use App\Models\Discount;
use App\Models\Gallery;
use App\Models\Group;
use App\Models\Image;
use App\Models\Item;
use App\Models\Post;
use App\Models\Product;
use App\Models\Prop;
use App\Models\Setting;
use App\Models\Slider;
use App\Models\State;
use App\Models\Transport;
use App\Models\XLang;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use App\Helper;
use Illuminate\Support\Facades\Artisan;
use Spatie\Image\Image as SpatieImage;
use Spatie\Tags\Tag;
use function App\Helpers\hasCreateRoute;
const PREFIX_PATH = __DIR__ . '/../../../../';
class XLangController extends XController
{
public $allowedModels = [
Attachment::class,
Discount::class,
Product::class,
Category::class,
Post::class,
Group::class,
Slider::class,
Item::class,
Gallery::class,
Clip::class,
Prop::class,
Setting::class,
Image::class,
State::class,
City::class,
Transport::class,
Tag::class,
];
// protected $_MODEL_ = XLang::class;
// protected $SAVE_REQUEST = XLangSaveRequest::class;
protected $cols = ['name', 'tag', 'emoji', 'is_default'];
protected $extra_cols = ['id', 'img'];
protected $searchable = [];
protected $listView = 'admin.xlangs.xlang-list';
protected $formView = 'admin.xlangs.xlang-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
// 'show' =>
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(XLang::class, XLangSaveRequest::class);
}
/**
* @param $xlang XLang
* @param $request XLangSaveRequest
* @return XLang
*/
public function save($xlang, $request)
{
if ($xlang->id == null && $request->tag != 'en') {
define("TRANSLATE_CONFIG_PATH", PREFIX_PATH . 'config/translator.php');
define("TRANSLATE_NEW_FILE", PREFIX_PATH . 'resources/lang/' . $request->tag . '.json');
$config = file_get_contents(TRANSLATE_CONFIG_PATH);
$re = '/\'languages\' \=\> (.*)\,/m';
preg_match_all($re, $config, $matches, PREG_SET_ORDER, 0);
$oldLangs = $matches[0][1];
$newLans = json_encode(array_unique(array_merge(json_decode($oldLangs), [$request->tag])));
$newConfig = (str_replace($oldLangs, $newLans, $config));
file_put_contents(TRANSLATE_CONFIG_PATH, $newConfig);
if (!file_exists(TRANSLATE_NEW_FILE)) {
file_put_contents(TRANSLATE_NEW_FILE, '{}');
}
}
$xlang->name = $request->input('name');
$xlang->tag = $request->input('tag');
$xlang->rtl = $request->has('rtl');
$xlang->is_default = $request->has('is_default');
if ($xlang->is_default) {
Xlang::where('is_default', '1')->update([
'is_default' => 0,
]);
}
if (!$request->has('emoji')) {
$xlang->emoji = $request->input('emoji');
} else {
$xlang->emoji = getEmojiLanguagebyCode($xlang->tag);
}
if ($request->has('img')) {
$xlang->img = $this->storeFile('img', $xlang, 'langz');
$key = 'img';
$format = $request->file($key)->guessExtension();
if (strtolower($format) == 'png') {
$format = 'webp';
}
$i = SpatieImage::load($request->file($key)->getPathname())
->optimize()
// ->nonQueued()
->format($format);
$i->save(storage_path() . '/app/public/langz/optimized-' . $xlang->$key);
}
$xlang->save();
Artisan::call('translator:update');
return $xlang;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(XLang $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(XLang $item)
{
return parent::delete($item);
}
public function update(Request $request, XLang $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(XLang::withTrashed()->where('id', $item)->first());
}
/*restore**/
public function translate()
{
$langs = Xlang::all();
// return Product::where('name->en',null)->get();
return view('admin.xlangs.xlang-translates', compact('langs'));
}
public function download($tag)
{
define("TRANSLATE_FILE", PREFIX_PATH . 'resources/lang/' . $tag . '.json');
return response()->download(TRANSLATE_FILE, $tag . '.json');
}
public function upload($tag, Request $request)
{
define("TRANSLATE_FILE", PREFIX_PATH . 'resources/lang/' . $tag . '.json');
if (!$request->hasFile('json')) {
return redirect()->back();
}
$data = (file_get_contents($request->file('json')->getRealPath()));
if (json_decode($data) == null) {
return redirect()->back()->withErrors(__("Invalid json file!"));
}
file_put_contents(TRANSLATE_FILE, $data);
return redirect()->back()->with(['message' => __("Translate updated")]);
}
public function ai($tag)
{
// set_time_limit(300);
define("TRANSLATE_FILE", PREFIX_PATH . 'resources/lang/' . $tag . '.json');
$file = file_get_contents(TRANSLATE_FILE);
$url = config('app.xlang.api_url') . '/json?form=en&to=' . $tag;
$client = new Client([
'headers' => ['Content-Type' => 'application/json']
]);
$response = $client->post($url,
['body' => $file]
);
file_put_contents(TRANSLATE_FILE, $response->getBody()->getContents());
return redirect()->back()->with(['message' => __("Translated by ai xstack service :TAG", ['TAG' => $tag])]);
}
public function translateModel($id, $model)
{
if (!in_array($model, $this->allowedModels)) {
return abort(404);
}
$langs = Xlang::where('is_default', 0)->get();
$cls = $model;
$model = ($model)::where('id', $id)->firstOrFail();
// return $model;
$translates = $model->translatable;
return view('admin.xlangs.xlang-translate-model', compact('model', 'translates', 'langs', 'cls'));
}
public function translateModelSave($id, $model, Request $request)
{
if (!in_array($model, $this->allowedModels)) {
return abort(404);
}
// $langs = Xlang::where('is_default', 0)->get();
$model = ($model)::where('id', $id)->firstOrFail();
// $model = Product::whereId('id',$id)->first();
foreach ($request->input('data') as $lang => $items) {
foreach ($items as $k => $item) {
if ($item != null) {
$model->setTranslation($k, $lang, $item);
}
}
}
$model->save();
return redirect()->back()->with(['message' => __('Translate updated')]);
}
public function translateModelAi($id, $model, $tag, $field)
{
if (!in_array($model, $this->allowedModels)) {
return abort(404);
}
$langs = Xlang::where('is_default', 0)->get();
$model = ($model)::where('id', $id)->firstOrFail();
// $model = Product::whereId('id',$id)->first();
$url = config('app.xlang.api_url').'/text?form=' . config('app.xlang_main') . '&to=' . $tag;
$client = new Client([
'headers' => ['Content-Type' => 'application/x-www-form-urlencoded']
]);
$response = $client->post($url,
['form_params' => ['body' => $model->$field]],
);
// file_put_contents(TRANSLATE_FILE, $response->getBody());
if ($response->getStatusCode() != 200) {
return redirect()->back()->withErrors(__("API error!"));
}
// dd($response->getBody()->getContents());
$model->setTranslation($field, $tag, $response->getBody()->getContents());
$model->save();
return redirect()->back()->with(['message' => __('Translate updated')]);
}
}

@ -0,0 +1,76 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\CategoriesCollection;
use App\Http\Resources\CategoryResource;
use App\Http\Resources\PropCollection;
use App\Models\Category;
use App\Models\Prop;
use Illuminate\Http\Request;
/**
* @OA\Info(title="xShop API", version="1.0.0")
*/
/**
* @OA\PathItem(path="/api/v1")
*/
class CategoryController extends Controller
{
/**
* @OA\Get(
* path="/api/v1/categories",
* summary="Get list of categories",
* @OA\Response(
* response=200,
* description="A list of categories"
* )
* )
*/
public function index()
{
return success(CategoriesCollection::collection(Category::orderBy('sort', 'asc')->get()));
}
/**
* @OA\Get(
* path="/api/v1/category/{category}",
* summary="Get category",
* @OA\Parameter(
* description="Slug of one category",
* name="category",
* in="path",
* required=true,
* @OA\Schema(
* type="string"
* ),
* ),
* @OA\Parameter(
* description="sub products per page",
* name="per_page",
* in="query",
* required=false,
* @OA\Schema(
* type="integer"
* )
* ),
* @OA\Response(
* response=200,
* description="A category with datas"
* )
* )
*/
public function show(Category $category)
{
return success(new CategoryResource($category));
}
public function props( $id){
$category = Category::whereId($id)->firstOrFail();
return PropCollection::collection($category->props()->orderBy('sort')->get());
}
}

@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\GroupCollection;
use App\Http\Resources\GroupsCollection;
use App\Models\Group;
use Illuminate\Http\Request;
/**
* @OA\Info(title="xShop API", version="1.0.0")
*/
/**
* @OA\PathItem(path="/api/v1")
*/
class GroupController extends Controller
{
/**
* @OA\Get(
* path="/api/v1/groups",
* summary="Get list of groups",
* @OA\Response(
* response=200,
* description="A list of categories"
* )
* )
*/
public function index()
{
//
return success(GroupsCollection::collection(Group::orderBy('sort', 'asc')->get()));
}
/**
* @OA\Get(
* path="/api/v1/group/{group}",
* summary="Get category",
* @OA\Parameter(
* description="Slug of one group",
* name="group",
* in="path",
* required=true,
* @OA\Schema(
* type="string"
* ),
* ),
* @OA\Parameter(
* description="sub posts per page",
* name="per_page",
* in="query",
* required=false,
* @OA\Schema(
* type="integer"
* )
* ),
* @OA\Response(
* response=200,
* description="A group with datas"
* )
* )
*/
public function show(Group $group)
{
//
return success(GroupCollection::make($group));
}
}

@ -0,0 +1,35 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\AdvResource;
use App\Http\Resources\CategoryResource;
use App\Http\Resources\PostResource;
use App\Http\Resources\SliderResource;
use App\Models\Adv;
use App\Models\Category;
use App\Models\Menu;
use App\Models\Post;
use App\Models\Slider;
class HomeController extends Controller
{
public function index()
{
$data = [];
$data['menu'] = Menu::with(['items' => function ($query) {
$query->select(['id', 'title', 'menuable_id', 'menuable_type', 'kind', 'meta', 'parent', 'sort', 'user_id', 'menu_id']);
}])->first(['id', 'name']);
$data['slider'] = SliderResource::collection(Slider::take(6)->get());
$data['categories'] = CategoryResource::collection(Category::with('products')->whereNull('parent_id')->orderBy('sort')->take(8)->get());
$data['adv'] = AdvResource::collection(
Adv::query()
->where('status', true)
->whereColumn('click', '<', 'max_click')
->get()
);
$data['post'] = PostResource::collection(Post::orderByDesc('created_at')->take(8)->get());
return success($data);
}
}

@ -0,0 +1,62 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Category;
use App\Models\Clip;
use App\Models\Gallery;
use App\Models\Group;
use App\Models\Post;
use App\Models\Product;
use Illuminate\Http\Request;
class MorphController extends Controller
{
public $limit = 5;
//
public function search(Request $request)
{
if (auth()->check() ){
return abort(403);
}
$morph = $request->input('morph', Product::class);
$q = '%' . $request->input('q') . '%';
switch ($morph) {
case Product::class:
$q = Product::where('name', 'LIKE', $q)
->orWhere('description', 'LIKE', $q);
break;
case Post::class:
$q = Post::where('title', 'LIKE', $q)
->orWhere('subtitle', 'LIKE', $q)
->orWhere('body', 'LIKE', $q);
break;
case Group::class:
$q = Group::where('name', 'LIKE', $q)
->orWhere('subtitle', 'LIKE', $q)
->orWhere('description', 'LIKE', $q);
break;
case Category::class:
$q = Category::where('name', 'LIKE', $q)
->orWhere('subtitle', 'LIKE', $q)
->orWhere('description', 'LIKE', $q);
break;
case Clip::class:
$q = Clip::where('title', 'LIKE', $q)
->orWhere('body', 'LIKE', $q);
break;
case Gallery::class:
$q = Gallery::where('title', 'LIKE', $q)
->orWhere('description', 'LIKE', $q);
break;
default:
return ['OK' => false, 'error' => __("Invalid morph")];
}
return ['OK' => true, 'data' => $q->orderByDesc('updated_at')->limit($this->limit)->get()];
}
}

@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(Post $post)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Post $post)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Post $post)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Post $post)
{
//
}
}

@ -0,0 +1,133 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\ProductResource;
use App\Models\Category;
use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
/**
* @OA\Info(title="xShop API", version="1.0.0")
*/
/**
* @OA\PathItem(path="/api/v1")
*/
class ProductController extends Controller
{
/**
* @OA\Get(
* path="/api/v1/products",
* summary="Get list of products",
* @OA\Parameter(
* name="sort",
* in="query",
* required=false,
* @OA\Schema(
* type="string",
* enum={"new", "old", "most_view", "less_view", "most_buy", "less_buy","cheap","expensive"}
* )
* ),
* @OA\Parameter(
* name="category",
* in="query",
* required=false,
* @OA\Schema(
* type="string"
* )
* ),
* @OA\Parameter(
* name="search",
* in="query",
* required=false,
* @OA\Schema(
* type="string"
* )
* ),
* @OA\Parameter(
* name="min_price",
* in="query",
* required=false,
* @OA\Schema(
* type="integer"
* )
* ),
* @OA\Parameter(
* name="max_price",
* in="query",
* required=false,
* @OA\Schema(
* type="integer"
* )
* ),
* @OA\Parameter(
* name="per_page",
* in="query",
* required=false,
* @OA\Schema(
* type="integer"
* )
* ),
* @OA\Response(
* response=200,
* description="A list of products"
* )
* )
*/
public function index(Request $request)
{
$cacheKey = 'products_' . md5($request->getUri());
$data = Cache::remember($cacheKey, now()->addMinutes(env('CACHE_LIFE_TIME', 10)), function () use ($request) {
$product = Product::query();
/**
* Product Sort by keys
*/
if (isset($request['sort']) && !is_null($request['sort'])) {
if ($request['sort'] === 'new')
$product = $product->orderByDesc('created_at');
if ($request['sort'] === 'old')
$product = $product->orderBy('created_at');
if ($request['sort'] === 'most_view')
$product = $product->orderByDesc('view');
if ($request['sort'] === 'less_view')
$product = $product->orderBy('view');
if ($request['sort'] === 'most_buy')
$product = $product->orderByDesc('sell');
if ($request['sort'] === 'less_buy')
$product = $product->orderBy('sell');
if ($request['sort'] === 'cheap')
$product = $product->orderBy('price');
if ($request['sort'] === 'expensive')
$product = $product->orderByDesc('price');
}
if (isset($request['category']) && !is_null($request['category']))
$product = $product->where('category_id', Category::firstWhere('slug', $request['category'])->id);
if (isset($request['search']) && !is_null($request['search']))
$product = $product->where('name', 'LIKE', "%$request->search%");
if (isset($request['min_price']) &&
isset($request['max_price']) &&
!is_null($request['min_price']) &&
!is_null($request['max_price'])
) {
$product = $product->whereBetween('buy_price', [
intval($request->min_price),
intval($request->max_price)
]);
}
$request->merge([
'loadCategory' => true
]);
return [
'products' => ProductResource::collection($product->paginate($request->input('per_page', 20)))->resource->toArray(),
];
});
return success($data);
}
}

@ -0,0 +1,70 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\CityCollection;
use App\Http\Resources\StateCollection;
use App\Models\State;
use Illuminate\Http\Request;
class StateController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
return StateCollection::collection(State::all());
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(State $state)
{
//
return CityCollection::collection($state->cities);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(State $state)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, State $state)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(State $state)
{
//
}
}

@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Mpdf\Tag;
class TagController extends Controller
{
//
public function search($q)
{
// // Check if $q contains any non-Latin characters
// if (preg_match('/[^\x20-\x7E]/', $q)) {
// // If it contains non-Latin characters, encode it
// $q =trim( json_encode($q),' "');
// }
// Perform the search
$tags = \Spatie\Tags\Tag::where('name->' . config('app.locale'), 'like', '%' . $q . '%')->limit(10)->pluck('name');
return ['OK' => true, 'data' => $tags];
}
}

@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Visitor;
use Illuminate\Http\Request;
class VisitorController extends Controller
{
//
public function display(Request $request){
$visitor = Visitor::where('ip', $request->ip())->orderByDesc('id')->first();
if ($visitor != null){
$visitor->display = $request->input('display',null);
$visitor->save();
}
return ['OK'=>true];
}
}

@ -0,0 +1,246 @@
<?php
namespace App\Http\Controllers;
use App\Contracts\Payment;
use App\Models\Customer;
use App\Models\Discount;
use App\Models\Invoice;
use App\Models\Order;
use App\Models\Product;
use App\Models\Quantity;
use App\Models\Transport;
use Illuminate\Http\Request;
class CardController extends Controller
{
public function __construct()
{
$this->middleware(function ($request, $next) {
if (\Session::has('locate')) {
app()->setLocale(\Session::get('locate'));
}
return $next($request);
});
}
public function productCardToggle(Product $product)
{
$quantity = \request()->input('quantity', null);
if (\Cookie::has('card')) {
$cards = json_decode(\Cookie::get('card'), true);
$qs = json_decode(\Cookie::get('q'), true);
if (in_array($product->id, $cards)) {
$found = false;
foreach ($cards as $i => $card) {
if ($card == $product->id && $qs[$i] == $quantity) {
$found = true;
break;
}
}
if ($found) {
$msg = "Product removed from card";
unset($cards[$i]);
unset($qs[$i]);
} else {
$cards[] = $product->id;
$qs[] = $quantity;
$msg = "Product added to card";
}
} else {
$cards[] = $product->id;
$qs[] = $quantity;
$msg = "Product added to card";
}
$count = count($cards);
\Cookie::queue('card', json_encode($cards), 2000);
\Cookie::queue('q', json_encode($qs), 2000);
} else {
$count = 1;
$msg = "Product added to card";
\Cookie::queue('card', "[$product->id]", 2000);
\Cookie::queue('q', "[null]", 2000);
$qs = [$quantity];
$cards = [$product->id];
}
if ($count > 0 && auth('customer')->check()) {
$customer = auth('customer')->user();
$customer->card = json_encode(['cards' => $cards, 'quantities' => $qs]);
$customer->save();
}
if (\request()->ajax()) {
return success(['count' => $count], $msg);
} else {
return redirect()->back()->with(['message' => $msg]);
}
}
public function index()
{
// auth('customer')->login(Customer::first());
$area = 'card';
$title = __("Shopping card");
$subtitle = '';
return view('client.default-list', compact('area', 'title', 'subtitle'));
}
public function check(Request $request)
{
$request->validate([
'product_id' => ['required', 'array'],
'count' => ['required', 'array'],
'address_id' => ['required', 'exists:addresses,id'],
'desc' => ['nullable', 'string']
]);
$total = 0;
// return $request->all();
$invoice = new Invoice();
$invoice->customer_id = auth('customer')->user()->id;
$invoice->count = array_sum($request->count);
$invoice->address_id = $request->address_id;
$invoice->desc = $request->desc;
if ($request->has('transport_id')) {
$request->transport_id = $request->input('transport_id');
$t = Transport::find($request->input('transport_id'));
$invoice->transport_price = $t->price;
$total += $t->price;
}
if ($request->has('discount_id')) {
$request->discount_id = $request->input('discount_id');
}
$invoice->save();
foreach ($request->product_id as $i => $product) {
$order = new Order();
$order->product_id = $product;
$order->invoice_id = $invoice->id;
$order->count = $request->count[$i];
if ($request->quantity_id[$i] != '') {
$order->quantity_id = $request->quantity_id[$i];
$q = Quantity::find($request->quantity_id[$i]);
$order->price_total = $q->price * $request->count[$i];
$order->data = $q->data;
} else {
$p = Product::find($request->product_id[$i]);
$order->price_total = $p->price * $request->count[$i];
}
$total += $order->price_total;
$order->save();
}
$invoice->total_price = $total;
$invoice->save();
// clear shopping card
// self::clear();
//prepare to redirect to bank gateway
$activeGateway = config('xshop.payment.active_gateway');
/** @var Payment $gateway */
$gateway = app($activeGateway . '-gateway');
logger()->info('pay controller', ["active_gateway" => $activeGateway, "invoice" => $invoice->toArray(),]);
if ($invoice->isCompleted()) {
return redirect()->back()->with('message', __('Invoice payed.'));
}
$callbackUrl = route('pay.check', ['invoice_hash' => $invoice->hash, 'gateway' => $gateway->getName()]);
$payment = null;
try {
$response = $gateway->request((($invoice->total_price - $invoice->credit_price) * config('app.currency.factor')), $callbackUrl);
$payment = $invoice->storePaymentRequest($response['order_id'], (($invoice->total_price - $invoice->credit_price) * config('app.currency.factor')), $response['token'] ?? null, null, $gateway->getName());
session(["payment_id" => $payment->id]);
\Session::save();
return $gateway->goToBank();
} catch (\Throwable $exception) {
$invoice->status = 'FAILED';
$invoice->save();
\Log::error("Payment REQUEST exception: " . $exception->getMessage());
\Log::warning($exception->getTraceAsString());
$result = false;
$message = __('error in payment. contact admin.');
return redirect()->back()->withErrors($message);
}
}
public static function clear()
{
if (auth('customer')->check()) {
$customer = auth('customer')->user();
$customer->card = null;
$customer->save();
}
\Cookie::expire('card');
\Cookie::expire('q');
return true;
}
public function clearing()
{
self::clear();
return __("Card cleared");
}
public function discount($code)
{
$discount = Discount::where('code', trim($code))->where(function ($query) {
$query->where('expire', '>=', date('Y-m-d'))
->orWhereNull('expire');
})->first();
if ($discount == null) {
return [
'OK' => false,
'err' => __("Discount code isn't valid."),
];
} else {
if ($discount->type == 'PERCENT') {
$human = $discount->title . '( ' . $discount->amount . '%' . ' )';
} else {
$human = '- ' . $discount->title . '( ' . $discount->amount . config('app.currency.symbol') . ' )';
}
return [
'OK' => true,
'msg' => __("Discount code is valid."),
'data' => $discount,
'human' => $human,
];
}
}
public function productCompareToggle($slug)
{
$product = Product::where('slug', $slug)->firstOrFail();
if (\Cookie::has('compares')) {
$compares = json_decode(\Cookie::get('compares'), true);
if (in_array($product->id, $compares)) {
$msg = __("Product removed from compare");
unset($compares[array_search($product->id, $compares)]);
} else {
$compares[] = $product->id;
$msg = __("Product added to compare");
}
\Cookie::queue('compares', json_encode($compares), 2000);
} else {
$msg = "Product added to compare";
\Cookie::queue('compares', "[$product->id]", 2000);
}
if (\request()->ajax()) {
return success(null, $msg);
} else {
return redirect()->back()->with(['message' => $msg]);
}
}
}

@ -0,0 +1,900 @@
<?php
namespace App\Http\Controllers;
use App\Contracts\Payment;
use App\Http\Requests\ContactSubmitRequest;
use App\Mail\AuthMail;
use App\Models\Attachment;
use App\Models\Category;
use App\Models\Clip;
use App\Models\Comment;
use App\Models\Contact;
use App\Models\Customer;
use App\Models\Gallery;
use App\Models\Group;
use App\Models\Invoice;
use App\Models\Post;
use App\Models\Product;
use App\Models\Quantity;
use App\Models\Rate;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Route;
use Plank\Metable\Meta;
use Spatie\Tags\Tag;
class ClientController extends Controller
{
public function __construct()
{
$this->middleware(function ($request, $next) {
if ($request->attributes->get('set_lang') != true) {
app()->setLocale(config('app.locale'));
\Session::remove('locate');
} elseif (\Session::has('locate')) {
app()->setLocale(\Session::get('locate'));
}
return $next($request);
});
}
public $paginate = 12;
//
public function welcome()
{
$area = 'index';
$title = config('app.name');
$subtitle = getSetting('subtitle');
return view('client.welcome', compact('area', 'title', 'subtitle'));
}
public function post($slug)
{
$post = Post::where('slug', $slug)->firstOrFail();
if ($post->status = 0 && !auth()->check()) {
return abort(403);
}
$area = 'post';
$title = $post->title;
$subtitle = $post->subtitle;
$post->increment('view');
$breadcrumb = [
__('Posts') => postsUrl(),
$post->mainGroup->name => $post->mainGroup->webUrl(),
$post->title => null,
];
return view('client.post', compact('area', 'post', 'title', 'subtitle', 'breadcrumb'));
}
public function clip($slug)
{
$clip = Clip::where('slug', $slug)->firstOrFail();
if ($clip->status = 0 && !auth()->check()) {
return abort(403);
}
$area = 'clip';
$title = $clip->title;
$subtitle = '';
$breadcrumb = [
__('Video clips') => clipsUrl(),
$clip->title => null,
];
$model = $clip;
return view('client.default-list', compact('area', 'clip', 'title', 'subtitle', 'breadcrumb','model'));
}
public function gallery($slug)
{
$gallery = Gallery::where('slug', $slug)->firstOrFail();
if ($gallery->status = 0 && !auth()->check()) {
return abort(403);
}
$area = 'gallery';
$title = $gallery->title;
$subtitle = \Str::limit(strip_tags($gallery->description), 15);
$gallery->increment('view');
$breadcrumb = [
__('Galleries') => gallariesUrl(),
$gallery->title => null,
];
return view('client.gallery', compact('area', 'gallery', 'title', 'subtitle', 'breadcrumb'));
}
public function posts()
{
$area = 'posts-list';
$title = __("Posts list");
$subtitle = '';
$posts = Post::where('status', 1)
->orderByDesc('id')->paginate($this->paginate);
return view('client.default-list', compact('area', 'posts', 'title', 'subtitle'));
}
public function products()
{
$area = 'products-list';
$title = __("Products list");
$subtitle = '';
$products = Product::where('status', 1)
->orderByDesc('id')->paginate($this->paginate);
return view('client.default-list', compact('area', 'products', 'title', 'subtitle'));
}
public function galleries()
{
$area = 'galleries-list';
$title = __("Galleries list");
$subtitle = '';
$galleries = Gallery::where('status', 1)
->orderByDesc('id')->paginate($this->paginate);
return view('client.default-list', compact('area', 'galleries', 'title', 'subtitle'));
}
public function clips()
{
$area = 'clips-list';
$title = __("Video clips list");
$subtitle = '';
$clips = Clip::where('status', 1)
->orderByDesc('id')->paginate($this->paginate);
return view('client.default-list', compact('area', 'clips', 'title', 'subtitle'));
}
public function attachments()
{
$area = 'attachments-list';
$title = __("Attachments list");
$subtitle = '';
$attachs = Attachment::where('is_fillable', 1)
->orderByDesc('id')->paginate($this->paginate);
return view('client.default-list', compact('area', 'attachs', 'title', 'subtitle'));
}
public function attachment($slug)
{
$attachment = Attachment::where('slug', $slug)->firstOrFail();
$area = 'attachment';
$title = $attachment->title;
$subtitle = $attachment->subtitle;
$breadcrumb = [
__('Attachments') => attachmentsUrl(),
$attachment->title => null,
];
$model = $attachment;
return view('client.default-list', compact('area', 'attachment', 'title', 'subtitle', 'breadcrumb','model'));
}
public function tag($slug)
{
$tag = Tag::where('slug->' . config('app.locale'), 'like', $slug)->first();
$posts = Post::withAnyTags([$tag])->where('status', 1)->paginate(100);
$products = Product::withAnyTags([$tag])->where('status', 1)->paginate(100);
$clips = Clip::withAnyTags([$tag])->where('status', 1)->paginate(100);
$title = __('Tag') . ': ' . $tag->name;
$subtitle = '';
return view('client.tag', compact('tag', 'posts', 'products', 'clips', 'title', 'subtitle'));
}
public function submitComment(Request $request)
{
$request->validate([
'commentable_type' => ['required', 'string', 'min:5'],
'commentable_id' => ['required', 'integer'],
'message' => ['required', 'string', 'min:5'],
'parent_id' => ['nullable', 'integer'],
]);
$comment = new Comment();
if (!auth()->check() && !auth('customer')->check()) {
$request->validate([
'name' => ['required', 'string', 'min:2'],
'email' => ['required', 'email'],
]);
$comment->name = $request->name;
$comment->email = $request->email;
$comment->status = 0;
} else {
if (auth()->check()) {
$comment->commentator_type = User::class;
$comment->commentator_id = auth()->id();
$comment->status = 1;
} else {
$comment->commentator_type = Customer::class;
$comment->commentator_id = auth('customer')->id();
$comment->status = 0;
}
}
if ($request->input('parent_id') != '') {
$comment->parent_id = $request->input('parent_id', null);
}
$comment->body = $request->input('message');
$comment->commentable_type = $request->input('commentable_type');
$comment->commentable_id = $request->input('commentable_id');
$comment->ip = request()->ip();
$comment->save();
return redirect()->back()->with(['message' => __('Your comment has been submitted')]);
}
public function search(Request $request)
{
if (isGuestMaxAttemptTry('search', 5, 1)) {
return abort(403);
}
guestLog('search');
$q = trim($request->input('q'));
if (mb_strlen($q) < 3) {
return abort(403, __('Search word is too short'));
}
$q = '%' . $q . '%';
$posts = Post::where('status', 1)->where(function ($query) use ($q) {
$query->where('title', 'LIKE', $q)
->orWhere('subtitle', 'LIKE', $q)
->orWhere('body', 'LIKE', $q);
})->paginate(100);
$products = Product::where('status', 1)->where(function ($query) use ($q) {
$query->where('name', 'LIKE', $q)
->orWhere('excerpt', 'LIKE', $q)
->orWhere('description', 'LIKE', $q);
})->paginate(100);
$clips = Clip::where('status', 1)->where(function ($query) use ($q) {
$query->where('title', 'LIKE', $q)
->orWhere('body', 'LIKE', $q);
})->paginate(100);
$title = __('Search for') . ': ' . $request->input('q');
$subtitle = '';
$noIndex = true;
return view('client.tag', compact('posts', 'products', 'clips', 'title', 'subtitle','noIndex'));
}
public function group($slug)
{
$group = Group::where('slug', $slug)->firstOrFail();
$area = 'group';
$title = $group->name;
$subtitle = $group->subtitle;
$posts = $group->posts()->where('status', 1)->orderByDesc('id')->paginate($this->paginate);
if ($group->parent_id == null) {
$breadcrumb = [
__('Posts') => postsUrl(),
$group->name => null,
];
} else {
$breadcrumb = [
__('Posts') => postsUrl(),
$group->parent->name => $group->parent->webUrl(),
$group->name => null,
];
}
return view('client.group', compact('area', 'posts', 'title', 'subtitle', 'group', 'breadcrumb'));
}
public function product($slug)
{
$product = Product::where('slug', $slug)->firstOrFail();
if ($product->status = 0 && !auth()->check()) {
return abort(403);
}
$area = 'product';
$title = $product->name;
$subtitle = $product->excerpt; // WIP SEO
$breadcrumb = [
__('Products') => productsUrl(),
$product->category->name => $product->category->webUrl(),
];
if ($product->category->parent_id != null) {
$breadcrumb[$product->category->parent->name] = $product->category->parent->webUrl();
}
$breadcrumb[$product->name] = null;
return view('client.product', compact('area', 'product', 'title', 'subtitle', 'breadcrumb'));
}
public function category($slug, Request $request)
{
$category = Category::where('slug', $slug)->firstOrFail();
$area = 'category';
$title = $category->name;
$subtitle = $category->subtitle;
$query = $category->products()->where('status', 1);
if ($request->has('only')) {
$query->where('stock_quantity', '>', 0);
}
if ($request->has('sort') && $request->input('sort') != '') {
switch ($request->input('sort')) {
case 'oldest':
$query = $query->orderBy('id');
break;
case 'cheap':
$query = $query->where('price', '<>', 0)->orderBy('price');
break;
case 'expensive':
$query = $query->orderByDesc('price');
break;
case 'fav':
$query = $query->orderByDesc('view');
break;
case 'sale':
$query = $query->orderByDesc('sell');
break;
default:
$query = $query->orderByDesc('id');
}
} else {
$query = $query->orderByDesc('id');
}
if ($request->has('meta')) {
foreach ($category->props()->where('searchable', 1)->get() as $prop) {
if (isset($request->input('meta')[$prop->name]) && $request->input('meta')[$prop->name] != '' && $request->input('meta')[$prop->name] != '[]') {
switch ($prop->type) {
case 'checkbox':
if ($prop->priceable) {
$id = Quantity::where('count', '>', 0)
->where('data', 'LIKE', '%"' . $prop->name . '":%')
->pluck('product_id')->toArray();
$query->whereIn('id', $id);
} else {
$query->whereHasMeta($prop->name);
}
break;
case 'number':
case 'select':
case 'color':
if ($prop->priceable) {
$id = Quantity::where('count', '>', 0)
->where('data', 'LIKE', '%"' . $prop->name . '":"' . $request->meta[$prop->name] . '"%')
->pluck('product_id')->toArray();
$id = array_merge($id, $query->whereMeta($prop->name, $request->input('meta')[$prop->name])->pluck('id')->toArray());
$id = array_unique($id);
$query->whereIn('id', $id);
} else {
$query->whereMeta($prop->name, $request->input('meta')[$prop->name]);
}
break;
case 'text':
$query->whereMeta($prop->name, 'LIKE', '%' . $request->input('meta')[$prop->name] . '%');
break;
case 'multi':
case 'singlemulti':
if ($prop->priceable) {
$q = Quantity::where('count', '>', 0);
$metas = json_decode($request->meta[$prop->name], true);
$q->where(function ($query) use ($metas) {
foreach ($metas as $meta) {
$query->orWhere('data', 'LIKE', '%' . $meta . '%');
}
});
$query->whereIn('id', $q->pluck('product_id')->toArray());
} else {
$q = Meta::where('key', $prop->name)->where('metable_type', Product::class);
$metas = json_decode($request->meta[$prop->name], true);
$q->where(function ($query) use ($metas) {
foreach ($metas as $meta) {
$query->orWhere('value', 'LIKE', '%' . $meta . '%');
}
});
$query->whereIn('id', $q->pluck('metable_id')->toArray());
}
}
}
}
}
$products = $query->paginate($this->paginate);
if ($category->parent_id == null) {
$breadcrumb = [
__('Products') => productsUrl(),
$category->name => null,
];
} else {
$breadcrumb = [
__('Products') => productsUrl(),
$category->parent->name => $category->parent->webUrl(),
$category->name => null,
];
}
return view('client.category', compact('area', 'products', 'title', 'subtitle', 'category', 'breadcrumb'));
}
public function attachDl($slug)
{
$attachment = Attachment::where('slug', $slug)->firstOrFail();
$attachment->increment('downloads');
$file = (storage_path() . '/app/public/attachments/' . $attachment->file);
if (file_exists($file)) {
return response()->download($file);
}
}
public function compare()
{
$area = 'compare';
$title = __("Compare products");
$subtitle = '';
$ids = json_decode(\Cookie::get('compares'), true);
$products = Product::whereIn('id', $ids)->where('status', 1)->get();
return view('client.default-list', compact('area', 'products', 'title', 'subtitle'));
}
public function contact()
{
$area = 'contact-us';
$title = __("Contact us");
$subtitle = '';
return view('client.default-list', compact('area', 'title', 'subtitle'));
}
public function sendContact(ContactSubmitRequest $request)
{
$con = new Contact();
$con->name = $request->full_name;
$con->email = $request->email;
$con->mobile = $request->phone;
$con->subject = $request->subject;
$con->body = $request->bodya;
$con->save();
return redirect()->route('client.contact')->with(['message' => __('Your message has been successfully sent.')]);
}
public function signOut()
{
auth('customer')->logout();
return redirect()->route('client.sign-in')->with(['message' => __("Signed out successfully")]);
}
public function signIn()
{
$area = 'login';
$title = __("sign in");
$subtitle = __('Sign in as customer');
return view('client.default-list', compact('area', 'title', 'subtitle'));
}
public function signUp()
{
if (config('app.sms.sign')){
return abort(403);
}
$area = 'register';
$title = __("sign up");
$subtitle = __('Sign up as customer');
return view('client.default-list', compact('area', 'title', 'subtitle'));
}
public function signUpNow(Request $request)
{
if (config('app.sms.sign')){
return abort(403);
}
$request->validate([
'email' => ['required','email']
]);
if (isGuestMaxAttemptTry('email', 1, 5)) {
return redirect()->back()->withErrors( __('You try attempts, Try it a few minutes'));
}
guestLog('email');
$passwd = generateUniqueID(12);
Mail::to($request->input('email'))->send(new AuthMail($passwd));
$c = Customer::where('email', $request->email);
if ($c->count() > 0) {
$customer = $c->first();
$msg = __('Your account password has been changed successfully.');
}else{
$customer = new Customer();
$customer->email = $request->email;
$msg = __('Your account has been created successfully.');
}
$customer->password = bcrypt($passwd);
$customer->save();
return redirect()->back()->with(['message' => $msg . __("Please check your email to find password, Don't forget check spam/junk too, If you find our email in spam folder, Please mark it `Not spam`")]);
}
public function singInDo(Request $request)
{
$max = 3;
$request->validate([
'email' => 'required|string|email|max:255',
'password' => 'required|string|min:6',
]);
if (isGuestMaxAttemptTry('login', $max)) {
return redirect()->back()->withErrors([__('You try more than :COUNT attempts, Try it later', ["COUNT" => $max])]);
}
guestLog('login');
$customer = Customer::where('email', $request->input('email'));
if ($customer->count() == 0) {
return redirect()->back()->withErrors([__('Email or password is incorrect')]);
}
$customer = $customer->first();
if (\Hash::check($request->input('password'), $customer->password)) {
auth('customer')->login($customer);
return redirect()->route('client.profile')->with(['message' => __('Signed in successfully')]);
} else {
return redirect()->back()->withErrors([__('Email or password is incorrect'), __('If you forget your password call us')]);
}
}
public function sendSms(Request $request)
{
if (isGuestMaxAttemptTry('sms', 1, 2)) {
return [
'OK' => false,
'message' => __('You try attempts, Try it a few minutes'),
'error' => __('You try attempts, Try it a few minutes'),
];
}
guestLog('sms');
$customer = Customer::where('mobile', $request->input('tel'));
$code = rand(11111, 99999);
if (config('app.sms.driver') == 'Kavenegar') {
$args = [
'receptor' => $request->input('tel'),
'template' => trim(getSetting('sign')),
'token' => $code
];
} else {
$args = [
'code' => $code,
];
}
sendingSMS(getSetting('sign'), $request->input('tel'), $args);
Log::info('auth code: ' . $code);
if ($customer->count() == 0) {
$customer = new Customer();
$customer->mobile = $request->input('tel');
$customer->code = $code;
$customer->save();
} else {
$customer = $customer->first();
$customer->code = $code;
$customer->save();
}
// WIP send sms
return [
'OK' => true,
'message' => __('Auth code send successfully'),
];
}
public function checkAuth(Request $request)
{
$max = 3;
$request->validate([
'tel' => 'required|string|min:6',
'code' => 'required|string|min:5',
]);
if (isGuestMaxAttemptTry('login', $max)) {
return redirect()->back()->withErrors([__('You try more than :COUNT attempts, Try it later', ["COUNT" => $max])]);
}
guestLog('login');
$customer = Customer::where('mobile', $request->input('tel'))
->where('code', $request->input('code'))->first();
if ($customer == null) {
return [
'OK' => false,
'message' => __('Auth code is invalid'),
'error' => __('Auth code is invalid'),
];
}
$customer->code = null;
$customer->save();
auth('customer')->login($customer);
return [
'OK' => true,
'message' => __('You are logged in successfully'),
];
}
public function sitemap()
{
$latestGroup = \App\Models\Group::orderByDesc('updated_at')->first();
// Get the most recent Category
$latestCategory = \App\Models\Category::orderByDesc('updated_at')->first();
// Initialize a variable to hold the latest update time
$latestUpdate = null;
// Check if we have a latestGroup and latestCategory and compare their updated_at
if ($latestGroup) {
$latestUpdate = $latestGroup->updated_at;
}
if ($latestCategory) {
if (!$latestUpdate || $latestCategory->updated_at > $latestUpdate) {
$latestUpdate = $latestCategory->updated_at;
}
}
$xmlContent = '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
$xmlContent .= view('website.sitemaps.sitemap',compact('latestUpdate'))->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function sitemapGroupCategory()
{
$xmlContent = '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
$xmlContent .= view('website.sitemaps.sitemap-groups-category')->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function sitemapPosts()
{
$xmlContent = '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
$xmlContent .= view('website.sitemaps.sitemap-posts')->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function sitemapProducts()
{
$xmlContent = '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
$xmlContent .= view('website.sitemaps.sitemap-products')->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function sitemapClips()
{
$xmlContent = '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
$xmlContent .= view('website.sitemaps.sitemap-clips')->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function sitemapGalleries()
{
$xmlContent = '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
$xmlContent .= view('website.sitemaps.sitemap-gallries')->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function sitemapAttachments()
{
$xmlContent = '<?xml version="1.0" encoding="utf-8" ?>' . PHP_EOL;
$xmlContent .= view('website.sitemaps.sitemap-attachments')->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function lang(Request $request)
{
$uri = '/' . $request->path();
// Iterate through all the defined routes
$r = null;
$n = '';
foreach (Route::getRoutes() as $route) {
// Just check client routes
if (substr($route->getName(), 0, 7) != 'client.' || $route->getName() == 'client.welcome') {
continue;
}
$uri2 = str_replace('/', '\\/', $route->uri());
$uri2 = preg_replace('/\{[a-z]*\}/m', '.*', $uri2);
// Check if the route matches the given URI
if (preg_match('/' . $uri2 . '/', $uri)) {
$r = $route->action['controller'];
$n = $route->uri();
break;
}
}
if (count(explode('@', $r)) == 1) {
return abort(404);
}
$method = explode('@', $r)[1];
$segments = $request->segments();
$routes = explode('/', $n);
$args = [];
foreach ($routes as $i => $route) {
if ($route[0] == '{') {
$args[] = $segments[$i + 1];
}
}
$args[] = $request;
return $this->$method(...$args);
}
public function langIndex()
{
return $this->welcome();
}
public function pay($hash)
{
$invoice = Invoice::where('hash', $hash)->first();
// dd($invoice->created_at->timestamp , (time() - 3600));
if (!in_array($invoice->status, ['PENDING', 'CANCELED', 'FAILED']) || $invoice->created_at->timestamp < (time() - 3600)) {
return redirect()->back()->withErrors(__('This payment method is not available.'));
}
$activeGateway = config('xshop.payment.active_gateway');
/** @var Payment $gateway */
$gateway = app($activeGateway . '-gateway');
logger()->info('pay controller', ["active_gateway" => $activeGateway, "invoice" => $invoice->toArray(),]);
if ($invoice->isCompleted()) {
return redirect()->back()->with('message', __('Invoice payed.'));
}
$callbackUrl = route('pay.check', ['invoice_hash' => $invoice->hash, 'gateway' => $gateway->getName()]);
$payment = null;
try {
$response = $gateway->request((($invoice->total_price - $invoice->credit_price) * config('app.currency.factor')), $callbackUrl);
$payment = $invoice->storePaymentRequest($response['order_id'], (($invoice->total_price - $invoice->credit_price) * config('app.currency.factor')), $response['token'] ?? null, null, $gateway->getName());
session(["payment_id" => $payment->id]);
\Session::save();
return $gateway->goToBank();
} catch (\Throwable $exception) {
$invoice->status = 'FAILED';
$invoice->save();
\Log::error("Payment REQUEST exception: " . $exception->getMessage());
\Log::warning($exception->getTraceAsString());
$result = false;
$message = __('error in payment. contact admin.');
return redirect()->back()->withErrors($message);
}
}
public function rate(Request $request)
{
$request->validate([
'rate.*' => ['required', 'integer'],
'rateable_id' => ['required', 'integer'],
'rateable_type' => ['required', 'string'],
]);
// return $request->all();
if (isGuestMaxAttemptTry('rate', 5, 10)) {
return [
'OK' => false,
'message' => __('You try attempts, Try it a few minutes'),
'error' => __('You try attempts, Try it a few minutes'),
];
}
guestLog('rate');
$changed = false;
foreach ($request->rate as $k => $rt) {
$r = Rate::where('rateable_type', $request->rateable_type)
->where('rateable_id', $request->rateable_id)
->where('rater_type', Customer::class)
->where('rater_id', auth('customer')->id())
->where('evaluation_id', $k);
if ($r->count() != 0) {
$rate = $r->first();
$changed = true;
} else {
$rate = new Rate();
}
if ($rt > 0 && $rt <= 5) {
$rate->rater_type = Customer::class;
$rate->rater_id = auth('customer')->id();
$rate->rateable_type = $request->rateable_type;
$rate->rateable_id = $request->rateable_id;
$rate->evaluation_id = $k;
$rate->rate = $rt;
$rate->save();
}
}
if ($changed) {
return [
'OK' => true,
'message' => __('Your rate updated'),
];
}
return [
'OK' => true,
'message' => __('Your rate registered'),
];
}
public function postRss(){
// Fetch the latest posts from the database
$posts = Post::orderBy('created_at', 'desc')->take(10)->get(); // Adjust the number of posts as needed
$xmlContent = '<?xml version="1.0" encoding="UTF-8" ?>' . PHP_EOL;
$xmlContent .= view('website.rss.post',compact('posts'))->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function productRss(){
// Fetch the latest products from the database
$products = Product::orderBy('created_at', 'desc')->take(10)->get(); // Adjust the number of posts as needed
$xmlContent = '<?xml version="1.0" encoding="UTF-8" ?>' . PHP_EOL;
$xmlContent .= view('website.rss.product',compact('products'))->render(); // Render the view and append to XML content
// Return the XML response
return response($xmlContent, 200)
->header('Content-Type', 'text/xml');
}
public function underConstruction(){
$title = __('Under Construction') . ' - ' . config('app.name');
return view('client.under-construction', compact('title'));
}
}

@ -0,0 +1,276 @@
<?php
namespace App\Http\Controllers;
use App\Models\Address;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\Product;
use App\Models\Ticket;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use Illuminate\Http\Request;
use Illuminate\Validation\Rules\In;
use Spatie\Image\Enums\AlignPosition;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Enums\Unit;
use Spatie\Image\Image;
class CustomerController extends Controller
{
public function addressSave(Address $address, Request $request)
{
$address->address = $request->input('address');
$address->lat = $request->input('lat');
$address->lng = $request->input('lng');
$address->state_id = $request->input('state_id') ?? null;
$address->city_id = $request->input('city_id') ?? null;
$address->zip = $request->input('zip');
$address->save();
return $address;
}
//
public function __construct()
{
$this->middleware(function ($request, $next) {
if (!auth('customer')->check()) {
return redirect()->route('client.sign-in');
}
if (\Session::has('locate')) {
app()->setLocale(\Session::get('locate'));
}
return $next($request);
});
}
public function profile()
{
$area = 'customer';
$title = __("Profile");
$subtitle = 'You information';
return view('client.default-list', compact('area', 'title', 'subtitle'));
return auth('customer')->user();
}
public function save(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255'],
'mobile' => ['required', 'string', 'max:255'],
'height' => ['nullable', 'numeric'],
'weight' => ['nullable', 'numeric'],
'password' => ['nullable', 'string', 'min:8', 'confirmed'],
'sex' => ['required', 'in:MALE,FEMALE'],
'dob' => ['nullable', 'date'],
'avatar' => ['nullable', 'image', 'mimes:jpeg','max:2048'],
]);
// dd($request->all());
$customer = auth('customer')->user();
$customer->name = $request->name;
$customer->email = $request->email;
$customer->sex = $request->input('sex');
if ($request->has('height') && trim($request->input('height')) != '') {
$customer->height = $request->input('height', null);
}
if ($request->has('weight') && trim($request->input('weight')) != '') {
$customer->weight = $request->input('weight', null);
}
$customer->description = $request->input('description');
if (trim($request->input('password')) != '') {
$customer->password = bcrypt($request->input('password'));
}
if ($request->has('dob') && $request->dob != '') {
$customer->dob = date('Y-m-d', floor($request->dob));
} else {
$customer->dob = null;
}
// $customer->mobile = $request->mobile;
if ($request->has('password') && trim($request->input('password')) != '') {
$customer->password = bcrypt($request->password);
}
if ($request->hasFile('avatar')) {
$name = time() . '.' . request()->avatar->getClientOriginalExtension();
$customer->avatar = $name;
$request->file('avatar')->storeAs('public/customers', $name);
$format = $request->file('avatar')->guessExtension();
$format = 'webp';
$key = 'avatar';
$i = Image::load($request->file($key)->getPathname())
->optimize()
->width(500)
->height(500)
->crop(500, 500)
// ->nonQueued()
->format($format);
$i->save(storage_path() . '/app/public/customers/'. $customer->avatar);
}
$customer->save();
return redirect()->route('client.profile')->with('message', __('Profile updated successfully'));
}
public function invoice(Invoice $invoice)
{
if (!auth('customer')->check() || $invoice->customer_id != auth('customer')->id()) {
return redirect()->route('client.sign-in')->withErrors([__('You need to login to access this page')]);
}
$area = 'invoice';
$title = __("Invoice");
$subtitle = __("Invoice ID:") . ' ' . $invoice->hash;
$options = new QROptions([
'version' => 5,
'outputType' => QRCode::OUTPUT_MARKUP_SVG,
'eccLevel' => QRCode::ECC_L,
// 'imageTransparent' => true,
]);
$qr = new QRCode($options);
return view('client.invoice', compact('area', 'title', 'subtitle', 'invoice', 'qr'));
}
public function ProductFavToggle($slug)
{
$product = Product::where('slug', $slug)->firstOrFail();
if (!auth('customer')->check()) {
return errors([
__("You need to login first"),
], 403, __("You need to login first"));
}
if (auth('customer')->user()->favorites()->where('product_id', $product->id)->count() == 0) {
auth('customer')->user()->favorites()->attach($product->id);
$message = __('Product added to favorites');
$fav = '1';
} else {
auth('customer')->user()->favorites()->detach($product->id);
$message = __('Product removed from favorites');
$fav = '0';
}
if (\request()->ajax()) {
return success($fav, $message);
} else {
return redirect()->back()->with(['message' => $message]);
}
}
public function addresses()
{
return auth('customer')->user()->addresses;
}
public function addressUpdate(Request $request, $item)
{
$item = Address::where('id', $item)->firstOrFail();
if ($item->customer_id != auth('customer')->user()->id) {
return abort(403);
}
//
$request->validate([
'address' => ['required', 'string', 'min:10'],
'zip' => ['required', 'string', 'min:5'],
'state_id' => ['required', 'exists:states,id'],
'city_id' => ['required', 'exists:cities,id'],
'lat' => ['nullable'],
'lng' => ['nullable'],
]);
$this->addressSave($item, $request);
return ['OK' => true, "message" => __("address updated")];
}
/**
* Remove the specified resource from storage.
*/
public function addressDestroy(Address $item)
{
//
if ($item->customer_id != auth('customer')->id()) {
return abort(403);
}
$add = $item->address;
$item->delete();
return ['OK' => true, "message" => __(":ADDRESS removed", ['ADDRESS' => $add])];
}
public function addressStore(Request $request)
{
//
$request->validate([
'address' => ['required', 'string', 'min:10'],
'zip' => ['required', 'string', 'min:5'],
'state_id' => ['required', 'exists:states,id'],
'city_id' => ['required', 'exists:cities,id'],
'lat' => ['nullable'],
'lng' => ['nullable'],
]);
$address = new Address();
$address->customer_id = auth('customer')->user()->id;
$address = $this->addressSave($address, $request);
return ['OK' => true, 'message' => __("Address added successfully"), 'list' => auth('customer')->user()->addresses];
}
public function submitTicket(Request $request)
{
$request->validate([
'title' => ['required', 'string', 'max:255'],
'body' => ['required', 'string'],
]);
$ticket = new Ticket();
$ticket->title = $request->title;
$ticket->body = trim($request->body);
$ticket->customer_id = auth('customer')->user()->id;
$ticket->save();
return redirect()->route('client.profile')->with('message', __('Ticket added successfully'));
}
public function showTicket(Ticket $ticket)
{
return view('client.ticket', compact('ticket'));
}
public function ticketAnswer(Ticket $ticket, Request $request)
{
$request->validate([
'body' => ['required', 'string'],
]);
$ticket->status = "PENDING";
$ticket->save();
$nticket = new Ticket();
$nticket->parent_id = $ticket->id;
$nticket->body = trim($request->body);
$nticket->customer_id = auth('customer')->user()->id;
$nticket->save();
return redirect(route('client.profile') . '#tickets')->with('message', __('Ticket answered successfully'));
}
}

@ -2,7 +2,13 @@
namespace App\Http\Controllers;
use App\Helpers\TDate;
use App\Models\Invoice;
use App\Models\Order;
use App\Models\Visitor;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class HomeController extends Controller
{
@ -23,6 +29,65 @@ class HomeController extends Controller
*/
public function index()
{
return view('home');
// make visit date
$visits = Visitor::where('created_at', '>=', Carbon::now()->subMonth())
->groupBy('date')
->orderBy('date', 'DESC')
->get([
DB::raw('Date(created_at) as date'),
DB::raw('COUNT(*) as "count"'),
DB::raw('SUM(visit) as "visits"'),
])->toArray();
$dates = range((count($visits) - 1) * -1, 0);
$dt = new TDate();
array_walk($dates, function (&$item, $key) use ($dt) {
$x = strtotime($item . ' days');
if (config('app.locale') == 'fa') {
$item = $dt->PDate('Y/m/d', $x);
} else {
$item = date('Y-m-d', $x);
}
});
// make device data
$mobiles_count = Visitor::where('created_at', '>=', Carbon::now()->subMonth())->where('is_mobile',1)->count();
$all_visitor = Visitor::where('created_at', '>=', Carbon::now()->subMonth())->count();
// make order data
$invoices = Invoice::where('created_at', '>=', Carbon::now()->subWeek())
->groupBy('date')
->orderBy('date', 'DESC')
->get(array(
DB::raw('Date(created_at) as date'),
DB::raw('COUNT(*) as "count"'),
))->pluck('count')->toArray();
$orders = Order::where('created_at', '>=', Carbon::now()->subWeek())
->groupBy('date')
->orderBy('date', 'DESC')
->get(array(
DB::raw('Date(created_at) as date'),
DB::raw('SUM(count) as "count"'),
))->pluck('count')->toArray();
$week = range((count($invoices) - 1) * -1, 0);
array_walk($week, function (&$item, $key) use ($dt) {
$x = strtotime($item . ' days');
if (config('app.locale') == 'fa') {
$item = $dt->PDate('Y/m/d', $x);
} else {
$item = date('Y-m-d', $x);
}
});
return view('home',compact('dates', 'visits',
'all_visitor','mobiles_count','week','invoices','orders'));
}
}

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Payment;
use App\Contracts\Payment;
use App\Http\Controllers\CardController;
use App\Models\Invoice;
class GatewayVerifyController
{
/**
* @param Invoice $invoice
* @param Payment $gateway
*/
public function __invoke($invoice_hash, $gateway)
{
try {
$invoice = Invoice::whereHash($invoice_hash)->firstOrFail();
$payment = null;
$message = null;
$result = true;
$paymentId = self::getPayment($invoice);
$response = $gateway->verify();
$payment = $invoice->storeSuccessPayment($paymentId, $response['reference_id'], $response['card_number']);
session(['card'=>serialize([])]);
} catch (\Throwable $exception) {
$result = false;
$invoice->storeFailPayment($paymentId, $exception->getMessage());
$message = $exception->getMessage();
\Log::debug("Payment RESPONSE Fail For Gateway {$gateway->getName()} :" . $exception->getMessage() . " On Line {$exception->getLine()} Of File {$exception->getFile()}", ['request' => request()->all(), 'session' => request()->session()->all(), 'user' => request()->user(), 'payment_id' => $paymentId]);
\Log::warning($exception->getTraceAsString());
return redirect()->route('client.card')->withErrors(__("error in payment.").$message);
}
CardController::clear();
return redirect()->route('client.profile')->with('message' , __("payment success"));
}
/**
* @param Invoice $invoice
* @return integer
*/
public static function getPayment($invoice)
{
$paymentId = session('payment_id');
if (empty($paymentId)) {
$paymentId = $invoice->payments->last()->id;
}
return $paymentId;
}
}

@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers;
use App\Models\Setting;
use Illuminate\Http\Request;
class ThemeController extends Controller
{
//
public function cssVariables()
{
$response = 'main{';
if (langIsRTL(app()->getLocale())) {
$response .= 'font-feature-settings: "ss01";';
}
foreach (Setting::where('section', 'Theme')->whereNotNull('data')
->get(['value', 'data']) as $color) {
$data = json_decode($color->data);
if (isset($data->name)) {
$response .= '--' . $data->name . ':' . $color->value;
if (isset($data->suffix)) {
$response .= $data->suffix;
}
$response .= ';';
}
}
$response .= '}';
if (langIsRTL(app()->getLocale())) {
$response .= ' .slider-content, .tns-outer .item{ direction: rtl; }';
}
if (langIsRTL(app()->getLocale())) {
$response .= ' .main-dir{ direction: rtl; }';
}else{
$response .= ' .main-dir{ direction: ltr; }';
}
return response($response)->header('Content-Type', 'text/css; charset=utf-8');
}
}

@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class VisitorController extends Controller
{
//
}

@ -0,0 +1,275 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\UserSaveRequest;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
abstract class XController extends Controller
{
protected $_MODEL_ = User::class;
protected $SAVE_REQUEST = UserSaveRequest::class;
protected $cols = [];
protected $extra_cols = ['id'];
protected $listView = 'admin.users.user-list';
protected $formView = 'admin.users.user-form';
protected $searchable = [];
public function __construct($model = null, $request = null)
{
if ($model != null) {
$this->_MODEL_ = $model;
}
if ($request != null) {
$this->SAVE_REQUEST = $request;
}
}
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-edit-2-line'],
];
public function save($item, $request)
{
}
protected function showList($query)
{
if (hasRoute('trashed')) {
$this->extra_cols[] = 'deleted_at';
}
$items = $query->paginate(config('app.panel.page_count'),
array_merge($this->extra_cols, $this->cols));
$cols = $this->cols;
$buttons = $this->buttons;
return view($this->listView, compact('items', 'cols', 'buttons'));
}
protected function makeSortAndFilter()
{
if (!\request()->has('sort') || !in_array(\request('sort'), $this->cols)) {
$query = $this->_MODEL_::orderByDesc('id');
} else {
$query = $this->_MODEL_::orderBy(\request('sort'), \request('sortType', 'asc'));
}
foreach (\request()->input('filter', []) as $col => $filter) {
if (isJson($filter)) {
$vals = json_decode($filter);
if (count($vals) != 0) {
$query->whereIn($col, $vals);
}
} else {
$query->where($col, $filter);
}
}
if (mb_strlen(trim(\request()->input('q', ''))) > 0) {
foreach ($this->searchable as $col) {
$query->where(function ($query) {
foreach ($this->searchable as $key => $col) {
if ($key === 0) {
$query->where($col, 'LIKE', '%' . \request()->input('q', '') . '%');
} else {
$query->orWhere($col, 'LIKE', '%' . \request()->input('q', '') . '%');
}
}
});
}
}
return $query;
}
/**
* Display a listing of the resource.
*/
public function index()
{
$query = $this->makeSortAndFilter();
return $this->showList($query);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
$validatedRequest = app()->make($this->SAVE_REQUEST)->merge($request->all());
$item = new ($this->_MODEL_)();
$item = $this->save($item, $request);
logAdmin(__METHOD__, $this->_MODEL_, $item->id);
if ($request->ajax()) {
return ['OK' => true, "message" => __('As you wished created successfully'),
"id" => $item->id,
"data" => modelWithCustomAttrs($item) ,
'url' => getRoute('edit', $item->{$item->getRouteKeyName()})];
} else {
return redirect(getRoute('edit', $item->{$item->getRouteKeyName()}))
->with(['message' => __('As you wished created successfully')]);
}
}
/**
* Display the specified resource.
*/
public function show($item)
{
$x = new $this->_MODEL_();
$m = $this->_MODEL_::where($x->getRouteKeyName(), $item)->first();
//
if (method_exists($m,'webUrl')){
return redirect($m->webUrl());
}
}
/**
* Update the specified resource in storage.
*/
public function bringUp(Request $request, $item)
{
//
$validatedRequest = app()->make($this->SAVE_REQUEST)->merge($request->all());
$item = $this->save($item, $request);
logAdmin(__METHOD__, $this->_MODEL_, $item->id);
if ($request->ajax()) {
return ['OK' => true,
"data" => modelWithCustomAttrs($item) ,
"message" => __('As you wished updated successfully'), "id" => $item->id];
} else {
return redirect(getRoute('edit', $item->{$item->getRouteKeyName()}))
->with(['message' => __('As you wished updated successfully')]);
}
}
/**
* Remove the specified resource from storage.
*/
public function delete($item)
{
//
logAdmin(__METHOD__, $this->_MODEL_, $item->id);
$item->delete();
return redirect()->back()->with(['message' => __('As you wished removed successfully')]);
}
/**
* restore removed the specified resource from storage.
*/
public function restoreing($item)
{
//
logAdmin(__METHOD__, $this->_MODEL_, $item->id);
$item->restore();
return redirect()->back()->with(['message' => __('As you wished restored successfully')]);
}
/**
* Show list of trashed
*/
public function trashed()
{
$query = $this->makeSortAndFilter()->onlyTrashed();
return $this->showList($query);
}
/**
* do bulk actions
* @param $msg
* @param $action
* @param $ids
* @return \Illuminate\Http\RedirectResponse
*/
protected function do_bulk($msg, $action, $ids)
{
logAdminBatch(__METHOD__ . '.' . $action, $this->_MODEL_, $ids);
return redirect()->back()->with(['message' => $msg]);
}
/**
* @param $key request key as column's name
* @param $model Model
* @param $folder string save directory name
* @return string|null
*/
public function storeFile($key, $model, $folder)
{
if (\request()->hasFile($key)) {
$name = time() . '-' . request()->file($key)->getClientOriginalName() ;
request()->file($key)->storeAs('public/' . $folder, $name);
return $name;
}
return null;
}
/**
* @param $model Model
* @param $key string key of slug request
* @param $name base slug col
* @return void
*/
public function getSlug($model, $key = 'slug', $name = 'name')
{
if (!\request()->has('slug') || request()->input('slug') == null) {
$slug = sluger($model->$name);
} else {
$slug = sluger(\request()->input($key, $model->$name));
}
return $this->createUniqueSlug($slug,$model->id);
}
/**
* create unique slug
* @param $slug
* @param $id integer|null
* @return mixed|string
*/
public function createUniqueSlug($slug,$id = null)
{
$originalSlug = $slug;
$counter = 1;
$q = $this->_MODEL_::where('slug', $slug);
if ($id != null){
$q = $q->where('id','<>',$id);
}
while ($q->count() > 0) {
$slug = $originalSlug . '-' . $counter;
$counter++;
$q = $this->_MODEL_::where('slug', $slug);
if ($id != null){
$q = $q->where('id','<>',$id);
}
}
return $slug;
}
}

@ -0,0 +1,50 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Acl
{
private $excepts = ['ckeditor', 'home'];
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$route = \Route::getCurrentRoute();
// check admin page & user is not super admin
if (auth()->check() && isset($route->action['as'])) {
// explode user request to process
$requestPath = explode('.', $route->action['as']);
// ignore admin and not admin page
if ($requestPath[0] == 'admin' && !auth()->user()->hasRole('developer') && !auth()->user()->hasRole('admin')) {
// check excpet and has 3 routes and has user acceess
if (!in_array($requestPath[1], $this->excepts) &&
isset($requestPath[2]) &&
!auth()->user()->hasAccess($route->action['as'])) {
return abort(403, __("You don't have access this action"));
}
// check delete or destroy with bulk action
if (isset($requestPath[2]) && $requestPath[2] == 'bulk' && $request->input('bulk') == 'delete') {
$requestPath[2] = 'delete';
if (!auth()->user()->hasAccess(implode('.', $requestPath))) {
$requestPath[2] = 'destroy';
if (!auth()->user()->hasAccess(implode('.', $requestPath))) {
return abort(403, __("You don't have access this action"));
}
}
}
if (auth()->user()->accesses()->count() == 0){
return redirect()->route('admin.logout')->with(['message' => "You don't have any access to login"]);
}
}
}
return $next($request);
}
}

@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class IgnoreFirstPage
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->has('page') && $request->get('page') == '1'){
$q = $request->all();
unset($q['page']);
return redirect($request->url().'?'.http_build_query($q));
}
return $next($request);
}
}

@ -0,0 +1,50 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\URL;
class LangControl
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$segments = $request->segments();
if (strlen($segments[0]) == 2 && preg_match('/[A-Za-z]/', $segments[0])) {
app()->setLocale($segments[0]);
$request->attributes->set('set_lang', true);
\Session::put('locate',app()->getLocale());
\Session::save();
} else {
app()->setLocale(config('app.locale'));
}
// array_shift($segments);
// $url = \request()->path();
// $url = str_replace(app()->getLocale(), '', $url);
// // Modify the request
// $newPath = '/' . implode('/', $segments);
// $newUrl = $request->root() . $newPath . ($request->getQueryString() ? '?'.$request->getQueryString() : '');
//
// $request->initialize(
// $request->query->all(),
// $request->request->all(),
// $request->attributes->all(),
// $request->cookies->all(),
// $request->files->all(),
// $request->server->all()
// );
//
return $next($request);
}
}

@ -0,0 +1,48 @@
<?php
namespace App\Http\Middleware;
use App\Helpers\TVisitor;
use App\Models\Visitor;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class VisitorCounter
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$visitor = Visitor::where('updated_at','>',date("Y-m-d H:i:s" ,time() - (60*60)))
->where('ip', $request->ip())->first();
if ($visitor === null) {
$visitor = new Visitor();
$visitor->ip = $request->ip();
$visitor->browser = TVisitor::DetectBrowser();
$visitor->os = TVisitor::DetectOS();
$visitor->version = TVisitor::BrowserVersion();
$visitor->referer = TVisitor::getRefererDomain();
$ref = TVisitor::GetKeyword();
if ($ref !== null) {
$visitor->keywords = $ref['keyword'];
$visitor->engine = $ref['engine'];
}
$visitor->is_mobile = TVisitor::IsMobile();
$visitor->page = $request->route()->getName();
$visitor->save();
}else{
$visitor->increment('visit');
$visitor->page = $request->route()->getName();
$visitor->save();
}
if (getSetting('under') == '1' && !auth()->check()) {
return redirect(route('client.under-construction'));
}
return $next($request);
}
}

@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AdvSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
'title' => ['required', 'string', 'max:255','min:5'],
'link' => ['required', 'string', 'max:255','min:5'],
'status' => ['required', 'boolean'],
'image' => ['nullable', 'image', 'mimes:jpeg,png,jpg,gif,svg', 'max:2048'],
'max_click' => ['required','numeric'],
];
}
}

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AttachmentSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'title' => ['required','string','min:2'],
'body' => ['nullable','string'],
'subtitle' => ['nullable','string'],
'file' => ['nullable','mimes:png,jpg,svg,mp4,pdf,docx,zip,rar,mp3','max:'.getMaxUploadSize()]
];
}
}

@ -0,0 +1,34 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CategorySaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'min:2', 'max:128'],
'subtitle' => ['nullable', 'string',],
'image' => ['nullable', 'file', 'mimes:jpg,svg,png'],
'bg' => ['nullable', 'file', 'mimes:jpg,svg,png'],
'description' => ['nullable', 'string',],
'parent_id' => ['nullable', 'exists:categories,id'],
'canonical' => ['nullable', 'url', 'min:5', 'max:128'],
];
}
}

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CitySaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
'name' => ['required','string','min:2'] ,
'state_id' => ['required','exists:states,id'] ,
'lat' => ['required','numeric','between:-90,90'],
'lng' => ['required','numeric','between:-180,180'],
];
}
}

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ClipSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255','min:5'],
'body' => ['nullable', 'string',],
'active' => ['nullable', 'boolean'],
'clip' => ['nullable', 'mimes:mp4', 'max:'.getMaxUploadSize()],
'cover' => ['nullable', 'image', 'mimes:jpeg,png,jpg,gif,svg', 'max:2048'],
];
}
}

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CommentSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
];
}
}

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ContactSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
];
}
}

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ContactSubmitRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'full_name' => ['required', 'string', 'max:255', 'min:3'],
'phone' => ['required', 'string', 'max:15', 'min:8'],
'subject' => ['nullable', 'string', 'max:255', 'min:4'],
'email' => ['required', 'email', 'max:255', 'min:4'],
'bodya' => ['required', 'string', 'max:4048', 'min:15'],
];
}
}

@ -0,0 +1,37 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CustomerSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:customers,email,'.$this->id],
'password' => ['nullable', 'string', 'min:6', 'confirmed'],
'mobile'=> ['required', 'string', 'min:10'],
'height' => ['nullable', 'numeric'],
'weight' => ['nullable', 'numeric'],
'sex' => ['required', 'in:MALE,FEMALE'],
'dob' => ['nullable', 'date'],
'avatar' => ['nullable', 'image', 'mimes:jpeg','max:2048'],
];
}
}

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class DiscountSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
'title' => ['nullable', 'string'],
'body' => ['nullable', 'string'],
'type' => ['required'],
'amount' => ['required', 'string', 'min:1'],
];
}
}

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EvaluationSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'title' => ['required', 'string','min:2'],
];
}
}

@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class GallerySaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
'title' => ['required', 'string', 'max:255','min:2'],
'description' => ['nullable', 'string'],
'status' => ['required', 'boolean'],
'image' => ['nullable', 'image', 'mimes:jpeg,png,jpg,gif,svg', 'max:2048'],
];
}
}

@ -0,0 +1,34 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class GroupSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'min:2', 'max:128'],
'subtitle' => ['nullable', 'string',],
'image' => ['nullable', 'file', 'mimes:jpg,svg,png'],
'bg' => ['nullable', 'file', 'mimes:jpg,svg,png'],
'description' => ['nullable', 'string',],
'parent_id' => ['nullable', 'exists:groups,id'],
'canonical' => ['nullable', 'url', 'min:5', 'max:128'],
];
}
}

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class GuestLogSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
];
}
}

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class InvoiceSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'transport_id' => ['nullable', 'integer', 'exists:transports,id'],
'address_id' => ['nullable', 'integer', 'exists:addresses,id'],
'tracking_code' => ['nullable', 'string'],
'status' => ['required', 'string'],
];
}
}

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MenuSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required','string','max:255','min:2'],
];
}
}

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255', 'min:2'],
'subtitle' => ['nullable', 'string', 'max:2048'],
'body' => ['required', 'string', 'min:5'],
'status' => ['required'],
'is_pin' => ['nullable'],
'image' => ['nullable', 'image', 'mimes:jpeg,png,jpg,gif,svg', 'max:2048'],
'icon' => ['nullable', 'string', 'min:3'],
'group_id' => ['required', 'exists:groups,id'],
'canonical' => ['nullable', 'url', 'min:5', 'max:128'],
];
}
}

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ProductSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'min:5', 'max:128', "unique:products,name," . $this->id],
'sku' => ['nullable', 'string', 'min:1', 'max:128', "unique:products,sku," . $this->id],
'body' => ['nullable', 'string', 'min:5'],
'excerpt' => ['required', 'string', 'min:5'],
'active' => ['nullable', 'boolean'],
'meta' => ['nullable'],
'category_id' => ['required', 'exists:categories,id'],
'image.*' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
'canonical' => ['nullable', 'url', 'min:5', 'max:128'],
];
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save