├── public ├── apiV1 │ ├── .gitignore │ ├── src │ │ ├── database │ │ │ ├── db.sqlite3 │ │ │ ├── conn.js │ │ │ └── migrations │ │ │ │ ├── 20200331091309_create_user.js │ │ │ │ ├── 20200513181845_form_payment.js │ │ │ │ ├── 20200509121051_create_service.js │ │ │ │ ├── 20200511102920_client_dependent.js │ │ │ │ ├── 20200511181140_attendance.js │ │ │ │ ├── 20200509110107_create_employee.js │ │ │ │ ├── 20200509110123_create_plans.js │ │ │ │ ├── 20200509105945_create_client.js │ │ │ │ ├── 20200510155129_plan_items.js │ │ │ │ ├── 20200511102855_client_plan.js │ │ │ │ ├── 20200511181148_attendance_items.js │ │ │ │ └── 20200511102933_client_payment.js │ │ ├── index.js │ │ ├── controllers │ │ │ ├── UserController.js │ │ │ ├── ServiceController.js │ │ │ ├── EmployeeController.js │ │ │ ├── FormPaymentController.js │ │ │ ├── DependentController.js │ │ │ ├── PlansController.js │ │ │ ├── CommissionController.js │ │ │ ├── ClientController.js │ │ │ └── AttendanceController.js │ │ └── Router.js │ ├── package.json │ └── knexfile.js ├── favicon.ico ├── notification │ └── index.js ├── electron.js └── index.html ├── src ├── react-app-env.d.ts ├── layout │ ├── scss │ │ └── Layout.scss │ ├── index.js │ └── Sidebar │ │ └── index.js ├── services │ ├── history.js │ └── api.js ├── App.js ├── index.js ├── pages │ ├── Main │ │ ├── index.js │ │ └── styles.js │ ├── Plans │ │ ├── index.js │ │ └── new.js │ ├── Attendance │ │ ├── index.js │ │ └── items.js │ ├── Commission │ │ ├── index.js │ │ └── detailed.js │ ├── Client │ │ ├── dependent.js │ │ └── index.js │ ├── Users │ │ └── index.js │ ├── Services │ │ └── index.js │ ├── FormPayment │ │ └── index.js │ └── Employee │ │ └── index.js ├── components │ ├── Header │ │ ├── index.js │ │ └── styles.js │ └── tables │ │ └── index.js ├── assets │ └── scss │ │ ├── _sidebar.scss │ │ ├── _variables.scss │ │ └── _base.scss └── Router.js ├── .rescriptsrc.js ├── .gitattributes ├── db ├── db.sqlite3 └── migrations │ ├── 20200513181113_form_payment.js │ ├── 20200331091309_create_user.js │ ├── 20200513181845_form_payment.js │ ├── 20200509121051_create_service.js │ ├── 20200511102920_client_dependent.js │ ├── 20200511181140_attendance.js │ ├── 20200509110107_create_employee.js │ ├── 20200509110123_create_plans.js │ ├── 20200509105945_create_client.js │ ├── 20200510155129_plan_items.js │ ├── 20200511102855_client_plan.js │ ├── 20200511181148_attendance_items.js │ └── 20200511102933_client_payment.js ├── paths.json ├── .gitignore ├── .webpack.config.js ├── tsconfig.json ├── knexfile.js ├── README.md └── package.json /public/apiV1/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.rescriptsrc.js: -------------------------------------------------------------------------------- 1 | module.exports = [require.resolve("./.webpack.config.js")]; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /db/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/augustolima1/barberPack/HEAD/db/db.sqlite3 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/augustolima1/barberPack/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /paths.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "~/*": ["*"] 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /public/apiV1/src/database/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/augustolima1/barberPack/HEAD/public/apiV1/src/database/db.sqlite3 -------------------------------------------------------------------------------- /db/migrations/20200513181113_form_payment.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | 4 | }; 5 | 6 | exports.down = function(knex) { 7 | 8 | }; 9 | -------------------------------------------------------------------------------- /src/layout/scss/Layout.scss: -------------------------------------------------------------------------------- 1 | @import "../../assets/scss/variables"; 2 | @import "../../assets/scss/base"; 3 | @import "../../assets/scss/sidebar"; 4 | 5 | -------------------------------------------------------------------------------- /src/services/history.js: -------------------------------------------------------------------------------- 1 | import { createBrowserHistory } from "history"; 2 | 3 | const history = createBrowserHistory(); 4 | 5 | export default history; 6 | -------------------------------------------------------------------------------- /public/apiV1/src/database/conn.js: -------------------------------------------------------------------------------- 1 | const knex = require('knex'); 2 | const configuration = require('../../knexfile'); 3 | 4 | const connection = knex(configuration.electron); 5 | 6 | module.exports = connection; 7 | -------------------------------------------------------------------------------- /src/services/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | 4 | const api = axios.create({ 5 | baseURL: 'http://localhost:3333', 6 | responseType: 'json', 7 | headers: { 8 | Accept: 'application/json', 9 | }, 10 | }); 11 | 12 | export default api; 13 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Routes from "./Router"; 3 | 4 | import Layout from "./layout" 5 | 6 | function App() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default App; -------------------------------------------------------------------------------- /public/apiV1/src/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cors = require('cors'); 3 | const routes = require('./Router'); 4 | 5 | const app = express(); 6 | 7 | app.use(cors()); 8 | app.use(express.json()); 9 | app.use(routes); 10 | 11 | app.listen(3333); 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | /dist 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.webpack.config.js: -------------------------------------------------------------------------------- 1 | const { addBabelPlugin, override } = require("customize-cra"); 2 | 3 | module.exports = config => { 4 | config.target = "electron-renderer"; 5 | return config; 6 | }; 7 | 8 | module.exports = override( 9 | addBabelPlugin([ 10 | "babel-plugin-root-import", 11 | { 12 | rootPathSuffix: "src" 13 | } 14 | ]) 15 | ); 16 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Router } from "react-router-dom"; 4 | import history from "./services/history"; 5 | import App from './App'; 6 | 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); -------------------------------------------------------------------------------- /src/layout/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Sidebar from "./Sidebar"; 3 | 4 | 5 | 6 | import "./scss/Layout.scss"; 7 | 8 | const Layout = ({ children }) => { 9 | return ( 10 | 11 | 12 | 13 | 14 |
15 | {children} 16 |
17 |
18 | ); 19 | }; 20 | 21 | export default Layout; 22 | -------------------------------------------------------------------------------- /public/notification/index.js: -------------------------------------------------------------------------------- 1 | const { ipcMain, Notification } = require('electron'); 2 | 3 | ipcMain.on('@notification/REQUEST', async (event, message) => { 4 | try { 5 | const { title, body } = message; 6 | 7 | const notification = new Notification({ 8 | title, 9 | body, 10 | }); 11 | 12 | notification.show(); 13 | } catch (err) { 14 | event.sender.send('@notification/FAILURE', 'Houve um erro na criação da notificação'); 15 | } 16 | }); -------------------------------------------------------------------------------- /src/pages/Main/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Header from '~/components/Header'; 3 | 4 | import { 5 | Container, 6 | Box, 7 | Message 8 | } from "./styles"; 9 | 10 | 11 | export default function Main() { 12 | 13 | 14 | 15 | 16 | return ( 17 | <> 18 |
19 | 20 | 21 | Bem Vindo 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /public/apiV1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon src/index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cors": "^2.8.5", 14 | "express": "^4.17.1", 15 | "knex": "^0.20.11", 16 | "moment": "^2.25.3", 17 | "sqlite3": "^4.1.1" 18 | }, 19 | "devDependencies": { 20 | "nodemon": "^2.0.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/pages/Main/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Container = styled.div` 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | flex-wrap: wrap; 8 | `; 9 | 10 | export const Box = styled.form` 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | width: 100%; 15 | margin: 0 auto; 16 | padding: 50px 0; 17 | text-align: center; 18 | `; 19 | 20 | 21 | export const Message = styled.h1` 22 | color: #333; 23 | `; 24 | -------------------------------------------------------------------------------- /db/migrations/20200331091309_create_user.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('users', function (table) { 4 | table.increments(); 5 | table.string('name').notNullable(); 6 | table.string('password').notNullable(); 7 | table.timestamp('created_at').defaultTo(knex.fn.now()) 8 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 9 | }); 10 | }; 11 | 12 | exports.down = function(knex) { 13 | return knex.schema.dropTable("users"); 14 | }; 15 | -------------------------------------------------------------------------------- /db/migrations/20200513181845_form_payment.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('form_payment', function (table) { 3 | table.increments(); 4 | table.string('description').notNullable(); 5 | table.decimal('rate').notNullable(); 6 | table.timestamp('created_at').defaultTo(knex.fn.now()) 7 | }); 8 | }; 9 | 10 | 11 | 12 | exports.down = function(knex) { 13 | return knex.schema.dropTable("form_payment"); 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /db/migrations/20200509121051_create_service.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('services', function (table) { 4 | table.increments(); 5 | table.string('description').notNullable(); 6 | table.decimal('value'); 7 | table.timestamp('created_at').defaultTo(knex.fn.now()) 8 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 9 | }); 10 | }; 11 | 12 | exports.down = function(knex) { 13 | return knex.schema.dropTable("services"); 14 | }; 15 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200331091309_create_user.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('users', function (table) { 4 | table.increments(); 5 | table.string('name').notNullable(); 6 | table.string('password').notNullable(); 7 | table.timestamp('created_at').defaultTo(knex.fn.now()) 8 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 9 | }); 10 | }; 11 | 12 | exports.down = function(knex) { 13 | return knex.schema.dropTable("users"); 14 | }; 15 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200513181845_form_payment.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('form_payment', function (table) { 3 | table.increments(); 4 | table.string('description').notNullable(); 5 | table.decimal('rate').notNullable(); 6 | table.timestamp('created_at').defaultTo(knex.fn.now()) 7 | }); 8 | }; 9 | 10 | 11 | 12 | exports.down = function(knex) { 13 | return knex.schema.dropTable("form_payment"); 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200509121051_create_service.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('services', function (table) { 4 | table.increments(); 5 | table.string('description').notNullable(); 6 | table.decimal('value'); 7 | table.timestamp('created_at').defaultTo(knex.fn.now()) 8 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 9 | }); 10 | }; 11 | 12 | exports.down = function(knex) { 13 | return knex.schema.dropTable("services"); 14 | }; 15 | -------------------------------------------------------------------------------- /db/migrations/20200511102920_client_dependent.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('client_dependent', function (table) { 3 | table.increments(); 4 | table.integer('client_id').notNullable(); 5 | table.integer('dependente_id').notNullable(); 6 | table.foreign('client_id').references('id').inTable('client'); 7 | table.timestamp('created_at').defaultTo(knex.fn.now()) 8 | }); 9 | }; 10 | 11 | 12 | exports.down = function(knex) { 13 | return knex.schema.dropTable("client_dependent"); 14 | }; 15 | -------------------------------------------------------------------------------- /src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Header as _header,Icon,Logo,Title,AlignItem} from './styles'; 3 | 4 | import {FaBars} from 'react-icons/fa'; 5 | 6 | const Header = ({title}) => { 7 | return ( 8 | <_header> 9 | {/*App*/} 10 | 11 | 12 | 13 | 14 | {title} 15 | 16 | 17 | ); 18 | }; 19 | 20 | export default Header; -------------------------------------------------------------------------------- /db/migrations/20200511181140_attendance.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('attendance', function (table) { 3 | table.increments(); 4 | table.integer('client_id').notNullable(); 5 | table.date('date').notNullable(); 6 | table.foreign('client_id').references('id').inTable('client'); 7 | table.timestamp('created_at').defaultTo(knex.fn.now()) 8 | }); 9 | }; 10 | 11 | 12 | 13 | exports.down = function(knex) { 14 | return knex.schema.dropTable("attendance"); 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200511102920_client_dependent.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('client_dependent', function (table) { 3 | table.increments(); 4 | table.integer('client_id').notNullable(); 5 | table.integer('dependente_id').notNullable(); 6 | table.foreign('client_id').references('id').inTable('client'); 7 | table.timestamp('created_at').defaultTo(knex.fn.now()) 8 | }); 9 | }; 10 | 11 | 12 | exports.down = function(knex) { 13 | return knex.schema.dropTable("client_dependent"); 14 | }; 15 | -------------------------------------------------------------------------------- /db/migrations/20200509110107_create_employee.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('employee', function (table) { 4 | table.increments(); 5 | table.string('name').notNullable(); 6 | table.string('phone').notNullable(); 7 | table.decimal('commission').notNullable(); 8 | table.decimal('rate_card'); 9 | 10 | table.timestamp('created_at').defaultTo(knex.fn.now()) 11 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 12 | }); 13 | }; 14 | 15 | exports.down = function(knex) { 16 | return knex.schema.dropTable("employee"); 17 | }; 18 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200511181140_attendance.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('attendance', function (table) { 3 | table.increments(); 4 | table.integer('client_id').notNullable(); 5 | table.date('date').notNullable(); 6 | table.foreign('client_id').references('id').inTable('client'); 7 | table.timestamp('created_at').defaultTo(knex.fn.now()) 8 | }); 9 | }; 10 | 11 | 12 | 13 | exports.down = function(knex) { 14 | return knex.schema.dropTable("attendance"); 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200509110107_create_employee.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('employee', function (table) { 4 | table.increments(); 5 | table.string('name').notNullable(); 6 | table.string('phone').notNullable(); 7 | table.decimal('commission').notNullable(); 8 | table.decimal('rate_card'); 9 | 10 | table.timestamp('created_at').defaultTo(knex.fn.now()) 11 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 12 | }); 13 | }; 14 | 15 | exports.down = function(knex) { 16 | return knex.schema.dropTable("employee"); 17 | }; 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./paths.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": false, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react", 22 | "baseUrl": "src" 23 | }, 24 | "include": [ 25 | "src" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /db/migrations/20200509110123_create_plans.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('plans', function (table) { 4 | table.increments(); 5 | table.string('description').notNullable(); 6 | table.decimal('value').notNullable(); 7 | table.decimal('value_promotion'); 8 | table.decimal('amount_promotion'); 9 | table.timestamp('created_at').defaultTo(knex.fn.now()) 10 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 11 | }); 12 | }; 13 | 14 | exports.down = function(knex) { 15 | return knex.schema.dropTable("plans"); 16 | }; 17 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200509110123_create_plans.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('plans', function (table) { 4 | table.increments(); 5 | table.string('description').notNullable(); 6 | table.decimal('value').notNullable(); 7 | table.decimal('value_promotion'); 8 | table.decimal('amount_promotion'); 9 | table.timestamp('created_at').defaultTo(knex.fn.now()) 10 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 11 | }); 12 | }; 13 | 14 | exports.down = function(knex) { 15 | return knex.schema.dropTable("plans"); 16 | }; 17 | -------------------------------------------------------------------------------- /db/migrations/20200509105945_create_client.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('client', function (table) { 4 | table.increments(); 5 | table.string('name').notNullable(); 6 | table.string('phone').notNullable(); 7 | table.string('active',2).notNullable(); 8 | 9 | table.integer('form_payment_id'); 10 | table.foreign('form_payment_id').references('id').inTable('payment_id'); 11 | 12 | table.timestamp('created_at').defaultTo(knex.fn.now()) 13 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 14 | }); 15 | }; 16 | 17 | exports.down = function(knex) { 18 | return knex.schema.dropTable("client"); 19 | }; 20 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200509105945_create_client.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('client', function (table) { 4 | table.increments(); 5 | table.string('name').notNullable(); 6 | table.string('phone').notNullable(); 7 | table.string('active',2).notNullable(); 8 | 9 | table.integer('form_payment_id'); 10 | table.foreign('form_payment_id').references('id').inTable('payment_id'); 11 | 12 | table.timestamp('created_at').defaultTo(knex.fn.now()) 13 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 14 | }); 15 | }; 16 | 17 | exports.down = function(knex) { 18 | return knex.schema.dropTable("client"); 19 | }; 20 | -------------------------------------------------------------------------------- /db/migrations/20200510155129_plan_items.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('plan_items', function (table) { 4 | table.increments(); 5 | table.integer('amount').notNullable(); 6 | table.integer('plan_id').notNullable(); 7 | table.integer('service_id').notNullable(); 8 | table.foreign('plan_id').references('id').inTable('plans'); 9 | table.foreign('service_id').references('id').inTable('services'); 10 | table.timestamp('created_at').defaultTo(knex.fn.now()) 11 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 12 | }); 13 | }; 14 | 15 | exports.down = function(knex) { 16 | return knex.schema.dropTable("plan_items"); 17 | }; 18 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200510155129_plan_items.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('plan_items', function (table) { 4 | table.increments(); 5 | table.integer('amount').notNullable(); 6 | table.integer('plan_id').notNullable(); 7 | table.integer('service_id').notNullable(); 8 | table.foreign('plan_id').references('id').inTable('plans'); 9 | table.foreign('service_id').references('id').inTable('services'); 10 | table.timestamp('created_at').defaultTo(knex.fn.now()) 11 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 12 | }); 13 | }; 14 | 15 | exports.down = function(knex) { 16 | return knex.schema.dropTable("plan_items"); 17 | }; 18 | -------------------------------------------------------------------------------- /knexfile.js: -------------------------------------------------------------------------------- 1 | // Update with your config settings. 2 | module.exports = { 3 | 4 | development: { 5 | client: 'sqlite3', 6 | connection: { 7 | filename:'./db/db.sqlite3' 8 | }, 9 | migrations: { 10 | directory:'./db/migrations' 11 | }, 12 | useNullAsDefault:true, 13 | }, 14 | 15 | staging: { 16 | client: 'postgresql', 17 | connection: { 18 | database: 'my_db', 19 | user: 'username', 20 | password: 'password' 21 | }, 22 | pool: { 23 | min: 2, 24 | max: 10 25 | }, 26 | migrations: { 27 | tableName: 'knex_migrations' 28 | } 29 | }, 30 | 31 | production: { 32 | client: 'postgresql', 33 | connection: { 34 | database: 'my_db', 35 | user: 'username', 36 | password: 'password' 37 | }, 38 | pool: { 39 | min: 2, 40 | max: 10 41 | }, 42 | migrations: { 43 | tableName: 'knex_migrations' 44 | } 45 | }, 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /db/migrations/20200511102855_client_plan.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('client_plan', function (table) { 3 | table.increments(); 4 | table.integer('client_id').notNullable(); 5 | table.integer('plan_id').notNullable(); 6 | table.integer('expiration_day',2); 7 | table.decimal('value'); 8 | table.decimal('value_promotion'); 9 | table.string('expiration_promotion'); 10 | table.string('active',2); 11 | table.string('user_id'); 12 | 13 | table.foreign('client_id').references('id').inTable('client'); 14 | table.foreign('plan_id').references('id').inTable('plans'); 15 | 16 | table.timestamp('created_at').defaultTo(knex.fn.now()) 17 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 18 | }); 19 | }; 20 | 21 | 22 | exports.down = function(knex) { 23 | return knex.schema.dropTable("client_plan"); 24 | }; 25 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200511102855_client_plan.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('client_plan', function (table) { 3 | table.increments(); 4 | table.integer('client_id').notNullable(); 5 | table.integer('plan_id').notNullable(); 6 | table.integer('expiration_day',2); 7 | table.decimal('value'); 8 | table.decimal('value_promotion'); 9 | table.string('expiration_promotion'); 10 | table.string('active',2); 11 | table.string('user_id'); 12 | 13 | table.foreign('client_id').references('id').inTable('client'); 14 | table.foreign('plan_id').references('id').inTable('plans'); 15 | 16 | table.timestamp('created_at').defaultTo(knex.fn.now()) 17 | table.timestamp('updated_at').defaultTo(knex.fn.now()) 18 | }); 19 | }; 20 | 21 | 22 | exports.down = function(knex) { 23 | return knex.schema.dropTable("client_plan"); 24 | }; 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BarberPack 2 | Sistema para gerenciar comissão dos profissionais de barbearia 3 | 4 | 5 | ## Getting Started (Começando) 6 | Assim que clonado o repositório, rodar na base do projeto 7 | ``` yarn ``` ou ```npm install```. 8 | 9 | Para rodar a aplicação: 10 | ``` 11 | yarn dev 12 | ``` 13 | 14 | ou 15 | ``` 16 | npm dev 17 | ``` 18 | 19 | ## API 20 | Os arquivos da API pode ser encontrado na pasta public\apiV1 21 | 22 | 23 | 24 | ## Import 25 | 26 | Para importar um componente/funçao a partir da pasta `src`, em qualquer nível de pasta que você esteja, utilize da seguinte forma: 27 | 28 | ```jsx 29 | // example 30 | import something from "~/pages/something"; 31 | ``` 32 | 33 | ## Material-UI 34 | 35 | Adicionado ao projeto o Material-UI utilizando o projeto [npm package](https://www.npmjs.com/package/@material-ui/core). 36 | 37 | 38 | ## Build 39 | 40 | Para gerar o executável, basta rodar na base do projeto: 41 | 42 | ``` 43 | yarn electron-build 44 | ``` 45 | 46 | ou 47 | ``` 48 | npm electron-build 49 | ``` 50 | 51 | Os arquivos gerados no processo de build podem ser encontrados na pasta dist. -------------------------------------------------------------------------------- /src/assets/scss/_sidebar.scss: -------------------------------------------------------------------------------- 1 | .navbar { 2 | z-index: 1; 3 | position: fixed; 4 | background-color: #282832; 5 | transition: width 600ms ease; 6 | width: 230px; 7 | height: 100%; 8 | overflow: hidden; 9 | top: 0; 10 | } 11 | 12 | @media (max-width: 767px){ 13 | .navbar { 14 | -webkit-transform: translate(-230px, 0); 15 | -ms-transform: translate(-230px, 0); 16 | -o-transform: translate(-230px, 0); 17 | transform: translate(-230px, 0); 18 | padding-top: 100px; 19 | } 20 | 21 | 22 | } 23 | 24 | .Side-menu-default,.Side-menu-default .divider{ 25 | background: #282832; 26 | color: #fff; 27 | } 28 | 29 | .Side-menu .item .item-title{ 30 | display: block; 31 | padding: 12px 5px 12px 15px; 32 | position: relative; 33 | } 34 | 35 | .Side-menu .item.item-level-1 > .item-title { 36 | height:auto; 37 | } 38 | 39 | .Side-menu .item .item-title { 40 | height:auto; 41 | } 42 | 43 | .Side-menu-default .divider{ 44 | height:auto; 45 | padding: 10px 25px 10px 15px; 46 | } 47 | .Side-menu-default .item.item-level-1:hover, .Side-menu-default .item.item-level-1.active { 48 | border-left: 4px solid #f50057; 49 | } -------------------------------------------------------------------------------- /public/apiV1/src/controllers/UserController.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const conn= require(`${path.resolve(__dirname, '../database/conn')}`); 3 | 4 | 5 | module.exports = { 6 | 7 | async index(resquest,response){ 8 | 9 | const users= await conn("users").select(['id','name']); 10 | 11 | return response.json(users); 12 | }, 13 | 14 | async cerate(resquest,response){ 15 | const {name,password} =resquest.body; 16 | 17 | const [id] =await conn("users").insert({ 18 | name, 19 | password 20 | }); 21 | 22 | return response.json({ id }); 23 | }, 24 | 25 | 26 | async delete(request, response) { 27 | const { id } = request.params; 28 | 29 | await conn('users').where('id', id).delete(); 30 | 31 | return response.status(204).send(); 32 | }, 33 | 34 | async update(resquest, response) { 35 | const {id,name,password} =resquest.body; 36 | 37 | await conn('users').where('id', id).update({ 38 | name, 39 | password 40 | }); 41 | 42 | return response.json({id}); 43 | } 44 | } -------------------------------------------------------------------------------- /src/assets/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | $blue-lighter: #a885fd; 2 | $black-lighter: #141414; 3 | $gray: #d8d8d8; 4 | $dark-gray: #141418; 5 | $medium-gray: #afafaf; 6 | $light-gray: #e6e6e6; 7 | $white-smoke: #f3f3f3; 8 | $section-bg: #fcfcfc; 9 | 10 | $white: #fff; 11 | $gray-10: #f8f8f8; 12 | $gray-20: #a4a4a4; 13 | $gray-30: #7f7f7f; 14 | $gray-40: #828282; 15 | $gray-50: #918e8c; 16 | $gray-60: #3a3a3a; 17 | $gray-70: #282832; 18 | $gray-80: #16171c; 19 | $black: #000; 20 | 21 | $magenta: #fe008c; 22 | $blue: #50afe6; 23 | $light-blue: #f1f9ff; 24 | $light-green: #ecf7eb; 25 | $green: #0fb969; 26 | $dark-green: #004b50; 27 | $yellow: #ffc837; 28 | $light-yellow: #ffeb8c; 29 | $orange: #fa8728; 30 | $purple: #502350; 31 | $red: #ff0000; 32 | $logo-color: #ef7663; 33 | 34 | /* Fonts */ 35 | $font-size-40: 40px; 36 | $font-size-30: 30px; 37 | $font-size-26: 26px; 38 | $font-size-18: 18px; 39 | $font-size-16: 16px; 40 | $font-size-14: 14px; 41 | $font-size-12: 12px; 42 | 43 | /* Sidebar */ 44 | $sidebar-mobile-height: 60px; 45 | $sidebar-mobile-icon: 24px; 46 | 47 | $transition-speed: 600ms; 48 | $default-font-family: "Ubuntu", sans-serif; 49 | $default-font-size: $font-size-16; 50 | 51 | $breakpoints: ( 52 | "mobile": 576px, 53 | "mobile-xl": 768px, 54 | "tablet": 992px, 55 | "desktop": 1200px 56 | ); 57 | -------------------------------------------------------------------------------- /public/apiV1/src/controllers/ServiceController.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const conn= require(`${path.resolve(__dirname, '../database/conn')}`); 3 | 4 | 5 | module.exports = { 6 | 7 | async index(resquest,response){ 8 | 9 | const services= await conn("services").select(['id','description','value']).orderBy('description', 'asc'); 10 | 11 | return response.json(services); 12 | }, 13 | 14 | 15 | async cerate(resquest,response){ 16 | const {description,value} =resquest.body; 17 | 18 | const [id] =await conn("services").insert({ 19 | description, 20 | value 21 | }); 22 | 23 | return response.json({ id }); 24 | }, 25 | 26 | async delete(request, response) { 27 | const { id } = request.params; 28 | 29 | await conn('services').where('id', id).delete(); 30 | 31 | return response.status(204).send(); 32 | }, 33 | 34 | async update(resquest, response) { 35 | const {id,description,value} =resquest.body; 36 | 37 | await conn('services').where('id', id).update({ 38 | description, 39 | value 40 | }); 41 | 42 | return response.json({id}); 43 | } 44 | 45 | 46 | 47 | } -------------------------------------------------------------------------------- /public/apiV1/knexfile.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const isDev = require('electron-is-dev'); 3 | let URL_BD= `${path.resolve(__dirname, '../../../../', 'db', 'db.sqlite3')}`; 4 | 5 | module.exports = { 6 | 7 | development: { 8 | client: 'sqlite3', 9 | connection: { 10 | filename: './src/database/db.sqlite3' 11 | }, 12 | migrations: { 13 | directory: './src/database/migrations' 14 | }, 15 | useNullAsDefault: true, 16 | }, 17 | 18 | electron: { 19 | client: 'sqlite3', 20 | connection: { 21 | filename:isDev ? path.join(__dirname,'../../db/db.sqlite3') : URL_BD.replace(/\\/g,'/') 22 | }, 23 | migrations: { 24 | directory:path.join(__dirname, '../../db/migrations') 25 | }, 26 | useNullAsDefault:true, 27 | }, 28 | 29 | staging: { 30 | client: 'postgresql', 31 | connection: { 32 | database: 'my_db', 33 | user: 'username', 34 | password: 'password' 35 | }, 36 | pool: { 37 | min: 2, 38 | max: 10 39 | }, 40 | migrations: { 41 | tableName: 'knex_migrations' 42 | } 43 | }, 44 | 45 | production: { 46 | client: 'postgresql', 47 | connection: { 48 | database: 'my_db', 49 | user: 'username', 50 | password: 'password' 51 | }, 52 | pool: { 53 | min: 2, 54 | max: 10 55 | }, 56 | migrations: { 57 | tableName: 'knex_migrations' 58 | } 59 | } 60 | 61 | }; 62 | -------------------------------------------------------------------------------- /public/apiV1/src/controllers/EmployeeController.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const conn= require(`${path.resolve(__dirname, '../database/conn')}`); 3 | 4 | 5 | module.exports = { 6 | 7 | async index(resquest,response){ 8 | 9 | const employee= await conn("employee").select(['id','name','phone','commission','rate_card']); 10 | 11 | return response.json(employee); 12 | }, 13 | 14 | 15 | async cerate(resquest,response){ 16 | const {name,phone,commission,rate_card} =resquest.body; 17 | 18 | const [id] =await conn("employee").insert({ 19 | name, 20 | phone, 21 | commission, 22 | rate_card 23 | }); 24 | 25 | return response.json({ id }); 26 | }, 27 | 28 | 29 | async delete(request, response) { 30 | const { id } = request.params; 31 | 32 | await conn('employee').where('id', id).delete(); 33 | 34 | return response.status(204).send(); 35 | }, 36 | 37 | async update(resquest, response) { 38 | const {id,name,phone,commission} =resquest.body; 39 | 40 | await conn('employee').where('id', id).update({ 41 | name, 42 | phone, 43 | commission, 44 | rate_card 45 | }); 46 | 47 | return response.json({id}); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /db/migrations/20200511181148_attendance_items.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('attendance_items', function (table) { 3 | table.increments(); 4 | table.integer('attendance_id').notNullable(); 5 | table.integer('service_id').notNullable(); 6 | table.integer('employee_id').notNullable(); 7 | table.integer('client_id').notNullable(); 8 | table.integer('plan_id').notNullable(); 9 | 10 | table.decimal('value').notNullable(); 11 | table.decimal('rate').notNullable(); 12 | table.decimal('commission').notNullable(); 13 | 14 | table.date('date').notNullable(); 15 | 16 | table.integer('user_id'); 17 | 18 | table.foreign('attendance_id').references('id').inTable('attendance'); 19 | table.foreign('service_id').references('id').inTable('services'); 20 | table.foreign('employee_id').references('id').inTable('employee'); 21 | table.foreign('client_id').references('id').inTable('client'); 22 | table.foreign('plan_id').references('id').inTable('plans'); 23 | table.foreign('user_id').references('id').inTable('users'); 24 | 25 | table.timestamp('created_at').defaultTo(knex.fn.now()) 26 | }); 27 | }; 28 | 29 | 30 | 31 | exports.down = function(knex) { 32 | return knex.schema.dropTable("attendance_items"); 33 | }; 34 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200511181148_attendance_items.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('attendance_items', function (table) { 3 | table.increments(); 4 | table.integer('attendance_id').notNullable(); 5 | table.integer('service_id').notNullable(); 6 | table.integer('employee_id').notNullable(); 7 | table.integer('client_id').notNullable(); 8 | table.integer('plan_id').notNullable(); 9 | 10 | table.decimal('value').notNullable(); 11 | table.decimal('rate').notNullable(); 12 | table.decimal('commission').notNullable(); 13 | 14 | table.date('date').notNullable(); 15 | 16 | table.integer('user_id'); 17 | 18 | table.foreign('attendance_id').references('id').inTable('attendance'); 19 | table.foreign('service_id').references('id').inTable('services'); 20 | table.foreign('employee_id').references('id').inTable('employee'); 21 | table.foreign('client_id').references('id').inTable('client'); 22 | table.foreign('plan_id').references('id').inTable('plans'); 23 | table.foreign('user_id').references('id').inTable('users'); 24 | 25 | table.timestamp('created_at').defaultTo(knex.fn.now()) 26 | }); 27 | }; 28 | 29 | 30 | 31 | exports.down = function(knex) { 32 | return knex.schema.dropTable("attendance_items"); 33 | }; 34 | -------------------------------------------------------------------------------- /public/electron.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | const path = require('path'); 3 | const isDev = require('electron-is-dev'); 4 | const log = require('electron-log'); 5 | require('./notification'); 6 | //let server = require('./api/express'); 7 | let server = require('./apiV1/src'); 8 | 9 | 10 | log.transports.file.format = '{h}:{i}:{s}:{ms} {text}'; 11 | log.transports.file.maxSize = 5 * 1024 * 1024; 12 | log.transports.file.level = 'info'; 13 | 14 | const { app } = electron; 15 | const { BrowserWindow } = electron; 16 | 17 | 18 | 19 | let mainWindow; 20 | 21 | const sendStatusToWindow = (text) => { 22 | log.info(text); 23 | mainWindow.webContents.send('message', text); 24 | }; 25 | 26 | 27 | 28 | 29 | function createWindow() { 30 | 31 | 32 | mainWindow = new BrowserWindow({ 33 | width: 800, 34 | height: 600, 35 | webPreferences: { 36 | nodeIntegration: true, 37 | }, 38 | }); 39 | 40 | mainWindow.setMenuBarVisibility(false) 41 | 42 | mainWindow.loadURL( 43 | isDev ? 'http://localhost:3000' : `file://${path.resolve(__dirname, '..', 'build', 'index.html')}`, 44 | ); 45 | 46 | if (isDev) { 47 | mainWindow.webContents.openDevTools(); 48 | } 49 | 50 | mainWindow.on('closed', () => { 51 | mainWindow = null; 52 | }); 53 | 54 | 55 | } 56 | 57 | app.on('ready', createWindow); 58 | 59 | app.on('window-all-closed', () => { 60 | if (process.platform !== 'darwin') { 61 | app.quit(); 62 | } 63 | }); 64 | 65 | app.on('activate', () => { 66 | if (mainWindow === null) { 67 | createWindow(); 68 | } 69 | }); -------------------------------------------------------------------------------- /src/components/Header/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Header = styled.header` 4 | display: flex; 5 | position: relative; 6 | max-height: 100px; 7 | background-color: #282832; 8 | z-index: 1030; 9 | height: 50px; 10 | `; 11 | 12 | export const Icon = styled.div` 13 | visibility:hidden; 14 | padding: 14px 15px; 15 | display: -webkit-flex; 16 | display: none; 17 | float: left; 18 | cursor:pointer; 19 | 20 | :hover{ 21 | background-color:#367fa9; 22 | } 23 | 24 | @media (max-width: 767px){ 25 | display:flex; 26 | } 27 | 28 | `; 29 | 30 | export const Logo = styled.div` 31 | -webkit-transition: width .3s ease-in-out; 32 | -o-transition: width .3s ease-in-out; 33 | transition: width .3s ease-in-out; 34 | display: block; 35 | float: left; 36 | height: 50px; 37 | font-size: 20px; 38 | line-height: 50px; 39 | text-align: center; 40 | width: 230px; 41 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; 42 | padding: 0 15px; 43 | font-weight: 300; 44 | overflow: hidden; 45 | 46 | background-color: #000; 47 | color: #fff; 48 | border-bottom: 0 solid transparent; 49 | 50 | @media (max-width: 767px){ 51 | width: 100%; 52 | float: none; 53 | } 54 | `; 55 | 56 | 57 | 58 | 59 | export const Title = styled.span` 60 | color: #fff; 61 | font-size: 20px; 62 | font-weight: bold; 63 | line-height: 24px; 64 | letter-spacing: 1px; 65 | margin-left: 15px; 66 | `; 67 | 68 | export const AlignItem = styled.div` 69 | display: flex; 70 | align-items: center; 71 | `; -------------------------------------------------------------------------------- /public/apiV1/src/controllers/FormPaymentController.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const conn = require(`${path.resolve(__dirname, '../database/conn')}`); 3 | 4 | 5 | 6 | module.exports = { 7 | 8 | async index(resquest,response){ 9 | 10 | const form_payment= await conn("form_payment").select(['id','description','rate']).orderBy('description', 'asc'); 11 | 12 | return response.json(form_payment); 13 | }, 14 | 15 | 16 | async cerate(resquest,response){ 17 | const {description,rate} =resquest.body; 18 | 19 | let error_msg=false; 20 | 21 | if (!description) 22 | error_msg='Infome a descrição'; 23 | 24 | 25 | if(error_msg) 26 | return response.status(401).json({ error:error_msg}); 27 | 28 | 29 | const [id] =await conn("form_payment").insert({ 30 | description, 31 | rate 32 | }); 33 | 34 | 35 | 36 | return response.json({ id }); 37 | }, 38 | 39 | 40 | async delete(request, response) { 41 | const { id } = request.params; 42 | 43 | await conn('form_payment').where('id', id).delete(); 44 | 45 | return response.status(204).send(); 46 | }, 47 | 48 | async update(resquest, response) { 49 | 50 | const {id,description,rate} =resquest.body; 51 | 52 | let error_msg=false; 53 | 54 | if (!description) 55 | error_msg='Infome a descrição'; 56 | 57 | 58 | if(error_msg) 59 | return response.status(401).json({ error:error_msg}); 60 | 61 | 62 | await conn('form_payment').where('id', id).update({ 63 | description, 64 | rate 65 | }); 66 | 67 | return response.json({id}); 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /db/migrations/20200511102933_client_payment.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('client_payment', function (table) { 3 | table.increments(); 4 | table.integer('client_id').notNullable(); 5 | table.integer('year').notNullable(); 6 | table.integer('january'); 7 | table.integer('january_form_payment_id'); 8 | table.integer('february'); 9 | table.integer('february_form_payment_id'); 10 | table.integer('march'); 11 | table.integer('march_form_payment_id'); 12 | table.integer('April'); 13 | table.integer('April_form_payment_id'); 14 | table.integer('may'); 15 | table.integer('may_form_payment_id'); 16 | table.integer('june'); 17 | table.integer('june_form_payment_id'); 18 | table.integer('july'); 19 | table.integer('july_form_payment_id'); 20 | table.integer('august'); 21 | table.integer('august_form_payment_id'); 22 | table.integer('september'); 23 | table.integer('september_form_payment_id'); 24 | table.integer('october'); 25 | table.integer('october_form_payment_id'); 26 | table.integer('november'); 27 | table.integer('november_form_payment_id'); 28 | table.integer('december'); 29 | table.integer('december_form_payment_id'); 30 | 31 | table.foreign('client_id').references('id').inTable('client'); 32 | table.timestamp('created_at').defaultTo(knex.fn.now()) 33 | }); 34 | }; 35 | 36 | 37 | exports.down = function(knex) { 38 | return knex.schema.dropTable("client_payment"); 39 | }; 40 | -------------------------------------------------------------------------------- /public/apiV1/src/database/migrations/20200511102933_client_payment.js: -------------------------------------------------------------------------------- 1 | exports.up = function(knex) { 2 | return knex.schema.createTable('client_payment', function (table) { 3 | table.increments(); 4 | table.integer('client_id').notNullable(); 5 | table.integer('year').notNullable(); 6 | table.integer('january'); 7 | table.integer('january_form_payment_id'); 8 | table.integer('february'); 9 | table.integer('february_form_payment_id'); 10 | table.integer('march'); 11 | table.integer('march_form_payment_id'); 12 | table.integer('April'); 13 | table.integer('April_form_payment_id'); 14 | table.integer('may'); 15 | table.integer('may_form_payment_id'); 16 | table.integer('june'); 17 | table.integer('june_form_payment_id'); 18 | table.integer('july'); 19 | table.integer('july_form_payment_id'); 20 | table.integer('august'); 21 | table.integer('august_form_payment_id'); 22 | table.integer('september'); 23 | table.integer('september_form_payment_id'); 24 | table.integer('october'); 25 | table.integer('october_form_payment_id'); 26 | table.integer('november'); 27 | table.integer('november_form_payment_id'); 28 | table.integer('december'); 29 | table.integer('december_form_payment_id'); 30 | 31 | table.foreign('client_id').references('id').inTable('client'); 32 | table.timestamp('created_at').defaultTo(knex.fn.now()) 33 | }); 34 | }; 35 | 36 | 37 | exports.down = function(knex) { 38 | return knex.schema.dropTable("client_payment"); 39 | }; 40 | -------------------------------------------------------------------------------- /src/layout/Sidebar/index.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react'; 2 | import SideMenu from 'react-sidemenu'; 3 | import 'react-sidemenu/dist/side-menu.css'; 4 | 5 | import history from "../../services/history"; 6 | const hangle=(value)=>{ 7 | history.push(value); 8 | } 9 | const items = [ 10 | //{divider: true, label: 'Main navigation', value: 'main-nav'}, 11 | {label: 'Home', value: '/', onClick:()=>hangle("/") }, 12 | {label: 'Atendimentos', value: '/attendance', onClick:()=>hangle("/attendance")}, 13 | {label: 'Clientes', value: '/client', onClick:()=>hangle("/client")}, 14 | {label: 'Serviços', value: '/services', onClick:()=>hangle("/services")}, 15 | {label: 'Planos', value: '/plans', onClick:()=>hangle("/plans")}, 16 | {label: 'Colaboradores', value: '/employee', onClick:()=>hangle("/employee")}, 17 | {label: 'Usuários', value: '/users', onClick:()=>hangle("/users")}, 18 | {label: 'Forma de Pagamento',value: '/payment/form',onClick:()=>hangle("/payment/form")}, 19 | {label: 'Relatórios', value: '/rel/comissao', 20 | children: [ 21 | {label: 'Comissão', value: '/rel/comissao' , onClick:()=>hangle("/rel/commission")} 22 | ] 23 | } 24 | /*{label: 'item 2', value: 'item2', icon: 'fa-automobile', 25 | children: [ 26 | {label: 'item 2.1', value: 'item2.1', 27 | children: [ 28 | {label: 'item 2.1.1', value: 'item2.1.1'}, 29 | {label: 'item 2.1.2', value: 'item2.1.2'}]}, 30 | {label: 'item 2.2', value: 'item2.2'}]}, 31 | {divider: true, label: 'Motors', value: 'motors-nav'}, 32 | {label: 'item 3', value: 'item3', icon: 'fa-beer'}*/ 33 | ]; 34 | 35 | class Sidebar extends Component { 36 | render() { 37 | return ( ); 40 | } 41 | } 42 | 43 | 44 | export default Sidebar; -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 28 | Barber Pack 29 | 30 | 31 | 32 |
33 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/assets/scss/_base.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | font-size: 16px; 3 | font-family: "Open Sans"; 4 | } 5 | 6 | 7 | main { 8 | animation: marginLeft 1s ease; 9 | margin-left: 230px; 10 | } 11 | 12 | @keyframes marginLeft { 13 | from { 14 | margin-left: 5rem; 15 | } 16 | to { 17 | margin-left: 230px; 18 | } 19 | 20 | } 21 | 22 | @media (max-width: 767px){ 23 | main{margin-left:0;} 24 | } 25 | 26 | 27 | @media only screen and (min-width: map-get($map: $breakpoints, $key: "tablet")) { 28 | main { 29 | animation: marginLeft 1s ease; 30 | margin-left: 230px; 31 | } 32 | } 33 | @keyframes marginLeft { 34 | from { 35 | margin-left: 5rem; 36 | } 37 | to { 38 | margin-left: 230px; 39 | } 40 | } 41 | 42 | body { 43 | 44 | /*background: $gray-80;*/ 45 | background: #eeeeee; 46 | font-size: 14px; 47 | font-family: "Roboto", "Helvetica", "Arial", sans-serif; 48 | font-weight: 300; 49 | margin: 0; 50 | 51 | -webkit-font-smoothing: antialiased; 52 | -moz-osx-font-smoothing: grayscale; 53 | } 54 | 55 | body::-webkit-scrollbar { 56 | width: 0.25rem; 57 | } 58 | 59 | body::-webkit-scrollbar-track { 60 | background: $gray-80; 61 | } 62 | 63 | body::-webkit-scrollbar-thumb { 64 | background: $gray-60; 65 | } 66 | 67 | html { 68 | scroll-behavior: smooth; 69 | } 70 | 71 | * { 72 | box-sizing: border-box; 73 | } 74 | 75 | a { 76 | text-decoration: none; 77 | color: white; 78 | } 79 | 80 | 81 | section.context{ 82 | padding: 30px 15px; 83 | 84 | } 85 | 86 | .form{ 87 | background: #ffffff; 88 | box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14); 89 | padding: 0.9375rem 20px; 90 | border-radius: 6px; 91 | } 92 | 93 | .MuiOutlinedInput-input,.MuiOutlinedInput-input { 94 | padding: 12.5px 14px !important; 95 | } 96 | 97 | .MuiInputLabel-outlined { 98 | transform: translate(14px, 15px) scale(1) !important; 99 | } 100 | 101 | .MuiInputLabel-outlined.MuiInputLabel-shrink{ 102 | transform: translate(14px, -6px) scale(0.75) !important; 103 | } 104 | 105 | .MuiAutocomplete-inputRoot[class*="MuiOutlinedInput-root"] { 106 | padding: 0px !important; 107 | } -------------------------------------------------------------------------------- /src/Router.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route,Switch} from "react-router-dom"; 3 | 4 | import Main from "./pages/Main"; 5 | import Client from "./pages/Client"; 6 | import Dependent from "./pages/Client/dependent"; 7 | import Service from "./pages/Services"; 8 | import Plans from "./pages/Plans"; 9 | import NewPlans from "./pages/Plans/new"; 10 | import Users from "./pages/Users"; 11 | import Employee from "./pages/Employee"; 12 | import Attendance from "./pages/Attendance"; 13 | import AttendanceItems from "./pages/Attendance/items"; 14 | import Commission from "./pages/Commission"; 15 | import CommissionDetailed from "./pages/Commission/detailed"; 16 | 17 | import FormPayment from "./pages/FormPayment"; 18 | 19 | 20 | export default function Routes() { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {/*

Desculpe, um erro ocorreu. Estaremos verificando o que aconteceu.

} />*/} 46 |
47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /public/apiV1/src/controllers/DependentController.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const conn= require(`${path.resolve(__dirname, '../database/conn')}`); 3 | 4 | 5 | 6 | module.exports = { 7 | 8 | 9 | async list(request,response){ 10 | const { id } = request.params; 11 | 12 | 13 | const client= await conn("client") 14 | .leftJoin('client_plan','client_plan.client_id', '=','client.id') 15 | .leftJoin('plans','plans.id','=','client_plan.plan_id') 16 | .select([ 17 | 'client.id', 18 | 'name', 19 | 'phone', 20 | 'plans.description', 21 | 'client_plan.expiration_day' 22 | ]) 23 | .orderBy('name', 'asc'); 24 | 25 | 26 | return response.json(client); 27 | }, 28 | 29 | async index(request,response){ 30 | const { id } = request.params; 31 | 32 | const client_dependent= await conn("client_dependent") 33 | .leftJoin('client','client.id', '=','client_dependent.dependente_id') 34 | .where('client_dependent.client_id',id) 35 | .select([ 36 | 'client_dependent.id', 37 | 'name', 38 | 'phone' 39 | ]) 40 | .orderBy('name', 'asc'); 41 | 42 | return response.json(client_dependent); 43 | }, 44 | 45 | 46 | async cerate(resquest,response){ 47 | const {client_id,dependente_id} =resquest.body; 48 | 49 | let error_msg=false; 50 | 51 | if (!client_id) 52 | error_msg='Infome o cliente'; 53 | 54 | if (!dependente_id) 55 | error_msg='Infome o dependente'; 56 | 57 | if(error_msg) 58 | return response.status(401).json({ error:error_msg}); 59 | 60 | 61 | const [id] =await conn("client_dependent").insert({ 62 | client_id, 63 | dependente_id 64 | }); 65 | 66 | const client_dependent= await conn("client_dependent") 67 | .join('client','client.id', '=','client_dependent.dependente_id') 68 | .where('client_dependent.client_id',client_id) 69 | .select([ 70 | 'client_dependent.id', 71 | 'name', 72 | 'phone' 73 | ]) 74 | .orderBy('name', 'asc') 75 | 76 | 77 | return response.json(client_dependent); 78 | }, 79 | 80 | 81 | async delete(request, response) { 82 | const { id } = request.params; 83 | 84 | await conn('client_dependent').where('id', id).delete(); 85 | 86 | return response.status(204).send(); 87 | }, 88 | 89 | 90 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BarberPack", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": { 6 | "name": "Seu nome", 7 | "email": "seu.email@email.com", 8 | "url": "https://seu-site.com" 9 | }, 10 | "homepage": "./", 11 | "main": "public/electron.js", 12 | "build": { 13 | "appId": "com.app.app", 14 | "productName": "Barber Pack", 15 | "copyright": "Copyright © 2020", 16 | "mac": { 17 | "category": "public.app-category.utilities" 18 | }, 19 | "files": [ 20 | "build/**/*", 21 | "node_modules/**/*" 22 | ], 23 | "extraFiles": [ 24 | "db" 25 | ], 26 | "directories": { 27 | "buildResources": "assets" 28 | } 29 | }, 30 | "dependencies": { 31 | "@date-io/date-fns": "1.x", 32 | "@material-ui/core": "^4.9.13", 33 | "@material-ui/icons": "^4.9.1", 34 | "@material-ui/lab": "^4.0.0-alpha.53", 35 | "@material-ui/pickers": "3", 36 | "@testing-library/jest-dom": "^4.2.4", 37 | "@testing-library/react": "^9.3.2", 38 | "@testing-library/user-event": "^7.1.2", 39 | "axios": "^0.19.2", 40 | "cors": "^2.8.5", 41 | "cross-env": "^7.0.2", 42 | "electron-is-dev": "^1.2.0", 43 | "electron-log": "^4.1.2", 44 | "express": "^4.17.1", 45 | "knex": "^0.21.1", 46 | "moment": "^2.25.3", 47 | "node-sass": "^4.14.1", 48 | "react": "^16.13.1", 49 | "react-dom": "^16.13.1", 50 | "react-hook-form": "^5.6.1", 51 | "react-icons": "^3.10.0", 52 | "react-router-dom": "^5.1.2", 53 | "react-scripts": "3.4.1", 54 | "react-sidemenu": "^1.1.0", 55 | "react-text-mask": "^5.4.3", 56 | "sqlite3": "^4.2.0", 57 | "styled-components": "^5.1.0", 58 | "typescript": "^3.9.2" 59 | }, 60 | "scripts": { 61 | "server": "nodemon ./public/api/express.js", 62 | "start": "rescripts start", 63 | "build": "rescripts build", 64 | "test": "rescripts test", 65 | "eject": "react-scripts eject", 66 | "dev": "concurrently \"cross-env BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"", 67 | "postinstall": "electron-builder install-app-deps", 68 | "preelectron-build": "yarn build", 69 | "electron-build": "electron-builder -w" 70 | }, 71 | "eslintConfig": { 72 | "extends": "react-app" 73 | }, 74 | "browserslist": { 75 | "production": [ 76 | ">0.2%", 77 | "not dead", 78 | "not op_mini all" 79 | ], 80 | "development": [ 81 | "last 1 chrome version", 82 | "last 1 firefox version", 83 | "last 1 safari version" 84 | ] 85 | }, 86 | "devDependencies": { 87 | "@rescripts/cli": "^0.0.14", 88 | "@rescripts/rescript-env": "^0.0.12", 89 | "babel-plugin-root-import": "^6.5.0", 90 | "concurrently": "^5.2.0", 91 | "customize-cra": "^0.9.1", 92 | "electron": "^8.2.5", 93 | "electron-builder": "^22.6.0", 94 | "eslint-import-resolver-babel-plugin-root-import": "^1.1.1", 95 | "nodemon": "^2.0.3", 96 | "react-app-rewired": "^2.1.5", 97 | "wait-on": "^5.0.0" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /public/apiV1/src/Router.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const userController = require('./controllers/UserController'); 4 | const clientController = require('./controllers/ClientController'); 5 | const dependentController = require('./controllers/DependentController'); 6 | const employeeController = require('./controllers/EmployeeController'); 7 | const plansController = require('./controllers/PlansController'); 8 | const serviceController = require('./controllers/ServiceController'); 9 | const attendanceController = require('./controllers/AttendanceController'); 10 | 11 | const commissionController = require('./controllers/CommissionController'); 12 | const formPaymentController = require('./controllers/FormPaymentController'); 13 | 14 | const router =express.Router(); 15 | 16 | 17 | router.get('/', function (req, res) { 18 | res.send('Hello World!'); 19 | }); 20 | 21 | 22 | router.get('/users',userController.index); 23 | router.post('/users',userController.cerate); 24 | router.put('/users',userController.update); 25 | router.delete('/users/:id',userController.delete); 26 | 27 | router.get('/attendance' ,attendanceController.index); 28 | router.get('/attendance/client' ,attendanceController.client); 29 | router.get('/attendance/services/:client_id/:date' ,attendanceController.services); 30 | router.get('/attendance/items/:attendance_id',attendanceController.attendanceItems); 31 | router.post('/attendance' ,attendanceController.cerate); 32 | router.delete('/attendance/:id' ,attendanceController.delete); 33 | router.delete('/attendance/item/:id' ,attendanceController.deleteItem); 34 | router.get('/attendance/all/:client_id' ,attendanceController.services_teste); 35 | 36 | router.get('/commission' ,commissionController.index); 37 | router.get('/commission/detailed' ,commissionController.detailed); 38 | 39 | 40 | 41 | router.get('/payment/form',formPaymentController.index); 42 | router.post('/payment/form',formPaymentController.cerate); 43 | router.put('/payment/form',formPaymentController.update); 44 | router.delete('/payment/form/:id',formPaymentController.delete); 45 | 46 | 47 | 48 | router.get('/client',clientController.index); 49 | router.post('/client',clientController.cerate); 50 | router.put('/client',clientController.update); 51 | router.delete('/client/:id',clientController.delete); 52 | 53 | router.get('/dependent/:id',dependentController.index); 54 | router.get('/dependent/list/:id',dependentController.list); 55 | router.post('/dependent',dependentController.cerate); 56 | router.delete('/dependent/:id',dependentController.delete); 57 | 58 | router.get('/employee',employeeController.index); 59 | router.post('/employee',employeeController.cerate); 60 | router.put('/employee',employeeController.update); 61 | router.delete('/employee/:id',employeeController.delete); 62 | 63 | 64 | router.get('/plans',plansController.index); 65 | router.post('/plans',plansController.cerate); 66 | router.put('/plans',plansController.update); 67 | router.delete('/plans/:id',plansController.delete); 68 | router.get('/plans/items/:id',plansController.items); 69 | router.delete('/plans/item/:id',plansController.deleteItem); 70 | 71 | 72 | 73 | router.get('/service',serviceController.index); 74 | router.post('/service',serviceController.cerate); 75 | router.put('/service',serviceController.update); 76 | router.delete('/service/:id',serviceController.delete); 77 | 78 | module.exports=router; -------------------------------------------------------------------------------- /public/apiV1/src/controllers/PlansController.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const conn= require(`${path.resolve(__dirname, '../database/conn')}`); 3 | 4 | 5 | 6 | module.exports = { 7 | 8 | async index(resquest,response){ 9 | 10 | const plans= await conn("plans").select(['id','description','value']).orderBy('description', 'asc'); 11 | 12 | return response.json(plans); 13 | }, 14 | 15 | async cerate(resquest,response){ 16 | const {id,description,value,amount,service_id} =resquest.body; 17 | 18 | let plan_id= id; 19 | 20 | if(!plan_id){ 21 | const [id]=await conn("plans").insert({ 22 | description, 23 | value 24 | }); 25 | 26 | plan_id=id; 27 | 28 | }else{ 29 | await conn('plans').where('id', id).update({ 30 | description, 31 | value 32 | }); 33 | } 34 | 35 | if(service_id) 36 | await conn("plan_items").insert({ 37 | amount, 38 | plan_id, 39 | service_id 40 | }); 41 | 42 | 43 | 44 | 45 | const services = await conn('services') 46 | .join('plan_items', 'plan_items.service_id', '=', 'services.id') 47 | .where('plan_id', plan_id) 48 | .select([ 49 | 'plan_items.id', 50 | 'services.description', 51 | 'plan_items.amount' 52 | ]).orderBy('plan_items.id','desc'); 53 | 54 | 55 | 56 | return response.json({ 57 | id:plan_id, 58 | data:services 59 | }); 60 | }, 61 | 62 | 63 | async delete(request, response) { 64 | const { id } = request.params; 65 | 66 | await conn('plans').where('id', id).delete(); 67 | await conn('plan_items').where('plan_id', id).delete(); 68 | await conn('client_plan').where('plan_id', id).delete(); 69 | 70 | return response.status(204).send(); 71 | }, 72 | 73 | 74 | async update(resquest, response) { 75 | const {id,description,amount,services_id} =resquest.body; 76 | 77 | await conn('plans').where('id', id).update({ 78 | description, 79 | amount, 80 | services_id 81 | }); 82 | 83 | return response.json({id}); 84 | }, 85 | 86 | 87 | 88 | async items(request,response){ 89 | const { id } = request.params; 90 | 91 | const plans= await conn("plans").where('id', id).select(['id','description','value']).first(); 92 | 93 | const services = await conn('services') 94 | .join('plan_items', 'plan_items.service_id', '=', 'services.id') 95 | .where('plan_id', id) 96 | .select([ 97 | 'plan_items.id', 98 | 'services.description', 99 | 'plan_items.amount' 100 | ]).orderBy('services.description', 'asc'); 101 | 102 | return response.json({ 103 | services:services, 104 | form:plans 105 | }); 106 | }, 107 | 108 | 109 | async deleteItem(request, response) { 110 | const { id } = request.params; 111 | 112 | await conn('plan_items').where('id', id).delete(); 113 | 114 | return response.status(204).send(); 115 | } 116 | } -------------------------------------------------------------------------------- /src/pages/Plans/index.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import DeleteIcon from '@material-ui/icons/Delete'; 6 | import PageviewIcon from '@material-ui/icons/Pageview'; 7 | import Header from '~/components/Header'; 8 | import CustomPaginationActionsTable from '~/components/tables'; 9 | 10 | import api from '~/services/api'; 11 | import history from '~/services/history'; 12 | 13 | const { ipcRenderer } = window.require("electron"); 14 | 15 | 16 | const useStyles = makeStyles((theme) => ({ 17 | root: { 18 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 19 | marginLeft: theme.spacing(1), 20 | } 21 | }, 22 | 23 | })); 24 | 25 | 26 | 27 | export default function Plans() { 28 | const classes = useStyles(); 29 | const initalValues={ 30 | id:null, 31 | description:'', 32 | amount:'', 33 | services_id:1 34 | } 35 | const [values, setValues] = useState(initalValues); 36 | const [data, setData] = useState([]); 37 | 38 | const [lblButton, setLblButton] = useState('Cadastrar'); 39 | 40 | useEffect(() => { 41 | api.get('plans').then(response => { 42 | setData(response.data); 43 | }) 44 | 45 | }, []); 46 | 47 | 48 | 49 | const handleChange = (event) => { 50 | setValues({ 51 | ...values, 52 | [event.target.name]: event.target.value, 53 | }); 54 | }; 55 | 56 | 57 | const handleNotification=(title,body)=>{ 58 | const message = { 59 | title: title, 60 | body: body 61 | }; 62 | 63 | ipcRenderer.send("@notification/REQUEST", message); 64 | } 65 | 66 | 67 | 68 | 69 | async function handleDelete(id) { 70 | 71 | var r = window.confirm("Tem certeza que deseja deletar?"); 72 | 73 | if (!r) return; 74 | 75 | try { 76 | await api.delete(`plans/${id}`); 77 | setValues(initalValues); 78 | setData(data.filter(_data =>_data.id!== parseInt(id))); 79 | handleNotification("Atenção","Registro excluído com sucesso"); 80 | } catch (err) { 81 | alert('Erro ao deletar o serviço, tente novamente.'); 82 | } 83 | 84 | } 85 | 86 | 87 | 88 | 89 | 90 | return ( 91 | <> 92 |
93 |
94 |
95 |
96 |
97 | 105 | 106 |
107 |
108 | 113 | history.push(`/plans/${e.target.closest('tr').getAttribute('data-id')}`)}> 117 | 118 | 119 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 123 | 124 | 125 | 126 | } 127 | /> 128 |
129 |
130 | 131 | ); 132 | } 133 | -------------------------------------------------------------------------------- /src/pages/Attendance/index.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import DeleteIcon from '@material-ui/icons/Delete'; 6 | import PageviewIcon from '@material-ui/icons/Pageview'; 7 | import Header from '~/components/Header'; 8 | import CustomPaginationActionsTable from '~/components/tables'; 9 | 10 | import api from '~/services/api'; 11 | import history from '~/services/history'; 12 | 13 | const { ipcRenderer } = window.require("electron"); 14 | 15 | 16 | const useStyles = makeStyles((theme) => ({ 17 | root: { 18 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 19 | marginLeft: theme.spacing(1), 20 | } 21 | }, 22 | 23 | })); 24 | 25 | 26 | 27 | export default function Attendance() { 28 | const classes = useStyles(); 29 | const initalValues={ 30 | id:null, 31 | description:'', 32 | amount:'', 33 | services_id:1 34 | } 35 | const [values, setValues] = useState(initalValues); 36 | const [data, setData] = useState([]); 37 | 38 | const [lblButton, setLblButton] = useState('Cadastrar'); 39 | 40 | useEffect(() => { 41 | api.get('attendance').then(response => { 42 | setData(response.data); 43 | }) 44 | 45 | }, []); 46 | 47 | 48 | 49 | const handleChange = (event) => { 50 | setValues({ 51 | ...values, 52 | [event.target.name]: event.target.value, 53 | }); 54 | }; 55 | 56 | 57 | const handleNotification=(title,body)=>{ 58 | const message = { 59 | title: title, 60 | body: body 61 | }; 62 | 63 | ipcRenderer.send("@notification/REQUEST", message); 64 | } 65 | 66 | 67 | 68 | 69 | async function handleDelete(id) { 70 | 71 | var r = window.confirm("Tem certeza que deseja deletar?"); 72 | 73 | if (!r) return; 74 | 75 | try { 76 | await api.delete(`attendance/${id}`); 77 | setValues(initalValues); 78 | setData(data.filter(_data =>_data.id!== parseInt(id))); 79 | handleNotification("Atenção","Registro excluído com sucesso"); 80 | } catch (err) { 81 | alert('Erro ao deletar o serviço, tente novamente.'); 82 | } 83 | 84 | } 85 | 86 | 87 | 88 | 89 | 90 | return ( 91 | <> 92 |
93 |
94 |
95 |
96 |
97 | 105 | 106 |
107 |
108 | 113 | history.push(`/attendance/items/${e.target.closest('tr').getAttribute('data-id')}`)}> 117 | 118 | 119 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 123 | 124 | 125 | 126 | } 127 | /> 128 |
129 |
130 | 131 | ); 132 | } 133 | -------------------------------------------------------------------------------- /public/apiV1/src/controllers/CommissionController.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const path = require('path'); 3 | const conn = require(`${path.resolve(__dirname, '../database/conn')}`); 4 | 5 | 6 | number_format= function( number, decimals, dec_point, thousands_sep ) { 7 | var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; 8 | var d = dec_point == undefined ? "," : dec_point; 9 | var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : ""; 10 | var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0; 11 | return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); 12 | } 13 | 14 | 15 | module.exports = { 16 | 17 | async index(request,response){ 18 | 19 | const { date_ini,date_end } = request.query; 20 | 21 | 22 | 23 | const comission= await conn({a:"attendance_items"}) 24 | .leftJoin({b:'employee'},'b.id', '=','a.employee_id') 25 | .leftJoin({d:'services'},'d.id', '=','a.service_id') 26 | .leftJoin({e:'client'} ,'e.id', '=','a.client_id') 27 | .leftJoin({f:'plans'} ,'f.id', '=','a.plan_id') 28 | .select([ 29 | 'b.name as employee', 30 | conn.raw('sum(a.value) as value'), 31 | conn.raw('sum(a.rate) as rate'), 32 | conn.raw('sum(a.commission) as commission') 33 | 34 | ]) 35 | .groupBy('b.id') 36 | .whereBetween('a.date',[date_ini,date_end]) 37 | .orderBy('b.name', 'desc') 38 | 39 | 40 | 41 | var comissionObj = comission.map(function(row) { 42 | return { 43 | ...row, 44 | value:number_format(row.value,'.',','), 45 | rate:number_format(row.rate), 46 | commission:number_format(row.commission) 47 | 48 | } 49 | }); 50 | 51 | 52 | return response.json(comissionObj); 53 | }, 54 | 55 | async detailed(request,response){ 56 | 57 | const { date_ini,date_end } = request.query; 58 | 59 | 60 | 61 | const comission= await conn({a:"attendance_items"}) 62 | .leftJoin({b:'employee'},'b.id', '=','a.employee_id') 63 | .leftJoin({d:'services'},'d.id', '=','a.service_id') 64 | .leftJoin({e:'client'} ,'e.id', '=','a.client_id') 65 | .leftJoin({f:'plans'} ,'f.id', '=','a.plan_id') 66 | .select([ 67 | 'b.name as employee', 68 | 'e.name as client', 69 | 'd.description as service', 70 | 'f.description as plan', 71 | 'a.value', 72 | 'a.rate', 73 | 'a.commission', 74 | 'a.date' 75 | ]) 76 | .whereBetween('a.date',[date_ini,date_end]) 77 | .orderBy('b.name', 'desc') 78 | 79 | 80 | var comissionObj = comission.map(function(row) { 81 | return { 82 | ...row, 83 | date:moment(row.date).format('DD/MM/YYYY'), 84 | value:number_format(row.value,'.',','), 85 | rate:number_format(row.rate), 86 | commission:number_format(row.commission) 87 | 88 | } 89 | }); 90 | 91 | 92 | return response.json(comissionObj); 93 | }, 94 | 95 | } -------------------------------------------------------------------------------- /src/pages/Commission/index.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | 5 | import { 6 | MuiPickersUtilsProvider, 7 | KeyboardDatePicker, 8 | } from '@material-ui/pickers'; 9 | 10 | import ptBrLocale from "date-fns/locale/pt-BR"; 11 | import DateFnsUtils from '@date-io/date-fns'; 12 | 13 | import * as moment from 'moment'; 14 | 15 | import Header from '~/components/Header'; 16 | import CustomPaginationActionsTable from '~/components/tables'; 17 | 18 | 19 | 20 | import api from '~/services/api'; 21 | import history from '~/services/history'; 22 | 23 | const { ipcRenderer } = window.require("electron"); 24 | 25 | 26 | const useStyles = makeStyles((theme) => ({ 27 | root: { 28 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root,.MuiAutocomplete-root +.MuiAutocomplete-root': { 29 | marginLeft: theme.spacing(1), 30 | }, 31 | '& .MuiFormControl-marginNormal':{ 32 | marginTop:0 33 | } 34 | 35 | }, 36 | 37 | })); 38 | 39 | 40 | export default function Commission() { 41 | const classes = useStyles(); 42 | 43 | const initalValues={ 44 | date_ini:moment().startOf('month').format(), 45 | date_end:moment().endOf('month').format() 46 | } 47 | 48 | const [values, setValues] = useState(initalValues); 49 | const [data, setData] = useState([]); 50 | 51 | 52 | 53 | 54 | 55 | async function loadCommission(_values) { 56 | let date_ini=moment(_values.date_ini).format('YYYY-MM-DD'); 57 | let date_end=moment(_values.date_end).format('YYYY-MM-DD'); 58 | 59 | const response = await api.get('commission',{ 60 | params:{date_ini,date_end} 61 | }); 62 | 63 | setData(response.data) 64 | 65 | } 66 | 67 | useEffect(() => { 68 | loadCommission(values); 69 | }, []); 70 | 71 | 72 | const handleDateIniChange = (date) => { 73 | setValues({...values,date_ini:date}) 74 | 75 | loadCommission({...values,date_ini:date}); 76 | }; 77 | 78 | 79 | const handleDateEndChange = (date) => { 80 | setValues({...values,date_end:date}) 81 | loadCommission({...values,date_end:date}); 82 | }; 83 | 84 | 85 | 86 | 87 | return ( 88 | <> 89 |
90 |
91 |
92 |
93 |
94 | 95 | 96 | 97 | 112 | 113 | 129 | 130 | 131 | 132 | 141 | 142 |
143 |
144 | 148 |
149 |
150 | 151 | ); 152 | } 153 | -------------------------------------------------------------------------------- /src/pages/Commission/detailed.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | 5 | import { 6 | MuiPickersUtilsProvider, 7 | KeyboardDatePicker, 8 | } from '@material-ui/pickers'; 9 | 10 | import ptBrLocale from "date-fns/locale/pt-BR"; 11 | import DateFnsUtils from '@date-io/date-fns'; 12 | 13 | import * as moment from 'moment'; 14 | 15 | import Header from '~/components/Header'; 16 | import CustomPaginationActionsTable from '~/components/tables'; 17 | 18 | 19 | 20 | import api from '~/services/api'; 21 | import history from '~/services/history'; 22 | 23 | const { ipcRenderer } = window.require("electron"); 24 | 25 | 26 | const useStyles = makeStyles((theme) => ({ 27 | root: { 28 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root,.MuiAutocomplete-root +.MuiAutocomplete-root': { 29 | marginLeft: theme.spacing(1), 30 | }, 31 | '& .MuiFormControl-marginNormal':{ 32 | marginTop:0 33 | } 34 | 35 | }, 36 | 37 | })); 38 | 39 | 40 | export default function Commission() { 41 | const classes = useStyles(); 42 | 43 | const initalValues={ 44 | date_ini:moment().startOf('month').format(), 45 | date_end:moment().endOf('month').format() 46 | } 47 | 48 | const [values, setValues] = useState(initalValues); 49 | const [data, setData] = useState([]); 50 | 51 | 52 | 53 | 54 | 55 | async function loadCommission(_values) { 56 | let date_ini=moment(_values.date_ini).format('YYYY-MM-DD'); 57 | let date_end=moment(_values.date_end).format('YYYY-MM-DD'); 58 | 59 | const response = await api.get('commission/detailed',{ 60 | params:{date_ini,date_end} 61 | }); 62 | 63 | setData(response.data) 64 | 65 | } 66 | 67 | useEffect(() => { 68 | loadCommission(values); 69 | }, []); 70 | 71 | 72 | const handleDateIniChange = (date) => { 73 | setValues({...values,date_ini:date}) 74 | 75 | loadCommission({...values,date_ini:date}); 76 | }; 77 | 78 | 79 | const handleDateEndChange = (date) => { 80 | setValues({...values,date_end:date}) 81 | loadCommission({...values,date_end:date}); 82 | }; 83 | 84 | 85 | 86 | 87 | return ( 88 | <> 89 |
90 |
91 |
92 |
93 |
94 | 95 | 96 | 97 | 112 | 113 | 129 | 130 | 131 | 132 | 141 | 142 |
143 |
144 | 148 |
149 |
150 | 151 | ); 152 | } 153 | -------------------------------------------------------------------------------- /public/apiV1/src/controllers/ClientController.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const path = require('path'); 3 | const conn= require(`${path.resolve(__dirname, '../database/conn')}`); 4 | 5 | 6 | async function getClient(client_id){ 7 | const client= await conn("client") 8 | .leftJoin('client_plan','client_plan.client_id', '=','client.id') 9 | .leftJoin('plans','plans.id','=','client_plan.plan_id') 10 | .where('client.id', client_id) 11 | .select([ 12 | 'client.id', 13 | 'name', 14 | 'phone', 15 | 'plans.description', 16 | 'client_plan.expiration_day' 17 | ]) 18 | .groupBy('client.id') 19 | .orderBy('name', 'asc') 20 | 21 | return client; 22 | } 23 | 24 | module.exports = { 25 | 26 | 27 | async index(resquest,response){ 28 | 29 | 30 | const client= await conn({a:"client"}) 31 | .leftJoin({b:'client_dependent'},'b.dependente_id', '=','a.id') 32 | .leftJoin({c:'client_plan'},function() { 33 | this.on('c.client_id', '=','a.id').orOn('c.client_id', '=','b.client_id') 34 | }) 35 | .leftJoin({d:'plans'},'d.id','=','c.plan_id') 36 | .leftJoin({e:'client'},'e.id','=','b.client_id') 37 | .select([ 38 | 'a.id', 39 | 'a.name', 40 | 'a.phone', 41 | 'd.description', 42 | 'c.expiration_day', 43 | 'e.name as holder' 44 | ]) 45 | .groupBy('a.id') 46 | .orderBy('a.name', 'asc'); 47 | 48 | 49 | return response.json(client); 50 | }, 51 | 52 | 53 | 54 | 55 | 56 | async cerate(resquest,response){ 57 | const {name,phone,plan_id,expiration_day,form_payment_id} =resquest.body; 58 | 59 | let error_msg=false; 60 | 61 | if (!name) 62 | error_msg='Infome o nome do cliente'; 63 | else if (!form_payment_id) 64 | error_msg='Infome a forma de pagamento'; 65 | 66 | if(error_msg) 67 | return response.status(401).json({ error:error_msg}); 68 | 69 | 70 | const [id] =await conn("client").insert({ 71 | name, 72 | phone, 73 | form_payment_id, 74 | active:'1' 75 | }); 76 | 77 | let client_id=id; 78 | 79 | if(plan_id){ 80 | 81 | const {value}= await conn("plans").where('id', plan_id).select(['value']).first(); 82 | 83 | await conn("client_plan").insert({ 84 | client_id, 85 | plan_id, 86 | expiration_day, 87 | value, 88 | active:'1', 89 | 90 | }); 91 | 92 | await conn("client_payment").insert({ 93 | client_id, 94 | year:moment().format('YYYY') 95 | 96 | }); 97 | } 98 | 99 | 100 | const client= await getClient(client_id); 101 | 102 | return response.json(client); 103 | 104 | }, 105 | 106 | 107 | async delete(request, response) { 108 | const { id } = request.params; 109 | 110 | await conn('client').where('id', id).delete(); 111 | await conn('client_plan').where('client_id', id).delete(); 112 | await conn('client_payment').where('client_id', id).delete(); 113 | 114 | return response.status(204).send(); 115 | }, 116 | 117 | async update(resquest, response) { 118 | 119 | const {id,name,phone,plan_id,expiration_day,form_payment_id} =resquest.body; 120 | 121 | let error_msg=false; 122 | 123 | if (!name) 124 | error_msg='Infome o nome do cliente'; 125 | 126 | 127 | if(error_msg) 128 | return response.status(401).json({ error:error_msg}); 129 | 130 | 131 | await conn('client').where('id', id).update({ 132 | name, 133 | phone, 134 | form_payment_id, 135 | active:'1' 136 | }); 137 | 138 | let client_id=id; 139 | 140 | 141 | const [count]= await conn("client_plan").where('client_id', client_id).count(); 142 | 143 | 144 | 145 | if(count>0){ 146 | await conn("client_plan").where('client_id', client_id).update({ 147 | plan_id, 148 | expiration_day 149 | }); 150 | 151 | }else if(plan_id){ 152 | 153 | const {value}= await conn("plans").where('id', plan_id).select(['value']).first(); 154 | 155 | await conn("client_plan").insert({ 156 | client_id, 157 | plan_id, 158 | expiration_day, 159 | value, 160 | active:'1' 161 | }); 162 | 163 | await conn("client_payment").insert({ 164 | client_id, 165 | year:moment().format('YYYY') 166 | 167 | }); 168 | } 169 | 170 | const client= await getClient(client_id); 171 | 172 | 173 | return response.json(client); 174 | } 175 | 176 | }; -------------------------------------------------------------------------------- /src/pages/Client/dependent.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import {useParams } from "react-router-dom"; 3 | import {TextField,Button,MenuItem} from '@material-ui/core'; 4 | import { makeStyles } from '@material-ui/core/styles'; 5 | import SaveIcon from '@material-ui/icons/Save'; 6 | import IconButton from '@material-ui/core/IconButton'; 7 | import DeleteIcon from '@material-ui/icons/Delete'; 8 | import ArrowBackIcon from '@material-ui/icons/ArrowBack'; 9 | import Header from '~/components/Header'; 10 | import CustomPaginationActionsTable from '~/components/tables'; 11 | 12 | import api from '~/services/api'; 13 | import history from '~/services/history'; 14 | 15 | const { ipcRenderer } = window.require("electron"); 16 | 17 | const useStyles = makeStyles((theme) => ({ 18 | root: { 19 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 20 | marginLeft: theme.spacing(1), 21 | } 22 | }, 23 | 24 | })); 25 | 26 | 27 | export default function Dependent() { 28 | const { id } = useParams(); 29 | 30 | const classes = useStyles(); 31 | const initalValues={ 32 | id:null, 33 | dependente_id:'', 34 | client_id: parseInt(id) 35 | } 36 | const [values, setValues] = useState(initalValues); 37 | const [data, setData] = useState([]); 38 | const [client, setClient] = useState([]); 39 | const [filterClient, setFilterClient] = useState({name:''}); 40 | const [lblButton, setLblButton] = useState('Adicionar'); 41 | 42 | 43 | useEffect(() => { 44 | api.get(`dependent/${id}`).then(response => { 45 | setData(response.data); 46 | }) 47 | 48 | api.get(`dependent/list/${id}`).then(response => { 49 | setClient( 50 | response.data.filter(data=>data.id!== parseInt(id)) 51 | ); 52 | 53 | setFilterClient(filterByClient(response.data)); 54 | }) 55 | 56 | }, []); 57 | 58 | 59 | const filterByClient=(data)=>{ 60 | return data.filter(_data =>_data.id== parseInt(id)).shift(); 61 | } 62 | 63 | 64 | 65 | const handleChange = (event) => { 66 | setValues({ 67 | ...values, 68 | [event.target.name]: event.target.value, 69 | }); 70 | }; 71 | 72 | 73 | const handleNotification=(title,body)=>{ 74 | const message = { 75 | title: title, 76 | body: body 77 | }; 78 | 79 | ipcRenderer.send("@notification/REQUEST", message); 80 | } 81 | 82 | 83 | async function handleSubmit(e) { 84 | e.preventDefault(); 85 | 86 | try { 87 | const response= await api.post('dependent',values) 88 | .catch(err=> alert(`Atenção, ${err.response.data.error}`) 89 | ); 90 | 91 | setClient( 92 | client.filter(data=>data.id!== parseInt(values.dependente_id)) 93 | ); 94 | 95 | setData([...response.data]); 96 | setValues(initalValues); 97 | handleNotification("Um novo dependente foi cadastrado"); 98 | }catch(err) { 99 | alert('Erro ao cadastrar o dependente, tente novamente.'); 100 | } 101 | } 102 | 103 | 104 | async function handleDelete(id) { 105 | 106 | var r = window.confirm("Tem certeza que deseja deletar?"); 107 | 108 | if (!r) return; 109 | 110 | try { 111 | await api.delete(`dependent/${id}`); 112 | setData(data.filter(_data =>_data.id!== parseInt(id))); 113 | handleNotification("Atenção","Registro excluído com sucesso"); 114 | }catch(err){ 115 | alert('Erro ao deletar o dependente, tente novamente.'); 116 | } 117 | 118 | } 119 | 120 | 121 | 122 | return ( 123 | <> 124 |
Dependentes`}/> 125 |
126 |
127 |
128 |
129 | 130 | 139 | - 140 | {client.map((row)=>( 141 | {row.name} - telefone: {row.phone} 142 | ))} 143 | 144 | 145 | 154 | 155 | 164 | 165 |
166 |
167 | 172 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 177 | 178 | 179 | 180 | } 181 | /> 182 |
183 |
184 | 185 | ); 186 | } 187 | -------------------------------------------------------------------------------- /src/pages/Users/index.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import {TextField,Button } from '@material-ui/core'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | import SaveIcon from '@material-ui/icons/Save'; 5 | import IconButton from '@material-ui/core/IconButton'; 6 | import DeleteIcon from '@material-ui/icons/Delete'; 7 | import EdiIcon from '@material-ui/icons/Edit'; 8 | import Header from '../../components/Header'; 9 | import CustomPaginationActionsTable from '../../components/tables'; 10 | 11 | import api from '../../services/api'; 12 | 13 | const { ipcRenderer } = window.require("electron"); 14 | 15 | 16 | const useStyles = makeStyles((theme) => ({ 17 | root: { 18 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 19 | marginLeft: theme.spacing(1), 20 | } 21 | }, 22 | 23 | })); 24 | 25 | 26 | 27 | export default function Users() { 28 | const classes = useStyles(); 29 | const initalValues={ 30 | id:null, 31 | name:'', 32 | password:'', 33 | } 34 | const [values, setValues] = useState(initalValues); 35 | const [data, setData] = useState([]); 36 | 37 | const [lblButton, setLblButton] = useState('Cadastrar'); 38 | 39 | useEffect(() => { 40 | api.get('users').then(response => { 41 | setData(response.data); 42 | }) 43 | 44 | }, []); 45 | 46 | 47 | 48 | const handleChange = (event) => { 49 | setValues({ 50 | ...values, 51 | [event.target.name]: event.target.value, 52 | }); 53 | }; 54 | 55 | 56 | const handleNotification=(title,body)=>{ 57 | const message = { 58 | title: title, 59 | body: body 60 | }; 61 | 62 | ipcRenderer.send("@notification/REQUEST", message); 63 | } 64 | 65 | 66 | 67 | function handleSubmit(e){ 68 | e.preventDefault(); 69 | 70 | if(!values.name) return; 71 | 72 | if(values.id) 73 | handleUpdate(); 74 | else 75 | handleNew(); 76 | 77 | } 78 | 79 | async function handleNew() { 80 | 81 | try { 82 | const response= await api.post('users',values); 83 | const resp = {...values,id:response.data.id}; 84 | 85 | setData([resp,...data]); 86 | setValues(initalValues); 87 | handleNotification("Um novo usuário foi cadastrado",`O serviço ${values.name} foi cadastrado`); 88 | 89 | }catch (err) { 90 | alert('Erro ao cadastrar o usuário, tente novamente.'); 91 | } 92 | } 93 | 94 | 95 | 96 | 97 | async function handleUpdate() { 98 | 99 | try { 100 | const response = await api.put('users',values); 101 | const _data = data.filter(_data =>_data.id!== parseInt(values.id)); 102 | const resp = {...values,id:response.data.id}; 103 | 104 | setData([resp,..._data]); 105 | setValues(initalValues); 106 | 107 | handleNotification("Um usuário foi atualizado",`O usuário ${values.name} foi Atulizado`); 108 | 109 | setLblButton("Cadastrar"); 110 | 111 | } catch (err) { 112 | alert('Erro ao atulizar o usuário, tente novamente.'); 113 | } 114 | } 115 | 116 | 117 | async function handleDelete(id) { 118 | 119 | var r = window.confirm("Tem certeza que deseja deletar?"); 120 | 121 | if (!r) return; 122 | 123 | try { 124 | await api.delete(`users/${id}`); 125 | setData(data.filter(_data =>_data.id!== parseInt(id))); 126 | handleNotification("Atenção","Registro excluído com sucesso"); 127 | } catch (err) { 128 | alert('Erro ao deletar o serviço, tente novamente.'); 129 | } 130 | 131 | } 132 | 133 | 134 | function handleGetUpdate(id) { 135 | const _data= data.filter(_data =>_data.id== parseInt(id)); 136 | 137 | setValues({...initalValues,..._data[0]}); 138 | 139 | setLblButton("Atualizar"); 140 | } 141 | 142 | 143 | 144 | return ( 145 | <> 146 |
147 |
148 |
149 |
150 |
151 | 158 | 166 | 167 | 176 |
177 |
178 | 183 | handleGetUpdate(e.target.closest('tr').getAttribute('data-id'))}> 187 | 188 | 189 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 193 | 194 | 195 | 196 | } 197 | /> 198 |
199 |
200 | 201 | ); 202 | } 203 | -------------------------------------------------------------------------------- /src/pages/Services/index.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import {TextField,Button } from '@material-ui/core'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | import SaveIcon from '@material-ui/icons/Save'; 5 | import IconButton from '@material-ui/core/IconButton'; 6 | import DeleteIcon from '@material-ui/icons/Delete'; 7 | import EdiIcon from '@material-ui/icons/Edit'; 8 | import Header from '~/components/Header'; 9 | import CustomPaginationActionsTable from '~/components/tables'; 10 | import api from '~/services/api'; 11 | 12 | const { ipcRenderer } = window.require("electron"); 13 | 14 | 15 | const useStyles = makeStyles((theme) => ({ 16 | root: { 17 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 18 | marginLeft: theme.spacing(1), 19 | } 20 | }, 21 | 22 | })); 23 | 24 | 25 | 26 | export default function Services() { 27 | const classes = useStyles(); 28 | const initalValues={ 29 | id:null, 30 | description:'', 31 | value:'', 32 | } 33 | const [values, setValues] = useState(initalValues); 34 | const [data, setData] = useState([]); 35 | 36 | const [lblButton, setLblButton] = useState('Cadastrar'); 37 | 38 | useEffect(() => { 39 | api.get('service').then(response => { 40 | setData(response.data); 41 | }) 42 | }, []); 43 | 44 | 45 | 46 | const handleChange = (event) => { 47 | setValues({ 48 | ...values, 49 | [event.target.name]: event.target.value, 50 | }); 51 | }; 52 | 53 | 54 | const handleNotification=(title,body)=>{ 55 | const message = { 56 | title: title, 57 | body: body 58 | }; 59 | 60 | ipcRenderer.send("@notification/REQUEST", message); 61 | } 62 | 63 | 64 | 65 | function handleSubmit(e){ 66 | e.preventDefault(); 67 | 68 | if(!values.description) return; 69 | 70 | if(values.id) 71 | handleUpdate(); 72 | else 73 | handleNew(); 74 | 75 | } 76 | 77 | async function handleNew() { 78 | 79 | try { 80 | const response= await api.post('service',values); 81 | const resp = {id:response.data.id,description:values.description,value:values.value}; 82 | 83 | setData([resp,...data]); 84 | setValues(initalValues); 85 | handleNotification("Um novo serviço foi cadastrado",`O serviço ${values.description} foi cadastrado`); 86 | 87 | }catch (err) { 88 | alert('Erro ao cadastrar o serviço, tente novamente.'); 89 | } 90 | } 91 | 92 | 93 | 94 | 95 | async function handleUpdate() { 96 | 97 | try { 98 | const response = await api.put('service',values); 99 | const _data = data.filter(_data =>_data.id!== parseInt(values.id)); 100 | const resp = {id:response.data.id,name:values.description,value:values.value}; 101 | 102 | setData([resp,..._data]); 103 | setValues(initalValues); 104 | 105 | handleNotification("Um serviço foi atualizado",`O serviço ${values.description} foi Atulizado`); 106 | 107 | setLblButton("Cadastrar"); 108 | 109 | } catch (err) { 110 | alert('Erro ao atulizar o serviço, tente novamente.'); 111 | } 112 | } 113 | 114 | 115 | async function handleDelete(id) { 116 | 117 | var r = window.confirm("Tem certeza que deseja deletar?"); 118 | 119 | if (!r) return; 120 | 121 | try { 122 | await api.delete(`service/${id}`); 123 | setData(data.filter(_data =>_data.id!== parseInt(id))); 124 | handleNotification("Atenção","Registro excluído com sucesso"); 125 | } catch (err) { 126 | alert('Erro ao deletar o serviço, tente novamente.'); 127 | } 128 | 129 | } 130 | 131 | 132 | function handleGetUpdate(id) { 133 | const _data= data.filter(_data =>_data.id== parseInt(id)); 134 | 135 | setValues({...initalValues,..._data[0]}); 136 | 137 | setLblButton("Atualizar"); 138 | } 139 | 140 | 141 | 142 | return ( 143 | <> 144 |
145 |
146 |
147 |
148 |
149 | 156 | 164 | 165 | 174 |
175 |
176 | 181 | handleGetUpdate(e.target.closest('tr').getAttribute('data-id'))}> 185 | 186 | 187 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 191 | 192 | 193 | 194 | } 195 | /> 196 |
197 |
198 | 199 | ); 200 | } 201 | -------------------------------------------------------------------------------- /src/pages/FormPayment/index.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import {TextField,Button } from '@material-ui/core'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | import SaveIcon from '@material-ui/icons/Save'; 5 | import IconButton from '@material-ui/core/IconButton'; 6 | import DeleteIcon from '@material-ui/icons/Delete'; 7 | import EdiIcon from '@material-ui/icons/Edit'; 8 | import Header from '../../components/Header'; 9 | import CustomPaginationActionsTable from '../../components/tables'; 10 | 11 | import api from '../../services/api'; 12 | 13 | const { ipcRenderer } = window.require("electron"); 14 | 15 | 16 | const useStyles = makeStyles((theme) => ({ 17 | root: { 18 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 19 | marginLeft: theme.spacing(1), 20 | } 21 | }, 22 | 23 | })); 24 | 25 | 26 | 27 | export default function Users() { 28 | const classes = useStyles(); 29 | const initalValues={ 30 | id:null, 31 | description:'', 32 | rate:'', 33 | } 34 | const [values, setValues] = useState(initalValues); 35 | const [data, setData] = useState([]); 36 | 37 | const [lblButton, setLblButton] = useState('Cadastrar'); 38 | 39 | useEffect(() => { 40 | api.get('/payment/form').then(response => setData(response.data)); 41 | }, []); 42 | 43 | 44 | 45 | const handleChange = (event) => { 46 | setValues({ 47 | ...values, 48 | [event.target.name]: event.target.value, 49 | }); 50 | }; 51 | 52 | 53 | const handleNotification=(title,body)=>{ 54 | const message = { 55 | title: title, 56 | body: body 57 | }; 58 | 59 | ipcRenderer.send("@notification/REQUEST", message); 60 | } 61 | 62 | 63 | 64 | function handleSubmit(e){ 65 | e.preventDefault(); 66 | 67 | 68 | 69 | if(values.id) 70 | handleUpdate(); 71 | else 72 | handleNew(); 73 | 74 | } 75 | 76 | async function handleNew() { 77 | 78 | try { 79 | const response= await api.post('payment/form',values) 80 | .catch(err=> alert(`Atenção, ${err.response.data.error}`) 81 | ); 82 | 83 | 84 | const resp = {...values,id:response.data.id}; 85 | 86 | setData([resp,...data]); 87 | setValues(initalValues); 88 | handleNotification("Uma nova forma de pagamento foi cadastrado",`A forma de pagamento ${values.description} foi cadastrado`); 89 | 90 | }catch (err) { 91 | //alert('Erro ao cadastrar o usuário, tente novamente.'); 92 | } 93 | } 94 | 95 | 96 | 97 | 98 | async function handleUpdate() { 99 | 100 | try { 101 | const response = await api.put('payment/form',values) 102 | .catch(err=> alert(`Atenção, ${err.response.data.error}`) 103 | ); 104 | 105 | const _data = data.filter(_data =>_data.id!== parseInt(values.id)); 106 | const resp = {...values,id:response.data.id}; 107 | 108 | setData([resp,..._data]); 109 | setValues(initalValues); 110 | 111 | handleNotification("Uma nova forma de pagemento foi atualizada",`A forma de pagamento ${values.description} foi atulizada`); 112 | 113 | setLblButton("Cadastrar"); 114 | 115 | } catch (err) { 116 | //alert('Erro ao atulizar o usuário, tente novamente.'); 117 | } 118 | } 119 | 120 | 121 | async function handleDelete(id) { 122 | 123 | var r = window.confirm("Tem certeza que deseja deletar?"); 124 | 125 | if (!r) return; 126 | 127 | try { 128 | await api.delete(`payment/form/${id}`); 129 | setData(data.filter(_data =>_data.id!== parseInt(id))); 130 | handleNotification("Atenção","Registro excluído com sucesso"); 131 | } catch (err) { 132 | //alert('Erro ao deletar o serviço, tente novamente.'); 133 | } 134 | 135 | } 136 | 137 | 138 | function handleGetUpdate(id) { 139 | const _data= data.filter(_data =>_data.id== parseInt(id)); 140 | 141 | console.log(data); 142 | 143 | setValues({...initalValues,..._data[0]}); 144 | 145 | setLblButton("Atualizar"); 146 | } 147 | 148 | 149 | 150 | return ( 151 | <> 152 |
153 |
154 |
155 |
156 |
157 | 164 | 172 | 173 | 182 |
183 |
184 | 189 | handleGetUpdate(e.target.closest('tr').getAttribute('data-id'))}> 193 | 194 | 195 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 199 | 200 | 201 | 202 | } 203 | /> 204 |
205 |
206 | 207 | ); 208 | } 209 | -------------------------------------------------------------------------------- /src/pages/Plans/new.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import {useParams } from "react-router-dom"; 3 | import {TextField,Button,MenuItem } from '@material-ui/core'; 4 | import { makeStyles } from '@material-ui/core/styles'; 5 | import SaveIcon from '@material-ui/icons/Save'; 6 | import ArrowBackIcon from '@material-ui/icons/ArrowBack'; 7 | import IconButton from '@material-ui/core/IconButton'; 8 | import DeleteIcon from '@material-ui/icons/Delete'; 9 | import Header from '~/components/Header'; 10 | import CustomPaginationActionsTable from '~/components/tables'; 11 | 12 | import api from '~/services/api'; 13 | import history from '~/services/history'; 14 | 15 | const { ipcRenderer } = window.require("electron"); 16 | 17 | 18 | const useStyles = makeStyles((theme) => ({ 19 | root: { 20 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 21 | marginLeft: theme.spacing(1), 22 | } 23 | }, 24 | 25 | })); 26 | 27 | 28 | 29 | export default function Plans() { 30 | const classes = useStyles(); 31 | const initalValues={ 32 | id:null, 33 | description:'', 34 | value:'', 35 | amount:'', 36 | service_id:'' 37 | } 38 | 39 | const [values, setValues] = useState(initalValues); 40 | const [data, setData] = useState([]); 41 | const [services, setService] = useState([]); 42 | const [disabled, setDisabled] = useState(false); 43 | 44 | const [lblButton, setLblButton] = useState('Cadastrar'); 45 | 46 | const { id } = useParams(); 47 | 48 | useEffect(() => { 49 | api.get('service').then(response => { 50 | setService(response.data); 51 | }) 52 | 53 | if(id) 54 | api.get(`plans/items/${id}`).then(response => { 55 | setData(response.data.services); 56 | setValues(response.data.form); 57 | //setDisabled(true); 58 | 59 | }).catch(err=>history.push('/plans')) 60 | 61 | }, []); 62 | 63 | 64 | 65 | const handleChange = (event) => { 66 | setValues({ 67 | ...values, 68 | [event.target.name]: event.target.value, 69 | }); 70 | }; 71 | 72 | 73 | const handleNotification=(title,body)=>{ 74 | const message = { 75 | title: title, 76 | body: body 77 | }; 78 | 79 | ipcRenderer.send("@notification/REQUEST", message); 80 | } 81 | 82 | 83 | 84 | 85 | 86 | async function handleSubmit(e) { 87 | e.preventDefault(); 88 | 89 | if(!values.description) return; 90 | 91 | try { 92 | const response= await api.post('plans',values); 93 | const resp = { 94 | ...values, 95 | id:response.data.id, 96 | amount:'', 97 | service_id:'' 98 | }; 99 | 100 | //setDisabled(true); 101 | setData(response.data.data); 102 | setValues(resp); 103 | 104 | handleNotification("Um novo registro foi cadastrado",'Registro Cadastrado com sucesso!'); 105 | 106 | }catch (err) { 107 | alert('Erro ao cadastrar o plano, tente novamente.'); 108 | } 109 | } 110 | 111 | 112 | 113 | async function handleDelete(id) { 114 | 115 | var r = window.confirm("Tem certeza que deseja deletar?"); 116 | 117 | if (!r) return; 118 | 119 | try { 120 | await api.delete(`plans/item/${id}`); 121 | 122 | setData(data.filter(ret => ret.id !== parseInt(id) )); 123 | handleNotification("Atenção","Registro excluído com sucesso"); 124 | }catch (err) { 125 | alert('Erro ao deletar o serviço, tente novamente.'); 126 | } 127 | 128 | } 129 | 130 | 131 | 132 | return ( 133 | <> 134 |
135 |
136 |
137 |
138 |
139 | 147 | 156 |
157 |

Itens do plano

158 |
159 | 160 | 169 | {services.map((row)=>( 170 | {row.description} 171 | ))} 172 | 173 | 174 | 175 | 183 | 184 | 185 | 194 | 195 | 204 |
205 |
206 | 211 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 215 | 216 | 217 | 218 | } 219 | /> 220 |
221 |
222 | 223 | ); 224 | } 225 | -------------------------------------------------------------------------------- /src/pages/Employee/index.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import {TextField,Button } from '@material-ui/core'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | import SaveIcon from '@material-ui/icons/Save'; 5 | import IconButton from '@material-ui/core/IconButton'; 6 | import DeleteIcon from '@material-ui/icons/Delete'; 7 | import EdiIcon from '@material-ui/icons/Edit'; 8 | import Header from '../../components/Header'; 9 | import CustomPaginationActionsTable from '../../components/tables'; 10 | 11 | import api from '../../services/api'; 12 | 13 | const { ipcRenderer } = window.require("electron"); 14 | 15 | 16 | const useStyles = makeStyles((theme) => ({ 17 | root: { 18 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 19 | marginLeft: theme.spacing(1), 20 | } 21 | }, 22 | 23 | })); 24 | 25 | 26 | 27 | export default function Employee() { 28 | const classes = useStyles(); 29 | const initalValues={ 30 | id:null, 31 | name:'', 32 | phone:'', 33 | commission:'', 34 | rate_card:0 35 | } 36 | const [values, setValues] = useState(initalValues); 37 | const [data, setData] = useState([]); 38 | 39 | const [lblButton, setLblButton] = useState('Cadastrar'); 40 | 41 | useEffect(() => { 42 | api.get('employee').then(response => { 43 | setData(response.data); 44 | }) 45 | 46 | }, []); 47 | 48 | 49 | 50 | const handleChange = (event) => { 51 | setValues({ 52 | ...values, 53 | [event.target.name]: event.target.value, 54 | }); 55 | }; 56 | 57 | 58 | const handleNotification=(title,body)=>{ 59 | const message = { 60 | title: title, 61 | body: body 62 | }; 63 | 64 | ipcRenderer.send("@notification/REQUEST", message); 65 | } 66 | 67 | 68 | 69 | function handleSubmit(e){ 70 | e.preventDefault(); 71 | 72 | if(!values.name) return; 73 | 74 | if(values.id) 75 | handleUpdate(); 76 | else 77 | handleNew(); 78 | 79 | } 80 | 81 | async function handleNew() { 82 | 83 | try { 84 | const response= await api.post('employee',values); 85 | const resp = {...values,id:response.data.id}; 86 | 87 | setData([resp,...data]); 88 | setValues(initalValues); 89 | handleNotification("Um novo colaborador foi cadastrado",`O colaborador ${values.name} foi cadastrado`); 90 | 91 | }catch (err) { 92 | alert('Erro ao cadastrar o colaborador, tente novamente.'); 93 | } 94 | } 95 | 96 | 97 | 98 | 99 | async function handleUpdate() { 100 | 101 | try { 102 | const response = await api.put('employee',values); 103 | const _data = data.filter(_data =>_data.id!== parseInt(values.id)); 104 | const resp = {...values,id:response.data.id}; 105 | 106 | setData([resp,..._data]); 107 | setValues(initalValues); 108 | 109 | handleNotification("Um serviço foi atualizado",`O serviço ${values.name} foi Atulizado`); 110 | 111 | setLblButton("Cadastrar"); 112 | 113 | } catch (err) { 114 | alert('Erro ao atulizar o serviço, tente novamente.'); 115 | } 116 | } 117 | 118 | 119 | async function handleDelete(id) { 120 | 121 | var r = window.confirm("Tem certeza que deseja deletar?"); 122 | 123 | if (!r) return; 124 | 125 | try { 126 | await api.delete(`employee/${id}`); 127 | setData(data.filter(_data =>_data.id!== parseInt(id))); 128 | handleNotification("Atenção","Registro excluído com sucesso"); 129 | } catch (err) { 130 | alert('Erro ao deletar o serviço, tente novamente.'); 131 | } 132 | 133 | } 134 | 135 | 136 | function handleGetUpdate(id) { 137 | const _data= data.filter(_data =>_data.id== parseInt(id)); 138 | 139 | setValues({...initalValues,..._data[0]}); 140 | 141 | setLblButton("Atualizar"); 142 | } 143 | 144 | 145 | 146 | return ( 147 | <> 148 |
149 |
150 |
151 |
152 |
153 | 160 | 168 | 176 | 177 | 186 | 187 | 196 |
197 |
198 | 203 | handleGetUpdate(e.target.closest('tr').getAttribute('data-id'))}> 207 | 208 | 209 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 213 | 214 | 215 | 216 | } 217 | /> 218 |
219 |
220 | 221 | ); 222 | } 223 | -------------------------------------------------------------------------------- /src/components/tables/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { makeStyles, useTheme,withStyles } from '@material-ui/core/styles'; 4 | import Table from '@material-ui/core/Table'; 5 | import TableBody from '@material-ui/core/TableBody'; 6 | import TableCell from '@material-ui/core/TableCell'; 7 | import TableContainer from '@material-ui/core/TableContainer'; 8 | import TableFooter from '@material-ui/core/TableFooter'; 9 | import TablePagination from '@material-ui/core/TablePagination'; 10 | import TableRow from '@material-ui/core/TableRow'; 11 | import Paper from '@material-ui/core/Paper'; 12 | import IconButton from '@material-ui/core/IconButton'; 13 | import FirstPageIcon from '@material-ui/icons/FirstPage'; 14 | import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'; 15 | import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'; 16 | import LastPageIcon from '@material-ui/icons/LastPage'; 17 | import TableHead from '@material-ui/core/TableHead'; 18 | import TextField from '@material-ui/core/TextField'; 19 | 20 | 21 | const useStyles1 = makeStyles((theme) => ({ 22 | root: { 23 | flexShrink: 0, 24 | marginLeft: theme.spacing(2.5), 25 | }, 26 | })); 27 | 28 | 29 | const StyledTableRow = withStyles((theme) => ({ 30 | root: { 31 | '&:nth-of-type(odd)': { 32 | backgroundColor: theme.palette.action.hover, 33 | }, 34 | }, 35 | }))(TableRow); 36 | 37 | 38 | const StyledTableCell = withStyles((theme) => ({ 39 | head: { 40 | backgroundColor: theme.palette.common.black, 41 | color: theme.palette.common.white, 42 | }, 43 | body: { 44 | fontSize: 14, 45 | }, 46 | }))(TableCell); 47 | 48 | 49 | function TablePaginationActions(props) { 50 | const classes = useStyles1(); 51 | const theme = useTheme(); 52 | const { count, page, rowsPerPage, onChangePage } = props; 53 | 54 | const handleFirstPageButtonClick = (event) => { 55 | onChangePage(event, 0); 56 | }; 57 | 58 | const handleBackButtonClick = (event) => { 59 | onChangePage(event, page - 1); 60 | }; 61 | 62 | const handleNextButtonClick = (event) => { 63 | onChangePage(event, page + 1); 64 | }; 65 | 66 | const handleLastPageButtonClick = (event) => { 67 | onChangePage(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); 68 | }; 69 | 70 | return ( 71 |
72 | 77 | {theme.direction === 'rtl' ? : } 78 | 79 | 80 | {theme.direction === 'rtl' ? : } 81 | 82 | = Math.ceil(count / rowsPerPage) - 1} 85 | aria-label="next page" 86 | > 87 | {theme.direction === 'rtl' ? : } 88 | 89 | = Math.ceil(count / rowsPerPage) - 1} 92 | aria-label="last page" 93 | > 94 | {theme.direction === 'rtl' ? : } 95 | 96 |
97 | ); 98 | } 99 | 100 | TablePaginationActions.propTypes = { 101 | count: PropTypes.number.isRequired, 102 | onChangePage: PropTypes.func.isRequired, 103 | page: PropTypes.number.isRequired, 104 | rowsPerPage: PropTypes.number.isRequired, 105 | }; 106 | 107 | function createData(name, calories, fat) { 108 | return { name, calories, fat }; 109 | } 110 | 111 | 112 | 113 | const useStyles2 = makeStyles({ 114 | table: { 115 | minWidth: 500, 116 | }, 117 | }); 118 | 119 | 120 | 121 | export default function CustomPaginationActionsTable(props) { 122 | const classes = useStyles2(); 123 | const [page, setPage] = React.useState(0); 124 | const [rowsPerPage, setRowsPerPage] = React.useState(8); 125 | const [sereachValue, setSereach] = React.useState([]); 126 | 127 | 128 | const title=props.title ? props.title : []; 129 | const rows=props.data; 130 | 131 | const getByFilter=()=>{ 132 | 133 | let filteredDatas = [] 134 | filteredDatas = props.data.filter(e => { 135 | let mathesItems = Object.values(e) 136 | return mathesItems.some(e => { 137 | const regex = new RegExp(sereachValue, 'gi') 138 | if (typeof e == 'string') 139 | return e.match(regex) 140 | else 141 | return false 142 | }) 143 | }) 144 | 145 | return filteredDatas; 146 | 147 | }; 148 | 149 | const newArr=(arr)=>{ 150 | const newRow={...arr}; 151 | delete newRow.id; 152 | 153 | return newRow; 154 | }; 155 | 156 | 157 | 158 | 159 | 160 | const emptyRows = rowsPerPage - Math.min(rowsPerPage, getByFilter().length - page * rowsPerPage); 161 | 162 | const handleChangePage = (event, newPage) => { 163 | setPage(newPage); 164 | }; 165 | 166 | const handleChangeRowsPerPage = (event) => { 167 | setRowsPerPage(parseInt(event.target.value, 10)); 168 | setPage(0); 169 | }; 170 | 171 | 172 | 173 | return ( 174 | 175 | 176 | 185 | 186 | 187 | {title.map((row)=>( 188 | {row} 189 | ))} 190 | 191 | 192 | 193 | 194 | {(rowsPerPage > 0 195 | ? getByFilter().slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) 196 | : getByFilter() 197 | ).map((rows) =>( 198 | 199 | 200 | {props.actionBody && 201 | 202 | {props.actionBody} 203 | 204 | } 205 | 206 | { Object.values(newArr(rows)).map((row) =>( 207 | 208 | {row} 209 | 210 | ))} 211 | 212 | 213 | ))} 214 | 215 | {emptyRows > 0 && ( 216 | 217 | 218 | 219 | )} 220 | 221 | 222 | 223 | `${from} - ${to === -1 ? count : to} de ${count !== -1 ? count : `${to}` }`} 228 | rowsPerPage={rowsPerPage} 229 | page={page} 230 | labelRowsPerPage={"Registros por Página"} 231 | onChangePage={handleChangePage} 232 | onChangeRowsPerPage={handleChangeRowsPerPage} 233 | ActionsComponent={TablePaginationActions} 234 | /> 235 | 236 | 237 |
177 | setSereach(e.target.value)} 182 | 183 | /> 184 |
238 |
239 | ); 240 | } -------------------------------------------------------------------------------- /src/pages/Client/index.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import {TextField,Button,MenuItem} from '@material-ui/core'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | import SaveIcon from '@material-ui/icons/Save'; 5 | import IconButton from '@material-ui/core/IconButton'; 6 | import DeleteIcon from '@material-ui/icons/Delete'; 7 | import GroupAddIcon from '@material-ui/icons/GroupAdd'; 8 | import EdiIcon from '@material-ui/icons/Edit'; 9 | import Header from '~/components/Header'; 10 | import CustomPaginationActionsTable from '~/components/tables'; 11 | 12 | import api from '~/services/api'; 13 | import history from '~/services/history'; 14 | 15 | 16 | 17 | const { ipcRenderer } = window.require("electron"); 18 | 19 | 20 | const useStyles = makeStyles((theme) => ({ 21 | root: { 22 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root': { 23 | marginLeft: theme.spacing(1), 24 | } 25 | }, 26 | 27 | })); 28 | 29 | export default function Cliente() { 30 | const classes = useStyles(); 31 | const initalValues={ 32 | id:null, 33 | name:'', 34 | phone:'', 35 | plan_id:0, 36 | expiration_day:'' 37 | } 38 | const [values, setValues] = useState(initalValues); 39 | const [data, setData] = useState([]); 40 | const [plans, setPlans] = useState([]); 41 | const [formPayment,setFormPayment] = useState([]); 42 | 43 | const [lblButton, setLblButton] = useState('Cadastrar'); 44 | 45 | useEffect(() => { 46 | api.get('client').then(response => setData(response.data)); 47 | 48 | api.get('plans').then(response => setPlans(response.data)); 49 | 50 | api.get('/payment/form').then(response => setFormPayment(response.data)); 51 | 52 | }, []); 53 | 54 | 55 | 56 | const handleChange = (event) => { 57 | setValues({ 58 | ...values, 59 | [event.target.name]: event.target.value, 60 | }); 61 | }; 62 | 63 | 64 | const handleNotification=(title,body)=>{ 65 | const message = { 66 | title: title, 67 | body: body 68 | }; 69 | 70 | ipcRenderer.send("@notification/REQUEST", message); 71 | } 72 | 73 | 74 | function handleSubmit(e){ 75 | e.preventDefault(); 76 | 77 | if(values.id) 78 | handleUpdate(); 79 | else 80 | handleNew(); 81 | 82 | } 83 | 84 | async function handleNew() { 85 | 86 | try { 87 | 88 | values.expiration_day=parseInt(values.expiration_day); 89 | values.plan_id=parseInt(values.plan_id); 90 | 91 | const response= await api.post('client',values) 92 | .catch(err=> alert(`Atenção, ${err.response.data.error}`) 93 | ); 94 | 95 | setData([...response.data,...data]); 96 | setValues(initalValues); 97 | handleNotification("Um novo cliente foi cadastrado",`O cliente ${values.name} foi cadastrado`); 98 | 99 | }catch(err) { 100 | //alert('Erro ao cadastrar o cliente, tente novamente.'); 101 | } 102 | } 103 | 104 | 105 | 106 | 107 | async function handleUpdate() { 108 | 109 | try { 110 | values.expiration_day=parseInt(values.expiration_day); 111 | values.plan_id=parseInt(values.plan_id); 112 | 113 | const response= await api.put('client',values) 114 | .catch(err=> alert(`Atenção, ${err.response.data.error}`) 115 | ); 116 | const _data = data.filter(_data =>_data.id!== parseInt(values.id)); 117 | 118 | setData([...response.data,..._data]); 119 | setValues(initalValues); 120 | handleNotification("Um cliente foi atualizado",`O cliente ${values.name} foi Atulizado`); 121 | setLblButton("Cadastrar"); 122 | } catch (err) { 123 | //alert('Erro ao atulizar o cliente, tente novamente.'); 124 | } 125 | } 126 | 127 | 128 | async function handleDelete(id) { 129 | 130 | var r = window.confirm("Tem certeza que deseja deletar?"); 131 | 132 | if (!r) return; 133 | 134 | try { 135 | await api.delete(`client/${id}`); 136 | setData(data.filter(_data =>_data.id!== parseInt(id))); 137 | handleNotification("Atenção","Registro excluído com sucesso"); 138 | } catch (err) { 139 | alert('Erro ao deletar o cliente, tente novamente.'); 140 | } 141 | 142 | } 143 | 144 | 145 | function handleGetUpdate(id) { 146 | const _data= data.filter(_data =>_data.id== parseInt(id)); 147 | 148 | setValues({...initalValues,..._data[0]}); 149 | 150 | setLblButton("Atualizar"); 151 | } 152 | 153 | 154 | 155 | return ( 156 | <> 157 |
158 |
159 |
160 |
161 |
162 | 169 | 178 | 179 | 188 | - 189 | {plans.map((row)=>( 190 | {row.description} - {row.value} 191 | ))} 192 | 193 | 194 | 203 | - 204 | {formPayment.map((row)=>( 205 | {row.description} 206 | ))} 207 | 208 | 209 | 210 | 218 | 219 | 220 | 230 |
231 |
232 | 237 | 238 | history.push(`/client/dependent/${e.target.closest('tr').getAttribute('data-id')}`)} 243 | > 244 | 245 | 246 | 247 | handleGetUpdate(e.target.closest('tr').getAttribute('data-id'))}> 252 | 253 | 254 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 259 | 260 | 261 | 262 | } 263 | /> 264 |
265 |
266 | 267 | ); 268 | } 269 | -------------------------------------------------------------------------------- /public/apiV1/src/controllers/AttendanceController.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const path = require('path'); 3 | const conn= require(`${path.resolve(__dirname, '../database/conn')}`); 4 | 5 | 6 | async function getAttendanceItems(attendance_id){ 7 | const attendance_items= await conn("services") 8 | .join('attendance_items','attendance_items.service_id', '=','services.id') 9 | .join('employee','employee.id', '=','attendance_items.employee_id') 10 | .where('attendance_id',attendance_id) 11 | .select([ 12 | 'attendance_items.id', 13 | 'services.description', 14 | 'employee.name', 15 | 'attendance_items.date' 16 | ]) 17 | .orderBy('attendance_items.id', 'desc'); 18 | 19 | 20 | 21 | var attendance_itemsObj = attendance_items.map(function(row) { 22 | return {...row,date:moment(row.date).format('DD/MM/YYYY')} 23 | }); 24 | 25 | return attendance_itemsObj; 26 | } 27 | 28 | 29 | 30 | module.exports = { 31 | 32 | async index(resquest,response){ 33 | 34 | const attendance= await conn("attendance") 35 | .join('client','client.id', '=','attendance.client_id') 36 | .select([ 37 | 'attendance.id', 38 | 'name', 39 | 'date' 40 | ]) 41 | .orderBy('name', 'asc'); 42 | 43 | var attendanceObj = attendance.map(function(row) { 44 | return {...row,date:moment(row.date).format('DD/MM/YYYY')} 45 | }); 46 | 47 | return response.json(attendanceObj); 48 | }, 49 | 50 | 51 | async attendanceItems(request,response){ 52 | const { attendance_id } = request.params; 53 | 54 | 55 | const attendance= await conn("attendance") 56 | .join('client','client.id', '=','attendance.client_id') 57 | .where('attendance.id',attendance_id) 58 | .select([ 59 | 'attendance.id', 60 | 'client_id', 61 | 'name', 62 | 'phone', 63 | 'date' 64 | ]).first(); 65 | 66 | const attendance_items= await getAttendanceItems(attendance_id); 67 | 68 | return response.json({ 69 | attendance:attendance, 70 | attendance_items:attendance_items 71 | }); 72 | }, 73 | 74 | 75 | async cerate(resquest,response){ 76 | 77 | const {id,client_id,date,service_id,employee_id} =resquest.body; 78 | 79 | let attendance_id=id; 80 | let error_msg =false; 81 | 82 | if (!client_id) 83 | error_msg='Infome o cliente'; 84 | else if (!date) 85 | error_msg='Infome a data'; 86 | else if (!service_id) 87 | error_msg='Infome o serviço'; 88 | else if (!employee_id) 89 | error_msg='Infome o profissional'; 90 | 91 | if (error_msg) 92 | return response.status(401).json({ error:error_msg}); 93 | 94 | if(!attendance_id){ 95 | const [id] = await conn("attendance").insert({ 96 | client_id, 97 | date 98 | }); 99 | 100 | attendance_id=id; 101 | } 102 | 103 | 104 | 105 | const employee = await conn("employee") 106 | .select([ 107 | "commission", 108 | "rate_card" 109 | ]) 110 | .where('employee.id',employee_id).first(); 111 | 112 | 113 | var PlanItemsCount = conn('plan_items') 114 | .select(conn.raw('SUM(amount)')) 115 | .where('plan_items.plan_id', conn.ref('d.id')) 116 | .as('PlanItemsCount'); 117 | 118 | const infoPlan = await conn({a:"client"}) 119 | .leftJoin({b:'client_dependent'},'b.dependente_id', '=','a.id') 120 | .leftJoin({c:'client_plan'},function() { 121 | this.on('c.client_id', '=','a.id').orOn('c.client_id', '=','b.client_id') 122 | }) 123 | .leftJoin({d:'plans'},'d.id', '=','c.plan_id') 124 | .leftJoin({e:'form_payment'},'e.id', '=','a.form_payment_id') 125 | .where('a.id',client_id) 126 | .select([ 127 | 'd.id', 128 | 'd.value', 129 | 'e.rate', 130 | PlanItemsCount 131 | ]) 132 | .first(); 133 | 134 | let serviceValue =infoPlan.value/infoPlan.PlanItemsCount; 135 | let serviceRate =(serviceValue*(infoPlan.rate/100))*(employee.rate_card/100); 136 | let serviceCommission =serviceValue*(employee.commission/100)-serviceRate 137 | 138 | 139 | 140 | 141 | await conn("attendance_items").insert({ 142 | attendance_id, 143 | service_id, 144 | employee_id, 145 | client_id, 146 | plan_id:infoPlan.id, 147 | value:serviceValue, 148 | rate:serviceRate, 149 | commission:serviceCommission, 150 | date 151 | }); 152 | 153 | 154 | const attendance_items= await getAttendanceItems(attendance_id); 155 | 156 | return response.json({ 157 | id:attendance_id, 158 | attendance:attendance_items 159 | }); 160 | }, 161 | 162 | async delete(request, response) { 163 | const { id } = request.params; 164 | 165 | await conn('attendance').where('id', id).delete(); 166 | await conn('attendance_items').where('attendance_id', id).delete(); 167 | attendance_id 168 | return response.status(204).send(); 169 | }, 170 | 171 | async deleteItem(request, response) { 172 | const { id } = request.params; 173 | 174 | await conn('attendance_items').where('id', id).delete(); 175 | 176 | return response.status(204).send(); 177 | }, 178 | 179 | 180 | async client(request,response){ 181 | const { id } = request.params; 182 | 183 | const client= await conn({a:"client"}) 184 | .leftJoin({b:'client_dependent'},'b.dependente_id', '=','a.id') 185 | .leftJoin({c:'client_plan'},function() { 186 | this.on('c.client_id', '=','a.id').orOn('c.client_id', '=','b.client_id') 187 | }) 188 | .leftJoin({d:'plans'},'d.id', '=','c.plan_id') 189 | .whereNotNull('d.id') 190 | .select([ 191 | 'a.id', 192 | 'a.name', 193 | 'a.phone', 194 | 'c.id as plan_id' 195 | ]) 196 | .groupBy('a.id') 197 | .orderBy('a.name', 'asc'); 198 | 199 | 200 | 201 | return response.json(client); 202 | }, 203 | 204 | 205 | async services_teste(request,response){ 206 | const { client_id } = request.params; 207 | 208 | const services= await await conn({a:"client"}) 209 | .leftJoin({b:'client_dependent'},'b.dependente_id', '=','a.id') 210 | .leftJoin({c:'client_plan'},function() { 211 | this.on('c.client_id', '=','a.id').orOn('c.client_id', '=','b.client_id') 212 | }) 213 | .leftJoin({d:'plan_items'}, 'd.plan_id','=','c.plan_id') 214 | .leftJoin({e:'services'}, 'e.id','=','d.service_id') 215 | .where('a.id', client_id) 216 | .select([ 217 | 'e.id', 218 | 'e.description', 219 | ]) 220 | .orderBy('e.description', 'asc'); 221 | 222 | 223 | 224 | 225 | return response.json(services); 226 | }, 227 | 228 | 229 | 230 | async services(request,response){ 231 | const { client_id,date } = request.params; 232 | 233 | var date_ini=moment(date).startOf('month').format('YYYY-MM-DD'); 234 | var date_end=moment(date).endOf('month').format('YYYY-MM-DD'); 235 | 236 | 237 | 238 | var Amount_used = conn({f:'attendance_items'}) 239 | .count('f.service_id') 240 | .whereBetween('f.date',[date_ini,date_end]) 241 | .andWhere('f.client_id', conn.ref('a.id')) 242 | .andWhere('f.service_id', conn.ref('e.id')) 243 | .as('amount_used'); 244 | 245 | 246 | const services= await await conn({a:"client"}) 247 | .leftJoin({b:'client_dependent'},'b.dependente_id', '=','a.id') 248 | .leftJoin({c:'client_plan'},function() { 249 | this.on('c.client_id', '=','a.id').orOn('c.client_id', '=','b.client_id') 250 | }) 251 | .leftJoin({d:'plan_items'}, 'd.plan_id', '=','c.plan_id') 252 | .leftJoin({e:'services'}, 'e.id', '=','d.service_id') 253 | .where('a.id', client_id) 254 | .groupBy('e.id') 255 | .select([ 256 | 'e.id', 257 | 'e.description', 258 | 'd.amount', 259 | Amount_used 260 | ]) 261 | .orderBy('e.description', 'asc') 262 | 263 | 264 | var servicesObj = services.map(function(row) { 265 | let balance=row.amount-row.amount_used 266 | 267 | const {amount,amount_used, ...obj} = {...row,balance} 268 | 269 | return obj; 270 | }); 271 | 272 | servicesObj=servicesObj.filter((row)=> row.balance>0); 273 | 274 | 275 | 276 | return response.json(servicesObj); 277 | }, 278 | 279 | } -------------------------------------------------------------------------------- /src/pages/Attendance/items.js: -------------------------------------------------------------------------------- 1 | import React,{useState,useEffect} from 'react'; 2 | import {useParams } from "react-router-dom"; 3 | import {TextField,Button } from '@material-ui/core'; 4 | import { makeStyles } from '@material-ui/core/styles'; 5 | import SaveIcon from '@material-ui/icons/Save'; 6 | import ArrowBackIcon from '@material-ui/icons/ArrowBack'; 7 | import IconButton from '@material-ui/core/IconButton'; 8 | import DeleteIcon from '@material-ui/icons/Delete'; 9 | import Autocomplete from '@material-ui/lab/Autocomplete'; 10 | import Header from '~/components/Header'; 11 | import CustomPaginationActionsTable from '~/components/tables'; 12 | 13 | import { 14 | MuiPickersUtilsProvider, 15 | KeyboardDatePicker, 16 | } from '@material-ui/pickers'; 17 | 18 | //import MomentUtils from '@date-io/moment'; 19 | import ptBrLocale from "date-fns/locale/pt-BR"; 20 | import DateFnsUtils from '@date-io/date-fns'; 21 | 22 | import * as moment from 'moment'; 23 | 24 | import api from '~/services/api'; 25 | import history from '~/services/history'; 26 | 27 | const { ipcRenderer } = window.require("electron"); 28 | 29 | 30 | const useStyles = makeStyles((theme) => ({ 31 | root: { 32 | '& .MuiTextField-root +.MuiTextField-root,.MuiButton-root,.MuiAutocomplete-root +.MuiAutocomplete-root': { 33 | marginLeft: theme.spacing(1), 34 | } 35 | }, 36 | 37 | })); 38 | 39 | 40 | 41 | export default function AttendanceItem() { 42 | const classes = useStyles(); 43 | const initalValues={ 44 | id:null, 45 | attendance_number:'', 46 | client_id:'', 47 | client_text:'', 48 | date:moment().format(), 49 | service_id :'', 50 | service_text:'', 51 | employee_id:'' 52 | } 53 | 54 | const [values, setValues] = useState(initalValues); 55 | const [disabled, setDisabled] = useState(false); 56 | const [disabledService, setDisabledService] = useState(true); 57 | const [data, setData] = useState([]); 58 | const [client, setClient] = useState([]); 59 | const [employee, setEmployee] = useState([]); 60 | const [services, setService] = useState([]); 61 | const [servicesClear, setServiceClear] = useState(''); 62 | 63 | const { attendance_id } = useParams(); 64 | 65 | 66 | useEffect(() => { 67 | 68 | 69 | api.get('employee').then(response => { 70 | setEmployee(response.data); 71 | }) 72 | 73 | if(attendance_id) 74 | api.get(`attendance/items/${attendance_id}`).then(response => { 75 | setData(response.data.attendance_items); 76 | servicesClient(response.data.attendance.client_id); 77 | setValues({ 78 | ...values, 79 | ...response.data.attendance, 80 | date:moment(response.data.attendance.date).format(), 81 | client_text:`${response.data.attendance.name} - ${response.data.attendance.phone}`, 82 | attendance_number:` N° ${response.data.attendance.id.toString().padStart(4, "0")}`, 83 | }); 84 | 85 | setDisabled(true); 86 | }) 87 | 88 | else 89 | api.get('attendance/client').then(response => { 90 | setClient(response.data); 91 | }) 92 | 93 | }, []); 94 | 95 | 96 | const handleDateChange = (date) => { 97 | 98 | let _date=moment(date).format('YYYY-MM-DD') 99 | 100 | getServicesClient(values.client_id,_date); 101 | 102 | setValues({...values,date:date}) 103 | }; 104 | 105 | 106 | const handleNotification=(title,body)=>{ 107 | const message = { 108 | title: title, 109 | body: body 110 | }; 111 | 112 | ipcRenderer.send("@notification/REQUEST", message); 113 | } 114 | 115 | 116 | 117 | 118 | async function getServicesClient(client_id,data) { 119 | await api.get(`attendance/services/${client_id}/${data}`).then(response => { 120 | setService(response.data); 121 | setDisabledService(false); 122 | }) 123 | } 124 | 125 | async function servicesClient(client_id) { 126 | let date=moment(values.date).format('YYYY-MM-DD') 127 | 128 | await getServicesClient(client_id,date); 129 | 130 | /*await api.get(`attendance/services/${client_id}`).then(response => { 131 | setService(response.data); 132 | setDisabledService(false); 133 | })*/ 134 | } 135 | 136 | async function handleChangeClient(clientValue) { 137 | setValues({ 138 | ...values, 139 | ...clientValue 140 | }) 141 | 142 | servicesClient(clientValue.client_id); 143 | } 144 | 145 | async function handleSubmit(e) { 146 | e.preventDefault(); 147 | 148 | try { 149 | 150 | values.date=moment(values.date).format('YYYY-MM-DD'); 151 | 152 | 153 | const response= await api.post('attendance',values). 154 | catch(err=> alert(`Atenção, ${err.response.data.error}`) ); 155 | 156 | setValues({ 157 | ...values, 158 | id:response.data.id, 159 | attendance_number:` N° ${response.data.id.toString().padStart(4, "0")}`, 160 | service_id:'', 161 | service_text:'', 162 | date:moment(values.date).format() 163 | }); 164 | 165 | setDisabled(true); 166 | setData(response.data.attendance); 167 | servicesClient(values.client_id); 168 | handleNotification("Um novo registro foi cadastrado",'Registro Cadastrado com sucesso!'); 169 | 170 | }catch (err) { 171 | 172 | } 173 | } 174 | 175 | 176 | 177 | async function handleDelete(id) { 178 | 179 | var r = window.confirm("Tem certeza que deseja deletar?"); 180 | 181 | if (!r) return; 182 | 183 | try { 184 | await api.delete(`attendance/item/${id}`); 185 | 186 | setData(data.filter(ret => ret.id !== parseInt(id) )); 187 | handleNotification("Atenção","Registro excluído com sucesso"); 188 | }catch (err) { 189 | alert('Erro ao deletar o serviço, tente novamente.'); 190 | } 191 | 192 | } 193 | 194 | 195 | 196 | return ( 197 | <> 198 |
199 |
200 |
201 |
202 |
203 | 204 | {values.client_text && 205 | 213 | } 214 | {!values.client_text && 215 | { 222 | if(newValue) 223 | handleChangeClient({ 224 | client_id:newValue.id 225 | }) 226 | }} 227 | 228 | className={'MuiTextField-root'} 229 | options={client} 230 | getOptionLabel={(option) => `${option.name} - ${option.phone}`} 231 | renderInput={(params) => ( 232 | 233 | )} 234 | /> 235 | } 236 | 237 | 238 | 239 | 255 | 256 | 257 | 258 |
259 |

Itens do Atendimento

260 |
261 | 262 | {/*inputValue={values.service_text*/} 263 | { 269 | if(newValue) 270 | setValues({ 271 | ...values, 272 | service_id:newValue.id, 273 | service_text:newValue.description 274 | }) 275 | }} 276 | options={services} 277 | getOptionLabel={(option) => `${option.description}`} 278 | renderInput={(params) => ( 279 | 280 | )} 281 | /> 282 | 283 | 284 | { 289 | if(newValue) 290 | setValues({...values,employee_id:newValue.id}) 291 | }} 292 | options={employee} 293 | getOptionLabel={(option) => `${option.name}`} 294 | renderInput={(params) => ( 295 | 296 | )} 297 | /> 298 | 299 | 308 | 309 | 318 |
319 |
320 | 321 | 326 | handleDelete(e.target.closest('tr').getAttribute('data-id'))}> 330 | 331 | 332 | 333 | } 334 | /> 335 | 336 |
337 |
338 | 339 | ); 340 | } 341 | --------------------------------------------------------------------------------