├── src ├── alpine.js └── tailwind.css ├── public ├── assets │ ├── img │ │ ├── dashboard.png │ │ ├── favicon.ico │ │ ├── login-office.jpeg │ │ ├── login-office-dark.jpeg │ │ ├── create-account-office.jpeg │ │ ├── forgot-password-office.jpeg │ │ ├── create-account-office-dark.jpeg │ │ ├── forgot-password-office-dark.jpeg │ │ ├── twitter.svg │ │ └── github.svg │ └── js │ │ ├── charts-bars.js │ │ ├── charts-pie.js │ │ ├── focus-trap.js │ │ ├── init-alpine.js │ │ └── charts-lines.js ├── js │ ├── store.js │ ├── store_fetch.js │ └── main.js ├── store0.html ├── pages │ ├── forgot-password.html │ ├── login.html │ ├── create-account.html │ ├── blank.html │ └── 404.html └── index0.html ├── postcss.config.js ├── limbo ├── limbo.html └── select.html ├── package.json ├── .gitignore ├── README.md ├── tailwind.config.js └── db.json /src/alpine.js: -------------------------------------------------------------------------------- 1 | import Alpine from 'alpinejs' 2 | 3 | window.Alpine = Alpine 4 | 5 | Alpine.start() 6 | -------------------------------------------------------------------------------- /public/assets/img/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/alpinejs-tailwindcss-example/HEAD/public/assets/img/dashboard.png -------------------------------------------------------------------------------- /public/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/alpinejs-tailwindcss-example/HEAD/public/assets/img/favicon.ico -------------------------------------------------------------------------------- /public/assets/img/login-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/alpinejs-tailwindcss-example/HEAD/public/assets/img/login-office.jpeg -------------------------------------------------------------------------------- /public/assets/img/login-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/alpinejs-tailwindcss-example/HEAD/public/assets/img/login-office-dark.jpeg -------------------------------------------------------------------------------- /public/assets/img/create-account-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/alpinejs-tailwindcss-example/HEAD/public/assets/img/create-account-office.jpeg -------------------------------------------------------------------------------- /public/assets/img/forgot-password-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/alpinejs-tailwindcss-example/HEAD/public/assets/img/forgot-password-office.jpeg -------------------------------------------------------------------------------- /public/assets/img/create-account-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/alpinejs-tailwindcss-example/HEAD/public/assets/img/create-account-office-dark.jpeg -------------------------------------------------------------------------------- /public/assets/img/forgot-password-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/alpinejs-tailwindcss-example/HEAD/public/assets/img/forgot-password-office-dark.jpeg -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('tailwindcss'), 4 | require('autoprefixer'), 5 | require('cssnano')({ 6 | preset: 'default', 7 | }), 8 | ], 9 | } 10 | -------------------------------------------------------------------------------- /src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer components { 6 | .input { 7 | @apply w-full h-10 px-3 text-base bg-white placeholder-gray-400 border border-gray-300 rounded-lg shadow-sm focus:border-indigo-500 focus:ring-indigo-500; 8 | } 9 | } -------------------------------------------------------------------------------- /public/js/store.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("alpine:init", () => { 2 | Alpine.store("getCategories", { 3 | categories: [ 4 | { 5 | id: 1, 6 | title: "notebook", 7 | }, 8 | { 9 | id: 2, 10 | title: "pc", 11 | }, 12 | ], 13 | }); 14 | 15 | Alpine.data("getNewCategories", () => ({ 16 | newCategories: [], 17 | init() { 18 | this.newCategories = this.$store.getCategories.categories; 19 | }, 20 | })); 21 | }); 22 | -------------------------------------------------------------------------------- /public/assets/img/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/img/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/js/store_fetch.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("alpine:init", () => { 2 | Alpine.store("getCategories", { 3 | categories: [], 4 | url: "http://localhost:3000/categories", 5 | init() { 6 | fetch(this.url) 7 | .then((response) => response.json()) 8 | .then((data) => (this.categories = data)); 9 | }, 10 | }); 11 | 12 | Alpine.data("getProducts", () => ({ 13 | products: [], 14 | url: "http://localhost:3000/products", 15 | init() { 16 | fetch(this.url) 17 | .then((response) => response.json()) 18 | .then((data) => (this.products = data)); 19 | }, 20 | teste() { 21 | console.log("this.$store.getCategories.categories"); 22 | console.table(this.$store.getCategories.categories); 23 | }, 24 | })); 25 | }); 26 | -------------------------------------------------------------------------------- /public/assets/js/charts-bars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For usage, visit Chart.js docs https://www.chartjs.org/docs/latest/ 3 | */ 4 | const barConfig = { 5 | type: "bar", 6 | data: { 7 | labels: ["January", "February", "March", "April", "May", "June", "July"], 8 | datasets: [ 9 | { 10 | label: "Shoes", 11 | backgroundColor: "#0694a2", 12 | // borderColor: window.chartColors.red, 13 | borderWidth: 1, 14 | data: [-3, 14, 52, 74, 33, 90, 70], 15 | }, 16 | { 17 | label: "Bags", 18 | backgroundColor: "#7e3af2", 19 | // borderColor: window.chartColors.blue, 20 | borderWidth: 1, 21 | data: [66, 33, 43, 12, 54, 62, 84], 22 | }, 23 | ], 24 | }, 25 | options: { 26 | responsive: true, 27 | legend: { 28 | display: false, 29 | }, 30 | }, 31 | }; 32 | 33 | const barsCtx = document.getElementById("bars"); 34 | window.myBar = new Chart(barsCtx, barConfig); 35 | -------------------------------------------------------------------------------- /public/assets/js/charts-pie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For usage, visit Chart.js docs https://www.chartjs.org/docs/latest/ 3 | */ 4 | const pieConfig = { 5 | type: "doughnut", 6 | data: { 7 | datasets: [ 8 | { 9 | data: [33, 33, 33], 10 | /** 11 | * These colors come from Tailwind CSS palette 12 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette 13 | */ 14 | backgroundColor: ["#0694a2", "#1c64f2", "#7e3af2"], 15 | label: "Dataset 1", 16 | }, 17 | ], 18 | labels: ["Shoes", "Shirts", "Bags"], 19 | }, 20 | options: { 21 | responsive: true, 22 | cutoutPercentage: 80, 23 | /** 24 | * Default legends are ugly and impossible to style. 25 | * See examples in charts.html to add your own legends 26 | * */ 27 | legend: { 28 | display: false, 29 | }, 30 | }, 31 | }; 32 | 33 | // change this to the id of your chart element in HMTL 34 | const pieCtx = document.getElementById("pie"); 35 | window.myPie = new Chart(pieCtx, pieConfig); 36 | -------------------------------------------------------------------------------- /limbo/limbo.html: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 44 | 45 | 62 | -------------------------------------------------------------------------------- /public/assets/js/focus-trap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Limit focus to focusable elements inside `element` 3 | * @param {HTMLElement} element - DOM element to focus trap inside 4 | * @return {Function} cleanup function 5 | */ 6 | function focusTrap(element) { 7 | const focusableElements = getFocusableElements(element); 8 | const firstFocusableEl = focusableElements[0]; 9 | const lastFocusableEl = focusableElements[focusableElements.length - 1]; 10 | 11 | // Wait for the case the element was not yet rendered 12 | setTimeout(() => firstFocusableEl.focus(), 50); 13 | 14 | /** 15 | * Get all focusable elements inside `element` 16 | * @param {HTMLElement} element - DOM element to focus trap inside 17 | * @return {HTMLElement[]} List of focusable elements 18 | */ 19 | function getFocusableElements(element = document) { 20 | return [ 21 | ...element.querySelectorAll( 22 | 'a, button, details, input, select, textarea, [tabindex]:not([tabindex="-1"])' 23 | ), 24 | ].filter((e) => !e.hasAttribute("disabled")); 25 | } 26 | 27 | function handleKeyDown(e) { 28 | const TAB = 9; 29 | const isTab = e.key.toLowerCase() === "tab" || e.keyCode === TAB; 30 | 31 | if (!isTab) return; 32 | 33 | if (e.shiftKey) { 34 | if (document.activeElement === firstFocusableEl) { 35 | lastFocusableEl.focus(); 36 | e.preventDefault(); 37 | } 38 | } else { 39 | if (document.activeElement === lastFocusableEl) { 40 | firstFocusableEl.focus(); 41 | e.preventDefault(); 42 | } 43 | } 44 | } 45 | 46 | element.addEventListener("keydown", handleKeyDown); 47 | 48 | return function cleanup() { 49 | element.removeEventListener("keydown", handleKeyDown); 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alpinejs-tailwindcss-example", 3 | "version": "1.0.0", 4 | "description": "Alpine.js TailwindCSS Example", 5 | "main": "index.js", 6 | "scripts": { 7 | "build-css": "npx tailwindcss -i src/tailwind.css -o public/dist/tailwind.css", 8 | "build-css-watch": "npx tailwindcss -i src/tailwind.css -o public/dist/tailwind.css --watch", 9 | "build-js": "npx esbuild src/alpine.js --outfile=public/dist/alpine.js --bundle", 10 | "build-js-watch": "npx esbuild src/alpine.js --outfile=public/dist/alpine.js --bundle --watch", 11 | "build": "npx tailwindcss -i src/tailwind.css -o public/dist/tailwind.css; npx esbuild src/alpine.js --outfile=public/dist/alpine.js --bundle; npx prettier --write public/", 12 | "lint": "npx prettier --write public/", 13 | "tailwind": "tailwindcss build public/assets/css/tailwind.css -o public/assets/css/tailwind.output.css", 14 | "cz": "git-cz", 15 | "release": "release-it" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/rg3915/alpinejs-tailwindcss-example.git" 20 | }, 21 | "author": "Regis Santos, Estevan Maito ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/rg3915/alpinejs-tailwindcss-example/issues" 25 | }, 26 | "homepage": "https://github.com/rg3915/alpinejs-tailwindcss-example#readme", 27 | "devDependencies": { 28 | "@tailwindcss/aspect-ratio": "^0.4.2", 29 | "@tailwindcss/forms": "^0.5.3", 30 | "@tailwindcss/typography": "^0.5.7", 31 | "autoprefixer": "^10.4.13", 32 | "postcss": "^8.4.18", 33 | "prettier": "2.7.1", 34 | "tailwindcss": "^3.2.1" 35 | }, 36 | "dependencies": { 37 | "alpinejs": "^3.10.5", 38 | "esbuild": "^0.15.12", 39 | "tailwindcss-multi-theme": "^1.0.4", 40 | "tw-elements": "^1.0.0-alpha12" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/assets/js/init-alpine.js: -------------------------------------------------------------------------------- 1 | function data() { 2 | function getThemeFromLocalStorage() { 3 | // if user already changed the theme, use it 4 | if (window.localStorage.getItem("dark")) { 5 | return JSON.parse(window.localStorage.getItem("dark")); 6 | } 7 | 8 | // else return their preferences 9 | return ( 10 | !!window.matchMedia && 11 | window.matchMedia("(prefers-color-scheme: dark)").matches 12 | ); 13 | } 14 | 15 | function setThemeToLocalStorage(value) { 16 | window.localStorage.setItem("dark", value); 17 | } 18 | 19 | return { 20 | dark: getThemeFromLocalStorage(), 21 | toggleTheme() { 22 | this.dark = !this.dark; 23 | setThemeToLocalStorage(this.dark); 24 | }, 25 | isSideMenuOpen: false, 26 | toggleSideMenu() { 27 | this.isSideMenuOpen = !this.isSideMenuOpen; 28 | }, 29 | closeSideMenu() { 30 | this.isSideMenuOpen = false; 31 | }, 32 | isNotificationsMenuOpen: false, 33 | toggleNotificationsMenu() { 34 | this.isNotificationsMenuOpen = !this.isNotificationsMenuOpen; 35 | }, 36 | closeNotificationsMenu() { 37 | this.isNotificationsMenuOpen = false; 38 | }, 39 | isProfileMenuOpen: false, 40 | toggleProfileMenu() { 41 | this.isProfileMenuOpen = !this.isProfileMenuOpen; 42 | }, 43 | closeProfileMenu() { 44 | this.isProfileMenuOpen = false; 45 | }, 46 | isPagesMenuOpen: false, 47 | togglePagesMenu() { 48 | this.isPagesMenuOpen = !this.isPagesMenuOpen; 49 | }, 50 | // Modal 51 | isModalOpen: false, 52 | trapCleanup: null, 53 | openModal() { 54 | this.isModalOpen = true; 55 | this.trapCleanup = focusTrap(document.querySelector("#modal")); 56 | }, 57 | closeModal() { 58 | this.isModalOpen = false; 59 | this.trapCleanup(); 60 | }, 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /public/assets/js/charts-lines.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For usage, visit Chart.js docs https://www.chartjs.org/docs/latest/ 3 | */ 4 | const lineConfig = { 5 | type: "line", 6 | data: { 7 | labels: ["January", "February", "March", "April", "May", "June", "July"], 8 | datasets: [ 9 | { 10 | label: "Organic", 11 | /** 12 | * These colors come from Tailwind CSS palette 13 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette 14 | */ 15 | backgroundColor: "#0694a2", 16 | borderColor: "#0694a2", 17 | data: [43, 48, 40, 54, 67, 73, 70], 18 | fill: false, 19 | }, 20 | { 21 | label: "Paid", 22 | fill: false, 23 | /** 24 | * These colors come from Tailwind CSS palette 25 | * https://tailwindcss.com/docs/customizing-colors/#default-color-palette 26 | */ 27 | backgroundColor: "#7e3af2", 28 | borderColor: "#7e3af2", 29 | data: [24, 50, 64, 74, 52, 51, 65], 30 | }, 31 | ], 32 | }, 33 | options: { 34 | responsive: true, 35 | /** 36 | * Default legends are ugly and impossible to style. 37 | * See examples in charts.html to add your own legends 38 | * */ 39 | legend: { 40 | display: false, 41 | }, 42 | tooltips: { 43 | mode: "index", 44 | intersect: false, 45 | }, 46 | hover: { 47 | mode: "nearest", 48 | intersect: true, 49 | }, 50 | scales: { 51 | x: { 52 | display: true, 53 | scaleLabel: { 54 | display: true, 55 | labelString: "Month", 56 | }, 57 | }, 58 | y: { 59 | display: true, 60 | scaleLabel: { 61 | display: true, 62 | labelString: "Value", 63 | }, 64 | }, 65 | }, 66 | }, 67 | }; 68 | 69 | // change this to the id of your chart element in HMTL 70 | const lineCtx = document.getElementById("line"); 71 | window.myLine = new Chart(lineCtx, lineConfig); 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /public/store0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | Windmill Dashboard 12 | 13 | Alpine.js and TailwindCSS 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | Voltar 39 |
40 | 41 |
42 |

Categorias

43 |
    44 | 50 |
51 |
52 | 53 | 54 |
55 |

56 | Produtos (clique aqui) 57 |

58 |
    59 | 62 |
63 |
64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /public/pages/forgot-password.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Forgot password - Windmill Dashboard 8 | 12 | 13 | 17 | 18 | 19 | 20 |
21 |
24 |
25 |
26 | 32 | 38 |
39 |
40 |
41 |

44 | Forgot password 45 |

46 | 53 | 54 | 55 | 59 | Recover password 60 | 61 |
62 |
63 |
64 |
65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /limbo/select.html: -------------------------------------------------------------------------------- 1 | 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alpine.js e TailwindCSS 2 | 3 | Alpine.js TailwindCSS Example 4 | 5 | ## Instalação 6 | 7 | 8 | ### Use [node](https://nodejs.org/en/) com [nvm](https://github.com/nvm-sh/nvm). 9 | 10 | ``` 11 | nvm use 18.3.0 12 | ``` 13 | 14 | 15 | ### Instale o [TailwindCSS](https://tailwindcss.com/) 16 | 17 | ``` 18 | npm init 19 | npm i -D tailwindcss postcss autoprefixer 20 | npx tailwindcss init -p 21 | ``` 22 | 23 | ```js 24 | /** @type {import('tailwindcss').Config} */ 25 | module.exports = { 26 | content: [ 27 | "./public/**/*.html", 28 | "./src/**/*.{js,ts,vue}" 29 | ], 30 | darkMode: 'class', 31 | theme: { 32 | extend: {}, 33 | }, 34 | plugins: [], 35 | } 36 | ``` 37 | 38 | ``` 39 | touch src/tailwind.css 40 | ``` 41 | 42 | ```css 43 | @tailwind base; 44 | @tailwind components; 45 | @tailwind utilities; 46 | ``` 47 | 48 | ### Instalando plugins e fontes 49 | 50 | ``` 51 | npm install -D @tailwindcss/typography 52 | ``` 53 | 54 | [Poppings](https://blog.logrocket.com/how-to-use-custom-fonts-tailwind-css) 55 | 56 | ```js 57 | /** @type {import('tailwindcss').Config} */ 58 | const defaultTheme = require('tailwindcss/defaultTheme') 59 | 60 | module.exports = { 61 | content: [ 62 | "./public/**/*.html", 63 | "./src/**/*.{js,ts,vue}" 64 | ], 65 | darkMode: 'class', 66 | theme: { 67 | extend: { 68 | fontFamily: { 69 | sans: ['Inter var', ...defaultTheme.fontFamily.sans], 70 | poppins: ['Poppins', 'sans-serif'] 71 | }, 72 | }, 73 | }, 74 | plugins: [ 75 | require('@tailwindcss/typography'), 76 | ], 77 | } 78 | ``` 79 | 80 | ### Instalando forms 81 | 82 | ``` 83 | npm i -D @tailwindcss/forms 84 | ``` 85 | 86 | ```js 87 | // tailwind.config.js 88 | module.exports = { 89 | theme: { 90 | // ... 91 | }, 92 | plugins: [ 93 | require('@tailwindcss/forms'), 94 | // ... 95 | ], 96 | } 97 | ``` 98 | 99 | ### Instalando aspect-ratio 100 | 101 | ``` 102 | npm i -D @tailwindcss/aspect-ratio 103 | ``` 104 | 105 | ```js 106 | // tailwind.config.js 107 | module.exports = { 108 | theme: { 109 | // ... 110 | }, 111 | corePlugins: { 112 | aspectRatio: false, 113 | }, 114 | plugins: [ 115 | require('@tailwindcss/aspect-ratio'), 116 | // ... 117 | ], 118 | } 119 | ``` 120 | 121 | ### Instalando Tailwind CSS Multi Theme 122 | 123 | https://github.com/estevanmaito/tailwindcss-multi-theme#tailwind-css-multi-theme 124 | 125 | ``` 126 | npm install tailwindcss-multi-theme 127 | ``` 128 | 129 | ### Instalando o [Alpine.js](https://alpinejs.dev/) 130 | 131 | ``` 132 | npm i alpinejs 133 | ``` 134 | 135 | Crie `alpine.js` 136 | 137 | ``` 138 | mkdir src 139 | touch src/alpine.js 140 | ``` 141 | 142 | ```js 143 | // alpine.js 144 | import Alpine from 'alpinejs' 145 | 146 | window.Alpine = Alpine 147 | 148 | Alpine.start() 149 | ``` 150 | 151 | ### Instalando esbuild 152 | 153 | ``` 154 | npm i esbuild 155 | ``` 156 | 157 | Edite `package.json` 158 | 159 | ```json 160 | "scripts": { 161 | "build-css": "npx tailwindcss -i src/tailwind.css -o public/dist/tailwind.css --watch", 162 | "build-js": "npx esbuild src/alpine.js --outfile=public/dist/alpine.js --bundle --watch", 163 | "build": "npx tailwindcss -i src/tailwind.css -o public/dist/tailwind.css; npx esbuild src/alpine.js --outfile=public/dist/alpine.js --bundle; npx prettier --write public/", 164 | "lint": "npx prettier --write public/" 165 | } 166 | ``` 167 | 168 | ### Rodando a aplicação com [http-server](https://www.npmjs.com/package/http-server) 169 | 170 | ``` 171 | mkdir public 172 | touch public/main.js 173 | touch public/index.html 174 | 175 | npm i -g http-server 176 | 177 | http-server 178 | ``` 179 | 180 | Veja [public/main.js](public/main.js) 181 | 182 | 183 | ### Dados com [json-server](https://www.npmjs.com/package/json-server) 184 | 185 | ``` 186 | npm i -g json-server 187 | ``` 188 | 189 | Crie `db.json` 190 | 191 | ``` 192 | touch db.json 193 | ``` 194 | 195 | ```json 196 | { 197 | "todos": [ 198 | { 199 | "id": 1, 200 | "task": "One" 201 | }, 202 | { 203 | "id": 2, 204 | "task": "Two" 205 | }, 206 | { 207 | "id": 3, 208 | "task": "Three" 209 | } 210 | ] 211 | } 212 | ``` 213 | 214 | Rode o json-server 215 | 216 | ``` 217 | json-server --watch db.json 218 | ``` 219 | 220 | ### [Prettier](https://prettier.io/) 221 | 222 | ``` 223 | npm install --save-dev --save-exact prettier 224 | ``` 225 | 226 | ``` 227 | echo {}> .prettierrc.json 228 | ``` 229 | 230 | ``` 231 | cat << EOF > .prettierignore 232 | # Ignore artifacts: 233 | build 234 | coverage 235 | EOF 236 | ``` 237 | 238 | #### Rodando o Prettier 239 | 240 | ``` 241 | npx prettier --write public/ 242 | ou 243 | npm run lint 244 | ``` 245 | 246 | ### Windmill Dashboard 247 | 248 | https://github.com/estevanmaito/windmill-dashboard 249 | 250 | https://windmill-dashboard.vercel.app/ 251 | 252 | https://api.github.com/repos/estevanmaito/windmill-dashboard/zipball/1.0.2 253 | 254 | https://tailwind-elements.com/ 255 | 256 | https://apexcharts.com/javascript-chart-demos/ 257 | 258 | https://www.uifaces.co/browse-avatars/ 259 | 260 | ![](https://windmillui.com/img/Dashboard.png) 261 | 262 | 263 | ### Links 264 | 265 | https://github.com/alpine-collective/awesome 266 | 267 | https://github.com/thecodeholic/tailwindcss-ecommerce/blob/master/src/app.js 268 | 269 | https://youtu.be/TN0955TvodA 270 | 271 | https://codewithhugo.com/alpine-tips/ 272 | 273 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | const defaultTheme = require('tailwindcss/defaultTheme') 3 | const defaultTheme = require('tailwindcss/defaultTheme') 4 | const plugin = require('tailwindcss/plugin') 5 | const Color = require('color') 6 | 7 | module.exports = { 8 | content: [ 9 | "./public/**/*.html", 10 | "./src/**/*.{js,ts,vue}", 11 | './node_modules/tw-elements/dist/js/**/*.js' 12 | ], 13 | darkMode: 'class', 14 | theme: { 15 | themeVariants: ['dark'], 16 | customForms: (theme) => ({ 17 | default: { 18 | 'input, textarea': { 19 | '&::placeholder': { 20 | color: theme('colors.gray.400'), 21 | }, 22 | }, 23 | }, 24 | }), 25 | colors: { 26 | transparent: 'transparent', 27 | white: '#ffffff', 28 | black: '#000000', 29 | gray: { 30 | '50': '#f9fafb', 31 | '100': '#f4f5f7', 32 | '200': '#e5e7eb', 33 | '300': '#d5d6d7', 34 | '400': '#9e9e9e', 35 | '500': '#707275', 36 | '600': '#4c4f52', 37 | '700': '#24262d', 38 | '800': '#1a1c23', 39 | '900': '#121317', 40 | // default values from Tailwind UI palette 41 | // '300': '#d2d6dc', 42 | // '400': '#9fa6b2', 43 | // '500': '#6b7280', 44 | // '600': '#4b5563', 45 | // '700': '#374151', 46 | // '800': '#252f3f', 47 | // '900': '#161e2e', 48 | }, 49 | 'cool-gray': { 50 | '50': '#fbfdfe', 51 | '100': '#f1f5f9', 52 | '200': '#e2e8f0', 53 | '300': '#cfd8e3', 54 | '400': '#97a6ba', 55 | '500': '#64748b', 56 | '600': '#475569', 57 | '700': '#364152', 58 | '800': '#27303f', 59 | '900': '#1a202e', 60 | }, 61 | red: { 62 | '50': '#fdf2f2', 63 | '100': '#fde8e8', 64 | '200': '#fbd5d5', 65 | '300': '#f8b4b4', 66 | '400': '#f98080', 67 | '500': '#f05252', 68 | '600': '#e02424', 69 | '700': '#c81e1e', 70 | '800': '#9b1c1c', 71 | '900': '#771d1d', 72 | }, 73 | orange: { 74 | '50': '#fff8f1', 75 | '100': '#feecdc', 76 | '200': '#fcd9bd', 77 | '300': '#fdba8c', 78 | '400': '#ff8a4c', 79 | '500': '#ff5a1f', 80 | '600': '#d03801', 81 | '700': '#b43403', 82 | '800': '#8a2c0d', 83 | '900': '#771d1d', 84 | }, 85 | yellow: { 86 | '50': '#fdfdea', 87 | '100': '#fdf6b2', 88 | '200': '#fce96a', 89 | '300': '#faca15', 90 | '400': '#e3a008', 91 | '500': '#c27803', 92 | '600': '#9f580a', 93 | '700': '#8e4b10', 94 | '800': '#723b13', 95 | '900': '#633112', 96 | }, 97 | green: { 98 | '50': '#f3faf7', 99 | '100': '#def7ec', 100 | '200': '#bcf0da', 101 | '300': '#84e1bc', 102 | '400': '#31c48d', 103 | '500': '#0e9f6e', 104 | '600': '#057a55', 105 | '700': '#046c4e', 106 | '800': '#03543f', 107 | '900': '#014737', 108 | }, 109 | teal: { 110 | '50': '#edfafa', 111 | '100': '#d5f5f6', 112 | '200': '#afecef', 113 | '300': '#7edce2', 114 | '400': '#16bdca', 115 | '500': '#0694a2', 116 | '600': '#047481', 117 | '700': '#036672', 118 | '800': '#05505c', 119 | '900': '#014451', 120 | }, 121 | blue: { 122 | '50': '#ebf5ff', 123 | '100': '#e1effe', 124 | '200': '#c3ddfd', 125 | '300': '#a4cafe', 126 | '400': '#76a9fa', 127 | '500': '#3f83f8', 128 | '600': '#1c64f2', 129 | '700': '#1a56db', 130 | '800': '#1e429f', 131 | '900': '#233876', 132 | }, 133 | indigo: { 134 | '50': '#f0f5ff', 135 | '100': '#e5edff', 136 | '200': '#cddbfe', 137 | '300': '#b4c6fc', 138 | '400': '#8da2fb', 139 | '500': '#6875f5', 140 | '600': '#5850ec', 141 | '700': '#5145cd', 142 | '800': '#42389d', 143 | '900': '#362f78', 144 | }, 145 | purple: { 146 | '50': '#f6f5ff', 147 | '100': '#edebfe', 148 | '200': '#dcd7fe', 149 | '300': '#cabffd', 150 | '400': '#ac94fa', 151 | '500': '#9061f9', 152 | '600': '#7e3af2', 153 | '700': '#6c2bd9', 154 | '800': '#5521b5', 155 | '900': '#4a1d96', 156 | }, 157 | pink: { 158 | '50': '#fdf2f8', 159 | '100': '#fce8f3', 160 | '200': '#fad1e8', 161 | '300': '#f8b4d9', 162 | '400': '#f17eb8', 163 | '500': '#e74694', 164 | '600': '#d61f69', 165 | '700': '#bf125d', 166 | '800': '#99154b', 167 | '900': '#751a3d', 168 | }, 169 | }, 170 | extend: { 171 | maxHeight: { 172 | '0': '0', 173 | xl: '36rem', 174 | }, 175 | fontFamily: { 176 | sans: ['Inter var', ...defaultTheme.fontFamily.sans], 177 | poppins: ['Poppins', 'sans-serif'] 178 | }, 179 | }, 180 | }, 181 | variants: { 182 | backgroundColor: [ 183 | 'hover', 184 | 'focus', 185 | 'active', 186 | 'odd', 187 | 'dark', 188 | 'dark:hover', 189 | 'dark:focus', 190 | 'dark:active', 191 | 'dark:odd', 192 | ], 193 | display: ['responsive', 'dark'], 194 | textColor: [ 195 | 'focus-within', 196 | 'hover', 197 | 'active', 198 | 'dark', 199 | 'dark:focus-within', 200 | 'dark:hover', 201 | 'dark:active', 202 | ], 203 | placeholderColor: ['focus', 'dark', 'dark:focus'], 204 | borderColor: ['focus', 'hover', 'dark', 'dark:focus', 'dark:hover'], 205 | divideColor: ['dark'], 206 | boxShadow: ['focus', 'dark:focus'], 207 | }, 208 | corePlugins: { 209 | aspectRatio: false, 210 | }, 211 | plugins: [ 212 | require('@tailwindcss/typography'), 213 | // require('@tailwindcss/forms'), 214 | require('@tailwindcss/aspect-ratio'), 215 | require('tw-elements/dist/plugin'), 216 | require('tailwindcss-multi-theme'), 217 | require('@tailwindcss/custom-forms'), 218 | plugin(({ addUtilities, e, theme, variants }) => { 219 | const newUtilities = {} 220 | Object.entries(theme('colors')).map(([name, value]) => { 221 | if (name === 'transparent' || name === 'current') return 222 | const color = value[300] ? value[300] : value 223 | const hsla = Color(color).alpha(0.45).hsl().string() 224 | 225 | newUtilities[`.shadow-outline-${name}`] = { 226 | 'box-shadow': `0 0 0 3px ${hsla}`, 227 | } 228 | }) 229 | 230 | addUtilities(newUtilities, variants('boxShadow')) 231 | }), 232 | ], 233 | } 234 | -------------------------------------------------------------------------------- /public/pages/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Login - Windmill Dashboard 8 | 12 | 13 | 17 | 18 | 19 | 20 |
21 |
24 |
25 |
26 | 32 | 38 |
39 |
40 |
41 |

44 | Login 45 |

46 | 53 | 61 | 62 | 63 | 67 | Log in 68 | 69 | 70 |
71 | 72 | 87 | 102 | 103 |

104 | 108 | Forgot your password? 109 | 110 |

111 |

112 | 116 | Create account 117 | 118 |

119 |
120 |
121 |
122 |
123 |
124 | 125 | 126 | -------------------------------------------------------------------------------- /public/js/main.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("alpine:init", async () => { 2 | Alpine.store("getCategories", { 3 | url: "http://localhost:3000/categories", 4 | categories: [], 5 | 6 | getAllCategories() { 7 | fetch(this.url) 8 | .then((response) => response.json()) 9 | .then((data) => (this.categories = data)); 10 | }, 11 | }); 12 | 13 | // Funciona 14 | // Alpine.store("getProducts", { 15 | // url: "http://localhost:3000/products", 16 | // products: [], 17 | // getAllProducts() { 18 | // fetch(this.url) 19 | // .then((response) => response.json()) 20 | // .then((data) => (this.products = data)); 21 | // }, 22 | // }); 23 | 24 | // Alpine.store("getProducts", { 25 | // url: "http://localhost:3000/products", 26 | // products: [], 27 | // getAllProducts() { 28 | // fetch(this.url) 29 | // .then((response) => response.json()) 30 | // .then((data) => (this.products = data)); 31 | // }, 32 | // }); 33 | }); 34 | 35 | document.addEventListener("alpine:init", () => { 36 | Alpine.store("getProducts", { 37 | url: "http://localhost:3000/products", 38 | products: [], 39 | 40 | getAllProducts() { 41 | fetch(this.url) 42 | .then((response) => response.json()) 43 | .then((data) => (this.products = data)); 44 | }, 45 | }); 46 | 47 | Alpine.store("getProducts").getAllProducts(); 48 | 49 | const lorem = () => ({ 50 | products: Alpine.store("getProducts").products, 51 | }); 52 | }); 53 | 54 | const getTodos = () => ({ 55 | url: "http://localhost:3000/todos", 56 | todos: [], 57 | task: "", 58 | required: false, 59 | 60 | init() { 61 | fetch(this.url) 62 | .then((response) => response.json()) 63 | .then((data) => (this.todos = data)); 64 | }, 65 | 66 | saveData() { 67 | if (!this.task) { 68 | this.required = true; 69 | return; 70 | } 71 | fetch(this.url, { 72 | method: "POST", 73 | headers: { "Content-Type": "application/json" }, 74 | body: JSON.stringify({ 75 | task: this.task, 76 | }), 77 | }) 78 | .then((response) => response.json()) 79 | .then(() => { 80 | this.init(); 81 | this.task = ""; 82 | }); 83 | }, 84 | 85 | toggleDone(id, value) { 86 | fetch(`http://localhost:3000/todos/${id}`, { 87 | method: "PATCH", 88 | headers: { "Content-Type": "application/json" }, 89 | body: JSON.stringify({ 90 | done: value, 91 | }), 92 | }) 93 | .then((response) => response.json()) 94 | .then(() => { 95 | this.init(); 96 | }); 97 | }, 98 | 99 | deleteTask(id) { 100 | fetch(`http://localhost:3000/todos/${id}`, { 101 | method: "DELETE", 102 | }) 103 | .then((response) => response.json()) 104 | .then(() => { 105 | this.init(); 106 | }); 107 | }, 108 | }); 109 | 110 | const getItems = () => ({ 111 | url: "http://localhost:3000/items", 112 | items: [], 113 | 114 | init() { 115 | fetch(this.url) 116 | .then((response) => response.json()) 117 | .then((data) => (this.items = data)); 118 | } 119 | }); 120 | 121 | const getSales = () => ({ 122 | url: "http://localhost:3000/sale_items", 123 | sales: [], 124 | total: 0, 125 | // newProducts: Alpine.store('getProducts').products, 126 | produto: "", 127 | form: { 128 | product: "", 129 | quantity: "", 130 | price: "", 131 | }, 132 | editTable: true, 133 | saveAuto: true, 134 | 135 | init() { 136 | fetch(this.url) 137 | .then((response) => response.json()) 138 | .then((data) => (this.sales = data)); 139 | }, 140 | 141 | getTotal(sale) { 142 | this.total += sale.quantity * sale.price; 143 | }, 144 | 145 | async findProduct() { 146 | const url = `http://localhost:3000/products/${this.form.product}`; 147 | await fetch(url) 148 | .then((response) => response.json()) 149 | .then((data) => (this.produto = data)); 150 | // Localiza e retorna o objeto product. 151 | // this.products.find((item) => { 152 | // if (this.product == item.id) { 153 | // this.price = item.price; 154 | // this.quantity = Math.floor(Math.random() * 10); 155 | // } 156 | // }); 157 | }, 158 | 159 | async getPrice() { 160 | // return price 161 | await this.findProduct(); 162 | this.form.price = this.produto.price; 163 | this.form.quantity = Math.floor(Math.random() * 10); 164 | }, 165 | 166 | focusInputProduct() { 167 | this.$refs.product.focus(); 168 | }, 169 | 170 | saveData() { 171 | if (!this.form.product) { 172 | this.required = true; 173 | return; 174 | } 175 | this.findProduct(); 176 | this.form.product = this.produto; 177 | // Salva a venda. 178 | fetch(this.url, { 179 | method: "POST", 180 | headers: { "Content-Type": "application/json" }, 181 | body: JSON.stringify({ 182 | product: this.form.product, 183 | quantity: parseInt(this.form.quantity), 184 | price: this.form.price, 185 | edit: false, 186 | }), 187 | }) 188 | .then((response) => response.json()) 189 | .then(() => { 190 | this.init(); 191 | this.form.product = ""; 192 | this.form.quantity = ""; 193 | this.form.price = ""; 194 | this.edit = false; 195 | }); 196 | }, 197 | }); 198 | 199 | const getProductReadOnly = (sale) => ({ 200 | deleteSale(id) { 201 | fetch(`http://localhost:3000/sale_items/${id}`, { 202 | method: "DELETE", 203 | }) 204 | .then((response) => response.json()) 205 | .then(() => { 206 | this.init(); 207 | }); 208 | }, 209 | }); 210 | 211 | const getProductEdition = (sale) => ({ 212 | async findProductEdition() { 213 | const url = `http://localhost:3000/products/${this.sale.product}`; 214 | await fetch(url) 215 | .then((response) => response.json()) 216 | .then((data) => (this.sale.product = data)); 217 | // Localiza e retorna o objeto product. 218 | // this.products.find((item) => { 219 | // if (this.product == item.id) { 220 | // this.price = item.price; 221 | // this.quantity = Math.floor(Math.random() * 10); 222 | // } 223 | // }); 224 | }, 225 | 226 | // Modo Edição 227 | async editSale(item) { 228 | if (!this.saveAuto) { 229 | return; 230 | } 231 | if (!item.product) { 232 | this.required = true; 233 | return; 234 | } 235 | await this.findProductEdition(item); 236 | item.edit = false; 237 | delete item.subtotal; 238 | // Edita a venda. 239 | const payload = { ...item }; 240 | fetch(`${this.url}/${item.id}`, { 241 | method: "PUT", 242 | headers: { "Content-Type": "application/json" }, 243 | body: JSON.stringify(payload), 244 | }) 245 | .then((response) => response.json()) 246 | .then((data) => { 247 | // Todo atualizar os itens 248 | this.sales.find((obj) => { 249 | if (obj.product == data.product) { 250 | item.product = data.product; 251 | } 252 | }); 253 | }); 254 | }, 255 | 256 | deleteSale(id) { 257 | fetch(`http://localhost:3000/sale_items/${id}`, { 258 | method: "DELETE", 259 | }) 260 | .then((response) => response.json()) 261 | .then(() => { 262 | this.init(); 263 | }); 264 | }, 265 | }); 266 | 267 | const getPersons = () => ({ 268 | persons: [ 269 | { id: 1, name: "Huguinho" }, 270 | { id: 2, name: "Zezinho" }, 271 | { id: 3, name: "Luizinho" }, 272 | ], 273 | }); 274 | 275 | const customer = (person) => ({ 276 | open: false, 277 | updated: false, 278 | 279 | toggle() { 280 | this.open = !this.open; 281 | }, 282 | 283 | changeName() { 284 | this.updated = !this.updated; 285 | person.name = "Lorem"; 286 | }, 287 | }); 288 | -------------------------------------------------------------------------------- /public/pages/create-account.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Create account - Windmill Dashboard 8 | 12 | 13 | 17 | 18 | 19 | 20 |
21 |
24 |
25 |
26 | 32 | 38 |
39 |
40 |
41 |

44 | Create account 45 |

46 | 53 | 61 | 71 | 72 |
73 | 83 |
84 | 85 | 86 | 90 | Create account 91 | 92 | 93 |
94 | 95 | 110 | 125 | 126 |

127 | 131 | Already have an account? Login 132 | 133 |

134 |
135 |
136 |
137 |
138 |
139 | 140 | 141 | -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "todos": [ 3 | { 4 | "id": 1, 5 | "task": "One", 6 | "done": true 7 | }, 8 | { 9 | "id": 2, 10 | "task": "Two", 11 | "done": true 12 | }, 13 | { 14 | "task": "The quick brown fox", 15 | "id": 6, 16 | "done": true 17 | }, 18 | { 19 | "task": "over the lazy dog", 20 | "id": 7 21 | }, 22 | { 23 | "task": "Windmill", 24 | "id": 8 25 | }, 26 | { 27 | "task": "Estudar AlpineJS", 28 | "id": 10 29 | } 30 | ], 31 | "items": [ 32 | { 33 | "id": 1, 34 | "title": "The quick brown fox", 35 | "count": 0 36 | }, 37 | { 38 | "id": 2, 39 | "title": "over the lazy dog", 40 | "count": 0 41 | }, 42 | { 43 | "id": 3, 44 | "title": "Windmill", 45 | "count": 0 46 | }, 47 | { 48 | "id": 4, 49 | "title": "AlpineJS", 50 | "count": 0 51 | } 52 | ], 53 | "products": [ 54 | { 55 | "id": 1, 56 | "title": "iPhone 9", 57 | "description": "An apple mobile which is nothing like apple", 58 | "price": 549, 59 | "brand": "Apple", 60 | "thumbnail": "https://dummyjson.com/image/i/products/1/thumbnail.jpg", 61 | "images": [ 62 | "https://dummyjson.com/image/i/products/1/1.jpg", 63 | "https://dummyjson.com/image/i/products/1/2.jpg", 64 | "https://dummyjson.com/image/i/products/1/3.jpg", 65 | "https://dummyjson.com/image/i/products/1/4.jpg", 66 | "https://dummyjson.com/image/i/products/1/thumbnail.jpg" 67 | ] 68 | }, 69 | { 70 | "id": 2, 71 | "title": "iPhone X", 72 | "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", 73 | "price": 899, 74 | "brand": "Apple", 75 | "thumbnail": "https://dummyjson.com/image/i/products/2/thumbnail.jpg", 76 | "images": [ 77 | "https://dummyjson.com/image/i/products/2/1.jpg", 78 | "https://dummyjson.com/image/i/products/2/2.jpg", 79 | "https://dummyjson.com/image/i/products/2/3.jpg", 80 | "https://dummyjson.com/image/i/products/2/thumbnail.jpg" 81 | ] 82 | }, 83 | { 84 | "id": 3, 85 | "title": "Samsung Universe 9", 86 | "description": "Samsung's new variant which goes beyond Galaxy to the Universe", 87 | "price": 1249, 88 | "brand": "Samsung", 89 | "thumbnail": "https://dummyjson.com/image/i/products/3/thumbnail.jpg", 90 | "images": [ 91 | "https://dummyjson.com/image/i/products/3/1.jpg" 92 | ] 93 | }, 94 | { 95 | "id": 4, 96 | "title": "OPPOF19", 97 | "description": "OPPO F19 is officially announced on April 2021.", 98 | "price": 280, 99 | "brand": "OPPO", 100 | "thumbnail": "https://dummyjson.com/image/i/products/4/thumbnail.jpg", 101 | "images": [ 102 | "https://dummyjson.com/image/i/products/4/1.jpg", 103 | "https://dummyjson.com/image/i/products/4/2.jpg", 104 | "https://dummyjson.com/image/i/products/4/3.jpg", 105 | "https://dummyjson.com/image/i/products/4/4.jpg", 106 | "https://dummyjson.com/image/i/products/4/thumbnail.jpg" 107 | ] 108 | }, 109 | { 110 | "id": 5, 111 | "title": "Huawei P30", 112 | "description": "Huawei’s re-badged P30 Pro New Edition was officially unveiled yesterday in Germany and now the device has made its way to the UK.", 113 | "price": 499, 114 | "brand": "Huawei", 115 | "thumbnail": "https://dummyjson.com/image/i/products/5/thumbnail.jpg", 116 | "images": [ 117 | "https://dummyjson.com/image/i/products/5/1.jpg", 118 | "https://dummyjson.com/image/i/products/5/2.jpg", 119 | "https://dummyjson.com/image/i/products/5/3.jpg" 120 | ] 121 | }, 122 | { 123 | "id": 6, 124 | "title": "MacBook Pro", 125 | "description": "MacBook Pro 2021 with mini-LED display may launch between September, November", 126 | "price": 1749, 127 | "brand": "Apple", 128 | "thumbnail": "https://dummyjson.com/image/i/products/6/thumbnail.png", 129 | "images": [ 130 | "https://dummyjson.com/image/i/products/6/1.png", 131 | "https://dummyjson.com/image/i/products/6/2.jpg", 132 | "https://dummyjson.com/image/i/products/6/3.png", 133 | "https://dummyjson.com/image/i/products/6/4.jpg" 134 | ] 135 | }, 136 | { 137 | "id": 7, 138 | "title": "Samsung Galaxy Book", 139 | "description": "Samsung Galaxy Book S (2020) Laptop With Intel Lakefield Chip, 8GB of RAM Launched", 140 | "price": 1499, 141 | "brand": "Samsung", 142 | "thumbnail": "https://dummyjson.com/image/i/products/7/thumbnail.jpg", 143 | "images": [ 144 | "https://dummyjson.com/image/i/products/7/1.jpg", 145 | "https://dummyjson.com/image/i/products/7/2.jpg", 146 | "https://dummyjson.com/image/i/products/7/3.jpg", 147 | "https://dummyjson.com/image/i/products/7/thumbnail.jpg" 148 | ] 149 | }, 150 | { 151 | "id": 8, 152 | "title": "Microsoft Surface Laptop 4", 153 | "description": "Style and speed. Stand out on HD video calls backed by Studio Mics. Capture ideas on the vibrant touchscreen.", 154 | "price": 1499, 155 | "brand": "Microsoft Surface", 156 | "thumbnail": "https://dummyjson.com/image/i/products/8/thumbnail.jpg", 157 | "images": [ 158 | "https://dummyjson.com/image/i/products/8/1.jpg", 159 | "https://dummyjson.com/image/i/products/8/2.jpg", 160 | "https://dummyjson.com/image/i/products/8/3.jpg", 161 | "https://dummyjson.com/image/i/products/8/4.jpg", 162 | "https://dummyjson.com/image/i/products/8/thumbnail.jpg" 163 | ] 164 | }, 165 | { 166 | "id": 9, 167 | "title": "Infinix INBOOK", 168 | "description": "Infinix Inbook X1 Ci3 10th 8GB 256GB 14 Win10 Grey – 1 Year Warranty", 169 | "price": 1099, 170 | "brand": "Infinix", 171 | "thumbnail": "https://dummyjson.com/image/i/products/9/thumbnail.jpg", 172 | "images": [ 173 | "https://dummyjson.com/image/i/products/9/1.jpg", 174 | "https://dummyjson.com/image/i/products/9/2.png", 175 | "https://dummyjson.com/image/i/products/9/3.png", 176 | "https://dummyjson.com/image/i/products/9/4.jpg", 177 | "https://dummyjson.com/image/i/products/9/thumbnail.jpg" 178 | ] 179 | }, 180 | { 181 | "id": 10, 182 | "title": "HP Pavilion 15-DK1056WM", 183 | "description": "HP Pavilion 15-DK1056WM Gaming Laptop 10th Gen Core i5, 8GB, 256GB SSD, GTX 1650 4GB, Windows 10", 184 | "price": 1220, 185 | "brand": "HP Pavilion", 186 | "thumbnail": "https://dummyjson.com/image/i/products/10/thumbnail.jpeg", 187 | "images": [ 188 | "https://dummyjson.com/image/i/products/10/1.jpg", 189 | "https://dummyjson.com/image/i/products/10/2.jpg", 190 | "https://dummyjson.com/image/i/products/10/3.jpg", 191 | "https://dummyjson.com/image/i/products/10/thumbnail.jpeg" 192 | ] 193 | } 194 | ], 195 | "categories": [ 196 | { 197 | "id": 1, 198 | "title": "notebook" 199 | }, 200 | { 201 | "id": 2, 202 | "title": "pc" 203 | } 204 | ], 205 | "sale_items": [ 206 | { 207 | "product": { 208 | "id": 6, 209 | "title": "MacBook Pro", 210 | "description": "MacBook Pro 2021 with mini-LED display may launch between September, November", 211 | "price": 1749, 212 | "brand": "Apple", 213 | "thumbnail": "https://dummyjson.com/image/i/products/6/thumbnail.png", 214 | "images": [ 215 | "https://dummyjson.com/image/i/products/6/1.png", 216 | "https://dummyjson.com/image/i/products/6/2.jpg", 217 | "https://dummyjson.com/image/i/products/6/3.png", 218 | "https://dummyjson.com/image/i/products/6/4.jpg" 219 | ] 220 | }, 221 | "quantity": 7, 222 | "price": 1249, 223 | "edit": false, 224 | "id": 10 225 | }, 226 | { 227 | "product": { 228 | "id": 1, 229 | "title": "iPhone 9", 230 | "description": "An apple mobile which is nothing like apple", 231 | "price": 549, 232 | "brand": "Apple", 233 | "thumbnail": "https://dummyjson.com/image/i/products/1/thumbnail.jpg", 234 | "images": [ 235 | "https://dummyjson.com/image/i/products/1/1.jpg", 236 | "https://dummyjson.com/image/i/products/1/2.jpg", 237 | "https://dummyjson.com/image/i/products/1/3.jpg", 238 | "https://dummyjson.com/image/i/products/1/4.jpg", 239 | "https://dummyjson.com/image/i/products/1/thumbnail.jpg" 240 | ] 241 | }, 242 | "quantity": "2", 243 | "price": 1499, 244 | "edit": false, 245 | "id": 11 246 | }, 247 | { 248 | "product": { 249 | "id": 7, 250 | "title": "Samsung Galaxy Book", 251 | "description": "Samsung Galaxy Book S (2020) Laptop With Intel Lakefield Chip, 8GB of RAM Launched", 252 | "price": 1499, 253 | "brand": "Samsung", 254 | "thumbnail": "https://dummyjson.com/image/i/products/7/thumbnail.jpg", 255 | "images": [ 256 | "https://dummyjson.com/image/i/products/7/1.jpg", 257 | "https://dummyjson.com/image/i/products/7/2.jpg", 258 | "https://dummyjson.com/image/i/products/7/3.jpg", 259 | "https://dummyjson.com/image/i/products/7/thumbnail.jpg" 260 | ] 261 | }, 262 | "quantity": "4", 263 | "price": 1499, 264 | "edit": false, 265 | "id": 13 266 | }, 267 | { 268 | "product": { 269 | "id": 10, 270 | "title": "HP Pavilion 15-DK1056WM", 271 | "description": "HP Pavilion 15-DK1056WM Gaming Laptop 10th Gen Core i5, 8GB, 256GB SSD, GTX 1650 4GB, Windows 10", 272 | "price": 1220, 273 | "brand": "HP Pavilion", 274 | "thumbnail": "https://dummyjson.com/image/i/products/10/thumbnail.jpeg", 275 | "images": [ 276 | "https://dummyjson.com/image/i/products/10/1.jpg", 277 | "https://dummyjson.com/image/i/products/10/2.jpg", 278 | "https://dummyjson.com/image/i/products/10/3.jpg", 279 | "https://dummyjson.com/image/i/products/10/thumbnail.jpeg" 280 | ] 281 | }, 282 | "quantity": 9, 283 | "price": 1220, 284 | "edit": false, 285 | "id": 16 286 | } 287 | ] 288 | } -------------------------------------------------------------------------------- /public/index0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | Windmill Dashboard 12 | 13 | Alpine.js and TailwindCSS 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | Categorias 36 |
37 | 38 |
39 | 45 | Conteúdo... 46 | 47 | 56 | 57 |
58 |
59 |
60 |

Garlic bread with cheese: What the science tells us ...

61 |

The quick brown fox jumps over the lazy dog.

62 |

63 | For years parents have espoused the health benefits of eating 64 | garlic bread with cheese to their children, with the food 65 | earning such an iconic status in our culture that kids will 66 | often dress up as warm, cheesy loaf for Halloween. 67 |

68 |

69 | But a recent study shows that the celebrated appetizer may be 70 | linked to a series of rabies cases springing up around the 71 | country. 72 |

73 |
74 |
75 |
76 |
77 |
78 | 79 |
80 | 81 |
82 |
87 |
88 |
89 |
90 |
91 |
92 | 101 |

102 | Por favor digite a tarefa. 103 |

104 |
105 |

110 |
111 |

112 | 118 |

119 |
120 |
121 | 122 | 123 | 138 | 139 |
140 |
141 |
142 |
143 | 144 |
145 |
149 |
150 |

Categorias com $store

151 | 152 | 157 | 158 |
    159 | 165 |
166 |
167 |
168 |
169 | 170 |
171 |
175 |
176 |

Produtos com $store

177 | 178 | 183 | 184 |
    185 | 191 |
192 |
193 |
194 |
195 |
196 | 197 | 202 | 203 |
204 | 205 |
206 |
207 | 208 |
209 |
212 |
213 | 214 | 229 |
230 |
231 | 232 | 240 |
241 |
242 | 243 | 252 |
253 |
254 | 260 |
261 |
262 |
263 |
264 |
265 | 266 | 267 | 268 |
269 |
270 |
271 |
272 |
273 | 274 | 275 | 278 | 284 | 288 | 329 | 330 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 543 | 544 | 545 | 546 | 658 | 659 |
282 | TOTAL 283 | 289 |
290 | 298 | 304 | 310 |
311 | 312 |
313 | 322 | 327 |
328 |
ProdutoQuantidadePreço UnitárioPreço TotalAções
660 |
661 |
662 |
663 |
664 |
665 | 666 | 671 | 672 | 673 |
674 |
675 |
678 |

679 | Produtos 680 |

681 | 682 |
685 | 721 |
722 |
723 |
724 |
725 | 726 |
727 | 728 | 733 | 734 | 735 |
736 |

Componentes

737 | 738 |
739 |
    740 | 758 |
759 |
760 |
761 | 762 | 763 | 764 | -------------------------------------------------------------------------------- /public/pages/blank.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Blank - Windmill Dashboard 8 | 12 | 13 | 17 | 18 | 19 | 20 |
24 | 25 | 333 | 334 | 335 |
345 | 618 |
619 |
620 |
623 | 624 | 642 | 643 |
644 |
647 |
648 | 660 |
661 | 667 |
668 |
669 |
    670 | 671 |
  • 672 | 704 |
  • 705 | 706 |
  • 707 | 730 | 776 |
  • 777 | 778 |
  • 779 | 793 | 872 |
  • 873 |
874 |
875 |
876 |
877 | 878 |
879 |

882 | Blank 883 |

884 |
885 |
886 |
887 |
888 | 889 | 890 | -------------------------------------------------------------------------------- /public/pages/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 404 - Windmill Dashboard 8 | 12 | 13 | 17 | 18 | 19 | 20 |
24 | 25 | 333 | 334 | 335 |
345 | 618 |
619 |
620 |
623 | 624 | 642 | 643 |
644 |
647 |
648 | 660 |
661 | 667 |
668 |
669 |
    670 | 671 |
  • 672 | 704 |
  • 705 | 706 |
  • 707 | 730 | 776 |
  • 777 | 778 |
  • 779 | 793 | 872 |
  • 873 |
874 |
875 |
876 |
877 |
878 | 883 | 888 | 889 |

890 | 404 891 |

892 |

893 | Page not found. Check the address or 894 | 898 | go back 899 | 900 | . 901 |

902 |
903 |
904 |
905 |
906 | 907 | 908 | --------------------------------------------------------------------------------