diff --git a/src/_nav.js b/src/_nav.js index be5a87e..11a0ef5 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -41,4 +41,16 @@ export const navs = [ icon: "list", role: "admin", }, + { + name: "pelanggan", + to: "/customers", + icon: "list", + role: "admin", + }, + { + name: "- pelanggan", + to: "/customers", + icon: "list", + role: "kasir", + }, ] \ No newline at end of file diff --git a/src/components/Common/DatePickerFilter.js b/src/components/Common/DatePickerFilter.js index 5fe5a52..d8630ff 100644 --- a/src/components/Common/DatePickerFilter.js +++ b/src/components/Common/DatePickerFilter.js @@ -25,13 +25,13 @@ export function useDatePickerFilter() { ] } -export function DatePickerFilter({ startDate, endDate, setter : { setStartDate, setEndDate } }) { +export function DatePickerFilter({ startDate, endDate, setter : { setStartDate, setEndDate }, ...restProps }) { const [startDateOpen, setStartDateOpen] = useState(false) const [endDateOpen, setEndDateOpen] = useState(false) return ( - + - + {/* Content */} }> diff --git a/src/layouts/_routeDashboard.js b/src/layouts/_routeDashboard.js index 0333da4..cf2eba0 100644 --- a/src/layouts/_routeDashboard.js +++ b/src/layouts/_routeDashboard.js @@ -3,13 +3,17 @@ const Dashboard = React.lazy(() => import("../views/dashboard")) const User = React.lazy(() => import("../views/users")) const Product = React.lazy(() => import("../views/products")) const Categories = React.lazy(() => import("../views/categories")) +const Sales = React.lazy(() => import("../views/sales")) +const Customers = React.lazy(() => import("../views/customers")) const routes = [ { path: '/', exact: true, name: 'Home' }, { path: "/dashboard", name: "dashboard", component: Dashboard }, + { path: "/customers", name: "pelanggan", component: Customers }, { path: "/products", name: "produk", component: Product }, { path: "/categories", name: "kategori", component: Categories }, { path: "/users", name: "pengguna", component: User }, + { path: "/sales", name: "penjualan", component: Sales }, ] export default routes; \ No newline at end of file diff --git a/src/views/categories/Api.js b/src/views/categories/Api.js index e5d6163..c178bf5 100644 --- a/src/views/categories/Api.js +++ b/src/views/categories/Api.js @@ -1,4 +1,4 @@ -import axios from "axios"; +import axios from "axios" import useSWR from "swr" import qs from "query-string" diff --git a/src/views/customers/Api.js b/src/views/customers/Api.js new file mode 100644 index 0000000..b162eb8 --- /dev/null +++ b/src/views/customers/Api.js @@ -0,0 +1,72 @@ +import axios from "axios" +import useSWR from "swr" +import qs from "query-string" + +export function useCustomers(user, params) { + const { data, error } = useSWR([ + `/customers?${qs.stringify(params)}`, user.accessToken + ]) + + return [ + data, + error + ] +} + +export function createCustomer(payload, token) { + return axios({ + method: 'POST', + url: '/customers', + data: payload, + headers: { + 'Authorization': `Bearer ${token}` + } + }) + .then(res => res.data) + .catch(err => { + throw err.response.data + }) +} + +export function getCustomer(id, token){ + return axios({ + method: 'GET', + url: `/customers/${id}`, + headers: { + 'Authorization': `Bearer ${token}` + } + }) + .then(res => res.data.data) + .catch(err => { + throw err.response.data + }) +} + +export function updateCustomer(id, payload, token) { + return axios({ + method: 'PUT', + url: `/customers/${id}`, + data: payload, + headers: { + 'Authorization': `Bearer ${token}` + } + }) + .then(res => res.data) + .catch(err => { + throw err.response.data + }) +} + +export function deleteCustomer(id, token) { + return axios({ + method: 'DELETE', + url: `/customers/${id}`, + headers: { + 'Authorization': `Bearer ${token}` + } + }) + .then(res => res.data) + .catch(err => { + throw err.response.data + }) +} \ No newline at end of file diff --git a/src/views/customers/Create.js b/src/views/customers/Create.js new file mode 100644 index 0000000..189d1e7 --- /dev/null +++ b/src/views/customers/Create.js @@ -0,0 +1,75 @@ +import { + Button, + Alert, + AlertIcon, + useToast +} from "@chakra-ui/react" +import { createCustomer } from "./Api" +import { useState } from "react" +import { Breadcrumb, Card, FormInput } from "../../components/Common" +import { useAuth } from "../../context/AppContext" + +export default function Create({ history }) { + const { user } = useAuth() + + const toast = useToast() + + const [name, setName] = useState('') + const [phone, setPhone] = useState('') + const [address, setAddress] = useState('') + const [description, setDescription] = useState('') + const [submit, setSubmit] = useState(false) + const [error, setError] = useState(null) + + const handleSubmit = () => { + setSubmit(true) + createCustomer({ + name, + phone, + address, + description + }, user.accessToken) + .then(res => { + toast({ + title: res.status, + description: "item ditambahkan", + status: "success", + duration: 4000, + isClosable: true, + position: "top-right" + }) + history.push('/customers') + }) + .catch(err => { + setError(err.message) + }) + .finally(() => { + setSubmit(false) + }) + } + + return ( + <> + + + {error !== null && ( + + + {error} + + )} + + + + + + + + ) +} \ No newline at end of file diff --git a/src/views/customers/Edit.js b/src/views/customers/Edit.js new file mode 100644 index 0000000..2417581 --- /dev/null +++ b/src/views/customers/Edit.js @@ -0,0 +1,100 @@ +import { + Button, + Alert, + AlertIcon, + useToast +} from "@chakra-ui/react" +import { getCustomer, updateCustomer } from "./Api" +import { useState } from "react" +import { Breadcrumb, Card, FormInput, Loading } from "../../components/Common" +import { useAuth } from "../../context/AppContext" +import { useEffect } from "react" + +export default function Edit(props) { + const id = props.match.params.id + const { user } = useAuth() + + const toast = useToast() + + const [name, setName] = useState('') + const [phone, setPhone] = useState('') + const [address, setAddress] = useState('') + const [description, setDescription] = useState('') + + const [loading, setLoading] = useState(false) + const [submit, setSubmit] = useState(false) + const [error, setError] = useState(null) + + const handleSubmit = () => { + setSubmit(true) + updateCustomer(id, { + name, + phone, + address, + description, + }, user.accessToken) + .then(res => { + toast({ + title: res.status, + description: "item diubah", + status: "success", + duration: 4000, + isClosable: true, + position: "top-right" + }) + props.history.push('/customers') + }) + .catch(err => { + setError(err.message) + }) + .finally(() => { + setSubmit(false) + }) + } + + useEffect(() => { + const { accessToken: token } = user + setLoading(true) + getCustomer(id, token) + .then(res => { + setName(res.customer.name) + setPhone(res.customer.phone) + setAddress(res.customer.address) + setDescription(res.customer.description) + }) + .catch(err => setError(err.message)) + .finally(() => setLoading(false)) + return () => {} + }, [id, user]) + + return ( + <> + + + {error !== null && ( + + + {error} + + )} + {loading ? ( + + ) : ( + <> + + + + + + + )} + + + ) +} \ No newline at end of file diff --git a/src/views/customers/List.js b/src/views/customers/List.js new file mode 100644 index 0000000..8aea139 --- /dev/null +++ b/src/views/customers/List.js @@ -0,0 +1,140 @@ +import { useState } from "react" +import { + Button, + Alert, + AlertIcon, + Table, + Thead, + Tr, + Td, + Th, + Tbody, + Menu, + MenuItem, + MenuButton, + MenuList, + useToast, +} from "@chakra-ui/react" +import { Link } from "react-router-dom" +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +import { mutate } from 'swr' +import qs from "query-string" +import { + Breadcrumb, + Card, + Loading, + Pagination, + useDebounce, + AlertDialog, + SearchInput, + useModalState, +} from "../../components/Common" +import { useCustomers, deleteCustomer } from "./Api" +import { useAuth } from "../../context/AppContext" + +export default function List() { + const { user } = useAuth() + const toast = useToast() + + const [page, setPage] = useState(1) + const [search, setSearch] = useState('') + const q = useDebounce(search, 600) + const params = { page, q } + const [data, error] = useCustomers(user, params) + + const [isOpen, toggle, selected] = useModalState(false) + + const handleDelete = async () => { + await deleteCustomer(selected.id, user.accessToken) + .then((res) => { + toast({ + title: res.status, + description: "item dihapus", + status: "success", + position: "top-right", + duration: 4000, + isClosable: true + }) + mutate([`/customers?${qs.stringify(params)}`, user.accessToken]) + }) + .catch((err) => { + toast({ + title: err.status, + description: err.message, + status: "error", + position: "top-right", + duration: 4000, + isClosable: true + }) + }) + } + + if(error) { + return ( + + + {error.message} + + ) + } + + return ( + <> + + + + + {data ? ( + <> + + + + + + + + + + + {data.customers.map((customer) => ( + + + + + + + ))} + +
namano. hpketerangan{''}
{ customer.name }{ customer.phone }{ customer.description } + + + + + + ubah + { + toggle(customer) + }} + > + hapus + + + +
+ + + ) : ( + + )} +
+ toggle()} + /> + + ) +} \ No newline at end of file diff --git a/src/views/customers/Modal.js b/src/views/customers/Modal.js new file mode 100644 index 0000000..2eab918 --- /dev/null +++ b/src/views/customers/Modal.js @@ -0,0 +1,72 @@ +import { useState } from "react" +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalBody, + ModalCloseButton, + Table, + Thead, + Tr,Th,Td, + Tbody, + Alert, + AlertIcon +} from "@chakra-ui/react" +import { Loading, Pagination, SearchInput, useDebounce } from "../../components/Common" +import { useAuth } from "../../context/AppContext" +import { useCategories } from "./Api" + +export default function ModalCom(props) { + const { isOpen, toggle, onClose } = props + const { user } = useAuth() + + const [page, setPage] = useState(1) + const [search, setSearch] = useState('') + const q = useDebounce(search, 600) + + const [data, error] = useCategories(user, { page, q }) + + return ( + + + + kategori + + + + {error && ( + + + {error.message} + + )} + {data ? ( + <> + + + + + + + + {data.categories.map((category) => ( + { + onClose(category) + toggle() + }} _hover={{bg: "gray.200"}}> + + + ))} + +
nama
{ category.name }
+ + + ) : ( + + )} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/views/customers/index.js b/src/views/customers/index.js new file mode 100644 index 0000000..28613d5 --- /dev/null +++ b/src/views/customers/index.js @@ -0,0 +1,18 @@ +import { Switch, Route } from 'react-router-dom' + +import Create from './Create' +import Edit from './Edit' +import List from './List' + + +function routes(props) { + return ( + + + + + + ) +} + +export default routes diff --git a/src/views/products/Api.js b/src/views/products/Api.js index 81f8e80..896b7b5 100644 --- a/src/views/products/Api.js +++ b/src/views/products/Api.js @@ -1,4 +1,4 @@ -import axios from "axios"; +import axios from "axios" import useSWR from "swr" import qs from "query-string" diff --git a/src/views/products/Edit.js b/src/views/products/Edit.js index 88d3dc5..1316ed4 100644 --- a/src/views/products/Edit.js +++ b/src/views/products/Edit.js @@ -36,7 +36,7 @@ export default function Edit(props) { setSubmit(true) updateProduct(id, { name, - description, + description: description ? description : '', cost, price, stock, diff --git a/src/views/products/List.js b/src/views/products/List.js index deadf22..6517627 100644 --- a/src/views/products/List.js +++ b/src/views/products/List.js @@ -114,6 +114,8 @@ export default function List({ history }) { harga beli harga jual stok + total jual + total beli deskripsi @@ -126,6 +128,8 @@ export default function List({ history }) { { formatIDR(product.cost) } { formatIDR(product.price) } { formatIDR(product.stock) } + { formatIDR(product.sale) } + { formatIDR(product.purchase) } { product.description } diff --git a/src/views/sales/Api.js b/src/views/sales/Api.js index ca419df..d4c028a 100644 --- a/src/views/sales/Api.js +++ b/src/views/sales/Api.js @@ -1,3 +1,4 @@ +import axios from "axios" import useSWR from "swr" import qs from "query-string" @@ -10,4 +11,33 @@ export function useSales(user, params) { data, error ] +} + +export function createSale(payload, token) { + return axios({ + method: 'POST', + url: '/sales', + data: payload, + headers: { + 'Authorization': `Bearer ${token}` + } + }) + .then(res => res.data) + .catch(err => { + throw err.response.data + }) +} + +export function getSale(id, token){ + return axios({ + method: 'GET', + url: `/sales/${id}`, + headers: { + 'Authorization': `Bearer ${token}` + } + }) + .then(res => res.data.data) + .catch(err => { + throw err.response.data + }) } \ No newline at end of file diff --git a/src/views/sales/List.js b/src/views/sales/List.js index 9223f7a..cac37ce 100644 --- a/src/views/sales/List.js +++ b/src/views/sales/List.js @@ -9,65 +9,36 @@ import { Td, Th, Tbody, - Menu, - MenuItem, - MenuButton, - MenuList, - useToast, } from "@chakra-ui/react" import { Link } from "react-router-dom" -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import { mutate } from 'swr' -import qs from "query-string" import { Breadcrumb, Card, Loading, Pagination, - useDebounce, - AlertDialog, + DatePickerFilter, SearchInput, - useModalState, + useDebounce, + useDatePickerFilter, } from "../../components/Common" -import { useUsers, deleteUser } from "./Api" +import { useSales } from "./Api" import { useAuth } from "../../context/AppContext" +import { formatDate, formatIDR } from "../../utils" export default function List() { const { user } = useAuth() - const toast = useToast() const [page, setPage] = useState(1) const [search, setSearch] = useState('') const q = useDebounce(search, 600) - const params = { page, q } - const [data, error] = useUsers(user, params) - - const [isOpen, toggle, selected] = useModalState(false) - - const handleDelete = async () => { - await deleteUser(selected.id, user.accessToken) - .then((res) => { - toast({ - title: res.status, - description: "item dihapus", - status: "success", - position: "top-right", - duration: 4000, - isClosable: true - }) - mutate([`/users?${qs.stringify(params)}`, user.accessToken]) - }) - .catch((err) => { - toast({ - title: err.status, - description: err.message, - status: "error", - position: "top-right", - duration: 4000, - isClosable: true - }) - }) + const [startDate, endDate, setter] = useDatePickerFilter() + const params = { + page, + q, + startDate: formatDate(startDate), + endDate: formatDate(endDate) } + const [data, error] = useSales(user, params) if(error) { return ( @@ -80,45 +51,40 @@ export default function List() { return ( <> - + - + {data ? ( <> - - - + + + + - {data.users.map((auser) => auser.id !== user.id && ( - - - - + {data.sales.map((sale) => ( + + + + + ))} @@ -130,11 +96,6 @@ export default function List() { )} - toggle()} - /> ) } \ No newline at end of file diff --git a/src/views/users/Api.js b/src/views/users/Api.js index 4810cf0..edf778e 100644 --- a/src/views/users/Api.js +++ b/src/views/users/Api.js @@ -1,4 +1,4 @@ -import axios from "axios"; +import axios from "axios" import useSWR from "swr" import qs from "query-string"
namaemailroleinvoicetanggaltotalkasir
{auser.name}{auser.email}{auser.role}
{sale.invoice}{formatDate(new Date(sale.date))}{formatIDR(sale.amount)}{sale.casier} - - - - - - ubah - { - toggle(auser) - }} - > - hapus - - - +