Compare commits
47 commits
master
...
funcion/ac
Author | SHA1 | Date | |
---|---|---|---|
9ee6de7877 | |||
8362cacc84 | |||
e029c42c0a | |||
596ac50e60 | |||
f6de37a1eb | |||
9d98a5cd5b | |||
4062f4b786 | |||
bd92c26b0a | |||
6ea534932f | |||
96516a5083 | |||
9c07a91914 | |||
d5e70dc291 | |||
dd463a6a79 | |||
35e1d6455e | |||
572a8cf3be | |||
b976826bf5 | |||
da4fd472ae | |||
89f886e32c | |||
92d6bc2035 | |||
fee23f389c | |||
7619196179 | |||
537bfd52ff | |||
3a313d033d | |||
cee049c545 | |||
0207fca0cc | |||
ed89065faf | |||
502aefd7a8 | |||
44f0d8c66b | |||
03475daa99 | |||
aa75e3de79 | |||
21b16e0243 | |||
e0d17e052b | |||
e3b17eea4a | |||
5e54d2afea | |||
9a57a826dc | |||
a1aa381052 | |||
ea9a9e2195 | |||
251dbd1120 | |||
98120f1ce9 | |||
71c4b166be | |||
c35eb49001 | |||
0cdb491246 | |||
01cf18b540 | |||
8dba92e8c1 | |||
c602742e33 | |||
a4951bfdfd | |||
88c82d9439 |
47 changed files with 4315 additions and 26576 deletions
|
@ -4,9 +4,11 @@ APP_KEY=
|
|||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
VITE_DEV_SERVER_URL=http://vite:5173
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
|
||||
USERID=
|
||||
USERID=1000
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=db
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM php:7.4-fpm
|
||||
FROM php:8.3-fpm
|
||||
|
||||
# Arguments defined in docker-compose.yml
|
||||
ARG user
|
||||
|
@ -12,7 +12,8 @@ RUN apt-get update && apt-get install -y \
|
|||
libonig-dev \
|
||||
libxml2-dev \
|
||||
zip \
|
||||
unzip
|
||||
unzip \
|
||||
libzip-dev
|
||||
|
||||
# Install node
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
|
||||
|
@ -22,7 +23,7 @@ RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
|
|||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
|
||||
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
|
||||
|
||||
# Get latest Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
|
87
README.md
87
README.md
|
@ -2,13 +2,14 @@
|
|||
|
||||
Aplicación de pedidos del Mercado Popular de Subsistencia.
|
||||
|
||||
Pedi2 está hecha en Laravel 7 y utiliza laravel7-docker de dyarleniber.
|
||||
Pedi2 está hecha en Laravel 12 y Vue 2 con Vite y Vuex.
|
||||
|
||||
Se utilizan los siguientes servicios, separadamente:
|
||||
|
||||
- `app`, un servicio que corre PHP7.4-FPM.
|
||||
- `app`, un servicio que corre PHP8.3-FPM.
|
||||
- `db`, un servicio que corre MySQL 5.7.
|
||||
- `nginx`, un servicio que usa el servicio app para parsear código PHP antes de servir la aplicación de Laravel al usuario final.
|
||||
- `vite`, un servicio que corre el frontend de la aplicación.
|
||||
|
||||
## Pre-requisitos
|
||||
- docker
|
||||
|
@ -17,28 +18,28 @@ Se utilizan los siguientes servicios, separadamente:
|
|||
## Instalación
|
||||
1. Una vez descargado el proyecto, hacé una copia del archivo `.env.example` que se encuentra en la raíz del proyecto y nombrala `.env`. Seteá los valores correctos - específicamente, para las variables, `APP_URL`, `DB_USERNAME` y `DB_PASSWORD`. Prestá atención a que `DB_HOST` sea el nombre del servicio que corre MySQL (por defecto `DB_HOST=db`).
|
||||
|
||||
2. Levantá los contenedores, construyendo la imagen de la app primero
|
||||
2. Levantá los contenedores, construyendo la imagen de la app primero:
|
||||
|
||||
```bash
|
||||
docker-compose up -d --build
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
El ambiente ahora está andando, pero necesitamos ejecutar un par de comandos para terminar la instalación de Laravel. Podemos usar `docker-compose exec [nombre-del-servicio]` previo a un comando para ejecutarlo dentro del contenedor.
|
||||
El ambiente ahora está andando, deberías ver los logs de cada servicio en la terminal. Falta ejecutar un par de comandos para terminar la instalación de Laravel. Podemos usar `docker-compose exec [nombre-del-servicio]` previo a un comando para ejecutarlo dentro del contenedor.
|
||||
|
||||
3. Terminá de instalar las dependencias de la app, según fueron definidas en `composer.json`.
|
||||
3. Abrí una nueva terminal, y terminá de instalar las dependencias de la app, según fueron definidas en `composer.json`:
|
||||
|
||||
|
||||
```bash
|
||||
docker-compose exec app composer install
|
||||
```
|
||||
|
||||
4. Generá una clave de aplicación. Esta clave se usa para encriptar datos sensibles.
|
||||
4. Generá una clave de aplicación. Esta clave se usa para encriptar datos sensibles:
|
||||
|
||||
```bash
|
||||
docker-compose exec app php artisan key:generate
|
||||
```
|
||||
|
||||
5. Corré las migraciones y seeders de Laravel
|
||||
5. Corré las migraciones y seeders de Laravel:
|
||||
|
||||
```bash
|
||||
docker-compose exec app php artisan migrate:fresh --seed
|
||||
|
@ -46,7 +47,7 @@ docker-compose exec app php artisan migrate:fresh --seed
|
|||
|
||||
6. Copia el token que se imprime al correr los seeders. Lo necesitamos para autenticar las llamadas que hagamos desde nuestro cliente web
|
||||
|
||||
7. Instala las dependencias de npm
|
||||
7. Instala las dependencias de npm:
|
||||
```bash
|
||||
docker-compose exec app npm install
|
||||
```
|
||||
|
@ -54,73 +55,13 @@ docker-compose exec app npm install
|
|||
Ahora la aplicación está corriendo y la podés ver en el puerto 8000 de tu dominio o IP. En caso de que estés en tu máquina local, la vas a ver accediendo a `http://localhost:8000` desde tu navegador.
|
||||
|
||||
Podés usar el comando `logs` para ver los logs generados por tus servicios:
|
||||
|
||||
```bash
|
||||
docker-compose logs nginx
|
||||
```
|
||||
---
|
||||
|
||||
8. Ejecuta npm para compilar el js y css
|
||||
```bash
|
||||
docker-compose exec app npm run prod
|
||||
Si estás actualizando o no te anda, probá limpiar los caches:
|
||||
```
|
||||
|
||||
## Services description
|
||||
|
||||
### Dockerfile
|
||||
|
||||
Although both `db` service and `nginx` service, will be based on default images obtained from the Docker Hub, the `app` service will be based on a custom image created by the `Dockerfile`.
|
||||
|
||||
The `Dockerfile` starts by defining the base image `php:7.4-fpm`.
|
||||
|
||||
After installing system packages and PHP extensions, the Composer will be installed by copying the composer executable from its latest official image.
|
||||
|
||||
A new system user is then created and set up using the `user` and `uid` arguments that were declared at the beginning of the `Dockerfile`. These values will be injected by Docker Compose at build time.
|
||||
|
||||
> This new system user is necessary to execute Laravel Artisan and Composer commands while developing the application. The `uid` setting ensures that the user inside the container has the same `uid` as your system user on your host machine. This way, any files created by these commands are replicated in the host with the correct permissions. This also means that you’ll be able to use your code editor of choice in the host machine to develop the application that is running inside containers.
|
||||
|
||||
Finally, the default working dir as `/var/www` and the newly created user are set. This will make sure you’re connecting as a regular user, and that you’re on the right directory, when running Laravel Artisan and Composer commands on the application container.
|
||||
|
||||
### PHP service
|
||||
|
||||
The `app` service will build an image called `laravel-image`, based on the `Dockerfile` previously created. The container defined by this service will run a php-fpm server to parse PHP code and send the results back to the nginx service, which will be running on a separate container. The mysql service defines a container running a MySQL 5.7 server. All these services will share a bridge network named `app-network`.
|
||||
|
||||
The application files will be synchronized on both the `app` and the `nginx` services via bind mounts. Bind mounts are useful in development environments because they allow for a performant two-way sync between host machine and containers.
|
||||
|
||||
Inside the `app` container you will be able to execute command line tasks with the Laravel Artisan and Composer.
|
||||
|
||||
The `app` service will set up a container named `laravel-app`. It builds a new Docker image based on a `Dockerfile` located in the same path as the `docker-compose.yml` file. The new image will be saved locally under the name `laravel-image`.
|
||||
|
||||
The `volumes` setting creates a shared volume that will synchronize contents from the current directory to `/var/www` inside the container. Notice that this is not your document root, since that will live in the nginx container.
|
||||
|
||||
Another file which will be synchronized is the `local.ini` file from the directory `./php/local.ini` to `/usr/local/etc/php/conf.d/local.ini` inside the container.
|
||||
|
||||
The `local.ini` is the configuration file (php.ini) that is read when PHP starts up.
|
||||
|
||||
### Nginx service
|
||||
|
||||
The `nginx` service uses a pre-built Nginx image on top of Alpine, a lightweight Linux distribution. It creates a container named `laravel-nginx`, and it uses the ports definition to create a redirection from port `8000` on the host system to port `80` inside the container.
|
||||
|
||||
The `volumes` setting creates two shared volumes. The first one will synchronize contents from the current directory to `/var/www` inside the container. This way, when you make local changes to the application files, they will be quickly reflected in the application being served by Nginx inside the container. The second volume will make sure the Nginx configuration file, located at `./nginx/conf.d/app.conf`, is copied to the container’s Nginx configuration folder. This configuration file will configure Nginx to listen on port `80` and use `index.php` as default index page. It will set the document root to `/var/www/public`, and then configure Nginx to use the `app` service on port `9000` to process all the php files.
|
||||
|
||||
### MySQL service
|
||||
|
||||
The `db` service uses a pre-built MySQL 5.7 image from Docker Hub. Because Docker Compose automatically loads `.env` variable files located in the same directory as the `docker-compose.yml` file, you can obtain the database settings from the Laravel `.env` file.
|
||||
|
||||
The `volumes` setting creates two shared volumes. The first one will make sure the MySQL configuration file, located at `./mysql/my.cnf`, is copied to the container’s MySQL configuration folder. The second volume will share a `.sql` database dump that will be used to initialize the application database. The MySQL image will automatically import `.sql` files placed in the `/docker-entrypoint-initdb.d` directory inside the container.
|
||||
|
||||
The `environment` setting defines environment variables in the new container. You can use values obtained from the Laravel `.env` file to set up the MySQL service, which will automatically create a new database and user based on the provided environment variables:
|
||||
|
||||
```bash
|
||||
DB_HOST=db
|
||||
DB_DATABASE=laravelapp
|
||||
DB_USERNAME=laravelapp_user
|
||||
DB_PASSWORD=password
|
||||
docker-compose exec app php artisan optimize:clear
|
||||
docker-compose exec app composer dump-autoload
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- https://www.digitalocean.com/community/tutorials/how-to-install-and-set-up-laravel-with-docker-compose-on-ubuntu-20-04
|
||||
- https://docs.docker.com/
|
||||
- https://docs.docker.com/compose/
|
||||
- https://github.com/dyarleniber/laravel7-docker
|
||||
- https://laravel.com/docs/7.x/installation
|
||||
|
|
|
@ -12,7 +12,6 @@ use TypeError;
|
|||
class Filtro extends Model
|
||||
{
|
||||
protected Request $request;
|
||||
protected $builder;
|
||||
protected array $MENSAJES_ERROR = [
|
||||
'ARGUMENTO' => 'Argumento inválido para el parámetro %s. Revise la documentación.'
|
||||
];
|
||||
|
|
|
@ -18,7 +18,7 @@ class Kernel extends HttpKernel
|
|||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Fruitcake\Cors\HandleCors::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\CheckForMaintenanceMode::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Fideloper\Proxy\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
|
@ -19,5 +19,8 @@ class TrustProxies extends Middleware
|
|||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
||||
protected $headers = Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO;
|
||||
}
|
||||
|
|
|
@ -8,23 +8,20 @@
|
|||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.4",
|
||||
"doctrine/dbal": "^2.2.0",
|
||||
"fideloper/proxy": "^4.4",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"guzzlehttp/guzzle": "^6.3.1|^7.0.1",
|
||||
"laravel/framework": "^7.29",
|
||||
"laravel/sanctum": "^2.13",
|
||||
"laravel/tinker": "^2.5",
|
||||
"laravel/ui": "*",
|
||||
"php": "^8.3",
|
||||
"doctrine/dbal": "^3.0",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/tinker": "^2.8",
|
||||
"laravel/ui": "^4.3",
|
||||
"league/csv": "^9.8",
|
||||
"mpdf/mpdf": "^8.1",
|
||||
"mpdf/mpdf": "^8.2",
|
||||
"prexview/prexview": "^1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"facade/ignition": "^2.0",
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"nunomaduro/collision": "^4.3"
|
||||
"nunomaduro/collision": "^8.0"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
|
@ -55,13 +52,13 @@
|
|||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php7.4 artisan package:discover --ansi"
|
||||
"@php artisan package:discover --ansi"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php7.4 -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php7.4 artisan key:generate --ansi"
|
||||
"@php artisan key:generate --ansi"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
3435
composer.lock
generated
3435
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -17,7 +17,7 @@ class UserSeeder extends Seeder
|
|||
$usersToInsert = [];
|
||||
|
||||
$usersToInsert[] = DatabaseSeeder::addTimestamps([
|
||||
'name' => 'comi',
|
||||
'name' => 'comision',
|
||||
'password' => Hash::make("123"),
|
||||
'role_id' => UserRole::where('nombre', 'comision')->first()->id,
|
||||
]);
|
||||
|
|
|
@ -18,6 +18,22 @@ services:
|
|||
networks:
|
||||
- app-network
|
||||
|
||||
vite:
|
||||
build:
|
||||
args:
|
||||
user: www
|
||||
uid: ${USERID}
|
||||
context: ./
|
||||
container_name: vite
|
||||
working_dir: /var/www
|
||||
command: npm run dev
|
||||
volumes:
|
||||
- ./:/var/www
|
||||
ports:
|
||||
- "5173:5173"
|
||||
networks:
|
||||
- app-network
|
||||
|
||||
db:
|
||||
image: mysql:5.7
|
||||
container_name: pedi2-db
|
||||
|
|
|
@ -13,6 +13,22 @@ server {
|
|||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
}
|
||||
location ^~ /@vite/ {
|
||||
proxy_pass http://vite:5173;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
location ^~ /resources/ {
|
||||
proxy_pass http://vite:5173;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
gzip_static on;
|
||||
|
|
26554
package-lock.json
generated
26554
package-lock.json
generated
File diff suppressed because it is too large
Load diff
17
package.json
17
package.json
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"watch": "npm run development -- --watch",
|
||||
"watch-poll": "npm run watch -- --watch-poll",
|
||||
|
@ -10,20 +11,24 @@
|
|||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"@types/axios": "^0.9.36",
|
||||
"@vitejs/plugin-vue2": "^2.3.3",
|
||||
"axios": "^0.27.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"laravel-mix": "^5.0.1",
|
||||
"resolve-url-loader": "^2.3.1",
|
||||
"laravel-vite-plugin": "^1.3.0",
|
||||
"sass": "^1.20.1",
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue": "^2.5.17",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^2.7.16",
|
||||
"vue-template-compiler": "^2.7.16",
|
||||
"animate.css": "^4.1.1",
|
||||
"bulma": "^0.9.4",
|
||||
"bulma-switch": "^2.0.4",
|
||||
"bulma-toast": "^2.4.1",
|
||||
"vue-router": "^3.5.4",
|
||||
"vuex": "^3.6.2"
|
||||
}
|
||||
}
|
||||
|
|
45
resources/js/app.js
vendored
45
resources/js/app.js
vendored
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
* First we will load all of this project's JavaScript dependencies which
|
||||
* includes Vue and other libraries. It is a great starting point when
|
||||
* building robust, powerful web applications using Vue and Laravel.
|
||||
*/
|
||||
import axios from 'axios';
|
||||
import Vue from 'vue';
|
||||
|
||||
window.Vue = require('vue');
|
||||
window.Event = new Vue();
|
||||
window.axios = axios;
|
||||
window.bulmaToast = require('bulma-toast');
|
||||
|
||||
/**
|
||||
* The following block of code may be used to automatically register your
|
||||
* Vue components. It will recursively scan this directory for the Vue
|
||||
* components and automatically register them with their "basename".
|
||||
*
|
||||
* Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
|
||||
*/
|
||||
import './components';
|
||||
import store from "./store";
|
||||
|
||||
/**
|
||||
* Global methods
|
||||
*/
|
||||
Vue.prototype.$toast = function (mensaje, duration = 2000) {
|
||||
return window.bulmaToast.toast({
|
||||
message: mensaje,
|
||||
duration: duration,
|
||||
type: 'is-danger',
|
||||
position: 'bottom-center',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Next, we will create a fresh Vue application instance and attach it to
|
||||
* the page. Then, you may begin adding components to this application
|
||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||
*/
|
||||
new Vue({
|
||||
el: '#root',
|
||||
store,
|
||||
});
|
||||
|
27
resources/js/app.ts
Normal file
27
resources/js/app.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import * as bulmaToast from 'bulma-toast';
|
||||
import Vue from '../../node_modules/vue/dist/vue.esm.js';
|
||||
import axios from 'axios';
|
||||
import store from "./store";
|
||||
import './shims-vue.d.ts';
|
||||
|
||||
// Registro de components
|
||||
const components = import.meta.glob('./components/**/*.vue', { eager: true });
|
||||
Object.entries(components).forEach(([path, module]) => {
|
||||
let name = path
|
||||
.replace(/^\.\/components\//, '') // Remove leading folder
|
||||
.replace(/\.vue$/, '') // Remove file extension
|
||||
.replace(/\//g, '-') // Replace subfolders with hyphens
|
||||
.replace(/([a-z])([A-Z])/g, '$1-$2') // camelCase to kebab-case
|
||||
.toLowerCase(); // Enforce kebab-case for HTML
|
||||
Vue.component(name, (module as any).default);
|
||||
});
|
||||
|
||||
window.Vue = Vue;
|
||||
window.Event = new Vue();
|
||||
window.axios = axios;
|
||||
window.bulmaToast = bulmaToast;
|
||||
|
||||
new Vue({
|
||||
el: '#root',
|
||||
store,
|
||||
});
|
25
resources/js/components.js
vendored
25
resources/js/components.js
vendored
|
@ -1,25 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
const requireComponent = require.context('./components', true, /\.vue$/);
|
||||
|
||||
// Registro automático de componentes:
|
||||
// e.g. components/foo/bar/UnComponente.vue
|
||||
// se registra como 'foo-bar-un-componente'
|
||||
requireComponent.keys().forEach(fileName => {
|
||||
// Get the component config
|
||||
const componentConfig = requireComponent(fileName);
|
||||
// Get the PascalCase name of the component
|
||||
const componentName = fileName
|
||||
.replace(/^\.\/(.*)\.\w+$/, '$1') // Remove "./" from the beginning and the file extension from the end
|
||||
.replace(/\//g, '-') // Replace directories with hyphens
|
||||
.replace(/([a-z])([A-Z])/g, '$1-$2') // Insert hyphen between camelCase words
|
||||
.toLowerCase() // Convert to lowercase
|
||||
// Globally register the component
|
||||
Vue.component(
|
||||
componentName,
|
||||
// Look for the component options on `.default`, which will
|
||||
// exist if the component was exported with `export default`,
|
||||
// otherwise fall back to module's root.
|
||||
componentConfig.default || componentConfig
|
||||
);
|
||||
});
|
|
@ -1,12 +1,12 @@
|
|||
<script>
|
||||
import LoginInput from "./login/LoginInput.vue";
|
||||
import LoginLoginTitulos from "./login/LoginTitulos.vue";
|
||||
import LoginTitulos from "./login/LoginTitulos.vue";
|
||||
import { mapGetters } from "vuex";
|
||||
import LoginDropdown from "./login/LoginDropdown.vue";
|
||||
|
||||
export default {
|
||||
name: 'LoginForm',
|
||||
components: { LoginDropdown, LoginTitulos: LoginLoginTitulos, LoginInput },
|
||||
name: 'AppLogin',
|
||||
components: { LoginDropdown, LoginTitulos, LoginInput },
|
||||
computed: {
|
||||
...mapGetters("login", ["estilos"])
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export default {
|
|||
<div id="login-form" :class="estilos.fondo">
|
||||
<section class="section">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-half">
|
||||
<div class="column is-three-quarters">
|
||||
<login-dropdown class="is-hidden-tablet"/>
|
||||
<login-titulos/>
|
||||
<login-input/>
|
||||
|
|
|
@ -14,14 +14,14 @@
|
|||
</div>
|
||||
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<a :href="'/admin/exportar-pedido-a-csv/' + grupo_de_compra_id" class="dropdown-item has-background-primary">
|
||||
Planilla para central (CSV)
|
||||
</a>
|
||||
<a :href="'/admin/exportar-planillas-a-pdf/' + grupo_de_compra_id" class="dropdown-item">
|
||||
Planillas para armado (PDF)
|
||||
</a>
|
||||
<a :href="'/admin/exportar-pedido-con-nucleos-a-csv/' + grupo_de_compra_id" class="dropdown-item">
|
||||
Planilla completa de la canasta (CSV)
|
||||
Pedidos por núcleo (CSV)
|
||||
</a>
|
||||
<a :href="'/admin/exportar-pedido-a-csv/' + grupo_de_compra_id" class="dropdown-item">
|
||||
Pedido barrial (CSV)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -38,7 +38,6 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../../node_modules/bulma';
|
||||
hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script >
|
||||
import { defineComponent } from "vue";
|
||||
import { mapMutations, mapState } from "vuex";
|
||||
import { mapGetters, mapMutations, mapState } from "vuex";
|
||||
import CategoriasContainer from "./CategoriasContainer.vue";
|
||||
import ProductosContainer from "./ProductosContainer.vue";
|
||||
import Chismosa from "./Chismosa.vue";
|
||||
|
@ -9,7 +9,8 @@ import DevolucionesModal from "./DevolucionesModal.vue";
|
|||
export default defineComponent({
|
||||
components: { DevolucionesModal, CategoriasContainer, ProductosContainer, Chismosa },
|
||||
computed: {
|
||||
...mapState('ui', ["show_chismosa", "show_devoluciones", "tags_interactuada"])
|
||||
...mapState('ui', ["show_chismosa", "show_devoluciones", "tags_interactuada"]),
|
||||
...mapGetters('productos', ["mostrarProductos"]),
|
||||
},
|
||||
methods: {
|
||||
...mapMutations("ui", ["toggleTags"]),
|
||||
|
@ -23,8 +24,8 @@ export default defineComponent({
|
|||
|
||||
<template>
|
||||
<div class="columns ml-3 mr-3" v-else>
|
||||
<categorias-container :class="show_chismosa ? 'hide-below-1024' : ''"/>
|
||||
<productos-container :class="show_chismosa ? 'hide-below-1024' : ''"/>
|
||||
<productos-container v-if="mostrarProductos" :class="show_chismosa ? 'hide-below-1024' : ''"/>
|
||||
<categorias-container v-else :class="show_chismosa ? 'hide-below-1024' : ''"/>
|
||||
<devoluciones-modal v-show="show_devoluciones"/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div v-show="visible" class="column">
|
||||
<div class="column">
|
||||
<div ref="categorias"
|
||||
class="columns is-multiline is-mobile"
|
||||
:class="{ 'align-last-left': isLastRowIncomplete }">
|
||||
|
@ -28,9 +28,6 @@ export default {
|
|||
computed: {
|
||||
...mapState('productos', ["categorias", "filtro"]),
|
||||
...mapState('ui', ["show_chismosa"]),
|
||||
visible() {
|
||||
return this.filtro === null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('productos', ["seleccionarCategoria"]),
|
||||
|
|
|
@ -85,7 +85,7 @@ export default {
|
|||
...mapActions('pedido', ["getGrupoDeCompra", "crearPedido", "elegirPedido"]),
|
||||
...mapMutations("ui", ["toggleTags"]),
|
||||
async getPedidos(nombre) {
|
||||
const response = await axios.get('/api/subpedidos/',{
|
||||
const response = await axios.get('/api/subpedidos',{
|
||||
params: {
|
||||
nombre: nombre,
|
||||
grupo_de_compra: this.grupo_de_compra.id,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
:requiere_notas="producto.requiere_notas"
|
||||
/>
|
||||
</td>
|
||||
<td class="has-text-right">{{ `${cantidad(producto.id) * producto.precio}` }}</td>
|
||||
<td class="has-text-right">{{ `${producto.total}` }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
<script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div v-show="visible" class="column">
|
||||
<div class="column">
|
||||
<div ref="productos"
|
||||
class="columns is-multiline is-mobile"
|
||||
:class="{ 'align-last-left': isLastRowIncomplete }">
|
||||
|
@ -21,9 +21,6 @@ export default {
|
|||
computed: {
|
||||
...mapState('productos', ["productos", "filtro"]),
|
||||
...mapState('ui', ["show_chismosa"]),
|
||||
visible() {
|
||||
return this.filtro !== null;
|
||||
},
|
||||
miga: function () {
|
||||
return {
|
||||
nombre: this.filtro.valor,
|
||||
|
|
10
resources/js/globals.d.ts
vendored
Normal file
10
resources/js/globals.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
declare global {
|
||||
interface Window {
|
||||
Vue: typeof Vue;
|
||||
Event: InstanceType<typeof Vue>;
|
||||
axios: typeof axios;
|
||||
bulmaToast: {
|
||||
toast: (options: any) => void;
|
||||
};
|
||||
}
|
||||
}
|
5
resources/js/shims-vue.d.ts
vendored
Normal file
5
resources/js/shims-vue.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// resources/js/shims-vue.d.ts
|
||||
declare module '*.vue' {
|
||||
import Vue from '../../node_modules/vue/dist/vue.esm.js';
|
||||
export default Vue;
|
||||
}
|
53
resources/js/store/comunes.ts
Normal file
53
resources/js/store/comunes.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
export interface ProductoResponse {
|
||||
id: number,
|
||||
fila: number,
|
||||
nombre: string,
|
||||
precio: number,
|
||||
categoria: string,
|
||||
bono: boolean,
|
||||
created_at: Date,
|
||||
updated_at: Date,
|
||||
requiere_notas: boolean,
|
||||
es_solidario: boolean,
|
||||
pivot: {
|
||||
subpedido_id: number,
|
||||
producto_id: number,
|
||||
notas: string,
|
||||
cantidad: number,
|
||||
total: number,
|
||||
}
|
||||
}
|
||||
|
||||
export interface Producto {
|
||||
id: number,
|
||||
fila: number,
|
||||
nombre: string,
|
||||
precio: number,
|
||||
categoria: string,
|
||||
bono: boolean,
|
||||
created_at: Date,
|
||||
updated_at: Date,
|
||||
requiere_notas: boolean,
|
||||
es_solidario: boolean,
|
||||
notas: string,
|
||||
cantidad: number,
|
||||
total: number,
|
||||
}
|
||||
|
||||
export function aplanarProducto(producto: ProductoResponse): Producto {
|
||||
return {
|
||||
id: producto.id,
|
||||
fila: producto.fila,
|
||||
nombre: producto.nombre,
|
||||
precio: producto.precio,
|
||||
categoria: producto.categoria,
|
||||
bono: producto.bono,
|
||||
created_at: producto.created_at,
|
||||
updated_at: producto.updated_at,
|
||||
requiere_notas: producto.requiere_notas,
|
||||
es_solidario: producto.es_solidario,
|
||||
notas: producto.pivot.notas,
|
||||
cantidad: producto.pivot.cantidad,
|
||||
total: producto.pivot.total,
|
||||
};
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import Vue from 'vue';
|
||||
import Vue from '../../../node_modules/vue/dist/vue.esm.js';
|
||||
import Vuex from 'vuex';
|
||||
import login from "./modules/login";
|
||||
import admin from "./modules/admin";
|
||||
import comisiones from "./modules/comisiones";
|
||||
import login from "./modules/login";
|
||||
import pedido from "./modules/pedido";
|
||||
import productos from "./modules/productos";
|
||||
import ui from "./modules/ui";
|
||||
|
@ -12,9 +12,9 @@ Vue.use(Vuex);
|
|||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
login,
|
||||
admin,
|
||||
comisiones,
|
||||
login,
|
||||
pedido,
|
||||
productos,
|
||||
ui,
|
|
@ -1,29 +1,36 @@
|
|||
import axios from "axios";
|
||||
import { AdminState } from "./types";
|
||||
import { aplanarProducto } from "../../comunes";
|
||||
|
||||
const state = {
|
||||
lastFetch: null,
|
||||
grupo_de_compra_id: null,
|
||||
nombre: null,
|
||||
devoluciones_habilitadas: null,
|
||||
pedidos: null,
|
||||
total_a_recaudar: null,
|
||||
total_sin_devoluciones: null,
|
||||
total_barrial: null,
|
||||
total_devoluciones: null,
|
||||
total_de_pedido: null,
|
||||
total_a_transferir: null,
|
||||
total_transporte: null,
|
||||
cantidad_transporte: null,
|
||||
saldo: null,
|
||||
const state: AdminState = {
|
||||
lastFetch: undefined,
|
||||
grupo_de_compra_id: undefined,
|
||||
nombre: undefined,
|
||||
devoluciones_habilitadas: undefined,
|
||||
pedidos: [],
|
||||
total_a_recaudar: undefined,
|
||||
total_sin_devoluciones: undefined,
|
||||
total_barrial: undefined,
|
||||
total_devoluciones: undefined,
|
||||
total_de_pedido: undefined,
|
||||
total_a_transferir: undefined,
|
||||
total_transporte: undefined,
|
||||
cantidad_transporte: undefined,
|
||||
saldo: undefined,
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setState(state, { grupo_de_compra }) {
|
||||
setState(state: AdminState, { grupo_de_compra }) {
|
||||
const aplanarProductos = (pedido) => {
|
||||
pedido.productos = pedido.productos.map(p => aplanarProducto(p));
|
||||
return pedido;
|
||||
};
|
||||
|
||||
state.lastFetch = new Date();
|
||||
state.grupo_de_compra_id = grupo_de_compra.id;
|
||||
state.nombre = grupo_de_compra.nombre;
|
||||
state.devoluciones_habilitadas = grupo_de_compra.devoluciones_habilitadas;
|
||||
state.pedidos = grupo_de_compra.pedidos;
|
||||
state.pedidos = grupo_de_compra.pedidos.map(p => aplanarProductos(p));
|
||||
state.total_a_recaudar = grupo_de_compra.total_a_recaudar;
|
||||
state.total_sin_devoluciones = grupo_de_compra.total_sin_devoluciones;
|
||||
state.total_barrial = grupo_de_compra.total_barrial;
|
||||
|
@ -41,8 +48,8 @@ const mutations = {
|
|||
|
||||
const actions = {
|
||||
async getGrupoDeCompra({ commit }) {
|
||||
const response = await axios.get('/user/grupo_de_compra');
|
||||
commit('setState', response.data);
|
||||
const { data } = await axios.get('/user/grupo_de_compra');
|
||||
commit('setState', data);
|
||||
},
|
||||
async setAprobacionPedido({ commit, dispatch }, { pedido_id, aprobacion }){
|
||||
await axios.post("/api/admin/subpedidos/" + pedido_id + "/aprobacion", { aprobacion: aprobacion });
|
||||
|
@ -59,7 +66,7 @@ const actions = {
|
|||
|
||||
const getters = {
|
||||
grupoDeCompraDefinido() {
|
||||
return state.lastFetch !== null;
|
||||
return state.lastFetch !== undefined;
|
||||
},
|
||||
barrio() {
|
||||
return state.nombre?.replace('_admin','') ?? '';
|
33
resources/js/store/modules/admin/types.ts
Normal file
33
resources/js/store/modules/admin/types.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { Producto } from "../../comunes";
|
||||
|
||||
export interface AdminState extends Barrio {
|
||||
lastFetch?: Date
|
||||
}
|
||||
export interface Barrio {
|
||||
grupo_de_compra_id?: number,
|
||||
nombre?: string,
|
||||
devoluciones_habilitadas?: boolean,
|
||||
pedidos: Pedido[],
|
||||
total_a_recaudar?: number,
|
||||
total_sin_devoluciones?: number,
|
||||
total_barrial?: number,
|
||||
total_devoluciones?: number,
|
||||
total_de_pedido?: number,
|
||||
total_a_transferir?: number,
|
||||
total_transporte?: number,
|
||||
cantidad_transporte?: number,
|
||||
saldo?: number,
|
||||
}
|
||||
|
||||
export interface Pedido {
|
||||
id: number,
|
||||
nombre: string,
|
||||
productos: Producto[],
|
||||
aprobado: boolean,
|
||||
total: number,
|
||||
total_transporte: number,
|
||||
cantidad_transporte: number,
|
||||
total_sin_devoluciones: number,
|
||||
devoluciones_total: number,
|
||||
devoluciones_notas: string
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
import axios from "axios";
|
||||
import { ComisionesState } from "./types";
|
||||
|
||||
const state = {
|
||||
const state: ComisionesState = {
|
||||
lastFetch: undefined,
|
||||
grupos_de_compra: [],
|
||||
parametros: [],
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setGruposDeCompra(state, { data }) {
|
||||
setGruposDeCompra(state: ComisionesState, { data }) {
|
||||
state.grupos_de_compra = data;
|
||||
state.lastFetch = new Date();
|
||||
},
|
||||
|
@ -20,17 +21,17 @@ const mutations = {
|
|||
actualizarParametro(state, { parametro_id, valor }) {
|
||||
state.parametros.find(p => p.id === parametro_id).valor = valor;
|
||||
},
|
||||
setSaldo(state, { gdc_id, saldo }) {
|
||||
setSaldo(state: ComisionesState, { gdc_id, saldo }) {
|
||||
const barrio = state.grupos_de_compra.find(gdc => gdc.id === gdc_id);
|
||||
const i = state.grupos_de_compra.indexOf(barrio);
|
||||
const i = state.grupos_de_compra.indexOf(barrio!);
|
||||
state.grupos_de_compra[i].saldo = saldo;
|
||||
},
|
||||
};
|
||||
|
||||
const actions = {
|
||||
async getGruposDeCompra({ commit }) {
|
||||
const response = await axios.get('/api/grupos-de-compra/saldos');
|
||||
commit('setGruposDeCompra', response.data);
|
||||
const { data } = await axios.get('/api/grupos-de-compra/saldos');
|
||||
commit('setGruposDeCompra', data);
|
||||
},
|
||||
async getParametros({ commit }) {
|
||||
const response = await axios.get('/api/parametros');
|
10
resources/js/store/modules/comisiones/types.ts
Normal file
10
resources/js/store/modules/comisiones/types.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
export interface ComisionesState {
|
||||
lastFetch?: Date,
|
||||
grupos_de_compra: Barrio[],
|
||||
}
|
||||
|
||||
export interface Barrio {
|
||||
id: number,
|
||||
nombre: string,
|
||||
saldo: number,
|
||||
}
|
|
@ -1,22 +1,23 @@
|
|||
import axios from "axios";
|
||||
import { Estilos, LoginState, OpcionLogin, Textos, UrlRol, UserRol } from "./types";
|
||||
|
||||
const state = {
|
||||
regiones: null,
|
||||
grupos_de_compra: null,
|
||||
region_elegida: null,
|
||||
grupo_de_compra_elegido: null,
|
||||
rol: null,
|
||||
const state: LoginState = {
|
||||
regiones: [],
|
||||
grupos_de_compra: [],
|
||||
region_elegida: undefined,
|
||||
grupo_de_compra_elegido: undefined,
|
||||
rol: undefined,
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setRegiones(state, { regiones }) {
|
||||
setRegiones(state, { regiones }): void {
|
||||
state.regiones = regiones;
|
||||
},
|
||||
setRegionYBarrios(state, { region, grupos_de_compra }) {
|
||||
setRegionYBarrios(state, { region, grupos_de_compra }): void {
|
||||
state.region_elegida = region;
|
||||
state.grupos_de_compra = grupos_de_compra;
|
||||
},
|
||||
selectGrupoDeCompra(state, { grupo_de_compra }) {
|
||||
selectGrupoDeCompra(state, { grupo_de_compra }): void {
|
||||
state.grupo_de_compra_elegido = grupo_de_compra;
|
||||
},
|
||||
setRol(state, { rol }) {
|
||||
|
@ -25,29 +26,29 @@ const mutations = {
|
|||
};
|
||||
|
||||
const actions = {
|
||||
async getRegiones({ commit }) {
|
||||
const response = await axios.get("/api/regiones");
|
||||
commit('setRegiones', { regiones: response.data });
|
||||
async getRegiones({ commit }): Promise<void> {
|
||||
const { data } = await axios.get("/api/regiones");
|
||||
commit('setRegiones', { regiones: data });
|
||||
},
|
||||
async selectRegion({ commit }, { region }) {
|
||||
const response = await axios.get(`/api/regiones/${region}`);
|
||||
commit('setRegionYBarrios', { region: region, grupos_de_compra: response.data });
|
||||
async selectRegion({ commit }, { region }): Promise<void> {
|
||||
const { data } = await axios.get(`/api/regiones/${region}`);
|
||||
commit('setRegionYBarrios', { region: region, grupos_de_compra: data });
|
||||
},
|
||||
async getRol({ commit }) {
|
||||
const response = await axios.get("/user/rol");
|
||||
commit('setRol', { rol: response.data.rol });
|
||||
async getRol({ commit }): Promise<void> {
|
||||
const { data }: UserRol = await axios.get("/user/rol");
|
||||
commit('setRol', { rol: data.rol });
|
||||
}
|
||||
};
|
||||
|
||||
const getters = {
|
||||
urlRol() {
|
||||
urlRol(): UrlRol {
|
||||
let split = window.location.pathname
|
||||
.replace('login', '')
|
||||
.split('/')
|
||||
.filter(x => x.length);
|
||||
return split[0] ?? 'pedido';
|
||||
return split[0] as UrlRol ?? 'pedido';
|
||||
},
|
||||
textos() {
|
||||
textos(): Textos {
|
||||
let rol = getters.urlRol();
|
||||
switch (rol) {
|
||||
case 'pedido':
|
||||
|
@ -86,7 +87,7 @@ const getters = {
|
|||
throw new Error("Url inválida");
|
||||
}
|
||||
},
|
||||
estilos() {
|
||||
estilos(): Estilos {
|
||||
let rol = getters.urlRol();
|
||||
switch (rol) {
|
||||
case 'pedido':
|
||||
|
@ -103,8 +104,8 @@ const getters = {
|
|||
};
|
||||
case 'comisiones':
|
||||
return {
|
||||
fondo: "has-background-grey",
|
||||
texto: "has-text-white",
|
||||
fondo: "has-background-dark",
|
||||
texto: "has-text-danger-light",
|
||||
botones: "danger-dark-button",
|
||||
};
|
||||
case 'ollas':
|
||||
|
@ -117,7 +118,7 @@ const getters = {
|
|||
throw new Error("Url inválida");
|
||||
}
|
||||
},
|
||||
opcionesLogin() {
|
||||
opcionesLogin(): OpcionLogin[] {
|
||||
let rol = getters.urlRol();
|
||||
switch (rol) {
|
||||
case 'pedido':
|
43
resources/js/store/modules/login/types.ts
Normal file
43
resources/js/store/modules/login/types.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
export interface LoginState {
|
||||
regiones: string[],
|
||||
grupos_de_compra: Barrio[],
|
||||
region_elegida?: string,
|
||||
grupo_de_compra_elegido?: Barrio,
|
||||
rol?: Rol,
|
||||
}
|
||||
|
||||
export interface Textos {
|
||||
titulo: string,
|
||||
subtitlo: string,
|
||||
password: string,
|
||||
ayuda: string,
|
||||
label: string,
|
||||
}
|
||||
|
||||
export interface Estilos {
|
||||
fondo: string,
|
||||
texto: string,
|
||||
botones: string,
|
||||
}
|
||||
|
||||
export interface OpcionLogin {
|
||||
nombre: string,
|
||||
href: string,
|
||||
}
|
||||
|
||||
export interface Barrio {
|
||||
id: number,
|
||||
nombre: string,
|
||||
region: string,
|
||||
created_at: Date,
|
||||
updated_at: Date,
|
||||
devoluciones_habilitadas: boolean,
|
||||
saldo: number,
|
||||
}
|
||||
|
||||
export interface UserRol {
|
||||
rol: Rol
|
||||
}
|
||||
|
||||
export type UrlRol = 'pedido' | 'admin' | 'comisiones' | 'ollas';
|
||||
export type Rol = 'barrio' | 'admin_barrio' | 'comision' | 'ollas';
|
|
@ -1,30 +1,32 @@
|
|||
import axios from "axios";
|
||||
import { PedidoState } from "./types";
|
||||
import { aplanarProducto } from "../../comunes";
|
||||
|
||||
const state = {
|
||||
lastFetch: null,
|
||||
grupo_de_compra: null,
|
||||
pedido_id: 0,
|
||||
nombre: "",
|
||||
const state: PedidoState = {
|
||||
lastFetch: undefined,
|
||||
grupo_de_compra: undefined,
|
||||
pedido_id: undefined,
|
||||
nombre: undefined,
|
||||
productos: [],
|
||||
aprobado: false,
|
||||
total: 0,
|
||||
total_transporte: 0,
|
||||
cantidad_transporte: 0,
|
||||
total_sin_devoluciones: 0,
|
||||
devoluciones_total: 0,
|
||||
devoluciones_notas: "",
|
||||
cantidad_de_ollas: 0,
|
||||
aprobado: undefined,
|
||||
total: undefined,
|
||||
total_transporte: undefined,
|
||||
cantidad_transporte: undefined,
|
||||
total_sin_devoluciones: undefined,
|
||||
devoluciones_total: undefined,
|
||||
devoluciones_notas: undefined,
|
||||
cantidad_de_ollas: undefined,
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setGrupoDeCompra(state, grupo_de_compra) {
|
||||
setGrupoDeCompra(state: PedidoState, grupo_de_compra) {
|
||||
state.grupo_de_compra = grupo_de_compra;
|
||||
},
|
||||
setPedido(state, pedido) {
|
||||
setPedido(state: PedidoState, pedido) {
|
||||
state.lastFetch = new Date();
|
||||
state.pedido_id = pedido.id;
|
||||
state.nombre = pedido.nombre;
|
||||
state.productos = pedido.productos;
|
||||
state.productos = pedido.productos.map(p => aplanarProducto(p));
|
||||
state.aprobado = pedido.aprobado;
|
||||
state.total = Number.parseFloat(pedido.total.replace(',',''));
|
||||
state.total_transporte = Number.parseInt(pedido.total_transporte?.replace(',',''));
|
||||
|
@ -47,18 +49,18 @@ const mutations = {
|
|||
delete state.devoluciones_total;
|
||||
delete state.devoluciones_notas;
|
||||
},
|
||||
reset(state) {
|
||||
state.lastFetch = null;
|
||||
state.pedido_id = null;
|
||||
state.nombre = null;
|
||||
state.productos = null;
|
||||
state.aprobado = null;
|
||||
state.total = null;
|
||||
state.total_transporte = null;
|
||||
state.cantidad_transporte = null;
|
||||
state.total_sin_devoluciones = null;
|
||||
state.devoluciones_total = null;
|
||||
state.devoluciones_notas = null;
|
||||
reset(state: PedidoState) {
|
||||
state.lastFetch = undefined;
|
||||
state.pedido_id = undefined;
|
||||
state.nombre = undefined;
|
||||
state.productos = [];
|
||||
state.aprobado = undefined;
|
||||
state.total = undefined;
|
||||
state.total_transporte = undefined;
|
||||
state.cantidad_transporte = undefined;
|
||||
state.total_sin_devoluciones = undefined;
|
||||
state.devoluciones_total = undefined;
|
||||
state.devoluciones_notas = undefined;
|
||||
},
|
||||
setCantidadOllas(state, { cantidad }) {
|
||||
if (cantidad >= 0)
|
||||
|
@ -68,8 +70,8 @@ const mutations = {
|
|||
|
||||
const actions = {
|
||||
async getGrupoDeCompra({ commit }) {
|
||||
const response = await axios.get('/user/grupo_de_compra');
|
||||
commit('setGrupoDeCompra', response.data.grupo_de_compra);
|
||||
const { data } = await axios.get('/user/grupo_de_compra');
|
||||
commit('setGrupoDeCompra', data.grupo_de_compra);
|
||||
},
|
||||
async guardarSesion(_, { pedido_id }) {
|
||||
const body = { id: pedido_id };
|
||||
|
@ -77,31 +79,35 @@ const actions = {
|
|||
},
|
||||
async crearPedido({ commit, dispatch }, { nombre, grupo_de_compra_id, tipo_id }) {
|
||||
const body = { nombre, grupo_de_compra_id, tipo_id };
|
||||
const response = await axios.post("/api/subpedidos", body);
|
||||
dispatch("guardarSesion", { pedido_id: response.data.data.id});
|
||||
commit('setPedido', response.data.data);
|
||||
const { data } = await axios.post("/api/subpedidos", body);
|
||||
dispatch("guardarSesion", { pedido_id: data.data.id});
|
||||
commit('setPedido', data.data);
|
||||
},
|
||||
async elegirPedido({ commit, dispatch }, { pedido_id }) {
|
||||
const response = await axios.get(`/api/subpedidos/${pedido_id}`);
|
||||
const body = { pedido_id: pedido_id};
|
||||
dispatch("guardarSesion", body)
|
||||
commit('setPedido', response.data.data);
|
||||
const { data } = await axios.get(`/api/subpedidos/${pedido_id}`);
|
||||
dispatch("guardarSesion", { pedido_id: pedido_id})
|
||||
commit('setPedido', data.data);
|
||||
},
|
||||
async modificarChismosa({ commit, dispatch }, { producto_id, cantidad, notas }) {
|
||||
const body = { cantidad: cantidad, producto_id: producto_id, notas: notas };
|
||||
try {
|
||||
const response = await axios.post("/api/subpedidos/" + state.pedido_id + "/sync", body);
|
||||
commit('setPedido', response.data.data);
|
||||
const { data } = await axios.post("/api/subpedidos/" + state.pedido_id + "/sync", {
|
||||
cantidad: cantidad,
|
||||
producto_id: producto_id,
|
||||
notas: notas,
|
||||
});
|
||||
commit('setPedido', data.data);
|
||||
dispatch("ui/toast", { mensaje: 'Pedido modificado con éxito' }, { root: true });
|
||||
} catch (error) {
|
||||
dispatch("ui/error", { error: error }, { root: true });
|
||||
}
|
||||
},
|
||||
async modificarDevoluciones({ commit, dispatch }, { monto, notas }) {
|
||||
const body = { total: monto, notas: notas };
|
||||
try {
|
||||
const response = await axios.post("api/subpedidos/" + state.pedido_id + "/sync_devoluciones", body);
|
||||
commit('setPedido', response.data.data);
|
||||
const { data } = await axios.post("api/subpedidos/" + state.pedido_id + "/sync_devoluciones", {
|
||||
total: monto,
|
||||
notas: notas,
|
||||
});
|
||||
commit('setPedido', data.data);
|
||||
dispatch("ui/toast", { mensaje: 'Devoluciones modificadas con éxito' }, { root: true });
|
||||
} catch (error) {
|
||||
dispatch("ui/error", { error: error }, { root: true });
|
||||
|
@ -121,16 +127,16 @@ const actions = {
|
|||
|
||||
const getters = {
|
||||
pedidoDefinido() {
|
||||
return state.lastFetch !== null;
|
||||
return state.lastFetch !== undefined;
|
||||
},
|
||||
enChismosa() {
|
||||
return ((producto_id) => state.productos.some(p => p.id === producto_id));
|
||||
},
|
||||
cantidad() {
|
||||
return ((producto_id) => state.productos.find(p => p.id === producto_id)?.pivot.cantidad ?? 0);
|
||||
return ((producto_id) => state.productos.find(p => p.id === producto_id)?.cantidad ?? 0);
|
||||
},
|
||||
notas() {
|
||||
return ((producto_id) => state.productos.find(p => p.id === producto_id)?.pivot.notas ?? "");
|
||||
return ((producto_id) => state.productos.find(p => p.id === producto_id)?.notas ?? "");
|
||||
}
|
||||
}
|
||||
|
22
resources/js/store/modules/pedido/types.ts
Normal file
22
resources/js/store/modules/pedido/types.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { Producto } from "../../comunes";
|
||||
|
||||
export interface PedidoState {
|
||||
lastFetch?: Date,
|
||||
grupo_de_compra?: Barrio,
|
||||
pedido_id?: number,
|
||||
nombre?: string,
|
||||
productos: Producto[],
|
||||
aprobado?: boolean,
|
||||
total?: number,
|
||||
total_transporte?: number,
|
||||
cantidad_transporte?: number,
|
||||
total_sin_devoluciones?: number,
|
||||
devoluciones_total?: number,
|
||||
devoluciones_notas?: string,
|
||||
}
|
||||
|
||||
export interface Barrio {
|
||||
id: number,
|
||||
nombre: string,
|
||||
devoluciones_habilitadas: boolean,
|
||||
}
|
|
@ -1,22 +1,23 @@
|
|||
import axios from "axios";
|
||||
import { ProductosState } from "./types";
|
||||
|
||||
const state = {
|
||||
lastFetch: null,
|
||||
const state: ProductosState = {
|
||||
lastFetch: undefined,
|
||||
categorias: [],
|
||||
productos: [],
|
||||
filtro: null,
|
||||
filtro: undefined,
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setCategorias(state, categorias) {
|
||||
setCategorias(state: ProductosState, categorias) {
|
||||
state.lastFetch = new Date();
|
||||
state.categorias = categorias;
|
||||
},
|
||||
setProductos(state, productos) {
|
||||
setProductos(state: ProductosState, productos) {
|
||||
state.lastFetch = new Date();
|
||||
state.productos = productos;
|
||||
},
|
||||
setFiltro(state, filtro) {
|
||||
setFiltro(state: ProductosState, filtro) {
|
||||
state.lastFetch = new Date();
|
||||
state.filtro = filtro;
|
||||
},
|
||||
|
@ -28,23 +29,29 @@ const actions = {
|
|||
dispatch('getProductos');
|
||||
},
|
||||
async getCategorias({ commit }) {
|
||||
const response = await axios.get('api/categorias');
|
||||
commit('setCategorias', response.data);
|
||||
const { data } = await axios.get('api/categorias');
|
||||
commit('setCategorias', data);
|
||||
},
|
||||
async getProductos({ commit }) {
|
||||
const response = await axios.get("/api/productos");
|
||||
commit('setFiltro', null);
|
||||
commit('setProductos', response.data.data);
|
||||
const { data } = await axios.get("/api/productos");
|
||||
commit('setFiltro', undefined);
|
||||
commit('setProductos', data.data);
|
||||
},
|
||||
async seleccionarCategoria({ dispatch }, { categoria }) {
|
||||
dispatch('filtrarProductos', { filtro: "categoria", valor: categoria });
|
||||
},
|
||||
async filtrarProductos({ commit }, { filtro, valor }) {
|
||||
const response = await axios.get("/api/productos", {
|
||||
const { data } = await axios.get("/api/productos", {
|
||||
params: { [filtro]: valor }
|
||||
});
|
||||
commit('setFiltro', { clave: filtro, valor: valor });
|
||||
commit('setProductos', response.data.data);
|
||||
commit('setProductos', data.data);
|
||||
}
|
||||
};
|
||||
|
||||
const getters = {
|
||||
mostrarProductos() {
|
||||
return state.filtro !== undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -53,4 +60,5 @@ export default {
|
|||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters,
|
||||
};
|
21
resources/js/store/modules/productos/types.ts
Normal file
21
resources/js/store/modules/productos/types.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
export interface ProductosState {
|
||||
lastFetch?: Date,
|
||||
categorias: string[],
|
||||
productos: Producto[],
|
||||
filtro?: Filtro,
|
||||
}
|
||||
|
||||
export interface Producto {
|
||||
id: number,
|
||||
nombre: string,
|
||||
precio: number,
|
||||
categoria: string,
|
||||
economia_solidaria: boolean,
|
||||
nacional: boolean,
|
||||
requiere_notas: boolean,
|
||||
}
|
||||
|
||||
export interface Filtro {
|
||||
clave: string,
|
||||
valor: string,
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
const state = {
|
||||
import { UiState } from "./types";
|
||||
|
||||
const state: UiState = {
|
||||
show_chismosa: false,
|
||||
show_devoluciones: false,
|
||||
show_tags: true,
|
||||
burger_activa: false,
|
||||
tags_interactuada: false,
|
||||
migas: [{ nombre: 'Pedidos', action: 'pedido/resetear' }],
|
||||
canasta_actual: null,
|
||||
canasta_actual: undefined,
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
|
@ -29,7 +31,7 @@ const mutations = {
|
|||
state.migas.push(miga);
|
||||
},
|
||||
popUltimaBusqueda(state) {
|
||||
if (!state.migas.at(-1).action)
|
||||
if (!state.migas.at(-1)?.action)
|
||||
state.migas.pop();
|
||||
},
|
||||
reset(state) {
|
||||
|
@ -43,8 +45,8 @@ const mutations = {
|
|||
|
||||
const actions = {
|
||||
async getCanastaActual({ commit }) {
|
||||
const response = await axios.get('/api/canasta-actual');
|
||||
commit("setCanastaActual", { canasta: response.data });
|
||||
const { data } = await axios.get('/api/canasta-actual');
|
||||
commit("setCanastaActual", { canasta: data });
|
||||
},
|
||||
clickMiga({ dispatch }, { miga }) {
|
||||
let dropWhile = (array, pred) => {
|
20
resources/js/store/modules/ui/types.ts
Normal file
20
resources/js/store/modules/ui/types.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
export interface UiState {
|
||||
show_chismosa: boolean,
|
||||
show_devoluciones: boolean,
|
||||
show_tags: boolean,
|
||||
tags_interactuada: boolean,
|
||||
migas: Miga[],
|
||||
canasta_actual?: DatosCanasta,
|
||||
}
|
||||
|
||||
export interface Miga {
|
||||
nombre: string,
|
||||
action: string,
|
||||
arguments?: { [key: string]: string },
|
||||
}
|
||||
|
||||
export interface DatosCanasta {
|
||||
nombre: string,
|
||||
fecha: Date,
|
||||
}
|
4
resources/sass/app.scss
vendored
4
resources/sass/app.scss
vendored
|
@ -4,8 +4,8 @@
|
|||
// Variables
|
||||
@import 'variables';
|
||||
|
||||
@import 'bulma';
|
||||
@import '~bulma-switch';
|
||||
@import '../../node_modules/bulma/bulma.sass';
|
||||
@import '../../node_modules/bulma-switch/src/sass/index.sass';
|
||||
|
||||
html, body {
|
||||
overflow-x: hidden;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ config('app.name', 'Pedidos del MPS') }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
|
||||
@vite(['resources/sass/app.scss', 'resources/js/app.ts'])
|
||||
<script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -15,6 +15,5 @@
|
|||
<app-login></app-login>
|
||||
</form>
|
||||
</div>
|
||||
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<!-- Fonts -->
|
||||
<script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
|
||||
@vite(['resources/sass/app.scss', 'resources/js/app.ts'])
|
||||
@yield('stylesheets')
|
||||
</head>
|
||||
<body class="has-navbar-fixed-top">
|
||||
|
@ -28,8 +28,6 @@
|
|||
@yield('content')
|
||||
</main>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||
@yield('scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -51,7 +51,7 @@ Route::get('/comisiones/login', 'ComisionesController@show')->name('comisiones.l
|
|||
Route::middleware(['auth', 'role:comision'])->group( function() {
|
||||
Route::get('/comisiones', 'RouteController@main')->name('comisiones');
|
||||
Route::get('/comisiones/pedidos/descargar', 'ComisionesController@descargarPedidos')->name('comisiones.pedidos.descargar');
|
||||
Route::get('/comisiones/pedidos/notas', 'ComisionesController@descargarNotas')->name('comisiones.pedidos.descargar');
|
||||
Route::get('/comisiones/pedidos/notas', 'ComisionesController@descargarNotas')->name('comisiones.pedidos.notas');
|
||||
Route::get('/comisiones/pedidos/pdf', 'ComisionesController@pdf')->name('comisiones.pedidos.pdf');
|
||||
Route::get('/comisiones/pedidos/ollas', 'ComisionesController@descargarPedidosDeOllas')->name('comisiones.pedidos.ollas');
|
||||
Route::get('/comisiones/canasta/ejemplo', 'ComisionesController@descargarCanastaEjemplo')->name('comisiones.canasta.ejemplo');
|
||||
|
|
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"lib": ["esnext", "dom"],
|
||||
"jsx": "preserve",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitAny": false,
|
||||
"skipLibCheck": true,
|
||||
"types": ["vite/client"]
|
||||
},
|
||||
"include": ["resources/js/**/*", "resources/**/*.vue"]
|
||||
}
|
23
vite.config.mjs
Normal file
23
vite.config.mjs
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue2';
|
||||
import laravel from 'laravel-vite-plugin';
|
||||
|
||||
export default defineConfig({
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 5173,
|
||||
},
|
||||
plugins: [
|
||||
laravel({
|
||||
input: ['resources/js/app.ts', 'resources/sass/app.scss'],
|
||||
refresh: true,
|
||||
}),
|
||||
vue(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': '/resources/js',
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
},
|
||||
},
|
||||
});
|
16
webpack.mix.js
vendored
16
webpack.mix.js
vendored
|
@ -1,16 +0,0 @@
|
|||
const mix = require('laravel-mix');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mix Asset Management
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Mix provides a clean, fluent API for defining some Webpack build steps
|
||||
| for your Laravel application. By default, we are compiling the Sass
|
||||
| file for the application as well as bundling up all the JS files.
|
||||
|
|
||||
*/
|
||||
|
||||
mix.js('resources/js/app.js', 'public/js')
|
||||
.sass('resources/sass/app.scss', 'public/css')
|
||||
.version();
|
Loading…
Add table
Reference in a new issue