├── .env.example ├── .gitignore ├── .vscode └── extensions.json ├── README.md ├── index.html ├── jsconfig.json ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico └── images │ ├── samples │ └── v1 │ │ ├── authors │ │ └── sample.jpg │ │ ├── chapters │ │ └── ebook-cover.png │ │ ├── guarantee │ │ ├── 10-days.png │ │ ├── 15-days.png │ │ ├── 30-days.png │ │ └── 7-days.png │ │ └── heros │ │ ├── cover-ebook.png │ │ ├── cover.png │ │ ├── ebook-cover-2.png │ │ └── ebook-cover.png │ └── thumbs │ └── v1 │ ├── authors │ └── image-left.png │ ├── buy │ └── simple.png │ ├── chapters │ └── collapse.png │ ├── faq │ ├── collapse.png │ └── list.png │ ├── features │ ├── grid-3-old.png │ └── grid-3.png │ ├── guarantee │ ├── 10-days.png │ ├── 15-days.png │ ├── 30-days.png │ └── 7-days.png │ └── heroes │ ├── form-right.png │ ├── image-right.png │ └── video-center.png ├── src ├── App.vue ├── Assets │ └── logo.png ├── Components │ ├── Alert.vue │ ├── Blocks │ │ ├── Author │ │ │ ├── AuthorImageLeft.vue │ │ │ └── AuthorImageLeftOptions.vue │ │ ├── BlockGroup.vue │ │ ├── BlockPreviewer.vue │ │ ├── BlockWrapper.vue │ │ ├── Buy │ │ │ ├── BuySimple.vue │ │ │ └── BuySimpleOptions.vue │ │ ├── Chapters │ │ │ ├── ChaptersCollapse.vue │ │ │ └── ChaptersCollapseOptions.vue │ │ ├── FAQ │ │ │ ├── FAQCollapse.vue │ │ │ ├── FAQCollapseOptions.vue │ │ │ ├── FAQList.vue │ │ │ └── FAQListOptions.vue │ │ ├── Features │ │ │ ├── FeaturesGrid3.vue │ │ │ └── FeaturesGrid3Options.vue │ │ ├── Guarantees │ │ │ ├── Guarantee10Days.vue │ │ │ ├── Guarantee10DaysOptions.vue │ │ │ ├── Guarantee15Days.vue │ │ │ ├── Guarantee15DaysOptions.vue │ │ │ ├── Guarantee30Days.vue │ │ │ ├── Guarantee30DaysOptions.vue │ │ │ ├── Guarantee7Days.vue │ │ │ └── Guarantee7DaysOptions.vue │ │ └── Heroes │ │ │ ├── HeroFormLeft.vue │ │ │ ├── HeroFormLeftOptions.vue │ │ │ ├── HeroImageLeft.vue │ │ │ ├── HeroImageLeftOptions.vue │ │ │ ├── HeroVideoCenter.vue │ │ │ └── HeroVideoCenterOptions.vue │ ├── BreakPointHelper.vue │ ├── Icon.vue │ ├── ImageCard.vue │ ├── ImageSelect.vue │ ├── ImageSelectModal.vue │ ├── ImageUploadButton.vue │ └── Navbar.vue ├── Layouts │ ├── App.vue │ ├── Auth.vue │ ├── Builder.vue │ └── Marketing.vue ├── Pages │ ├── App │ │ ├── App.vue │ │ ├── Dashboard │ │ │ ├── Components │ │ │ │ └── LandingPageCard.vue │ │ │ └── Index.vue │ │ ├── ImageGallery │ │ │ └── Index.vue │ │ ├── LandingPages │ │ │ └── Create.vue │ │ └── PageBuilder │ │ │ ├── Builder.vue │ │ │ └── Preview.vue │ ├── Auth │ │ ├── Login.vue │ │ └── Register.vue │ └── Public │ │ ├── LandingPage │ │ └── View.vue │ │ └── Marketing │ │ └── Home.vue ├── Utils │ ├── action-types.js │ ├── api.js │ ├── blocks.js │ ├── middleware.js │ ├── mutation-types.js │ ├── router.js │ └── store.js ├── index.css └── main.js ├── tailwind.config.js └── vite.config.js /.env.example: -------------------------------------------------------------------------------- 1 | VITE_APP_ENV=local 2 | VITE_APP_DEBUG=true 3 | VITE_BACKEND_BASE_URL=http://localhost -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | .env -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EZ Landing Page Builder 2 | 3 | ## The app 4 | 5 | Simple web app to easily create landing pages by dragging and dropping prebuilt blocks. 6 | 7 | Each block makes available a set of options to easily customize its content. 8 | 9 | Users don't need to write a single line of code. 10 | 11 | ## Video demo (click to go to YouTube) 12 | 13 | [![YouTube Demo Video](https://img.youtube.com/vi/4MfJ4UAkQjg/0.jpg)](https://www.youtube.com/watch?v=4MfJ4UAkQjg) 14 | 15 | ## The stack 16 | 17 | ### Frontend 18 | 19 | 1. [Vue.js 3](https://v3.vuejs.org/) + [Vuex 4](https://vuex.vuejs.org/) + [Vue Router 4](https://router.vuejs.org/) 20 | 2. [Vite.js](https://vitejs.dev/) 21 | 3. [Tailwind CSS 3](https://tailwindcss.com/) + [Daisy UI](https://daisyui.com/) 22 | 5. [Sortable.js](https://github.com/SortableJS/vue.draggable.next) 23 | 24 | ### Backend 25 | 26 | 1. [Laravel 8](https://laravel.com/docs/8.x) 27 | 2. [Laravel Sail](https://laravel.com/docs/8.x/sail) 28 | 3. [Laravel Sanctum](https://laravel.com/docs/8.x/sanctum) 29 | 4. [Laravel Fortify](https://laravel.com/docs/8.x/fortify) 30 | 31 | ## Setting up the backend 32 | 33 | 1. Clone the repo and navigate to the directory 34 | ``` 35 | git clone git@github.com:isaac-souza/pagebuilder-laravel.git 36 | cd pagebuilder-laravel 37 | ``` 38 | 2. Copy the sample .env file 39 | ``` 40 | cp .env.example .env 41 | ``` 42 | 3. Install the dependencies (requires at least PHP 8.0) 43 | ``` 44 | composer install 45 | ``` 46 | 4. Start Laravel Sail (needs Docker installed in your system) 47 | ``` 48 | vendor/bin/sail up 49 | ``` 50 | 5. Generate key, run the migrations and link the storage folder 51 | ``` 52 | vendor/bin/sail artisan key:generate 53 | vendor/bin/sail artisan migrate --seed 54 | vendor/bin/sail artisan storage:link 55 | ``` 56 | 6. Run the tests 57 | ``` 58 | vendor/bin/sail artisan test 59 | ``` 60 | 7. The backend should be available at 61 | ``` 62 | http://localhost 63 | ``` 64 | 65 | ## Setting up the frontend 66 | 67 | 1. Clone the repo and navigate to the directory 68 | ``` 69 | git clone git@github.com:isaac-souza/pagebuilder-vue3.git 70 | cd pagebuilder-vue3 71 | ``` 72 | 2. Install the dependencies 73 | ``` 74 | npm install 75 | ``` 76 | 3. Copy the sample .env file 77 | ``` 78 | cp .env.example .env 79 | ``` 80 | 4. Start the dev server 81 | ``` 82 | npm run dev 83 | ``` 84 | 5. The frontend should be available at 85 | ``` 86 | http://localhost:3000 87 | ``` 88 | 89 | ## Testing 90 | 91 | Now you should be able to go to http://localhost:3000, access the login page and sign in into the app 92 | 93 | ## My development environment 94 | 95 | OS: 96 | ``` 97 | No LSB modules are available. 98 | Distributor ID: Ubuntu 99 | Description: Ubuntu 18.04.6 LTS 100 | Release: 18.04 101 | Codename: bionic 102 | ``` 103 | 104 | Docker: 105 | ``` 106 | Docker version 20.10.12, build e91ed57 107 | ``` 108 | 109 | Docker-compose: 110 | ``` 111 | docker-compose version 1.29.2, build 5becea4c 112 | ``` 113 | 114 | PHP: 115 | ``` 116 | PHP 8.0.15 (cli) (built: Jan 29 2022 07:24:35) ( NTS ) 117 | Copyright (c) The PHP Group 118 | Zend Engine v4.0.15, Copyright (c) Zend Technologies 119 | with Zend OPcache v8.0.15, Copyright (c), by Zend Technologies 120 | ``` 121 | Composer 122 | ``` 123 | Composer version 2.1.6 2021-08-19 17:11:08 124 | ``` 125 | Node 126 | ``` 127 | v14.19.0 128 | ``` 129 | NPM 130 | ``` 131 | 6.14.16 132 | ``` -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | EZ LandingPage 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.25.0", 11 | "daisyui": "^1.24.3", 12 | "uuid": "^8.3.2", 13 | "vue": "^3.2.25", 14 | "vue-router": "^4.0.12", 15 | "vuedraggable": "^4.1.0", 16 | "vuex": "^4.0.2" 17 | }, 18 | "devDependencies": { 19 | "@tailwindcss/aspect-ratio": "^0.4.0", 20 | "@vitejs/plugin-vue": "^2.0.0", 21 | "autoprefixer": "^10.4.2", 22 | "postcss": "^8.4.5", 23 | "tailwindcss": "^3.0.15", 24 | "vite": "^2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/favicon.ico -------------------------------------------------------------------------------- /public/images/samples/v1/authors/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/authors/sample.jpg -------------------------------------------------------------------------------- /public/images/samples/v1/chapters/ebook-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/chapters/ebook-cover.png -------------------------------------------------------------------------------- /public/images/samples/v1/guarantee/10-days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/guarantee/10-days.png -------------------------------------------------------------------------------- /public/images/samples/v1/guarantee/15-days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/guarantee/15-days.png -------------------------------------------------------------------------------- /public/images/samples/v1/guarantee/30-days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/guarantee/30-days.png -------------------------------------------------------------------------------- /public/images/samples/v1/guarantee/7-days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/guarantee/7-days.png -------------------------------------------------------------------------------- /public/images/samples/v1/heros/cover-ebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/heros/cover-ebook.png -------------------------------------------------------------------------------- /public/images/samples/v1/heros/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/heros/cover.png -------------------------------------------------------------------------------- /public/images/samples/v1/heros/ebook-cover-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/heros/ebook-cover-2.png -------------------------------------------------------------------------------- /public/images/samples/v1/heros/ebook-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/samples/v1/heros/ebook-cover.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/authors/image-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/authors/image-left.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/buy/simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/buy/simple.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/chapters/collapse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/chapters/collapse.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/faq/collapse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/faq/collapse.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/faq/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/faq/list.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/features/grid-3-old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/features/grid-3-old.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/features/grid-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/features/grid-3.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/guarantee/10-days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/guarantee/10-days.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/guarantee/15-days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/guarantee/15-days.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/guarantee/30-days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/guarantee/30-days.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/guarantee/7-days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/guarantee/7-days.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/heroes/form-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/heroes/form-right.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/heroes/image-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/heroes/image-right.png -------------------------------------------------------------------------------- /public/images/thumbs/v1/heroes/video-center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/public/images/thumbs/v1/heroes/video-center.png -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | -------------------------------------------------------------------------------- /src/Assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isaac-souza/pagebuilder-vue3/b10a0624cf73f36a69d665c91515ddd6e4131e4c/src/Assets/logo.png -------------------------------------------------------------------------------- /src/Components/Alert.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 98 | 99 | -------------------------------------------------------------------------------- /src/Components/Blocks/Author/AuthorImageLeft.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 49 | -------------------------------------------------------------------------------- /src/Components/Blocks/Author/AuthorImageLeftOptions.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 64 | -------------------------------------------------------------------------------- /src/Components/Blocks/BlockGroup.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 52 | -------------------------------------------------------------------------------- /src/Components/Blocks/BlockPreviewer.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 55 | -------------------------------------------------------------------------------- /src/Components/Blocks/BlockWrapper.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 137 | -------------------------------------------------------------------------------- /src/Components/Blocks/Buy/BuySimple.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 43 | -------------------------------------------------------------------------------- /src/Components/Blocks/Buy/BuySimpleOptions.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 58 | -------------------------------------------------------------------------------- /src/Components/Blocks/Chapters/ChaptersCollapse.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 51 | -------------------------------------------------------------------------------- /src/Components/Blocks/Chapters/ChaptersCollapseOptions.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 122 | -------------------------------------------------------------------------------- /src/Components/Blocks/FAQ/FAQCollapse.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 46 | -------------------------------------------------------------------------------- /src/Components/Blocks/FAQ/FAQCollapseOptions.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 121 | -------------------------------------------------------------------------------- /src/Components/Blocks/FAQ/FAQList.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 45 | -------------------------------------------------------------------------------- /src/Components/Blocks/FAQ/FAQListOptions.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 119 | -------------------------------------------------------------------------------- /src/Components/Blocks/Features/FeaturesGrid3.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 50 | -------------------------------------------------------------------------------- /src/Components/Blocks/Features/FeaturesGrid3Options.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 120 | -------------------------------------------------------------------------------- /src/Components/Blocks/Guarantees/Guarantee10Days.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 49 | -------------------------------------------------------------------------------- /src/Components/Blocks/Guarantees/Guarantee10DaysOptions.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 51 | -------------------------------------------------------------------------------- /src/Components/Blocks/Guarantees/Guarantee15Days.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 49 | -------------------------------------------------------------------------------- /src/Components/Blocks/Guarantees/Guarantee15DaysOptions.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 51 | -------------------------------------------------------------------------------- /src/Components/Blocks/Guarantees/Guarantee30Days.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 49 | -------------------------------------------------------------------------------- /src/Components/Blocks/Guarantees/Guarantee30DaysOptions.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 51 | -------------------------------------------------------------------------------- /src/Components/Blocks/Guarantees/Guarantee7Days.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 49 | -------------------------------------------------------------------------------- /src/Components/Blocks/Guarantees/Guarantee7DaysOptions.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 51 | -------------------------------------------------------------------------------- /src/Components/Blocks/Heroes/HeroFormLeft.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 52 | -------------------------------------------------------------------------------- /src/Components/Blocks/Heroes/HeroFormLeftOptions.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 52 | -------------------------------------------------------------------------------- /src/Components/Blocks/Heroes/HeroImageLeft.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 44 | -------------------------------------------------------------------------------- /src/Components/Blocks/Heroes/HeroImageLeftOptions.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 80 | -------------------------------------------------------------------------------- /src/Components/Blocks/Heroes/HeroVideoCenter.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 41 | -------------------------------------------------------------------------------- /src/Components/Blocks/Heroes/HeroVideoCenterOptions.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 52 | -------------------------------------------------------------------------------- /src/Components/BreakPointHelper.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Components/Icon.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 31 | -------------------------------------------------------------------------------- /src/Components/ImageCard.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 63 | 64 | -------------------------------------------------------------------------------- /src/Components/ImageSelect.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 55 | -------------------------------------------------------------------------------- /src/Components/ImageSelectModal.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 60 | -------------------------------------------------------------------------------- /src/Components/ImageUploadButton.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 64 | -------------------------------------------------------------------------------- /src/Components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 50 | -------------------------------------------------------------------------------- /src/Layouts/App.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 33 | -------------------------------------------------------------------------------- /src/Layouts/Auth.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 35 | -------------------------------------------------------------------------------- /src/Layouts/Builder.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | -------------------------------------------------------------------------------- /src/Layouts/Marketing.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Pages/App/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /src/Pages/App/Dashboard/Components/LandingPageCard.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 68 | -------------------------------------------------------------------------------- /src/Pages/App/Dashboard/Index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 45 | -------------------------------------------------------------------------------- /src/Pages/App/ImageGallery/Index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 46 | -------------------------------------------------------------------------------- /src/Pages/App/LandingPages/Create.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 64 | -------------------------------------------------------------------------------- /src/Pages/App/PageBuilder/Builder.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 156 | -------------------------------------------------------------------------------- /src/Pages/App/PageBuilder/Preview.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 53 | -------------------------------------------------------------------------------- /src/Pages/Auth/Login.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 70 | -------------------------------------------------------------------------------- /src/Pages/Auth/Register.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 79 | -------------------------------------------------------------------------------- /src/Pages/Public/LandingPage/View.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 43 | -------------------------------------------------------------------------------- /src/Pages/Public/Marketing/Home.vue: -------------------------------------------------------------------------------- 1 | 370 | 371 | 378 | -------------------------------------------------------------------------------- /src/Utils/action-types.js: -------------------------------------------------------------------------------- 1 | export const ACTION_GET_LANDING_PAGES = 'ACTION_GET_LANDING_PAGES' 2 | export const ACTION_GET_LANDING_PAGE_BY_SLUG = 'ACTION_GET_LANDING_PAGE_BY_SLUG' 3 | export const ACTION_DELETE_LANDING_PAGE = 'ACTION_DELETE_LANDING_PAGE' 4 | export const ACTION_CREATE_LANDING_PAGE = 'ACTION_CREATE_LANDING_PAGE' 5 | 6 | export const ACTION_UPDATE_PAGES = 'ACTION_UPDATE_PAGES' 7 | export const ACTION_UPDATE_DRAFT = 'ACTION_UPDATE_DRAFT' 8 | 9 | export const ACTION_DISMISS_ALERT = 'ACTION_DISMISS_ALERT' 10 | export const ACTION_SHOW_ALERT = 'ACTION_SHOW_ALERT' 11 | 12 | export const ACTION_GET_IMAGES = 'ACTION_GET_IMAGES' 13 | export const ACTION_UPLOAD_IMAGE = 'ACTION_UPLOAD_IMAGE' 14 | export const ACTION_FIND_IMAGE_BY_UUID = 'ACTION_FIND_IMAGE_BY_UUID' 15 | export const ACTION_DELETE_IMAGE = 'ACTION_DELETE_IMAGE' 16 | -------------------------------------------------------------------------------- /src/Utils/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | axios.defaults.withCredentials = true 4 | axios.defaults.baseURL = import.meta.env.VITE_BACKEND_BASE_URL 5 | 6 | const Api = { 7 | fetchLandingPages: () => { 8 | return request('GET', '/v1/landing-pages') 9 | }, 10 | 11 | getLandingPage: (uuid) => { 12 | return request('GET', '/v1/landing-pages/' + uuid) 13 | }, 14 | 15 | getLandingPageBySlug: (slug) => { 16 | return request('GET', '/v1/public/landing-pages/' + slug) 17 | }, 18 | 19 | updatePages: (uuid, data) => { 20 | return request('PUT', '/v1/landing-pages/' + uuid, {pages: data}) 21 | }, 22 | 23 | updateDraft: (uuid, data) => { 24 | return request('PUT', '/v1/landing-pages/' + uuid + '/draft', {pages: data}) 25 | }, 26 | 27 | deleteLandingPage: (uuid) => { 28 | return request('DELETE', '/v1/landing-pages/' + uuid) 29 | }, 30 | 31 | createLandingPage: (data) => { 32 | return request('POST', '/v1/landing-pages', data) 33 | }, 34 | 35 | getImages: () => { 36 | return request('GET', '/v1/images') 37 | }, 38 | 39 | uploadImage: (data) => { 40 | return request('POST', '/v1/images', data) 41 | }, 42 | 43 | deleteImage: (uuid) => { 44 | return request('DELETE', '/v1/images/' + uuid) 45 | }, 46 | 47 | login: (credentials) => { 48 | return new Promise((resolve, reject) => { 49 | axios.get('/sanctum/csrf-cookie') 50 | .then(() => { 51 | axios.post('/login', credentials) 52 | .then(response => { 53 | if(response.status == 200) { 54 | localStorage.setItem('ez_landingpage_authenticated', true) 55 | } 56 | 57 | log(response, 'POST - then - /login') 58 | resolve(response) 59 | }) 60 | .catch(error => { 61 | log(error, 'POST - catch - /login') 62 | reject(error) 63 | }) 64 | }) 65 | .catch(error => { 66 | log(error, 'GET - catch - /sanctum/csrf-cookie') 67 | reject(error) 68 | }) 69 | }) 70 | }, 71 | 72 | logout: () => { 73 | return new Promise((resolve, reject) => { 74 | axios.post('/logout') 75 | .then(response => { 76 | localStorage.setItem('ez_landingpage_authenticated', false) 77 | log(response, 'POST - then - /logout') 78 | resolve(response) 79 | }) 80 | .catch(error => { 81 | log(error, 'POST - catch - /logout') 82 | reject(error) 83 | }) 84 | }) 85 | }, 86 | 87 | register: (data) => { 88 | return new Promise((resolve, reject) => { 89 | axios.get('/sanctum/csrf-cookie') 90 | .then(() => { 91 | axios.post('/register', data) 92 | .then(response => { 93 | if(response.status == 200) { 94 | localStorage.setItem('ez_landingpage_authenticated', true) 95 | } 96 | 97 | log(response, 'POST - then - /register') 98 | resolve(response) 99 | }) 100 | .catch(error => { 101 | log(error, 'POST - catch - /register') 102 | reject(error) 103 | }) 104 | }) 105 | .catch(error => { 106 | log(error, 'POST - catch - /sanctum/csrf-cookie') 107 | reject(error) 108 | }) 109 | }) 110 | } 111 | 112 | } 113 | 114 | function request(method, endpoint, data = null, raw = false) { 115 | if(method == 'GET') { 116 | return new Promise((resolve, reject) => { 117 | axios.get(endpoint) 118 | .then(response => { 119 | log(response, 'GET - then - ' + endpoint) 120 | 121 | if(raw) { 122 | resolve(response) 123 | } 124 | else { 125 | resolve(response.data) 126 | } 127 | }) 128 | .catch(error => { 129 | log(error, 'GET - catch - ' + endpoint) 130 | reject(error) 131 | }) 132 | }) 133 | } 134 | 135 | if(method == 'POST') { 136 | return new Promise((resolve, reject) => { 137 | axios.post(endpoint, data) 138 | .then(response => { 139 | log(response, 'POST - then - ' + endpoint) 140 | 141 | if(raw) { 142 | resolve(response) 143 | } 144 | else { 145 | resolve(response.data) 146 | } 147 | }) 148 | .catch(error => { 149 | log(error, 'POST - catch - ' + endpoint) 150 | reject(error) 151 | }) 152 | }) 153 | } 154 | 155 | if(method == 'PUT') { 156 | return new Promise((resolve, reject) => { 157 | axios.put(endpoint, data) 158 | .then(response => { 159 | log(response, 'PUT - then - ' + endpoint) 160 | 161 | if(raw) { 162 | resolve(response) 163 | } 164 | else { 165 | resolve(response.data) 166 | } 167 | }) 168 | .catch(error => { 169 | log(error, 'PUT - catch - ' + endpoint) 170 | reject(error) 171 | }) 172 | }) 173 | } 174 | 175 | if(method == 'DELETE') { 176 | return new Promise((resolve, reject) => { 177 | axios.delete(endpoint) 178 | .then(response => { 179 | log(response, 'DELETE - then - ' + endpoint) 180 | 181 | if(raw) { 182 | resolve(response) 183 | } 184 | else { 185 | resolve(response.data) 186 | } 187 | }) 188 | .catch(error => { 189 | log(error, 'DELETE - catch - ' + endpoint) 190 | reject(error) 191 | }) 192 | }) 193 | } 194 | } 195 | 196 | function log(data, description) { 197 | if(import.meta.env.VITE_APP_DEBUG == 'true') { 198 | console.log(description) 199 | console.log(data) 200 | } 201 | } 202 | 203 | export default Api 204 | -------------------------------------------------------------------------------- /src/Utils/blocks.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { v4 as uuidv4 } from 'uuid' 3 | 4 | const groups = ref([ 5 | { 6 | uuid: uuidv4(), 7 | title: 'Heroes', 8 | blocks: [ 9 | { 10 | uuid: uuidv4(), 11 | type: 'hero', 12 | model: 'image-left', 13 | componentName: 'HeroImageLeft', 14 | optionsComponentName: 'HeroImageLeftOptions', 15 | description: 'Image left', 16 | thumbUrl: '/images/thumbs/v1/heroes/image-right.png', 17 | data: { 18 | darkMode: false, 19 | title: 'Compeling title about your product!!!', 20 | subtitle: 'Describe the main benefit you product delivers to your customers.', 21 | imageUrl: '/images/samples/v1/heros/cover.png', 22 | buttonText: 'Buy NOW!!!', 23 | buttonLink: 'https://google.com', 24 | } 25 | }, 26 | { 27 | uuid: uuidv4(), 28 | type: 'hero', 29 | model: 'form-left', 30 | componentName: 'HeroFormLeft', 31 | optionsComponentName: 'HeroFormLeftOptions', 32 | description: 'Form left', 33 | thumbUrl: '/images/thumbs/v1/heroes/form-right.png', 34 | data: { 35 | darkMode: false, 36 | title: 'Compeling title about your product!!!', 37 | subtitle: 'Describe the main benefit you product delivers to your customers.', 38 | buttonText: 'I want a copy!!!', 39 | } 40 | }, 41 | { 42 | uuid: uuidv4(), 43 | type: 'hero', 44 | model: 'video-center', 45 | componentName: 'HeroVideoCenter', 46 | optionsComponentName: 'HeroVideoCenterOptions', 47 | description: 'Video center', 48 | thumbUrl: '/images/thumbs/v1/heroes/video-center.png', 49 | data: { 50 | darkMode: false, 51 | title: 'Compeling title about your product!!!', 52 | subtitle: 'Describe the main benefit you product delivers to your customers.', 53 | youtubeVideoCode: '5DVDewOReoY', 54 | } 55 | }, 56 | ] 57 | }, 58 | { 59 | uuid: uuidv4(), 60 | title: 'Features', 61 | blocks: [ 62 | { 63 | uuid: uuidv4(), 64 | type: 'features', 65 | model: 'grid-3', 66 | componentName: 'FeaturesGrid3', 67 | optionsComponentName: 'FeaturesGrid3Options', 68 | description: 'Grid 3x1', 69 | thumbUrl: '/images/thumbs/v1/features/grid-3.png', 70 | data: { 71 | darkMode: false, 72 | title: 'Main benefits', 73 | list: [ 74 | { 75 | uuid: uuidv4(), 76 | title: 'Features 1', 77 | description: 'Description of features 1 should go here, ideally no more than 50 lines' 78 | }, 79 | { 80 | uuid: uuidv4(), 81 | title: 'Features 2', 82 | description: 'Description of features 1 should go here, ideally no more than 50 lines' 83 | }, 84 | { 85 | uuid: uuidv4(), 86 | title: 'Features 3', 87 | description: 'Description of features 1 should go here, ideally no more than 50 lines' 88 | }, 89 | { 90 | uuid: uuidv4(), 91 | title: 'Features 4', 92 | description: 'Description of features 1 should go here, ideally no more than 50 lines' 93 | }, 94 | { 95 | uuid: uuidv4(), 96 | title: 'Features 5', 97 | description: 'Description of features 1 should go here, ideally no more than 50 lines' 98 | }, 99 | ] 100 | } 101 | }, 102 | ] 103 | }, 104 | { 105 | uuid: uuidv4(), 106 | title: 'Authors', 107 | blocks: [ 108 | { 109 | uuid: uuidv4(), 110 | type: 'author', 111 | model: 'image-left', 112 | componentName: 'AuthorImageLeft', 113 | optionsComponentName: 'AuthorImageLeftOptions', 114 | description: 'Image Left', 115 | thumbUrl: '/images/thumbs/v1/authors/image-left.png', 116 | data: { 117 | darkMode: false, 118 | title: 'About the author', 119 | description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Debitis provident, itaque doloribus earum nostrum iste nesciunt excepturi neque eius, numquam alias eos, delectus quo illum eaque consequuntur consectetur! Sequi soluta porro, magni possimus a tempore quas rem commodi aut pariatur quaerat natus sunt, nihil voluptate maxime beatae atque assumenda similique!', 120 | imageUrl: '/images/samples/v1/authors/sample.jpg' 121 | } 122 | }, 123 | ] 124 | }, 125 | { 126 | uuid: uuidv4(), 127 | title: 'Buy', 128 | blocks: [ 129 | { 130 | uuid: uuidv4(), 131 | type: 'buy', 132 | model: 'simple', 133 | componentName: 'BuySimple', 134 | optionsComponentName: 'BuySimpleOptions', 135 | description: 'Simple', 136 | thumbUrl: '/images/thumbs/v1/buy/simple.png', 137 | data: { 138 | darkMode: true, 139 | title: 'Secure you copy right NOW!', 140 | subtitle: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Repudiandae, ex.', 141 | buttonLink: 'https://google.com', 142 | buttonText: 'Buy NOW', 143 | } 144 | }, 145 | ] 146 | }, 147 | { 148 | uuid: uuidv4(), 149 | title: 'FAQ', 150 | blocks: [ 151 | { 152 | uuid: uuidv4(), 153 | type: 'faq', 154 | model: 'list', 155 | componentName: 'FAQList', 156 | optionsComponentName: 'FAQListOptions', 157 | description: 'List', 158 | thumbUrl: '/images/thumbs/v1/faq/list.png', 159 | data: { 160 | darkMode: false, 161 | title: 'Frequently Asked Questions', 162 | list: [ 163 | { 164 | uuid: uuidv4(), 165 | question: 'Question 1', 166 | answer: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum!', 167 | }, 168 | { 169 | uuid: uuidv4(), 170 | question: 'Question 2', 171 | answer: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum!', 172 | }, 173 | { 174 | uuid: uuidv4(), 175 | question: 'Question 3', 176 | answer: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum!', 177 | }, 178 | ] 179 | } 180 | }, 181 | { 182 | uuid: uuidv4(), 183 | type: 'faq', 184 | model: 'collapse', 185 | componentName: 'FAQCollapse', 186 | optionsComponentName: 'FAQCollapseOptions', 187 | description: 'Collapse', 188 | thumbUrl: '/images/thumbs/v1/faq/collapse.png', 189 | data: { 190 | darkMode: false, 191 | title: 'Dúvidas mais Frequentes', 192 | list: [ 193 | { 194 | uuid: uuidv4(), 195 | question: 'Question 1 goes here', 196 | answer: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 197 | }, 198 | { 199 | uuid: uuidv4(), 200 | question: 'Question 2 goes here', 201 | answer: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 202 | }, 203 | { 204 | uuid: uuidv4(), 205 | question: 'Question 3 goes here', 206 | answer: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 207 | }, 208 | { 209 | uuid: uuidv4(), 210 | question: 'Question 4 goes here', 211 | answer: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 212 | }, 213 | { 214 | uuid: uuidv4(), 215 | question: 'Question 5 goes here', 216 | answer: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 217 | }, 218 | ] 219 | } 220 | }, 221 | ] 222 | }, 223 | { 224 | uuid: uuidv4(), 225 | title: 'Chapters', 226 | blocks: [ 227 | { 228 | uuid: uuidv4(), 229 | type: 'chapters', 230 | model: 'collapse', 231 | componentName: 'ChaptersCollapse', 232 | optionsComponentName: 'ChaptersCollapseOptions', 233 | description: 'Collapse', 234 | thumbUrl: '/images/thumbs/v1/chapters/collapse.png', 235 | data: { 236 | darkMode: false, 237 | title: 'The Chapters', 238 | imageUrl: '/images/samples/v1/chapters/ebook-cover.png', 239 | list: [ 240 | { 241 | uuid: uuidv4(), 242 | title: 'Chapter 1 - Lorem ipsum dolor sit amet', 243 | description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 244 | }, 245 | { 246 | uuid: uuidv4(), 247 | title: 'Chapter 2 - Lorem ipsum dolor sit amet', 248 | description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 249 | }, 250 | { 251 | uuid: uuidv4(), 252 | title: 'Chapter 3 - Lorem ipsum dolor sit amet', 253 | description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 254 | }, 255 | { 256 | uuid: uuidv4(), 257 | title: 'Chapter 4 - Lorem ipsum dolor sit amet', 258 | description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 259 | }, 260 | { 261 | uuid: uuidv4(), 262 | title: 'Chapter 5 - Lorem ipsum dolor sit amet', 263 | description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit perspiciatis sint enim autem quia minima saepe voluptatum sit ex quidem neque atque iusto, impedit error? Cumque, obcaecati! Ratione, magni corporis!', 264 | }, 265 | ] 266 | } 267 | }, 268 | ] 269 | }, 270 | { 271 | uuid: uuidv4(), 272 | title: 'Guarantees', 273 | blocks: [ 274 | { 275 | uuid: uuidv4(), 276 | type: 'guarantee', 277 | model: '7-days', 278 | componentName: 'Guarantee7Days', 279 | optionsComponentName: 'Guarantee7DaysOptions', 280 | description: '7 Days', 281 | thumbUrl: '/images/thumbs/v1/guarantee/7-days.png', 282 | data: { 283 | darkMode: false, 284 | title: 'Garantia Incondicional de 7 Dias', 285 | description: 'Esse ebook é tudo que você precisa para começar a vender online e construir uma renda passiva mensal que trabalha 24h por dia, 7 dias por semana automaticamente enquanto vocẽ dorme.', 286 | imageUrl: '/images/samples/v1/guarantee/7-days.png', 287 | } 288 | }, 289 | { 290 | uuid: uuidv4(), 291 | type: 'guarantee', 292 | model: '10-days', 293 | componentName: 'Guarantee10Days', 294 | optionsComponentName: 'Guarantee10DaysOptions', 295 | description: '10 Days', 296 | thumbUrl: '/images/thumbs/v1/guarantee/10-days.png', 297 | data: { 298 | darkMode: false, 299 | title: 'Garantia Incondicional de 10 Dias', 300 | description: 'Esse ebook é tudo que você precisa para começar a vender online e construir uma renda passiva mensal que trabalha 24h por dia, 10 dias por semana automaticamente enquanto vocẽ dorme.', 301 | imageUrl: '/images/samples/v1/guarantee/10-days.png', 302 | } 303 | }, 304 | { 305 | uuid: uuidv4(), 306 | type: 'guarantee', 307 | model: '15-days', 308 | componentName: 'Guarantee15Days', 309 | optionsComponentName: 'Guarantee15DaysOptions', 310 | description: '15 Days', 311 | thumbUrl: '/images/thumbs/v1/guarantee/15-days.png', 312 | data: { 313 | darkMode: false, 314 | title: 'Garantia Incondicional de 15 Dias', 315 | description: 'Esse ebook é tudo que você precisa para começar a vender online e construir uma renda passiva mensal que trabalha 24h por dia, 15 dias por semana automaticamente enquanto vocẽ dorme.', 316 | imageUrl: '/images/samples/v1/guarantee/15-days.png', 317 | } 318 | }, 319 | { 320 | uuid: uuidv4(), 321 | type: 'guarantee', 322 | model: '30-days', 323 | componentName: 'Guarantee30Days', 324 | optionsComponentName: 'Guarantee30DaysOptions', 325 | description: '30 Days', 326 | thumbUrl: '/images/thumbs/v1/guarantee/30-days.png', 327 | data: { 328 | darkMode: false, 329 | title: 'Garantia Incondicional de 30 Dias', 330 | description: 'Esse ebook é tudo que você precisa para começar a vender online e construir uma renda passiva mensal que trabalha 24h por dia, 30 dias por semana automaticamente enquanto vocẽ dorme.', 331 | imageUrl: '/images/samples/v1/guarantee/30-days.png', 332 | } 333 | }, 334 | ] 335 | } 336 | ]) 337 | 338 | export default { 339 | groups 340 | } 341 | -------------------------------------------------------------------------------- /src/Utils/middleware.js: -------------------------------------------------------------------------------- 1 | const Middleware = { 2 | auth: (to, from, next) => { 3 | const isAuthenticated = localStorage.getItem('ez_landingpage_authenticated') 4 | 5 | if (isAuthenticated == 'true') { 6 | next() 7 | } 8 | else { 9 | next({ name: 'Login' }) 10 | } 11 | }, 12 | 13 | guest: (to, from, next) => { 14 | const isAuthenticated = localStorage.getItem('ez_landingpage_authenticated') 15 | 16 | if ((to.name == 'Login' || to.name == 'Register') && isAuthenticated == 'true') { 17 | next({ name: 'Dashboard' }) 18 | } 19 | else { 20 | next() 21 | } 22 | }, 23 | } 24 | 25 | export default Middleware 26 | -------------------------------------------------------------------------------- /src/Utils/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const MUTATION_SET_LANDING_PAGES = 'MUTATION_SET_LANDING_PAGES' 2 | export const MUTATION_ADD_LANDING_PAGE = 'MUTATION_ADD_LANDING_PAGE' 3 | export const MUTATION_DELETE_LANDING_PAGE = 'MUTATION_DELETE_LANDING_PAGE' 4 | export const MUTATION_UPDATE_LANDING_PAGE = 'MUTATION_UPDATE_LANDING_PAGE' 5 | 6 | export const MUTATION_SET_ALERT = 'MUTATION_SET_ALERT' 7 | 8 | export const MUTATION_SET_IMAGES = 'MUTATION_SET_IMAGES' 9 | export const MUTATION_ADD_IMAGE = 'MUTATION_ADD_IMAGE' 10 | export const MUTATION_DELETE_IMAGE = 'MUTATION_DELETE_IMAGE' 11 | -------------------------------------------------------------------------------- /src/Utils/router.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | 3 | import Middleware from './middleware' 4 | 5 | import Login from '../Pages/Auth/Login.vue' 6 | import Register from '../Pages/Auth/Register.vue' 7 | 8 | import App from '../Pages/App/App.vue' 9 | import Dashboard from '../Pages/App/Dashboard/Index.vue' 10 | 11 | import PageBuilder from '../Pages/App/PageBuilder/Builder.vue' 12 | import PagePreview from '../Pages/App/PageBuilder/Preview.vue' 13 | import PageView from '../Pages/Public/LandingPage/View.vue' 14 | 15 | import CreateLandingPage from '../Pages/App/LandingPages/Create.vue' 16 | 17 | import ImageGallery from '../Pages/App/ImageGallery/Index.vue' 18 | 19 | import WebsiteHome from '../Pages/Public/Marketing/Home.vue' 20 | 21 | const routes = [ 22 | // 23 | // Website routes 24 | // 25 | { 26 | path: '/', 27 | name: 'WebsiteHome', 28 | component: WebsiteHome, 29 | }, 30 | 31 | // 32 | // Authentication routes 33 | // 34 | { 35 | path: '/login', 36 | name: 'Login', 37 | component: Login, 38 | beforeEnter: (to, from, next) => { 39 | return Middleware.guest(to, from, next) 40 | } 41 | }, 42 | { 43 | path: '/register', 44 | name: 'Register', 45 | component: Register, 46 | beforeEnter: (to, from, next) => { 47 | return Middleware.guest(to, from, next) 48 | } 49 | }, 50 | 51 | // 52 | // App routes 53 | // 54 | { 55 | path: '/app', 56 | name: 'App', 57 | component: App, 58 | beforeEnter: (to, from, next) => { 59 | return Middleware.auth(to, from, next) 60 | }, 61 | children: [ 62 | { 63 | path: '', 64 | name: 'Dashboard', 65 | component: Dashboard, 66 | beforeEnter: (to, from, next) => { 67 | return Middleware.auth(to, from, next) 68 | }, 69 | }, 70 | { 71 | path: '/app/landing-pages/create', 72 | name: 'CreateLandingPage', 73 | component: CreateLandingPage, 74 | beforeEnter: (to, from, next) => { 75 | return Middleware.auth(to, from, next) 76 | }, 77 | }, 78 | { 79 | path: '/app/builder/:uuid/:type', 80 | name: 'PageBuilder', 81 | component: PageBuilder, 82 | beforeEnter: (to, from, next) => { 83 | return Middleware.auth(to, from, next) 84 | }, 85 | }, 86 | { 87 | path: '/app/preview/:uuid/:type', 88 | name: 'PagePreview', 89 | component: PagePreview, 90 | beforeEnter: (to, from, next) => { 91 | return Middleware.auth(to, from, next) 92 | }, 93 | }, 94 | { 95 | path: '/app/image-gallery', 96 | name: 'ImageGallery', 97 | component: ImageGallery, 98 | beforeEnter: (to, from, next) => { 99 | return Middleware.auth(to, from, next) 100 | }, 101 | }, 102 | ], 103 | }, 104 | 105 | // 106 | // Public landing pages 107 | // 108 | { 109 | path: '/:slug', 110 | name: 'PageView', 111 | component: PageView, 112 | } 113 | ] 114 | 115 | const Router = createRouter({ 116 | history: createWebHistory(import.meta.env.BASE_URL), 117 | routes: routes 118 | }) 119 | 120 | export default Router 121 | -------------------------------------------------------------------------------- /src/Utils/store.js: -------------------------------------------------------------------------------- 1 | import { createStore } from 'vuex'; 2 | import Api from './api' 3 | 4 | import { 5 | MUTATION_SET_LANDING_PAGES, 6 | MUTATION_ADD_LANDING_PAGE, 7 | MUTATION_DELETE_LANDING_PAGE, 8 | MUTATION_UPDATE_LANDING_PAGE, 9 | 10 | MUTATION_SET_ALERT, 11 | 12 | MUTATION_SET_IMAGES, 13 | MUTATION_ADD_IMAGE, 14 | MUTATION_DELETE_IMAGE, 15 | } from './mutation-types' 16 | 17 | import { 18 | ACTION_GET_LANDING_PAGES, 19 | ACTION_GET_LANDING_PAGE_BY_SLUG, 20 | ACTION_DELETE_LANDING_PAGE, 21 | ACTION_CREATE_LANDING_PAGE, 22 | ACTION_UPDATE_DRAFT, 23 | ACTION_UPDATE_PAGES, 24 | 25 | ACTION_DISMISS_ALERT, 26 | ACTION_SHOW_ALERT, 27 | 28 | ACTION_GET_IMAGES, 29 | ACTION_UPLOAD_IMAGE, 30 | ACTION_FIND_IMAGE_BY_UUID, 31 | ACTION_DELETE_IMAGE, 32 | } from './action-types' 33 | 34 | export default createStore({ 35 | state() { 36 | return { 37 | landingPages: null, 38 | images: null, 39 | alert: { 40 | show: false, 41 | type: null, 42 | message: null 43 | } 44 | } 45 | }, 46 | 47 | mutations: { 48 | [MUTATION_SET_LANDING_PAGES](state, payload) { 49 | state.landingPages = payload.landingPages 50 | }, 51 | 52 | [MUTATION_ADD_LANDING_PAGE](state, payload) { 53 | state.landingPages.push(payload.landingPage) 54 | }, 55 | 56 | [MUTATION_DELETE_LANDING_PAGE](state, payload) { 57 | let index = state.landingPages.findIndex(landingPage => landingPage.uuid == payload.uuid) 58 | state.landingPages.splice(index, 1) 59 | }, 60 | 61 | [MUTATION_UPDATE_LANDING_PAGE](state, payload) { 62 | let index = state.landingPages.findIndex(landingPage => landingPage.uuid == payload.landingPage.uuid) 63 | state.landingPages[index] = payload.landingPage 64 | }, 65 | 66 | [MUTATION_SET_ALERT](state, payload) { 67 | state.alert = payload.alert 68 | }, 69 | 70 | [MUTATION_SET_IMAGES](state, payload) { 71 | state.images = payload.images 72 | }, 73 | 74 | [MUTATION_ADD_IMAGE](state, payload) { 75 | state.images.push(payload.image) 76 | }, 77 | 78 | [MUTATION_DELETE_IMAGE](state, payload) { 79 | let index = state.images.findIndex(image => image.uuid == payload.uuid) 80 | state.images.splice(index, 1) 81 | }, 82 | }, 83 | 84 | getters: { 85 | findLandingPage: (state) => (uuid) => { 86 | if(state.landingPages == null) { 87 | return null 88 | } 89 | 90 | return state.landingPages.find(landingPage => landingPage.uuid == uuid) 91 | }, 92 | findImagePageByUuid: (state) => (uuid) => { 93 | if(state.images == null) { 94 | return null 95 | } 96 | 97 | return state.images.find(image => image.uuid == uuid) 98 | }, 99 | }, 100 | 101 | actions: { 102 | [ACTION_GET_LANDING_PAGES]: (context) => { 103 | return new Promise((resolve, reject) => { 104 | if(context.state.landingPages == null) { 105 | Api.fetchLandingPages() 106 | .then(response => { 107 | context.commit(MUTATION_SET_LANDING_PAGES, { landingPages: response.data }) 108 | resolve(response) 109 | }) 110 | .catch(error => { 111 | reject(error) 112 | }) 113 | } 114 | }) 115 | }, 116 | 117 | [ACTION_DELETE_LANDING_PAGE]: (context, uuid) => { 118 | return new Promise((resolve, reject) => { 119 | Api.deleteLandingPage(uuid) 120 | .then(response => { 121 | context.commit(MUTATION_DELETE_LANDING_PAGE, { uuid: uuid }) 122 | resolve(response) 123 | }) 124 | .catch(error => { 125 | reject(error) 126 | }) 127 | }) 128 | }, 129 | 130 | [ACTION_CREATE_LANDING_PAGE]: (context, data) => { 131 | return new Promise((resolve, reject) => { 132 | Api.createLandingPage(data) 133 | .then(response => { 134 | if(context.state.landingPages == null) { 135 | context.dispatch(ACTION_GET_LANDING_PAGES) 136 | .then(() => { 137 | context.commit(MUTATION_ADD_LANDING_PAGE, { landingPage: response.data }) 138 | resolve(response) 139 | }) 140 | .catch(error => { 141 | reject(error) 142 | }) 143 | } 144 | else { 145 | context.commit(MUTATION_ADD_LANDING_PAGE, { landingPage: response.data }) 146 | resolve(response) 147 | } 148 | }) 149 | .catch(error => { 150 | reject(error) 151 | }) 152 | }) 153 | }, 154 | 155 | [ACTION_UPDATE_DRAFT]: (context, data) => { 156 | return new Promise((resolve, reject) => { 157 | Api.updateDraft(data.uuid, data.draft) 158 | .then(response => { 159 | if(context.state.landingPages == null) { 160 | context.dispatch(ACTION_GET_LANDING_PAGES) 161 | .then(() => { 162 | resolve(response) 163 | }) 164 | .catch(error => { 165 | reject(error) 166 | }) 167 | } 168 | else { 169 | context.commit(MUTATION_UPDATE_LANDING_PAGE, { landingPage: response.data }) 170 | resolve(response) 171 | } 172 | }) 173 | .catch(error => { 174 | reject(error) 175 | }) 176 | }) 177 | }, 178 | 179 | [ACTION_UPDATE_PAGES]: (context, data) => { 180 | return new Promise((resolve, reject) => { 181 | Api.updatePages(data.uuid, data.pages) 182 | .then(response => { 183 | if(context.state.landingPages == null) { 184 | context.dispatch(ACTION_GET_LANDING_PAGES) 185 | .then(() => { 186 | resolve(response) 187 | }) 188 | .catch(error => { 189 | reject(error) 190 | }) 191 | } 192 | else { 193 | context.commit(MUTATION_UPDATE_LANDING_PAGE, { landingPage: response.data }) 194 | resolve(response) 195 | } 196 | }) 197 | .catch(error => { 198 | reject(error) 199 | }) 200 | }) 201 | }, 202 | 203 | [ACTION_GET_LANDING_PAGE_BY_SLUG]: (context, slug) => { 204 | return new Promise((resolve, reject) => { 205 | Api.getLandingPageBySlug(slug) 206 | .then(response => { 207 | resolve(response.data) 208 | }) 209 | .catch(error => { 210 | reject(error) 211 | }) 212 | }) 213 | }, 214 | 215 | [ACTION_DISMISS_ALERT]: (context) => { 216 | context.commit(MUTATION_SET_ALERT, { 217 | alert: { 218 | show: false, 219 | type: null, 220 | message: null 221 | } 222 | }) 223 | }, 224 | 225 | [ACTION_SHOW_ALERT]: (context, data) => { 226 | context.commit(MUTATION_SET_ALERT, { 227 | alert: { 228 | show: true, 229 | type: data.type, 230 | message: data.message 231 | } 232 | }) 233 | }, 234 | 235 | [ACTION_GET_IMAGES]: (context) => { 236 | return new Promise((resolve, reject) => { 237 | Api.getImages() 238 | .then(response => { 239 | context.commit(MUTATION_SET_IMAGES, {images: response.data}) 240 | resolve(response) 241 | }) 242 | .catch(error => { 243 | reject(error) 244 | }) 245 | }) 246 | }, 247 | 248 | [ACTION_UPLOAD_IMAGE]: (context, data) => { 249 | return new Promise((resolve, reject) => { 250 | Api.uploadImage(data) 251 | .then(response => { 252 | context.commit(MUTATION_ADD_IMAGE, {image: response}) 253 | resolve(response) 254 | }) 255 | .catch(error => { 256 | reject(error) 257 | }) 258 | }) 259 | }, 260 | 261 | [ACTION_FIND_IMAGE_BY_UUID]: (context, slug) => { 262 | return new Promise((resolve, reject) => { 263 | Api.getLandingPageBySlug(slug) 264 | .then(response => { 265 | resolve(response.data) 266 | }) 267 | .catch(error => { 268 | reject(error) 269 | }) 270 | }) 271 | }, 272 | 273 | [ACTION_DELETE_IMAGE]: (context, uuid) => { 274 | return new Promise((resolve, reject) => { 275 | Api.deleteImage(uuid) 276 | .then(response => { 277 | context.commit(MUTATION_DELETE_IMAGE, {uuid: uuid}) 278 | resolve(response.data) 279 | }) 280 | .catch(error => { 281 | reject(error) 282 | }) 283 | }) 284 | }, 285 | 286 | } 287 | }) 288 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import Router from './Utils/router' 4 | import Store from './Utils/store' 5 | import './index.css' 6 | 7 | createApp(App) 8 | .use(Router) 9 | .use(Store) 10 | .mount('#app') 11 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./index.html", 4 | "./src/**/*.{vue,js,ts,jsx,tsx}", 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [ 10 | require('daisyui'), 11 | ], 12 | daisyui: { 13 | styled: true, 14 | themes: [ 15 | { 16 | 'custom': { 17 | 'primary': '#1E40AF', 18 | 'primary-focus': '#345DE3', 19 | 'primary-content': '#FFFFFF', 20 | 'secondary': '#7E22CE', 21 | 'secondary-focus': '#9B38F2', 22 | 'secondary-content': '#FFFFFF', 23 | 'accent': '#EDE6E1', 24 | 'accent-focus': '#C1B5B5', 25 | 'accent-content': '#000000', 26 | 'neutral': '#3d4451', 27 | 'neutral-focus': '#2a2e37', 28 | 'neutral-content': '#ffffff', 29 | 'base-100': '#ffffff', 30 | 'base-200': '#f9fafb', 31 | 'base-300': '#d1d5db', 32 | 'base-content': '#1f2937', 33 | 'info': '#2094f3', 34 | 'success': '#009485', 35 | 'warning': '#ff9900', 36 | 'error': '#ff5724', 37 | }, 38 | }, 39 | ], 40 | base: true, 41 | utils: true, 42 | logs: true, 43 | rtl: false, 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()] 7 | }) 8 | --------------------------------------------------------------------------------