add customer, list sales

dev
Aji Kamaludin 3 years ago
parent 5be138ae63
commit c6d532b1aa
No known key found for this signature in database
GPG Key ID: 670E1F26AD5A8099

@ -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",
},
]

@ -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 (
<Flex direction={{base: "column", md: "row"}} mt="5" mb="1" gridGap="2">
<Flex direction={{base: "column", md: "row"}} mt="5" mb="1" gridGap="2" {...restProps}>
<Box flexDirection="column" flex="1">
<Popover
isOpen={startDateOpen}

@ -55,7 +55,7 @@ export default function DashboardLayout(props) {
onLogout={handleLogout}
user={user}
/>
<Container maxW={{base: "90rem", xl: "110rem"}} py="10" bg="gray.100" minH={{ base: "38rem", sm: "40rem", xl: "53.5rem"}}>
<Container maxW={{base: "80rem", xl: "110rem"}} py="10" bg="gray.100" minH={{ base: "38rem", sm: "40rem", xl: "53.5rem"}}>
{/* Content */}
<Suspense fallback={<Loading/>}>
<Switch>

@ -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;

@ -1,4 +1,4 @@
import axios from "axios";
import axios from "axios"
import useSWR from "swr"
import qs from "query-string"

@ -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
})
}

@ -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 (
<>
<Breadcrumb main={["/customers", "pelanggan", "baru"]}/>
<Card>
{error !== null && (
<Alert status="error" mb="5" rounded="md">
<AlertIcon />
{error}
</Alert>
)}
<FormInput data={['nama', name, setName]}/>
<FormInput data={['no.hp', phone, setPhone]}/>
<FormInput data={['alamat', address, setAddress]}/>
<FormInput data={['keterangan', description, setDescription]}/>
<Button
mt="4"
isLoading={submit}
onClick={() => handleSubmit()}
>
simpan
</Button>
</Card>
</>
)
}

@ -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 (
<>
<Breadcrumb main={["/customers", "pelanggan", "ubah"]}/>
<Card>
{error !== null && (
<Alert status="error" mb="5" rounded="md">
<AlertIcon />
{error}
</Alert>
)}
{loading ? (
<Loading/>
) : (
<>
<FormInput data={['nama', name, setName]}/>
<FormInput data={['no.hp', phone, setPhone]}/>
<FormInput data={['alamat', address, setAddress]}/>
<FormInput data={['keterangan', description, setDescription]}/>
<Button
mt="4"
isLoading={submit}
onClick={() => handleSubmit()}
>
simpan
</Button>
</>
)}
</Card>
</>
)
}

@ -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 (
<Alert status="error">
<AlertIcon />
{error.message}
</Alert>
)
}
return (
<>
<Breadcrumb main={["/customers", "pelanggan"]}/>
<Card>
<Button as={Link} to="/customers/create" size="md" mb="3">
tambah
</Button>
<SearchInput setter={[search, setSearch]}/>
{data ? (
<>
<Table variant="simple" mt="2" mb="4">
<Thead>
<Tr>
<Th>nama</Th>
<Th>no. hp</Th>
<Th>keterangan</Th>
<Th isNumeric>{''}</Th>
</Tr>
</Thead>
<Tbody>
{data.customers.map((customer) => (
<Tr key={customer.id}>
<Td>{ customer.name }</Td>
<Td>{ customer.phone }</Td>
<Td>{ customer.description }</Td>
<Td isNumeric>
<Menu>
<MenuButton as={Button}>
<FontAwesomeIcon icon="ellipsis-v"/>
</MenuButton>
<MenuList>
<MenuItem as={Link} to={`/customers/${customer.id}/edit`}>ubah</MenuItem>
<MenuItem
onClick={() => {
toggle(customer)
}}
>
hapus
</MenuItem>
</MenuList>
</Menu>
</Td>
</Tr>
))}
</Tbody>
</Table>
<Pagination page={page} setPage={setPage} totalPages={data.meta.totalPages}/>
</>
) : (
<Loading/>
)}
</Card>
<AlertDialog
isOpen={isOpen}
onClose={handleDelete}
toggle={() => toggle()}
/>
</>
)
}

@ -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 (
<Modal isOpen={isOpen} onClose={toggle}>
<ModalOverlay />
<ModalContent minW="40rem">
<ModalHeader>kategori</ModalHeader>
<ModalCloseButton />
<ModalBody>
<SearchInput setter={[search, setSearch]}/>
{error && (
<Alert status="error">
<AlertIcon />
{error.message}
</Alert>
)}
{data ? (
<>
<Table variant="simple" mt="2" mb="4">
<Thead>
<Tr>
<Th>nama</Th>
</Tr>
</Thead>
<Tbody>
{data.categories.map((category) => (
<Tr key={category.id} onClick={() => {
onClose(category)
toggle()
}} _hover={{bg: "gray.200"}}>
<Td>{ category.name }</Td>
</Tr>
))}
</Tbody>
</Table>
<Pagination page={page} setPage={setPage} totalPages={data.meta.totalPages} mb="4"/>
</>
) : (
<Loading/>
)}
</ModalBody>
</ModalContent>
</Modal>
)
}

@ -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 (
<Switch>
<Route path="/customers/:id/edit" exect component={Edit} />
<Route path="/customers/create" exact component={Create} />
<Route path="/customers" exact component={List} />
</Switch>
)
}
export default routes

@ -1,4 +1,4 @@
import axios from "axios";
import axios from "axios"
import useSWR from "swr"
import qs from "query-string"

@ -36,7 +36,7 @@ export default function Edit(props) {
setSubmit(true)
updateProduct(id, {
name,
description,
description: description ? description : '',
cost,
price,
stock,

@ -114,6 +114,8 @@ export default function List({ history }) {
<Th isNumeric>harga beli</Th>
<Th isNumeric>harga jual</Th>
<Th isNumeric>stok</Th>
<Th isNumeric>total jual</Th>
<Th isNumeric>total beli</Th>
<Th>deskripsi</Th>
<Th></Th>
</Tr>
@ -126,6 +128,8 @@ export default function List({ history }) {
<Td isNumeric>{ formatIDR(product.cost) }</Td>
<Td isNumeric>{ formatIDR(product.price) }</Td>
<Td isNumeric>{ formatIDR(product.stock) }</Td>
<Td isNumeric>{ formatIDR(product.sale) }</Td>
<Td isNumeric>{ formatIDR(product.purchase) }</Td>
<Td>{ product.description }</Td>
<Td isNumeric>
<Menu>

@ -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
})
}

@ -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 (
<>
<Breadcrumb main={["/users", "pengguna"]}/>
<Breadcrumb main={["/sales", "penjualan"]}/>
<Card>
<Button as={Link} to="/users/create" size="md" mb="3">
<Button as={Link} to="/sales/create" size="md" mb="3">
tambah
</Button>
<SearchInput setter={[search, setSearch]}/>
<DatePickerFilter
mx="3"
mt="2"
startDate={startDate}
endDate={endDate}
setter={setter}
/>
{data ? (
<>
<Table variant="simple" mt="2" mb="4">
<Thead>
<Tr>
<Th>nama</Th>
<Th>email</Th>
<Th>role</Th>
<Th>invoice</Th>
<Th isNumeric>tanggal</Th>
<Th isNumeric>total</Th>
<Th>kasir</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{data.users.map((auser) => auser.id !== user.id && (
<Tr key={auser.id}>
<Td>{auser.name}</Td>
<Td>{auser.email}</Td>
<Td>{auser.role}</Td>
{data.sales.map((sale) => (
<Tr key={sale.id}>
<Td>{sale.invoice}</Td>
<Td isNumeric>{formatDate(new Date(sale.date))}</Td>
<Td isNumeric>{formatIDR(sale.amount)}</Td>
<Td>{sale.casier}</Td>
<Td isNumeric>
<Menu>
<MenuButton as={Button}>
<FontAwesomeIcon icon="ellipsis-v"/>
</MenuButton>
<MenuList>
<MenuItem as={Link} to={`/users/${auser.id}/edit`}>ubah</MenuItem>
<MenuItem
onClick={() => {
toggle(auser)
}}
>
hapus
</MenuItem>
</MenuList>
</Menu>
<Button as={Link} to={`/users/${sale.id}/detail`}>detail</Button>
</Td>
</Tr>
))}
@ -130,11 +96,6 @@ export default function List() {
<Loading/>
)}
</Card>
<AlertDialog
isOpen={isOpen}
onClose={handleDelete}
toggle={() => toggle()}
/>
</>
)
}

@ -1,4 +1,4 @@
import axios from "axios";
import axios from "axios"
import useSWR from "swr"
import qs from "query-string"

Loading…
Cancel
Save