Compare commits

..

No commits in common. "funcion/actualizaciones" and "master" have entirely different histories.

47 changed files with 26799 additions and 4538 deletions

View file

@ -4,11 +4,9 @@ APP_KEY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=http://localhost APP_URL=http://localhost
VITE_DEV_SERVER_URL=http://vite:5173
LOG_CHANNEL=stack LOG_CHANNEL=stack
USERID=1000 USERID=
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=db DB_HOST=db
@ -52,4 +50,4 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
WEB_CLIENT_EMAIL=informaticamps@buzon.uy WEB_CLIENT_EMAIL=informaticamps@buzon.uy
WEB_CLIENT_NAME=web WEB_CLIENT_NAME=web
WEB_CLIENT_PASS=pass WEB_CLIENT_PASS=pass
NGINX_PORT=8000 NGINX_PORT=8000

View file

@ -1,4 +1,4 @@
FROM php:8.3-fpm FROM php:7.4-fpm
# Arguments defined in docker-compose.yml # Arguments defined in docker-compose.yml
ARG user ARG user
@ -12,8 +12,7 @@ RUN apt-get update && apt-get install -y \
libonig-dev \ libonig-dev \
libxml2-dev \ libxml2-dev \
zip \ zip \
unzip \ unzip
libzip-dev
# Install node # Install node
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \ RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
@ -23,7 +22,7 @@ RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
RUN apt-get clean && rm -rf /var/lib/apt/lists/* RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install PHP extensions # Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
# Get latest Composer # Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

View file

@ -2,14 +2,13 @@
Aplicación de pedidos del Mercado Popular de Subsistencia. Aplicación de pedidos del Mercado Popular de Subsistencia.
Pedi2 está hecha en Laravel 12 y Vue 2 con Vite y Vuex. Pedi2 está hecha en Laravel 7 y utiliza laravel7-docker de dyarleniber.
Se utilizan los siguientes servicios, separadamente: Se utilizan los siguientes servicios, separadamente:
- `app`, un servicio que corre PHP8.3-FPM. - `app`, un servicio que corre PHP7.4-FPM.
- `db`, un servicio que corre MySQL 5.7. - `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. - `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 ## Pre-requisitos
- docker - docker
@ -18,28 +17,28 @@ Se utilizan los siguientes servicios, separadamente:
## Instalación ## 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`). 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 ```bash
docker-compose up --build docker-compose up -d --build
``` ```
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. 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.
3. Abrí una nueva terminal, y terminá de instalar las dependencias de la app, según fueron definidas en `composer.json`: 3. Terminá de instalar las dependencias de la app, según fueron definidas en `composer.json`.
```bash ```bash
docker-compose exec app composer install 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 ```bash
docker-compose exec app php artisan key:generate 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 ```bash
docker-compose exec app php artisan migrate:fresh --seed docker-compose exec app php artisan migrate:fresh --seed
@ -47,7 +46,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 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 ```bash
docker-compose exec app npm install docker-compose exec app npm install
``` ```
@ -55,13 +54,73 @@ 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. 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: Podés usar el comando `logs` para ver los logs generados por tus servicios:
```bash ```bash
docker-compose logs nginx docker-compose logs nginx
``` ```
---
Si estás actualizando o no te anda, probá limpiar los caches: 8. Ejecuta npm para compilar el js y css
```bash
docker-compose exec app npm run prod
``` ```
docker-compose exec app php artisan optimize:clear
docker-compose exec app composer dump-autoload ## 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 youll 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 youre connecting as a regular user, and that youre 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 containers 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 containers 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
``` ```
## 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

View file

@ -12,6 +12,7 @@ use TypeError;
class Filtro extends Model class Filtro extends Model
{ {
protected Request $request; protected Request $request;
protected $builder;
protected array $MENSAJES_ERROR = [ protected array $MENSAJES_ERROR = [
'ARGUMENTO' => 'Argumento inválido para el parámetro %s. Revise la documentación.' 'ARGUMENTO' => 'Argumento inválido para el parámetro %s. Revise la documentación.'
]; ];

View file

@ -18,7 +18,7 @@ class Kernel extends HttpKernel
protected $middleware = [ protected $middleware = [
// \App\Http\Middleware\TrustHosts::class, // \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class, \App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class, \Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\CheckForMaintenanceMode::class, \App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class, \App\Http\Middleware\TrimStrings::class,

View file

@ -2,8 +2,8 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware; use Fideloper\Proxy\TrustProxies as Middleware;
use Symfony\Component\HttpFoundation\Request; use Illuminate\Http\Request;
class TrustProxies extends Middleware class TrustProxies extends Middleware
{ {
@ -19,8 +19,5 @@ class TrustProxies extends Middleware
* *
* @var int * @var int
*/ */
protected $headers = Request::HEADER_X_FORWARDED_FOR | protected $headers = Request::HEADER_X_FORWARDED_ALL;
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO;
} }

View file

@ -8,20 +8,23 @@
], ],
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.3", "php": "^7.4",
"doctrine/dbal": "^3.0", "doctrine/dbal": "^2.2.0",
"guzzlehttp/guzzle": "^7.0.1", "fideloper/proxy": "^4.4",
"laravel/framework": "^12.0", "fruitcake/laravel-cors": "^2.0",
"laravel/sanctum": "^4.0", "guzzlehttp/guzzle": "^6.3.1|^7.0.1",
"laravel/tinker": "^2.8", "laravel/framework": "^7.29",
"laravel/ui": "^4.3", "laravel/sanctum": "^2.13",
"laravel/tinker": "^2.5",
"laravel/ui": "*",
"league/csv": "^9.8", "league/csv": "^9.8",
"mpdf/mpdf": "^8.2", "mpdf/mpdf": "^8.1",
"prexview/prexview": "^1.1" "prexview/prexview": "^1.1"
}, },
"require-dev": { "require-dev": {
"facade/ignition": "^2.0",
"fakerphp/faker": "^1.9.1", "fakerphp/faker": "^1.9.1",
"nunomaduro/collision": "^8.0" "nunomaduro/collision": "^4.3"
}, },
"config": { "config": {
"optimize-autoloader": true, "optimize-autoloader": true,
@ -52,13 +55,13 @@
"scripts": { "scripts": {
"post-autoload-dump": [ "post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi" "@php7.4 artisan package:discover --ansi"
], ],
"post-root-package-install": [ "post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" "@php7.4 -r \"file_exists('.env') || copy('.env.example', '.env');\""
], ],
"post-create-project-cmd": [ "post-create-project-cmd": [
"@php artisan key:generate --ansi" "@php7.4 artisan key:generate --ansi"
] ]
} }
} }

3413
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,7 @@ class UserSeeder extends Seeder
$usersToInsert = []; $usersToInsert = [];
$usersToInsert[] = DatabaseSeeder::addTimestamps([ $usersToInsert[] = DatabaseSeeder::addTimestamps([
'name' => 'comision', 'name' => 'comi',
'password' => Hash::make("123"), 'password' => Hash::make("123"),
'role_id' => UserRole::where('nombre', 'comision')->first()->id, 'role_id' => UserRole::where('nombre', 'comision')->first()->id,
]); ]);

View file

@ -1,4 +1,4 @@
version: '3.2' version: '3.2'
services: services:
app: app:
@ -18,22 +18,6 @@ services:
networks: networks:
- app-network - 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: db:
image: mysql:5.7 image: mysql:5.7
container_name: pedi2-db container_name: pedi2-db

View file

@ -13,22 +13,6 @@ server {
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info; 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 / { location / {
try_files $uri $uri/ /index.php?$query_string; try_files $uri $uri/ /index.php?$query_string;
gzip_static on; gzip_static on;

27048
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,7 @@
{ {
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "npm run development",
"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", "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": "npm run development -- --watch",
"watch-poll": "npm run watch -- --watch-poll", "watch-poll": "npm run watch -- --watch-poll",
@ -11,24 +10,20 @@
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js" "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js"
}, },
"devDependencies": { "devDependencies": {
"@types/axios": "^0.9.36", "axios": "^0.19.2",
"@vitejs/plugin-vue2": "^2.3.3",
"axios": "^0.27.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"laravel-vite-plugin": "^1.3.0", "laravel-mix": "^5.0.1",
"resolve-url-loader": "^2.3.1",
"sass": "^1.20.1", "sass": "^1.20.1",
"sass-loader": "^8.0.0", "sass-loader": "^8.0.0",
"typescript": "^5.4.5", "vue": "^2.5.17",
"vite": "^5.2.8" "vue-template-compiler": "^2.6.10"
}, },
"dependencies": { "dependencies": {
"vue": "^2.7.16",
"vue-template-compiler": "^2.7.16",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"bulma-switch": "^2.0.4", "bulma-switch": "^2.0.4",
"bulma-toast": "^2.4.1", "bulma-toast": "^2.4.1",
"vue-router": "^3.5.4",
"vuex": "^3.6.2" "vuex": "^3.6.2"
} }
} }

45
resources/js/app.js vendored Normal file
View file

@ -0,0 +1,45 @@
/**
* 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,
});

View file

@ -1,27 +0,0 @@
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 Normal file
View file

@ -0,0 +1,25 @@
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
);
});

View file

@ -1,12 +1,12 @@
<script> <script>
import LoginInput from "./login/LoginInput.vue"; import LoginInput from "./login/LoginInput.vue";
import LoginTitulos from "./login/LoginTitulos.vue"; import LoginLoginTitulos from "./login/LoginTitulos.vue";
import { mapGetters } from "vuex"; import { mapGetters } from "vuex";
import LoginDropdown from "./login/LoginDropdown.vue"; import LoginDropdown from "./login/LoginDropdown.vue";
export default { export default {
name: 'AppLogin', name: 'LoginForm',
components: { LoginDropdown, LoginTitulos, LoginInput }, components: { LoginDropdown, LoginTitulos: LoginLoginTitulos, LoginInput },
computed: { computed: {
...mapGetters("login", ["estilos"]) ...mapGetters("login", ["estilos"])
} }
@ -17,7 +17,7 @@ export default {
<div id="login-form" :class="estilos.fondo"> <div id="login-form" :class="estilos.fondo">
<section class="section"> <section class="section">
<div class="columns is-centered"> <div class="columns is-centered">
<div class="column is-three-quarters"> <div class="column is-half">
<login-dropdown class="is-hidden-tablet"/> <login-dropdown class="is-hidden-tablet"/>
<login-titulos/> <login-titulos/>
<login-input/> <login-input/>

View file

@ -14,14 +14,14 @@
</div> </div>
<div class="dropdown-menu" id="dropdown-menu" role="menu"> <div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content"> <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"> <a :href="'/admin/exportar-planillas-a-pdf/' + grupo_de_compra_id" class="dropdown-item">
Planillas para armado (PDF) Planillas para armado (PDF)
</a> </a>
<a :href="'/admin/exportar-pedido-con-nucleos-a-csv/' + grupo_de_compra_id" class="dropdown-item"> <a :href="'/admin/exportar-pedido-con-nucleos-a-csv/' + grupo_de_compra_id" class="dropdown-item">
Pedidos por núcleo (CSV) Planilla completa de la canasta (CSV)
</a>
<a :href="'/admin/exportar-pedido-a-csv/' + grupo_de_compra_id" class="dropdown-item">
Pedido barrial (CSV)
</a> </a>
</div> </div>
</div> </div>

View file

@ -38,6 +38,7 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../../../../node_modules/bulma';
hr { hr {
border: none; border: none;
height: 1px; height: 1px;

View file

@ -1,6 +1,6 @@
<script > <script >
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import { mapGetters, mapMutations, mapState } from "vuex"; import { mapMutations, mapState } from "vuex";
import CategoriasContainer from "./CategoriasContainer.vue"; import CategoriasContainer from "./CategoriasContainer.vue";
import ProductosContainer from "./ProductosContainer.vue"; import ProductosContainer from "./ProductosContainer.vue";
import Chismosa from "./Chismosa.vue"; import Chismosa from "./Chismosa.vue";
@ -9,8 +9,7 @@ import DevolucionesModal from "./DevolucionesModal.vue";
export default defineComponent({ export default defineComponent({
components: { DevolucionesModal, CategoriasContainer, ProductosContainer, Chismosa }, components: { DevolucionesModal, CategoriasContainer, ProductosContainer, Chismosa },
computed: { computed: {
...mapState('ui', ["show_chismosa", "show_devoluciones", "tags_interactuada"]), ...mapState('ui', ["show_chismosa", "show_devoluciones", "tags_interactuada"])
...mapGetters('productos', ["mostrarProductos"]),
}, },
methods: { methods: {
...mapMutations("ui", ["toggleTags"]), ...mapMutations("ui", ["toggleTags"]),
@ -24,8 +23,8 @@ export default defineComponent({
<template> <template>
<div class="columns ml-3 mr-3" v-else> <div class="columns ml-3 mr-3" v-else>
<productos-container v-if="mostrarProductos" :class="show_chismosa ? 'hide-below-1024' : ''"/> <categorias-container :class="show_chismosa ? 'hide-below-1024' : ''"/>
<categorias-container v-else :class="show_chismosa ? 'hide-below-1024' : ''"/> <productos-container :class="show_chismosa ? 'hide-below-1024' : ''"/>
<devoluciones-modal v-show="show_devoluciones"/> <devoluciones-modal v-show="show_devoluciones"/>
</div> </div>
</template> </template>

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="column"> <div v-show="visible" class="column">
<div ref="categorias" <div ref="categorias"
class="columns is-multiline is-mobile" class="columns is-multiline is-mobile"
:class="{ 'align-last-left': isLastRowIncomplete }"> :class="{ 'align-last-left': isLastRowIncomplete }">
@ -28,6 +28,9 @@ export default {
computed: { computed: {
...mapState('productos', ["categorias", "filtro"]), ...mapState('productos', ["categorias", "filtro"]),
...mapState('ui', ["show_chismosa"]), ...mapState('ui', ["show_chismosa"]),
visible() {
return this.filtro === null;
}
}, },
methods: { methods: {
...mapActions('productos', ["seleccionarCategoria"]), ...mapActions('productos', ["seleccionarCategoria"]),

View file

@ -85,7 +85,7 @@ export default {
...mapActions('pedido', ["getGrupoDeCompra", "crearPedido", "elegirPedido"]), ...mapActions('pedido', ["getGrupoDeCompra", "crearPedido", "elegirPedido"]),
...mapMutations("ui", ["toggleTags"]), ...mapMutations("ui", ["toggleTags"]),
async getPedidos(nombre) { async getPedidos(nombre) {
const response = await axios.get('/api/subpedidos',{ const response = await axios.get('/api/subpedidos/',{
params: { params: {
nombre: nombre, nombre: nombre,
grupo_de_compra: this.grupo_de_compra.id, grupo_de_compra: this.grupo_de_compra.id,

View file

@ -7,7 +7,7 @@
:requiere_notas="producto.requiere_notas" :requiere_notas="producto.requiere_notas"
/> />
</td> </td>
<td class="has-text-right">{{ `${producto.total}` }}</td> <td class="has-text-right">{{ `${cantidad(producto.id) * producto.precio}` }}</td>
</tr> </tr>
</template> </template>
<script> <script>

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="column"> <div v-show="visible" class="column">
<div ref="productos" <div ref="productos"
class="columns is-multiline is-mobile" class="columns is-multiline is-mobile"
:class="{ 'align-last-left': isLastRowIncomplete }"> :class="{ 'align-last-left': isLastRowIncomplete }">
@ -21,6 +21,9 @@ export default {
computed: { computed: {
...mapState('productos', ["productos", "filtro"]), ...mapState('productos', ["productos", "filtro"]),
...mapState('ui', ["show_chismosa"]), ...mapState('ui', ["show_chismosa"]),
visible() {
return this.filtro !== null;
},
miga: function () { miga: function () {
return { return {
nombre: this.filtro.valor, nombre: this.filtro.valor,

View file

@ -1,10 +0,0 @@
declare global {
interface Window {
Vue: typeof Vue;
Event: InstanceType<typeof Vue>;
axios: typeof axios;
bulmaToast: {
toast: (options: any) => void;
};
}
}

View file

@ -1,5 +0,0 @@
// resources/js/shims-vue.d.ts
declare module '*.vue' {
import Vue from '../../node_modules/vue/dist/vue.esm.js';
export default Vue;
}

View file

@ -1,53 +0,0 @@
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,
};
}

View file

@ -1,8 +1,8 @@
import Vue from '../../../node_modules/vue/dist/vue.esm.js'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import login from "./modules/login";
import admin from "./modules/admin"; import admin from "./modules/admin";
import comisiones from "./modules/comisiones"; import comisiones from "./modules/comisiones";
import login from "./modules/login";
import pedido from "./modules/pedido"; import pedido from "./modules/pedido";
import productos from "./modules/productos"; import productos from "./modules/productos";
import ui from "./modules/ui"; import ui from "./modules/ui";
@ -12,9 +12,9 @@ Vue.use(Vuex);
export default new Vuex.Store({ export default new Vuex.Store({
modules: { modules: {
login,
admin, admin,
comisiones, comisiones,
login,
pedido, pedido,
productos, productos,
ui, ui,

View file

@ -1,36 +1,29 @@
import axios from "axios"; import axios from "axios";
import { AdminState } from "./types";
import { aplanarProducto } from "../../comunes";
const state: AdminState = { const state = {
lastFetch: undefined, lastFetch: null,
grupo_de_compra_id: undefined, grupo_de_compra_id: null,
nombre: undefined, nombre: null,
devoluciones_habilitadas: undefined, devoluciones_habilitadas: null,
pedidos: [], pedidos: null,
total_a_recaudar: undefined, total_a_recaudar: null,
total_sin_devoluciones: undefined, total_sin_devoluciones: null,
total_barrial: undefined, total_barrial: null,
total_devoluciones: undefined, total_devoluciones: null,
total_de_pedido: undefined, total_de_pedido: null,
total_a_transferir: undefined, total_a_transferir: null,
total_transporte: undefined, total_transporte: null,
cantidad_transporte: undefined, cantidad_transporte: null,
saldo: undefined, saldo: null,
}; };
const mutations = { const mutations = {
setState(state: AdminState, { grupo_de_compra }) { setState(state, { grupo_de_compra }) {
const aplanarProductos = (pedido) => {
pedido.productos = pedido.productos.map(p => aplanarProducto(p));
return pedido;
};
state.lastFetch = new Date(); state.lastFetch = new Date();
state.grupo_de_compra_id = grupo_de_compra.id; state.grupo_de_compra_id = grupo_de_compra.id;
state.nombre = grupo_de_compra.nombre; state.nombre = grupo_de_compra.nombre;
state.devoluciones_habilitadas = grupo_de_compra.devoluciones_habilitadas; state.devoluciones_habilitadas = grupo_de_compra.devoluciones_habilitadas;
state.pedidos = grupo_de_compra.pedidos.map(p => aplanarProductos(p)); state.pedidos = grupo_de_compra.pedidos;
state.total_a_recaudar = grupo_de_compra.total_a_recaudar; state.total_a_recaudar = grupo_de_compra.total_a_recaudar;
state.total_sin_devoluciones = grupo_de_compra.total_sin_devoluciones; state.total_sin_devoluciones = grupo_de_compra.total_sin_devoluciones;
state.total_barrial = grupo_de_compra.total_barrial; state.total_barrial = grupo_de_compra.total_barrial;
@ -48,8 +41,8 @@ const mutations = {
const actions = { const actions = {
async getGrupoDeCompra({ commit }) { async getGrupoDeCompra({ commit }) {
const { data } = await axios.get('/user/grupo_de_compra'); const response = await axios.get('/user/grupo_de_compra');
commit('setState', data); commit('setState', response.data);
}, },
async setAprobacionPedido({ commit, dispatch }, { pedido_id, aprobacion }){ async setAprobacionPedido({ commit, dispatch }, { pedido_id, aprobacion }){
await axios.post("/api/admin/subpedidos/" + pedido_id + "/aprobacion", { aprobacion: aprobacion }); await axios.post("/api/admin/subpedidos/" + pedido_id + "/aprobacion", { aprobacion: aprobacion });
@ -66,7 +59,7 @@ const actions = {
const getters = { const getters = {
grupoDeCompraDefinido() { grupoDeCompraDefinido() {
return state.lastFetch !== undefined; return state.lastFetch !== null;
}, },
barrio() { barrio() {
return state.nombre?.replace('_admin','') ?? ''; return state.nombre?.replace('_admin','') ?? '';

View file

@ -1,33 +0,0 @@
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
}

View file

@ -1,14 +1,13 @@
import axios from "axios"; import axios from "axios";
import { ComisionesState } from "./types";
const state: ComisionesState = { const state = {
lastFetch: undefined, lastFetch: undefined,
grupos_de_compra: [], grupos_de_compra: [],
parametros: [], parametros: [],
}; };
const mutations = { const mutations = {
setGruposDeCompra(state: ComisionesState, { data }) { setGruposDeCompra(state, { data }) {
state.grupos_de_compra = data; state.grupos_de_compra = data;
state.lastFetch = new Date(); state.lastFetch = new Date();
}, },
@ -21,17 +20,17 @@ const mutations = {
actualizarParametro(state, { parametro_id, valor }) { actualizarParametro(state, { parametro_id, valor }) {
state.parametros.find(p => p.id === parametro_id).valor = valor; state.parametros.find(p => p.id === parametro_id).valor = valor;
}, },
setSaldo(state: ComisionesState, { gdc_id, saldo }) { setSaldo(state, { gdc_id, saldo }) {
const barrio = state.grupos_de_compra.find(gdc => gdc.id === gdc_id); 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; state.grupos_de_compra[i].saldo = saldo;
}, },
}; };
const actions = { const actions = {
async getGruposDeCompra({ commit }) { async getGruposDeCompra({ commit }) {
const { data } = await axios.get('/api/grupos-de-compra/saldos'); const response = await axios.get('/api/grupos-de-compra/saldos');
commit('setGruposDeCompra', data); commit('setGruposDeCompra', response.data);
}, },
async getParametros({ commit }) { async getParametros({ commit }) {
const response = await axios.get('/api/parametros'); const response = await axios.get('/api/parametros');

View file

@ -1,10 +0,0 @@
export interface ComisionesState {
lastFetch?: Date,
grupos_de_compra: Barrio[],
}
export interface Barrio {
id: number,
nombre: string,
saldo: number,
}

View file

@ -1,23 +1,22 @@
import axios from "axios"; import axios from "axios";
import { Estilos, LoginState, OpcionLogin, Textos, UrlRol, UserRol } from "./types";
const state: LoginState = { const state = {
regiones: [], regiones: null,
grupos_de_compra: [], grupos_de_compra: null,
region_elegida: undefined, region_elegida: null,
grupo_de_compra_elegido: undefined, grupo_de_compra_elegido: null,
rol: undefined, rol: null,
}; };
const mutations = { const mutations = {
setRegiones(state, { regiones }): void { setRegiones(state, { regiones }) {
state.regiones = regiones; state.regiones = regiones;
}, },
setRegionYBarrios(state, { region, grupos_de_compra }): void { setRegionYBarrios(state, { region, grupos_de_compra }) {
state.region_elegida = region; state.region_elegida = region;
state.grupos_de_compra = grupos_de_compra; state.grupos_de_compra = grupos_de_compra;
}, },
selectGrupoDeCompra(state, { grupo_de_compra }): void { selectGrupoDeCompra(state, { grupo_de_compra }) {
state.grupo_de_compra_elegido = grupo_de_compra; state.grupo_de_compra_elegido = grupo_de_compra;
}, },
setRol(state, { rol }) { setRol(state, { rol }) {
@ -26,29 +25,29 @@ const mutations = {
}; };
const actions = { const actions = {
async getRegiones({ commit }): Promise<void> { async getRegiones({ commit }) {
const { data } = await axios.get("/api/regiones"); const response = await axios.get("/api/regiones");
commit('setRegiones', { regiones: data }); commit('setRegiones', { regiones: response.data });
}, },
async selectRegion({ commit }, { region }): Promise<void> { async selectRegion({ commit }, { region }) {
const { data } = await axios.get(`/api/regiones/${region}`); const response = await axios.get(`/api/regiones/${region}`);
commit('setRegionYBarrios', { region: region, grupos_de_compra: data }); commit('setRegionYBarrios', { region: region, grupos_de_compra: response.data });
}, },
async getRol({ commit }): Promise<void> { async getRol({ commit }) {
const { data }: UserRol = await axios.get("/user/rol"); const response = await axios.get("/user/rol");
commit('setRol', { rol: data.rol }); commit('setRol', { rol: response.data.rol });
} }
}; };
const getters = { const getters = {
urlRol(): UrlRol { urlRol() {
let split = window.location.pathname let split = window.location.pathname
.replace('login', '') .replace('login', '')
.split('/') .split('/')
.filter(x => x.length); .filter(x => x.length);
return split[0] as UrlRol ?? 'pedido'; return split[0] ?? 'pedido';
}, },
textos(): Textos { textos() {
let rol = getters.urlRol(); let rol = getters.urlRol();
switch (rol) { switch (rol) {
case 'pedido': case 'pedido':
@ -87,7 +86,7 @@ const getters = {
throw new Error("Url inválida"); throw new Error("Url inválida");
} }
}, },
estilos(): Estilos { estilos() {
let rol = getters.urlRol(); let rol = getters.urlRol();
switch (rol) { switch (rol) {
case 'pedido': case 'pedido':
@ -104,8 +103,8 @@ const getters = {
}; };
case 'comisiones': case 'comisiones':
return { return {
fondo: "has-background-dark", fondo: "has-background-grey",
texto: "has-text-danger-light", texto: "has-text-white",
botones: "danger-dark-button", botones: "danger-dark-button",
}; };
case 'ollas': case 'ollas':
@ -118,7 +117,7 @@ const getters = {
throw new Error("Url inválida"); throw new Error("Url inválida");
} }
}, },
opcionesLogin(): OpcionLogin[] { opcionesLogin() {
let rol = getters.urlRol(); let rol = getters.urlRol();
switch (rol) { switch (rol) {
case 'pedido': case 'pedido':

View file

@ -1,43 +0,0 @@
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';

View file

@ -1,32 +1,30 @@
import axios from "axios"; import axios from "axios";
import { PedidoState } from "./types";
import { aplanarProducto } from "../../comunes";
const state: PedidoState = { const state = {
lastFetch: undefined, lastFetch: null,
grupo_de_compra: undefined, grupo_de_compra: null,
pedido_id: undefined, pedido_id: 0,
nombre: undefined, nombre: "",
productos: [], productos: [],
aprobado: undefined, aprobado: false,
total: undefined, total: 0,
total_transporte: undefined, total_transporte: 0,
cantidad_transporte: undefined, cantidad_transporte: 0,
total_sin_devoluciones: undefined, total_sin_devoluciones: 0,
devoluciones_total: undefined, devoluciones_total: 0,
devoluciones_notas: undefined, devoluciones_notas: "",
cantidad_de_ollas: undefined, cantidad_de_ollas: 0,
}; };
const mutations = { const mutations = {
setGrupoDeCompra(state: PedidoState, grupo_de_compra) { setGrupoDeCompra(state, grupo_de_compra) {
state.grupo_de_compra = grupo_de_compra; state.grupo_de_compra = grupo_de_compra;
}, },
setPedido(state: PedidoState, pedido) { setPedido(state, pedido) {
state.lastFetch = new Date(); state.lastFetch = new Date();
state.pedido_id = pedido.id; state.pedido_id = pedido.id;
state.nombre = pedido.nombre; state.nombre = pedido.nombre;
state.productos = pedido.productos.map(p => aplanarProducto(p)); state.productos = pedido.productos;
state.aprobado = pedido.aprobado; state.aprobado = pedido.aprobado;
state.total = Number.parseFloat(pedido.total.replace(',','')); state.total = Number.parseFloat(pedido.total.replace(',',''));
state.total_transporte = Number.parseInt(pedido.total_transporte?.replace(',','')); state.total_transporte = Number.parseInt(pedido.total_transporte?.replace(',',''));
@ -49,18 +47,18 @@ const mutations = {
delete state.devoluciones_total; delete state.devoluciones_total;
delete state.devoluciones_notas; delete state.devoluciones_notas;
}, },
reset(state: PedidoState) { reset(state) {
state.lastFetch = undefined; state.lastFetch = null;
state.pedido_id = undefined; state.pedido_id = null;
state.nombre = undefined; state.nombre = null;
state.productos = []; state.productos = null;
state.aprobado = undefined; state.aprobado = null;
state.total = undefined; state.total = null;
state.total_transporte = undefined; state.total_transporte = null;
state.cantidad_transporte = undefined; state.cantidad_transporte = null;
state.total_sin_devoluciones = undefined; state.total_sin_devoluciones = null;
state.devoluciones_total = undefined; state.devoluciones_total = null;
state.devoluciones_notas = undefined; state.devoluciones_notas = null;
}, },
setCantidadOllas(state, { cantidad }) { setCantidadOllas(state, { cantidad }) {
if (cantidad >= 0) if (cantidad >= 0)
@ -70,8 +68,8 @@ const mutations = {
const actions = { const actions = {
async getGrupoDeCompra({ commit }) { async getGrupoDeCompra({ commit }) {
const { data } = await axios.get('/user/grupo_de_compra'); const response = await axios.get('/user/grupo_de_compra');
commit('setGrupoDeCompra', data.grupo_de_compra); commit('setGrupoDeCompra', response.data.grupo_de_compra);
}, },
async guardarSesion(_, { pedido_id }) { async guardarSesion(_, { pedido_id }) {
const body = { id: pedido_id }; const body = { id: pedido_id };
@ -79,35 +77,31 @@ const actions = {
}, },
async crearPedido({ commit, dispatch }, { nombre, grupo_de_compra_id, tipo_id }) { async crearPedido({ commit, dispatch }, { nombre, grupo_de_compra_id, tipo_id }) {
const body = { nombre, grupo_de_compra_id, tipo_id }; const body = { nombre, grupo_de_compra_id, tipo_id };
const { data } = await axios.post("/api/subpedidos", body); const response = await axios.post("/api/subpedidos", body);
dispatch("guardarSesion", { pedido_id: data.data.id}); dispatch("guardarSesion", { pedido_id: response.data.data.id});
commit('setPedido', data.data); commit('setPedido', response.data.data);
}, },
async elegirPedido({ commit, dispatch }, { pedido_id }) { async elegirPedido({ commit, dispatch }, { pedido_id }) {
const { data } = await axios.get(`/api/subpedidos/${pedido_id}`); const response = await axios.get(`/api/subpedidos/${pedido_id}`);
dispatch("guardarSesion", { pedido_id: pedido_id}) const body = { pedido_id: pedido_id};
commit('setPedido', data.data); dispatch("guardarSesion", body)
commit('setPedido', response.data.data);
}, },
async modificarChismosa({ commit, dispatch }, { producto_id, cantidad, notas }) { async modificarChismosa({ commit, dispatch }, { producto_id, cantidad, notas }) {
const body = { cantidad: cantidad, producto_id: producto_id, notas: notas };
try { try {
const { data } = await axios.post("/api/subpedidos/" + state.pedido_id + "/sync", { const response = await axios.post("/api/subpedidos/" + state.pedido_id + "/sync", body);
cantidad: cantidad, commit('setPedido', response.data.data);
producto_id: producto_id,
notas: notas,
});
commit('setPedido', data.data);
dispatch("ui/toast", { mensaje: 'Pedido modificado con éxito' }, { root: true }); dispatch("ui/toast", { mensaje: 'Pedido modificado con éxito' }, { root: true });
} catch (error) { } catch (error) {
dispatch("ui/error", { error: error }, { root: true }); dispatch("ui/error", { error: error }, { root: true });
} }
}, },
async modificarDevoluciones({ commit, dispatch }, { monto, notas }) { async modificarDevoluciones({ commit, dispatch }, { monto, notas }) {
const body = { total: monto, notas: notas };
try { try {
const { data } = await axios.post("api/subpedidos/" + state.pedido_id + "/sync_devoluciones", { const response = await axios.post("api/subpedidos/" + state.pedido_id + "/sync_devoluciones", body);
total: monto, commit('setPedido', response.data.data);
notas: notas,
});
commit('setPedido', data.data);
dispatch("ui/toast", { mensaje: 'Devoluciones modificadas con éxito' }, { root: true }); dispatch("ui/toast", { mensaje: 'Devoluciones modificadas con éxito' }, { root: true });
} catch (error) { } catch (error) {
dispatch("ui/error", { error: error }, { root: true }); dispatch("ui/error", { error: error }, { root: true });
@ -127,16 +121,16 @@ const actions = {
const getters = { const getters = {
pedidoDefinido() { pedidoDefinido() {
return state.lastFetch !== undefined; return state.lastFetch !== null;
}, },
enChismosa() { enChismosa() {
return ((producto_id) => state.productos.some(p => p.id === producto_id)); return ((producto_id) => state.productos.some(p => p.id === producto_id));
}, },
cantidad() { cantidad() {
return ((producto_id) => state.productos.find(p => p.id === producto_id)?.cantidad ?? 0); return ((producto_id) => state.productos.find(p => p.id === producto_id)?.pivot.cantidad ?? 0);
}, },
notas() { notas() {
return ((producto_id) => state.productos.find(p => p.id === producto_id)?.notas ?? ""); return ((producto_id) => state.productos.find(p => p.id === producto_id)?.pivot.notas ?? "");
} }
} }

View file

@ -1,22 +0,0 @@
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,
}

View file

@ -1,23 +1,22 @@
import axios from "axios"; import axios from "axios";
import { ProductosState } from "./types";
const state: ProductosState = { const state = {
lastFetch: undefined, lastFetch: null,
categorias: [], categorias: [],
productos: [], productos: [],
filtro: undefined, filtro: null,
}; };
const mutations = { const mutations = {
setCategorias(state: ProductosState, categorias) { setCategorias(state, categorias) {
state.lastFetch = new Date(); state.lastFetch = new Date();
state.categorias = categorias; state.categorias = categorias;
}, },
setProductos(state: ProductosState, productos) { setProductos(state, productos) {
state.lastFetch = new Date(); state.lastFetch = new Date();
state.productos = productos; state.productos = productos;
}, },
setFiltro(state: ProductosState, filtro) { setFiltro(state, filtro) {
state.lastFetch = new Date(); state.lastFetch = new Date();
state.filtro = filtro; state.filtro = filtro;
}, },
@ -29,29 +28,23 @@ const actions = {
dispatch('getProductos'); dispatch('getProductos');
}, },
async getCategorias({ commit }) { async getCategorias({ commit }) {
const { data } = await axios.get('api/categorias'); const response = await axios.get('api/categorias');
commit('setCategorias', data); commit('setCategorias', response.data);
}, },
async getProductos({ commit }) { async getProductos({ commit }) {
const { data } = await axios.get("/api/productos"); const response = await axios.get("/api/productos");
commit('setFiltro', undefined); commit('setFiltro', null);
commit('setProductos', data.data); commit('setProductos', response.data.data);
}, },
async seleccionarCategoria({ dispatch }, { categoria }) { async seleccionarCategoria({ dispatch }, { categoria }) {
dispatch('filtrarProductos', { filtro: "categoria", valor: categoria }); dispatch('filtrarProductos', { filtro: "categoria", valor: categoria });
}, },
async filtrarProductos({ commit }, { filtro, valor }) { async filtrarProductos({ commit }, { filtro, valor }) {
const { data } = await axios.get("/api/productos", { const response = await axios.get("/api/productos", {
params: { [filtro]: valor } params: { [filtro]: valor }
}); });
commit('setFiltro', { clave: filtro, valor: valor }); commit('setFiltro', { clave: filtro, valor: valor });
commit('setProductos', data.data); commit('setProductos', response.data.data);
}
};
const getters = {
mostrarProductos() {
return state.filtro !== undefined;
} }
}; };
@ -60,5 +53,4 @@ export default {
state, state,
mutations, mutations,
actions, actions,
getters,
}; };

View file

@ -1,21 +0,0 @@
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,
}

View file

@ -1,13 +1,11 @@
import { UiState } from "./types"; const state = {
const state: UiState = {
show_chismosa: false, show_chismosa: false,
show_devoluciones: false, show_devoluciones: false,
show_tags: true, show_tags: true,
burger_activa: false, burger_activa: false,
tags_interactuada: false, tags_interactuada: false,
migas: [{ nombre: 'Pedidos', action: 'pedido/resetear' }], migas: [{ nombre: 'Pedidos', action: 'pedido/resetear' }],
canasta_actual: undefined, canasta_actual: null,
}; };
const mutations = { const mutations = {
@ -31,7 +29,7 @@ const mutations = {
state.migas.push(miga); state.migas.push(miga);
}, },
popUltimaBusqueda(state) { popUltimaBusqueda(state) {
if (!state.migas.at(-1)?.action) if (!state.migas.at(-1).action)
state.migas.pop(); state.migas.pop();
}, },
reset(state) { reset(state) {
@ -45,8 +43,8 @@ const mutations = {
const actions = { const actions = {
async getCanastaActual({ commit }) { async getCanastaActual({ commit }) {
const { data } = await axios.get('/api/canasta-actual'); const response = await axios.get('/api/canasta-actual');
commit("setCanastaActual", { canasta: data }); commit("setCanastaActual", { canasta: response.data });
}, },
clickMiga({ dispatch }, { miga }) { clickMiga({ dispatch }, { miga }) {
let dropWhile = (array, pred) => { let dropWhile = (array, pred) => {

View file

@ -1,20 +0,0 @@
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,
}

View file

@ -4,8 +4,8 @@
// Variables // Variables
@import 'variables'; @import 'variables';
@import '../../node_modules/bulma/bulma.sass'; @import 'bulma';
@import '../../node_modules/bulma-switch/src/sass/index.sass'; @import '~bulma-switch';
html, body { html, body {
overflow-x: hidden; overflow-x: hidden;

View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name', 'Pedidos del MPS') }}</title> <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="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
@vite(['resources/sass/app.scss', 'resources/js/app.ts']) <link rel="stylesheet" href="{{ mix('css/app.css') }}">
<script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script>
</head> </head>
<body> <body>
@ -15,5 +15,6 @@
<app-login></app-login> <app-login></app-login>
</form> </form>
</div> </div>
<script src="{{ mix('js/app.js') }}" defer></script>
</body> </body>
</html> </html>

View file

@ -12,7 +12,7 @@
<!-- Fonts --> <!-- Fonts -->
<script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script> <script src="https://kit.fontawesome.com/9235d1c676.js" crossorigin="anonymous"></script>
@vite(['resources/sass/app.scss', 'resources/js/app.ts']) <link rel="stylesheet" href="{{ mix('css/app.css') }}">
@yield('stylesheets') @yield('stylesheets')
</head> </head>
<body class="has-navbar-fixed-top"> <body class="has-navbar-fixed-top">
@ -28,6 +28,8 @@
@yield('content') @yield('content')
</main> </main>
</div> </div>
<!-- Scripts -->
<script src="{{ mix('js/app.js') }}" defer></script>
@yield('scripts') @yield('scripts')
</body> </body>
</html> </html>

View file

@ -51,7 +51,7 @@ Route::get('/comisiones/login', 'ComisionesController@show')->name('comisiones.l
Route::middleware(['auth', 'role:comision'])->group( function() { Route::middleware(['auth', 'role:comision'])->group( function() {
Route::get('/comisiones', 'RouteController@main')->name('comisiones'); Route::get('/comisiones', 'RouteController@main')->name('comisiones');
Route::get('/comisiones/pedidos/descargar', 'ComisionesController@descargarPedidos')->name('comisiones.pedidos.descargar'); Route::get('/comisiones/pedidos/descargar', 'ComisionesController@descargarPedidos')->name('comisiones.pedidos.descargar');
Route::get('/comisiones/pedidos/notas', 'ComisionesController@descargarNotas')->name('comisiones.pedidos.notas'); Route::get('/comisiones/pedidos/notas', 'ComisionesController@descargarNotas')->name('comisiones.pedidos.descargar');
Route::get('/comisiones/pedidos/pdf', 'ComisionesController@pdf')->name('comisiones.pedidos.pdf'); 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/pedidos/ollas', 'ComisionesController@descargarPedidosDeOllas')->name('comisiones.pedidos.ollas');
Route::get('/comisiones/canasta/ejemplo', 'ComisionesController@descargarCanastaEjemplo')->name('comisiones.canasta.ejemplo'); Route::get('/comisiones/canasta/ejemplo', 'ComisionesController@descargarCanastaEjemplo')->name('comisiones.canasta.ejemplo');

View file

@ -1,17 +0,0 @@
{
"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"]
}

View file

@ -1,23 +0,0 @@
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 Normal file
View file

@ -0,0 +1,16 @@
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();