Compare commits

...

433 Commits

Author SHA1 Message Date
A1Gard f6cd392d9a added modern slider theme 1 month ago
A1Gard e703833e29 fixed slider bug 1 month ago
A1Gard 0ff59e87fc improved ui 1 month ago
A1Gard 237f069b91 added nested list control 1 month ago
A1Gard 1f79ba74b0 fixed contact form data 1 month ago
A1Gard bca73f1275 fixed ui bg post modren post 1 month ago
A1Gard 8a1f7df30b improve js quality 1 month ago
A1Gard 8773db7d99 improve js quality 1 month ago
A1Gard 5e7820061a added autoplay video to groups
added submenu expandable to homayon-menu repsonosive
1 month ago
A1Gard 6c7bc027d6 added link to sub [group & category] 2 months ago
A1Gard 1f53abc399 added group & category description
added sub-menu repsonsive homayon
2 months ago
A1Gard f87f46f813 fixed hide menu with sidebar 2 months ago
A1Gard 7d3e2d0cfc fixed hide menu with sidebar 2 months ago
A1Gard a9d3fb27e9 added sub menu to homayon 2 months ago
A1Gard 685afb3bcc fixed 5 rate bug 2 months ago
A1Gard 042a4d6d75 fixed responsive ui 2 months ago
A1Gard 6934eb95af fixed new product bug 2 months ago
A1Gard 6280704af2 optimized decription theme part 2 months ago
A1Gard 548f09a100 fixed publish bug 2 months ago
A1Gard 019b679ed9 fixed bug theme part 2 months ago
A1Gard 825f6dd721 added pedram theme part 2 months ago
A1Gard 3b6a686fbe added tal theme part 2 months ago
A1Gard 2f146029c5 added Sina theme part 3 months ago
A1Gard ec15cb07dd added under construction timer theme part 3 months ago
A1Gard fd5d7042a4 added under construction logic and one theme part 3 months ago
A1Gard 86a6de6804 added timer event theme part 3 months ago
A1Gard 3f0950f13a added version 3 months ago
A1Gard 0311d71944 fixed bug props name change error [safe name change] 3 months ago
A1Gard b7c6751cb7 fixed bug props remove error [safe remove] 3 months ago
A1Gard 61d6fe43d4 fixed bug props restore 3 months ago
A1Gard 40eb923721 updated navbar style 3 months ago
A1Gard 76f316212a added price sort 3 months ago
A1Gard c9966474c7 added swagger for project api 3 months ago
A1Gard 9bc2cde901 added posts and groups api 3 months ago
A1Gard ca310273b8 improved APIs 3 months ago
A1Gard bbae5955d5 improved helper function 3 months ago
A1Gard 33a60eb170 added fast attachment upload to improve system ux
update detach word mistake fixed
3 months ago
A1Gard d2e5dc4387 added hide menu category and group 3 months ago
A1Gard eedc1a71e4 fixed persian date bug persian2Gregorian 3 months ago
A1Gard 01071196df added auto complete for tags 4 months ago
A1Gard 893ff16735 added main group filter to post list 4 months ago
A1Gard 59b3405350 added fast edit group ux feature
added persian translate
added some comments to code
4 months ago
A1Gard d1ef32bb79 added fast category edit ux feature 4 months ago
A1Gard 89f80a50ce optimize theme parts 4 months ago
A1Gard 5edcdb19a9 fixed some bug setting query type 4 months ago
A1Gard 60efc8830c added products slider theme part 4 months ago
A1Gard 73bc2d6b2e fixed some bugs and optimize codes 4 months ago
A1Gard cf081b24fa added product and post query setting 4 months ago
A1Gard 01c1438eee fixed test error bug 4 months ago
A1Gard 76c0f2ee11 updated user seeder 4 months ago
A1Gard 6e478ce10f added rss feed to project 4 months ago
A1Gard 9d2c7a1dae updated read me 4 months ago
A1Gard 36d3501ae5 added safety remove for category and group 4 months ago
A1Gard b0cb0d1ca4 added documentation to project 4 months ago
A1Gard 21050c0727 fixed bug 4 months ago
A1Gard 74023b6736 optimize homayon menu 4 months ago
A1Gard 7eefdef9bc added curve footer theme part
fixed live edit bugs
4 months ago
A1Gard 893b6ac19a added grid clip theme part
fixed autoplay limit problem
4 months ago
A1Gard fe60d7d4d3 added button checkbox to homayon menu 4 months ago
A1Gard 6f17ba0f23 added cache to design page 4 months ago
A1Gard 96453239b7 improved responsive theme parts 4 months ago
A1Gard b30901a9bc add curve posts slider theme part
fixed bug curve cat theme part
4 months ago
A1Gard c29eaec1c9 fixed ara bug added sub groups function 4 months ago
A1Gard dd40d445bc improved ui homayon & autoplay theme part 4 months ago
A1Gard 639136d37c added ara group 4 months ago
A1Gard 6435c0a641 fixed js bug 4 months ago
A1Gard bc061e7ecb fixed bug darkmode other footer 4 months ago
A1Gard 7fd5388929 fixed bug parent 4 months ago
A1Gard 8217e109d5 fixed vido loop 4 months ago
A1Gard bbc03e859c added optimize setting images
fixed upload file bug
4 months ago
A1Gard 0f70183154 added optimize image ckeditor uploader 4 months ago
A1Gard 11619dcdb1 fixed parent bug 4 months ago
A1Gard 1d300cd039 added live edit setting 4 months ago
A1Gard 600273e0f6 added gisoo theme part 4 months ago
A1Gard 54035e8caa added homayon menu theme part 4 months ago
A1Gard 6707a2055d Merge remote-tracking branch 'origin/master' 4 months ago
A1Gard 0c3d21163a fixed bug setting first time by observer 4 months ago
A1Gard 446f26828f fixed bug setting observer 4 months ago
A1Gard c55c8665d2 added other footer 4 months ago
A1Gard c2a4649388 fixed bug post modern bg 4 months ago
A1Gard 84ac62409e added port modern posts theme part 5 months ago
A1Gard 6f1b9ed1e6 translate update 5 months ago
A1Gard 98e487cbc9 added developer guide area 5 months ago
A1Gard 0ff404d8a6 optimize panel buttons
improved ui
5 months ago
A1Gard 5aa1d513c5 fixed add card removed test code 5 months ago
A1Gard 4a75c66267 fixed add to card bug for yasamin theme part 5 months ago
A1Gard dcb556edcf fix invoice bug 5 months ago
A1Gard aa3f6e06e6 updated composer 5 months ago
A1Gard 884a0917fa fixed bug for windows area design 5 months ago
A1Gard 71ceef39c9 update readme and env 5 months ago
A1Gard 0a4788be01 update read me 5 months ago
A1Gard e34ca79fe1 added svg support to image seeder 5 months ago
A1Gard fbd2c43f0b added Main categories icon theme part
optimize other parts
5 months ago
A1Gard 43f8e35d3c added Hod header theme part 5 months ago
A1Gard 095d187bb2 added curve category theme part 5 months ago
A1Gard 5cd729f4ce added samira theme part 5 months ago
A1Gard e82372d5c7 fixed comments send bugs 5 months ago
A1Gard 9dd5008ee2 improve ux datepicker for persian dates 5 months ago
A1Gard d1fb62ef30 added date, datetime & time picker to setting 5 months ago
A1Gard f5a2f3dc67 added seeding all model 5 months ago
A1Gard 58962bf448 fixed bug 5 months ago
A1Gard 62028c6f99 improved ui/ux by stock quantity 5 months ago
A1Gard 2a7714845b upated readme 5 months ago
A1Gard d1ee8fe206 added assets build command to use by site 5 months ago
A1Gard a117e04ee4 fixed some user bug 6 months ago
A1Gard cad797d8bf fixed multi-lang problem 6 months ago
A1Gard c8b4ec1810 optimized ui/ux yasamin theme part 6 months ago
A1Gard a721a4d64b added Farhad theme part 6 months ago
A1Gard 64c653163f added Yasamin theme part 6 months ago
A1Gard a2e141bb52 added hidden sidebar theme part
optimized pages thumb image
6 months ago
A1Gard 7e4a857654 added theme part Maryam
fixed ui responsive bug natalia
6 months ago
A1Gard cb1254951f fixed sitemap bug 6 months ago
A1Gard 6cbeb91924 fixed ios ui bug horizontal scroll 6 months ago
A1Gard ee0069dcd0 added little footer 6 months ago
A1Gard c44e3cc03e added text splitter 6 months ago
A1Gard 122f89b881 added natalia theme part 6 months ago
A1Gard f906151be3 fixed ui bugs
added Vickushka theme part
6 months ago
A1Gard c3d04f24ff fixed ui bugs
improve translates
6 months ago
A1Gard 4a9ecdc6ca translates update 6 months ago
A1Gard 73e3025917 added seo content analyzer 6 months ago
A1Gard 7bd3fae3f5 added parallel categories grid theme part 6 months ago
A1Gard cd294fc596 added shiva theme part 6 months ago
A1Gard c316001abf added main categories theme part 6 months ago
A1Gard 8a5dec377e added minoo theme part 6 months ago
A1Gard 48ae713ae1 added product grid template
added default product grid theme part
added sort to area
6 months ago
A1Gard 1e6b040c4e updated translate persian 6 months ago
A1Gard 161492b49c optimized sitemap
added group and categories to sitemap
6 months ago
A1Gard 9099bdc105 fixed sitemap bug 6 months ago
A1Gard ed4d58bd4d improved sitemap 6 months ago
A1Gard 239ae1009c updated translate 6 months ago
A1Gard fb7403e906 optimized panel responsive to show better in mobile 6 months ago
A1Gard c4f5662c12 added blade cache system to Page Speed Optimization
added clear cache to settings
6 months ago
A1Gard bf4422530b update robots.txt 6 months ago
A1Gard 391caa1bd3 added lazy load to images 6 months ago
A1Gard ecf88a7dac added minify output 6 months ago
A1Gard 84824aaee0 fixed error 6 months ago
A1Gard 3775fec3b4 optimized canonical for filters category 6 months ago
A1Gard d43045d073 added persian readme 6 months ago
A1Gard 16e422fd51 updated readme 6 months ago
A1Gard 4fe5b08613 optimized gitignore 6 months ago
A1Gard 3879d00c44 optimized client assets generator 6 months ago
farazdy 3d7a03e703 added custom theme each post, category, group, product 6 months ago
A1Gard a75eda97e4 improved product seeder 6 months ago
A1Gard 316452d7b7 added canonical tag to post, product, group, category 6 months ago
A1Gard 0a33a22a36 added theme and promote fields 6 months ago
A1Gard b6019d821e optimized time picker 7 months ago
A1Gard 178f851f4f improved ui/ux 7 months ago
A1Gard a7034eba5c added date & time to meta properties 7 months ago
A1Gard a7cd5420cd added time picker 7 months ago
A1Gard 11ea1cbf2f added attempts to rate 7 months ago
A1Gard 0f8b99cd3e fixed navbar title mistake 7 months ago
A1Gard 87c2184f46 added mail register & reset password
fixed some bugs
added simple register theme part
7 months ago
A1Gard 802896a135 fixed test unit 7 months ago
SadeghPM 77e762d54c
Update app.php 7 months ago
SadeghPM fee54645df
Create Kernel.php 7 months ago
SadeghPM 0a3a63867e
Update laravel.yml 7 months ago
A1Gard 083ee2f5ab fixe client default 7 months ago
A1Gard a45724c163 added avatar to user 7 months ago
A1Gard 388a22f9cb added avatar & etc. to customer 7 months ago
A1Gard d518446aef added rate list to panel
added last rate history
improved security of rates
7 months ago
A1Gard c49bfe7565 added rate system 7 months ago
A1Gard 44b499d082 added evaluationables relations to models 7 months ago
A1Gard 9ceeb619a6 added evaluation to project 7 months ago
A1Gard 5eff6259d9 added rate input vue component 7 months ago
A1Gard fc33d4ed2a fixed no comment bug 7 months ago
A1Gard 935828dabb added table of content [SEO] 7 months ago
A1Gard 267213cff7 added online demo
fixed menu link bug
7 months ago
A1Gard 8c2724aabb added demo version 7 months ago
A1Gard 6f0779b769 added author slider theme part 7 months ago
A1Gard 7f8f5f14aa added theme part post slider 7 months ago
A1Gard 63c021658e fixed ui bugs 7 months ago
A1Gard 981ffabf07 fixed use default 7 months ago
A1Gard e05338c0f9 added category group 7 months ago
A1Gard 4a6d0c61ed updated read me 7 months ago
A1Gard d944d317a4 added sms auth system 7 months ago
A1Gard ceb48845ab added simple footer theme part 7 months ago
A1Gard 70132d9ed6 fixed some bugs 7 months ago
A1Gard 6c45d9a8e7 fixed template list bug 7 months ago
A1Gard 0de66aa3c5 fixed some bug ui/ux & translate mistakes 7 months ago
A1Gard f634f41bcb optimized ui/ux
fixed seeder
7 months ago
A1Gard 5ac382ab9d favicon changed 7 months ago
A1Gard 79cce6ca4e fixed some bugs
added version
7 months ago
A1Gard b706b8976a added follow us theme part 7 months ago
A1Gard 0a48fd16df updated developer access role 7 months ago
A1Gard b602fdb140 updated persian translate 7 months ago
A1Gard 406bd37310 updated theme part seeder 7 months ago
A1Gard a0bd025b7e added Liana theme part to invoice 7 months ago
A1Gard 925437ac3c fixed avisa theme part
fixed home links
7 months ago
A1Gard 63b96c1674 added invoice controller
added order relations
fixed ticket bug
7 months ago
A1Gard e9402cac80 optimized ui/ux of menus and customer 7 months ago
A1Gard 9332f1e9d2 optimized ui/ux 7 months ago
A1Gard 21b837759f added persian translate 7 months ago
A1Gard 319a64a748 optimized ux of area designer 7 months ago
A1Gard 9809517250 fixed read me
update version
7 months ago
A1Gard ce679db156 fixed search apl-menu 7 months ago
A1Gard 4acb148b16 added full screen button to mp4 player 7 months ago
A1Gard 738e8d7835 added search
improved ui of tag & search page
7 months ago
A1Gard b75f078c56 added tag page
fixed readme
fixed map bugs (inline map & icons)
7 months ago
farazdy ddb5b346f2 Added contact area 8 months ago
A1Gard 266d38fd35 Merge remote-tracking branch 'origin/master' 8 months ago
A1Gard bc3b4f4d61 added dark-mode 8 months ago
A1Gard 47a624a590 fixed font ui button 8 months ago
sadeghpm bdf3ef01a2 Merge remote-tracking branch 'origin/master' 8 months ago
A1Gard 974c2b9390 fixed error 8 months ago
A1Gard fb64ad1403 added node to workflow test 8 months ago
A1Gard f81688a0fa fixed visitor counter middleware 8 months ago
A1Gard f74c979e95 added sample test unit 8 months ago
A1Gard e8860ab086 fixed bug dena theme 8 months ago
A1Gard 07f13ca078 fixed test 8 months ago
A1Gard f996253950 updated github test 8 months ago
A1Gard b6fe85bb2b added github test 8 months ago
A1Gard 2ea6356c53 added siemap and customer test 8 months ago
A1Gard b7665af600 added client web page test 8 months ago
A1Gard 1b47932a06 update readme 8 months ago
A1Gard 5d807f1168 fixed bug ui apl menu 8 months ago
A1Gard 8d9efa1f55 fixed bug 8 months ago
A1Gard 78d8a90e7b added default header
optimized area and part seeder
8 months ago
A1Gard 8e0e562bd3 fixed no parent bugs group & category 8 months ago
sadeghpm 169196317d Payment gateway added 8 months ago
A1Gard 2bb6bf6931 added multi lang items into sitemap 8 months ago
A1Gard b583f31627 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 ebd75dbc11 optimized translate code 8 months ago
A1Gard 1b0f113227 added tag translate name and model 8 months ago
A1Gard f936feb684 added Translation to setting 8 months ago
A1Gard 3abd1b6d38 added ticket control customer profile 8 months ago
A1Gard 5437e5f44b added address control to customer profile
added comment list
added product favorites control
8 months ago
A1Gard 1eda1fe3c6 improved code quality
fixed some bugs
8 months ago
A1Gard 43a0202338 improved user experience tab control 8 months ago
A1Gard fcaa56f6e6 improved user experience multi langs 8 months ago
A1Gard 3bb8222120 added multi-lang support client
fixed rtl bootstrap file for rtl lang
WIP: need fixed for messages maybe sessions
8 months ago
sadeghpm 6d31dca0f3 Bug fix 8 months ago
A1Gard f69a2fc8db fixed synatax error sitemap 8 months ago
A1Gard d1922ac609 added sitemap [seo]
WIP: multi-lang website sitemaps
8 months ago
A1Gard d9bbb8f49d added meta description [seo]
added twitter [x] preview
8 months ago
A1Gard e19c37c1f8 added Open Graph Tags to models
updated markup
8 months ago
A1Gard 545045005c added markup json to product and post 8 months ago
A1Gard 3b0017010b added markup json breadcrumb [seo] 8 months ago
A1Gard 2eb89ead83 added screenshot to readme 8 months ago
A1Gard d2485f9fd2 updated screenshot 8 months ago
A1Gard 2daed24405 fixed word mistake 8 months ago
A1Gard 6389e88ccf added some translate 8 months ago
A1Gard 458ad20e35 updated translate [persian] 8 months ago
A1Gard 174775fc69 remove duplicated assets
added latest products theme part
fix some bugs
updated part seeder
8 months ago
A1Gard 5b257a3da4 added data seeder 8 months ago
A1Gard 03ffcc7638 added slider seeder
added slider image seeder
8 months ago
A1Gard 35b2ba64eb added Avisa theme part
added customer controller to profile
WIP: complete
8 months ago
A1Gard 01ae09b208 added theme part ns card
added CardController
added transport seeder
fixed meta input ui bug
8 months ago
A1Gard 148880a754 added autoplay video clips 8 months ago
A1Gard 57169f8dcf removed group and category from area seeder 8 months ago
A1Gard 211c0de4c8 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 65bc608437 added attachment list theme part
added attach with preview theme part
8 months ago
A1Gard e132a01437 added video player
added mp3 player
8 months ago
A1Gard 5328c33b34 added Karen theme part
improved ux of preview QuantitiesAddToCard
8 months ago
A1Gard 1d4622f270 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 52c0f48bc1 fixed bug discount expire time 8 months ago
A1Gard daa50de251 added login pattern bg background
added simple pattern
removed card from theme part
8 months ago
A1Gard 1edd5b1202 improved sub category - sub group ui 8 months ago
A1Gard 1b2123daac added login big bg
added login js
added customer sign-in process
8 months ago
A1Gard c6ade855d2 added go to top theme part
added background color to compare
8 months ago
A1Gard 7051a0e40b 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 5956918dad complete meta filter 8 months ago
A1Gard 209541fb93 added first page ignore middleware [seo]
improved fix css switch
added basic meta filter [WIP]
8 months ago
A1Gard 66cc2b1acc added subcategories grid theme part
changed directory some theme part
8 months ago
A1Gard a7d9607343 added product grid side theme part [WIP: filter category] 8 months ago
A1Gard f88163e674 added product grid theme part 8 months ago
A1Gard 15ca8ce927 added card toggle
added card storage to customer
8 months ago
A1Gard d7292f7457 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 ff0395f4a4 added image seeding command 8 months ago
A1Gard 8adccb8580 added seeding prepare command 8 months ago
A1Gard 1549f933da added seeder image git ignore 8 months ago
A1Gard 3ff5f7543d added attachment temp download link
added simple attachs list
fixed area seeder
9 months ago
A1Gard fc5a288067 added galleries list theme part 9 months ago
A1Gard f73fa0921f added grid gallery to theme part 9 months ago
A1Gard 46c98f8178 added gallery grid theme part
added aparat gallery theme part
9 months ago
A1Gard 879ea9d1a3 added sub-group theme part
optimized theme parts
improved group seeder
change screenshots
9 months ago
farazdy 55a6b0e62e Added grid post list sidebar 9 months ago
farazdy 7e521942e3 Added grid post list 9 months ago
farazdy db451376ca Added simple post list sidebar 9 months ago
A1Gard 14a8ae5100 fixed pagination ul 9 months ago
A1Gard c25e17d6f5 added simple post list theme part
fixed some bugs
9 months ago
A1Gard 76212ee579 updated area seeder
updated remix icon version
9 months ago
A1Gard 98589ebeab fixed bug setting duplicate 9 months ago
A1Gard 156ee392f6 fixed width setting 9 months ago
A1Gard 3989d68775 added post sidebar theme part 9 months ago
A1Gard 37ed92edfb updated git ignore upload folder 9 months ago
A1Gard 9cd2f7521a added simple comment theme part
added safe form
added comment submit method
9 months ago
A1Gard 14cc7005dd fixd bug & added approved comment 9 months ago
A1Gard 1733b9c1c1 added comment area & post view & time-spend 9 months ago
A1Gard b42e1b460d added post view increment 9 months ago
A1Gard d03617e998 added simple post 9 months ago
A1Gard dc59afb928 optimized xcontroller & route
added client controller
9 months ago
A1Gard 2d3bb229c5 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	resources/views/segments/header/SimpleHeader/SimpleHeader.blade.php
9 months ago
A1Gard c902d89da7 added ParallaxHeaderPin theme part
fixed some ui bugs
9 months ago
A1Gard cbb392e7be added simple part
added slider
9 months ago
A1Gard 6a2dafc22a added simple part 9 months ago
A1Gard 86d77c031b added inline map theme part 9 months ago
A1Gard 08a2340965 added location picker to setting 9 months ago
A1Gard fd002df592 added counter theme part 9 months ago
A1Gard ac57eb9447 added icon field to setting 9 months ago
A1Gard 4d5398e880 added grid category 9 months ago
A1Gard eb4147a65e added categories fav image 9 months ago
A1Gard c51d84abae added parallax footer part 9 months ago
A1Gard 6841e087ba added long height posts
added sample images
9 months ago
A1Gard 69d58ac6ef added ParallaxSlider part
fix slider auto problem
9 months ago
A1Gard 10d26d730c optimize new slider data match by theme 9 months ago
A1Gard dc7daa365e added add data to slider
added remove data to slider
9 months ago
A1Gard 77925f51c3 added theme controller
optimized website gtmetrix [removed inline styles]
9 months ago
A1Gard 0d026213a6 added side menu 9 months ago
A1Gard 57ebac9f6a added main to website 9 months ago
A1Gard bc60ae2498
Merge pull request #45 from CyberAli1989/master
feat: Implement product listing API with caching and sorting
9 months ago
Ali 18a2c89294
Merge branch '4xmen:master' into master 9 months ago
cyberali dcead2a865 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 a8099343ad Roll back Category model 9 months ago
cyberali f794d1f083 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 f1f8c87e39 fixed dir problem 9 months ago
A1Gard 4848ed87de added Third Grid product 9 months ago
A1Gard 83daefba26 added index image post theme part 9 months ago
A1Gard a05298b15c added no link post theme part 9 months ago
A1Gard 73fc73dd6b optimized firefooter 9 months ago
A1Gard 42c9b0453c added fire footer 9 months ago
A1Gard 78d032fa37 added wave footer 9 months ago
A1Gard 6dced06316
Merge pull request #44 from CyberAli1989/master
Add helper functions for standard JSON responses
9 months ago
cyberali 3f75d919cf 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 cec05b9a4b added Apl Menu theme part
added published to group and category
9 months ago
A1Gard e77d1dc21a added recet menu 9 months ago
A1Gard 247b490185 added menu to setting
added menu seeder
9 months ago
A1Gard 89cafad7c4 Merge remote-tracking branch 'origin/master' 9 months ago
A1Gard 80c2973c28 optimized panel sidebar with acl 9 months ago
A1Gard f91986c4b7 added acl to project 9 months ago
A1Gard 8188820ac3 added sort to menu items 9 months ago
A1Gard f1dec8db9f added menu controller [WIP: sort]
added menu item input vue component
added v-model(s) & name support to MorphSelector
9 months ago
YasinDehfuli e7a60e1c9a
Update README.md 9 months ago
YasinDehfuli d07b35ce72
Update README.md 9 months ago
YasinDehfuli 31809ec8cc
Update README.md 9 months ago
A1Gard 5661e73e11 removed old code comment 9 months ago
A1Gard 91c22e0046 added live preview 9 months ago
A1Gard 2a1e43bc25 improved seeder customer, area & post 9 months ago
A1Gard 60698fce0c fixed bug gfx vue component 9 months ago
A1Gard ecf6301212 added preloader to panel 9 months ago
A1Gard 1004ec3bc8 improved Seeders 9 months ago
A1Gard d879825606 added last week orders to home 9 months ago
A1Gard e8993dcd81 added invoice seeder
added device pie chart
optimized order & invoice table struct
9 months ago
A1Gard 597beda443 added status to invoice
added user relations
added invoice and ticket to index
9 months ago
A1Gard abb4330d62 added last month visits to home 9 months ago
A1Gard 264e5faed0 added referer website to visitor 9 months ago
A1Gard 3166710510 optimized visitor model, migration, middleware, seeder 9 months ago
A1Gard 8bef2c82b4 added visitor statistics 9 months ago
A1Gard 1c09293f1c added fixed bug 9 months ago
A1Gard 75a3c1f8db fixed Increment.vue component inc bug 9 months ago
A1Gard ba233b3798 added gradient text 9 months ago
A1Gard b9944a2e2d added simple text
added invert outline btn
9 months ago
A1Gard b99a6b420a fixed readme 9 months ago
A1Gard 54bf95efe7 change readme 9 months ago
A1Gard b28eaeb578 Merge branch 'master' of https://github.com/4xmen/xshop 9 months ago
A1Gard f7e082ba70 remove laravel README.md 9 months ago
A1Gard 861b6fd5a0 added some feature for theme and setting
fixed some bugs
optimized make part
9 months ago
A1Gard feb51a2781 added parallax short theme part 9 months ago
A1Gard 8c544652fe added fav product with meta theme part 9 months ago
A1Gard 229fc5204b added git ignore to media library 9 months ago
A1Gard 8ae1915297 optimized client command
added post icon simple
9 months ago
A1Gard b45f3cbce7 added multi language support 9 months ago
A1Gard d885ce07be added sort button to category group
added sort to prop
9 months ago
A1Gard 6b1b8baeaa added panel rtl theme 9 months ago
A1Gard 56d18b1766 added persian lang 9 months ago
A1Gard fb4ba85c61 optimized png for slider group category 9 months ago
A1Gard 2325536a26 added cities and states controller 9 months ago
A1Gard 2a00c64fea added open file by click on image [UX] 9 months ago
A1Gard 061784523b added watermark size and opacity to app setting 9 months ago
A1Gard 7467fbd7fe added optimize for category group slider
fixed bug part seeder
added watermark2 to SettingSeeder
added upload gitignore
9 months ago
A1Gard 3be0a8c4e8 added up-down button to areea 9 months ago
A1Gard 8ed804f615 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 a49b9095ae optimized client assets generator
completed two preloader sample
9 months ago
A1Gard 5839e85f55 added Area and Part
added two preloader sample
9 months ago
A1Gard 7552e86c6b added client asset generator [WIP] 9 months ago
A1Gard 56e7c39248 added client assets 9 months ago
A1Gard e24fa89717 optimized 9 months ago
A1Gard ecd926b9ed added theme part command 9 months ago
A1Gard 801de6ae1d added color to setting 9 months ago
A1Gard e280b8deb8 optimized editor with site template 9 months ago
A1Gard 2cc4a75976 added GFX to panel 9 months ago
A1Gard a38f273f04 added admin log to setting 9 months ago
A1Gard 4f0928f8e2 fixed some fatal errors 9 months ago
A1Gard 6c21182f14 added sort save to groups 10 months ago
A1Gard a265c57277 added sort save category 10 months ago
A1Gard 22a302f975 added setting to panel 10 months ago
A1Gard 58bc5e97d1 added comment controller 10 months ago
A1Gard 494c642956 added contact controller 10 months ago
A1Gard 0e338e44a1 added hash to contact and invoice 10 months ago
A1Gard 1082906bf5 optimized list 10 months ago
A1Gard b634056dd1 added ticket controller 10 months ago
A1Gard 3bfdd29e17 added question controller 10 months ago
A1Gard 3d0f49be0c optimized attachment and list template 10 months ago
A1Gard 84ad1c1584 added attachment relations
added deattach
10 months ago
A1Gard 56acb26137 added attachment controller 10 months ago
A1Gard 40c538212f added morph selector 10 months ago
A1Gard dffb0dd187 fixed navbar ui bugs 10 months ago
A1Gard 2a3379ffad added transports 10 months ago
A1Gard 585e0cfecc added discounts to product
added located date to Carbon
10 months ago
A1Gard a57e934a52 added discount controller 10 months ago
A1Gard 03b4d64893 added product seeder WIP: need picture and category 10 months ago
A1Gard e19a69215e fixed toggle bulk action bug 10 months ago
A1Gard 507b655c94 fixed restore annd trashed bug for product 10 months ago
A1Gard 2cc3475431 save quantities
fixed some bugs for update and edit page
10 months ago
A1Gard dec15afcc3 fixed currency input bug 10 months ago
A1Gard 740aca9349 added vuex compatible to SearchableSelect
added meta input
10 months ago
A1Gard 5d2c2bd5eb added vuex to project 10 months ago
A1Gard 82504300cd fixed ckeditor change bug 10 months ago
A1Gard 5dd4f3b03d added buy price
fixed redirect for insert category_id in list
10 months ago
A1Gard 6265c8cf77 added slug unique fixer to xcontroller 10 months ago
A1Gard bb60c111a6 added prop controller
added category api [props]
10 months ago
A1Gard de88d0ce50 added product controller [WIP: discount & meta & quantity] 10 months ago
A1Gard 62debcbe6d added customer controller
added state and city
installed api route
added address input
10 months ago
A1Gard a20dc80748 added adv xcontroller
added disable date picker
10 months ago
A1Gard 342f1f91c4 added category controller 10 months ago
A1Gard 16c68954ce added dataz attribute 10 months ago
A1Gard 991962ea43 added slider with meta data values 10 months ago
A1Gard 0c6148c84d added clip controller 10 months ago
A1Gard 05f5fc05f2 added custom slug to gallery 10 months ago
A1Gard f864f8cf4a added galley xController
optimize ui
added imgUrl to list template
10 months ago
A1Gard aa974c76c3 added admin logs controller
added log to users
10 months ago
A1Gard 4ef0238dce added ckeditor to panel
added post xController
10 months ago
A1Gard 391a57c566 fixed breadcrumb component 10 months ago
A1Gard 970dfd0795 added Group
added some essential function to helper and xcontoller
10 months ago
A1Gard c7f582ed46 added xcontroller commands 10 months ago
A1Gard 22c6f45111 added xController store ad update
form template
edited menu and order migration
10 months ago
A1Gard 56110bb254 xController list + template-list 10 months ago
A1Gard 5a3deaa61b added panel list component
fixed post group id
11 months ago
A1Gard 5bdd998c54 modified starterkit models
added helper
11 months ago
A1Gard ade533da84 added migrations 11 months ago
A1Gard 5e6038619b added user roles 11 months ago
A1Gard f31a84fa28 fixed style for panel 11 months ago
A1Gard 8f0f9272d1 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,226 @@
<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,195 @@
<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
## 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,91 @@
<?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;
foreach ($vars as $k => $var) {
$variables .= '$'."$k:$var;" . PHP_EOL;
}
$variables .= ":root{" . PHP_EOL;
foreach ($vars as $k => $var) {
$variables .= "--$k:$var;" . PHP_EOL;
}
$variables .= "}" . PHP_EOL . PHP_EOL;
// 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 .= '@import "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 .= '@import "../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', $variables);
file_put_contents(resource_path() . '/js/client.js', $js);
}
}

@ -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: 37 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.png',$folderPath .'/screenshot.png');
$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.png', ['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