diff --git a/src/components/CardMesinRusak/CardMesinRusak.js b/src/components/CardMesinRusak/CardMesinRusak.js index dd16a6999af5e724e75d7068b35625d38f55ddb8..59eafee8c39448fe7b7f63f0fa70d8cd7c67d6a0 100644 --- a/src/components/CardMesinRusak/CardMesinRusak.js +++ b/src/components/CardMesinRusak/CardMesinRusak.js @@ -2,6 +2,8 @@ import './CardMesinRusak.css' import Card from 'react-bootstrap/Card' import {Row} from 'react-bootstrap' import axios from 'axios' +import PopUpFeedback from '../PopUpFeedback/PopUpFeedback'; +import { useEffect, useState } from "react"; export default function CardMesinRusak ({ @@ -15,12 +17,31 @@ export default function CardMesinRusak ({ status, tanggal, }) { + const [feedback, setFeedback] = useState({timestamp: '', deskripsi:''}); const config = { headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `JWT ${localStorage.getItem('access')}`, } }; + const fetchFeedback = async (id) => { + if (localStorage.getItem('access')) { + try { + var feedbackObj = await axios.get(`${process.env.REACT_APP_BACKEND_API_URL}/api/feedback/${id}`, config); + setFeedback(feedbackObj.data); + } catch (err) { + console.log(err); + alert("Terdapat kesalahan saat fetch data feedback") + } + } else { + alert('Terdapat kesalahan pada autentikasi akun anda. Anda dapat melakukan refresh pada halaman ini') + } + } + + useEffect(() => { + fetchFeedback(idLaporan); + }, []); + const deleteLaporan = (pk) => { if (localStorage.getItem('access')) { axios.delete(`${process.env.REACT_APP_BACKEND_API_URL}/api/laporan/${pk}/delete/`, config) @@ -52,6 +73,10 @@ export default function CardMesinRusak ({ <span>Tanggal Pelaporan: {tanggal}</span><br></br> <span>Operator: {operator}</span></p> <p className="card-content-limit card-daftartoko-text">{deskripsi}</p> + {(feedback.length > 0) ? + <div><PopUpFeedback feedback={feedback}></PopUpFeedback></div> + :<div><button className="ml-2 mesin-button disable" disabled data-testid="detail-btn">Feedback</button><br /></div>} + <br /> <Row className='justify-content-center'> <a onClick={() => deleteLaporan(idLaporan)}> <button data-testid='hapus' disabled={status!=="NAS"} className={`mesin-button ${status!=="NAS" ? 'disable':'mesin-blue-border-button'}`} > diff --git a/src/components/CardMesinRusak/CardMesinRusak.test.js b/src/components/CardMesinRusak/CardMesinRusak.test.js index 4cea91e3e5cc72f057d54ec0496f94cd480e8631..4952f5b6b89fa59894a05695f24f29e12b2c572d 100644 --- a/src/components/CardMesinRusak/CardMesinRusak.test.js +++ b/src/components/CardMesinRusak/CardMesinRusak.test.js @@ -24,6 +24,15 @@ describe('<CardMesinRusak />', () => { "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", }, ] + const feedbackData = [ + { + pk:1, + laporan: 1, + operator: "operator@gmail.com", + deskripsi: "Ini adalah feedback", + timestamp: "2022-04-02T16:59:00.240219+07:00" + } + ] const initialState = { auth: { isAuthenticated: true, @@ -44,12 +53,13 @@ describe('<CardMesinRusak />', () => { var mock = new MockAdapter(axios); + mock.onGet(`${process.env.REACT_APP_BACKEND_API_URL}/api/feedback/1`, config).reply(200, feedbackData); mock.onDelete(`${process.env.REACT_APP_BACKEND_API_URL}/api/laporan/1/delete/`, config).reply(200); render( <Provider store={store}> <BrowserRouter> - <CardMesinRusak post={mockCurrentPosts} deleteLaporan={deleteLaporan} /> + <CardMesinRusak post={mockCurrentPosts} idLaporan={1} deleteLaporan={deleteLaporan} status={"NAS"}/> </BrowserRouter> </Provider>); @@ -68,6 +78,15 @@ describe('<CardMesinRusak />', () => { "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", } ] + const feedbackData = [ + { + pk:1, + laporan: 1, + operator: "operator@gmail.com", + deskripsi: "Ini adalah feedback", + timestamp: "2022-04-02T16:59:00.240219+07:00" + } + ] const initialState = { auth: { isAuthenticated: true, @@ -87,12 +106,13 @@ describe('<CardMesinRusak />', () => { var mock = new MockAdapter(axios); + mock.onGet(`${process.env.REACT_APP_BACKEND_API_URL}/api/feedback/1`, config).reply(200, feedbackData); mock.onDelete(`${process.env.REACT_APP_BACKEND_API_URL}/api/laporan/1/delete/`, config).reply(200); render( <Provider store={store}> <BrowserRouter> - <CardMesinRusak post={mockCurrentPosts} deleteLaporan={deleteLaporan} /> + <CardMesinRusak post={mockCurrentPosts} idLaporan={1} deleteLaporan={deleteLaporan} status={"ASG"} /> </BrowserRouter> </Provider>); @@ -101,4 +121,53 @@ describe('<CardMesinRusak />', () => { fireEvent.click(button); }); -}) \ No newline at end of file + + it('cannot get when not authorize', () => { + const mockCurrentPosts = [ + { + "pk": 1, + "id": 1, + "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + } + ] + const feedbackData = [ + { + pk:1, + laporan: 1, + operator: "operator@gmail.com", + deskripsi: "Ini adalah feedback", + timestamp: "2022-04-02T16:59:00.240219+07:00" + } + ] + const initialState = { + auth: { + isAuthenticated: true, + user: { + role: "Mitra" + } + } + } + localStorage.setItem('access', 'token') + const store = mockStore(initialState) + const deleteLaporan = jest.fn() + const config = { + headers: { + 'Authorization': false, + } + }; + + var mock = new MockAdapter(axios); + + mock.onGet(`${process.env.REACT_APP_BACKEND_API_URL}/api/feedback/1`, config).reply(200, feedbackData); + + localStorage.removeItem('access', 'token'); + render( + <Provider store={store}> + <BrowserRouter> + <CardMesinRusak post={mockCurrentPosts} idLaporan={1} deleteLaporan={deleteLaporan} status={"RSV"}/> + </BrowserRouter> + </Provider>); + + }); +}) diff --git a/src/components/CardMesinRusak/DeleteLaporan.test.js b/src/components/CardMesinRusak/DeleteLaporan.test.js index bee58f58e3281d3eafa0ab613e8c448da0c5a750..61fd55f107fd8dce06058c1d7aeb588dbf79dc63 100644 --- a/src/components/CardMesinRusak/DeleteLaporan.test.js +++ b/src/components/CardMesinRusak/DeleteLaporan.test.js @@ -13,49 +13,116 @@ const middlewares = [thunk] const mockStore = configureStore(middlewares) describe('<CardMesinRusak />', () => { -it('test delete laporan success', () => { - const initialState = { - auth: { - isAuthenticated: true, - user: { - role: "Mitra" + it('test delete laporan success', () => { + const initialState = { + auth: { + isAuthenticated: true, + user: { + role: "Mitra" + } } } - } - localStorage.setItem('access', 'token') - const store = mockStore(initialState) - const deleteLaporan = jest.fn() - - const laporanData = [ - { - pk: 1, - mainanPengadaan: { - id: 23, - mainan: { - id: 1, - namaMainan: "Kiddie Ride Submarine", - deskripsiMainan: "A SpongeBob theme kid's ride in the shape of a submarine. Available colors: Red, Green, Yellow.", - harga: 11000000.0, - gambarMainan: "http://walkiddie.cs.ui.ac.id/media/mainan/media/pngfind-2.png" + localStorage.setItem('access', 'token') + const store = mockStore(initialState) + const deleteLaporan = jest.fn() + + const laporanData = [ + { + pk: 1, + mainanPengadaan: { + id: 23, + mainan: { + id: 1, + namaMainan: "Kiddie Ride Submarine", + deskripsiMainan: "A SpongeBob theme kid's ride in the shape of a submarine. Available colors: Red, Green, Yellow.", + harga: 11000000.0, + gambarMainan: "http://walkiddie.cs.ui.ac.id/media/mainan/media/pngfind-2.png" + }, + stringMainan: "Kiddie Ride Submarine_23", + status: "RSK", }, - stringMainan: "Kiddie Ride Submarine_23", - status: "RSK", - }, + } + ] + + const feedbackData = [ + { + pk:1, + laporan: 1, + operator: "operator@gmail.com", + deskripsi: "Ini adalah feedback", + timestamp: "2022-04-02T16:59:00.240219+07:00" + } + ] + axios.get.mockImplementationOnce(() => Promise.resolve(feedbackData)); + render( + <Provider store={store}> + <BrowserRouter> + <CardMesinRusak post={laporanData} deleteLaporan={deleteLaporan} /> + </BrowserRouter> + </Provider>); + + + axios.delete.mockImplementationOnce(() => Promise.resolve(laporanData)); + const button = screen.getByTestId('hapus'); + fireEvent.click(button); + expect(axios.delete).toHaveBeenCalledTimes(1); + localStorage.removeItem('access', 'token') + }); + + it('test delete laporan failed', () => { + const initialState = { + auth: { + isAuthenticated: true, + user: { + role: "Mitra" + } + } } - ] - - render( - <Provider store={store}> - <BrowserRouter> - <CardMesinRusak post={laporanData} deleteLaporan={deleteLaporan} /> - </BrowserRouter> - </Provider>); - - axios.delete.mockImplementationOnce(() => Promise.resolve(laporanData)); - const button = screen.getByTestId('hapus'); - fireEvent.click(button); - expect(axios.delete).toHaveBeenCalledTimes(1); - localStorage.removeItem('access', 'token') -}); - -}) \ No newline at end of file + localStorage.setItem('access', 'token') + const store = mockStore(initialState) + const deleteLaporan = jest.fn() + + const laporanData = [ + { + pk: 1, + mainanPengadaan: { + id: 23, + mainan: { + id: 1, + namaMainan: "Kiddie Ride Submarine", + deskripsiMainan: "A SpongeBob theme kid's ride in the shape of a submarine. Available colors: Red, Green, Yellow.", + harga: 11000000.0, + gambarMainan: "http://walkiddie.cs.ui.ac.id/media/mainan/media/pngfind-2.png" + }, + stringMainan: "Kiddie Ride Submarine_23", + status: "RSK", + }, + } + ] + + const feedbackData = [ + { + pk:1, + laporan: 1, + operator: "operator@gmail.com", + deskripsi: "Ini adalah feedback", + timestamp: "2022-04-02T16:59:00.240219+07:00" + } + ] + axios.get.mockImplementationOnce(() => Promise.resolve(feedbackData)); + render( + <Provider store={store}> + <BrowserRouter> + <CardMesinRusak post={laporanData} deleteLaporan={deleteLaporan} /> + </BrowserRouter> + </Provider>); + + const err = new Error('Wrong inputs passed in'); + axios.delete.mockRejectedValueOnce(err); + const button = screen.getByTestId('hapus'); + fireEvent.click(button); + expect(axios.delete).toHaveBeenCalledTimes(1); + localStorage.removeItem('access', 'token') + }); + +}) diff --git a/src/components/CardsToken/CardsToken.js b/src/components/CardsToken/CardsToken.js index ed9b11ee6e735a1c9e0880df843db6409fd9a531..50543df2e68ac55cfde7edd20c5f8d7f7e7adee9 100644 --- a/src/components/CardsToken/CardsToken.js +++ b/src/components/CardsToken/CardsToken.js @@ -28,7 +28,6 @@ const CardsToken = ({ posts, loading, role }) => { } } - console.log(pks); let empty_message = [] if (role == "Mitra"){ diff --git a/src/components/CardsToken/CardsToken.test.js b/src/components/CardsToken/CardsToken.test.js index fd51d31532c84aacd38e21d4cabe9944e3a8b993..d0428f7815e0d4db134685bc9fd953cfad805932 100644 --- a/src/components/CardsToken/CardsToken.test.js +++ b/src/components/CardsToken/CardsToken.test.js @@ -203,7 +203,7 @@ describe('<CardsToken />', () => { "pengadaan": { "pk": 1, "files": [ - "" + "https://storage.googleapis.com/walkiddie-django-bucket/toko/pengadaan/metromart.jpg?GoogleAccessId=walkiddie-django-bucket%40my-project-52056-324503.iam.gserviceaccount.com&Expires=1648897794&Signature=LdROb1tFL4uaZ2uP8GY20kF4P8MgPQLfaz%2Bx3a44Ul29gU6MXtD8%2Fl9a6Hm0NyZpYaMl47u2vRYKo9CIZx1TLr91dFpD5gHJOIcqJnNGA5Lc%2FwhxWROt2RQ%2BV3aZ%2FPk1dd61ex3CibljVhxOjmuXXndy15F2s4pHT7DJZszS0plQQBTVU9LIP1eoFOTJdikN4pvXxUkv8oN8LYbuISZ%2FW7l2EXBi%2FdvrjMCiEll0ARR6n2ioqabDEIsHRs4KB6yrMxqDITqn12xEoqwev0jSk4vgB%2BdHv1mo0W8JVpaPTIprUJL96Vvl02z83WIgZWgJEbyBjCAFy4paNmMM%2BgCLBQ%3D%3D" ], "toko": { "pk": 1, diff --git a/src/components/Pagination/Pagination.js b/src/components/Pagination/Pagination.js index 5f433b6f58b621cddf53770550357572f94fcc03..39cbf7367eb4b6c61fcb8f6b0c72afda96d29003 100644 --- a/src/components/Pagination/Pagination.js +++ b/src/components/Pagination/Pagination.js @@ -56,7 +56,11 @@ const Pagination = ({ currentPage, postsPerPage, totalPosts, paginate }) => { }); return ( - <nav> + <nav + style={{ + marginTop: '40px' + }} + > {pages.length !== 0 && <ul className='pageNumbers' > diff --git a/src/components/PopUpFeedback/PopUpFeedback.css b/src/components/PopUpFeedback/PopUpFeedback.css new file mode 100644 index 0000000000000000000000000000000000000000..19140bfaf3955a0d62578a6d52f12d476d0c4004 --- /dev/null +++ b/src/components/PopUpFeedback/PopUpFeedback.css @@ -0,0 +1,3 @@ +.pof-gray { + color: rgb(129, 129, 129); +} diff --git a/src/components/PopUpFeedback/PopUpFeedback.js b/src/components/PopUpFeedback/PopUpFeedback.js new file mode 100644 index 0000000000000000000000000000000000000000..9b6cab98a1bce614508160b65b796b0738b77cac --- /dev/null +++ b/src/components/PopUpFeedback/PopUpFeedback.js @@ -0,0 +1,41 @@ +import { useState } from "react"; +import { Modal } from 'react-bootstrap'; +import React from 'react'; +import moment from 'moment'; +import './PopUpFeedback.css'; +import TextArea from "antd/lib/input/TextArea"; + +const PopUpFeedback = ({feedback}) => { + const [showModal, setShowModal] = useState(false); + const handleClose = () => setShowModal(false); + const handleOpen = () => { + setShowModal(true); + } + + var feedback_reversed = [...feedback].reverse(); + + return ( + <div> + <button className="ml-2 mesin-button wkd-tosca-button" onClick={handleOpen} data-testid="detail-btn">Feedback</button> + <Modal show={showModal} onHide={handleClose} data-testid="popup" centered> + <Modal.Header closeButton> + <p>Feedback <span id="mainan-red">Laporan</span></p> + </Modal.Header> + <Modal.Body> + <div className="justify-content-center mt-n5 pt-3 pb-4"> + {feedback_reversed.map(item =>( + <div className="pt-3"> + <p className="pof-gray mb-n1 pb-2">Dikirim pada {moment(new Date(item.timestamp)).format("DD/MM/YYYY")}, {moment(new Date(item.timestamp)).format("HH:mm")} WIB</p> + <TextArea className="feedback-display" + value={item.deskripsi} + readOnly + ></TextArea> + </div>))} + </div> + </Modal.Body> + </Modal> + </div> + ); +} + +export default PopUpFeedback; diff --git a/src/components/PopUpFeedback/PopUpFeedback.test.js b/src/components/PopUpFeedback/PopUpFeedback.test.js new file mode 100644 index 0000000000000000000000000000000000000000..3d92c72c08971abeda5f481caaf1125e2d9e5970 --- /dev/null +++ b/src/components/PopUpFeedback/PopUpFeedback.test.js @@ -0,0 +1,58 @@ +import PopUpFeedback from './PopUpFeedback'; +import { render, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { Provider } from 'react-redux'; +import userEvent from '@testing-library/user-event'; +import { BrowserRouter } from 'react-router-dom' +import configureStore from 'redux-mock-store' +import thunk from 'redux-thunk' + +jest.mock('axios'); + +const middlewares = [thunk] +const mockStore = configureStore(middlewares) + +describe('<PopUpFeedback />', () => { + it('should shows popup when button Feedback is clicked', async () => { + const initialState = { + auth: { + isAuthenticated: true, + user: { + role: "Mitra" + } + } + } + localStorage.setItem('access', 'token'); + const store = mockStore(initialState); + + const feedbackData = [ + { + pk:1, + laporan: 1, + operator: "operator@gmail.com", + deskripsi: "Ini adalah feedback", + timestamp: "2022-04-02T16:59:00.240219+07:00" + } + ] + + const { getByTestId, getByText } = render( + <Provider store={store}> + <BrowserRouter> + <PopUpFeedback + feedback={feedbackData} + />; + </BrowserRouter> + </Provider>); + + const btnPopUp = getByTestId("detail-btn"); + userEvent.click(btnPopUp); + + const popup = getByTestId("popup"); + expect(popup).toBeInTheDocument(); + + const btnClosePopUp = getByText('×'); + userEvent.click(btnClosePopUp); + + await waitFor(() => expect(popup).not.toBeInTheDocument()); + }, 20000); +}) diff --git a/src/page/DetailInvestasi/DetailInvestasi.js b/src/page/DetailInvestasi/DetailInvestasi.js index 0bec7fc3911d30da24c564ae247e10b5b2256563..1fdbf1d9b9d2164293de8a0ef60a3f7c985dded8 100644 --- a/src/page/DetailInvestasi/DetailInvestasi.js +++ b/src/page/DetailInvestasi/DetailInvestasi.js @@ -101,6 +101,7 @@ const DetailInvestasi = ({ isAuthenticated, user, location }) => { const [tahun, setTahun] = useState([]); const [pendapatanBulanan, setpendapatanBulanan] = useState([]); const [bulan, setBulan] = useState([]); + const data = location.state; const email = user.email; let totalPendapatan = 0; @@ -313,7 +314,8 @@ const DetailInvestasi = ({ isAuthenticated, user, location }) => { if (!isAuthenticated) return <Redirect to="/masuk" /> if (user.role !== "Investor") return <Redirect to="/" /> - + console.log("fina here"); + console.log(data); return ( <div className="detail-investasi-wrapper"> <h3 className="back-button" onClick={() => window.history.back()}><ChevronLeft size="40" className="chevron-left" />Kembali</h3> diff --git a/src/page/LaporanDetail/LaporanDetail.css b/src/page/LaporanDetail/LaporanDetail.css index e0caf2fb040beff280b16924c30f011965291426..875ba2030395f123411850257b9cdc91690e9609 100644 --- a/src/page/LaporanDetail/LaporanDetail.css +++ b/src/page/LaporanDetail/LaporanDetail.css @@ -34,3 +34,72 @@ .img-detail{ height: 350px; } + +.modal { + width: 100%; + display: flex; + flex-direction: column; + padding: 0px 20px; + font-family: 'DM Sans', sans-serif; + border-radius: 10px; + box-shadow: 0px 0px 10px rgba(0,0,0,0.25); +} + +.modal .modal-dialog { + max-width: 600px !important; +} + +.modal-header { + font-size: 30px; + margin-top: 16px; + margin-bottom: -5px; + padding: 15px 30px; + font-family: 'DM Sans', sans-serif; + font-weight: 700; + border-bottom: 0 none; +} + +#investasi { + color: #E94745; +} + +button.close { + color: #fc383f; + font-size: 40px; +} + +button.close:hover { + color: #FA595F; +} + +.modal-body { + padding: 10px 30px; +} + +.modal-body > form { + display: flex; + flex-direction: column; + flex-grow: 3; +} + +.modal-body > form > input { + width: 100%; + padding: 10px 15px; + border-radius: 5px; + border: 0.7px solid black; +} + +.modal-body > form > label { + font-weight: 500; + font-size: 15px; + line-height: 10px; +} + +#space { + padding: 10px; +} + +.modal-footer { + border-top: 0 none; + padding: 20px 30px; +} diff --git a/src/page/LaporanDetail/LaporanDetail.js b/src/page/LaporanDetail/LaporanDetail.js index 1168589d22ff8ccc8e27ccde360ee503eb058bda..60e0531a1a0199a1989ddca9adff1c307914786b 100644 --- a/src/page/LaporanDetail/LaporanDetail.js +++ b/src/page/LaporanDetail/LaporanDetail.js @@ -4,12 +4,20 @@ import { Redirect } from 'react-router-dom'; import axios from 'axios'; import { useState, useEffect } from 'react'; import { ChevronLeft } from 'react-feather'; - +import {Modal} from 'react-bootstrap'; +import React from 'react'; const LaporanDetail = ({ isAuthenticated, match, user }) => { const [laporan, setLaporan] = useState({}); const [detail, setDetail] = useState({}); - + const [formData, setFormData] = useState({ + laporanFeedback: '' + }); + const [showModal, setShowModal] = useState(false); + const handleClose = () => setShowModal(false); + const handleOpen = () => { + setShowModal(true); + } const configGet = { headers: { 'Content-Type': 'multipart/form-data', @@ -23,6 +31,11 @@ const LaporanDetail = ({ isAuthenticated, match, user }) => { } }; + const { + laporanFeedback + } = formData; + + const handleAssign = async () => { await axios.patch(`${process.env.REACT_APP_BACKEND_API_URL}/api/laporan/${match.params.pk}/assign/`, {}, configPut); setTimeout('window.location.href="/operator"', 0); @@ -57,6 +70,51 @@ const LaporanDetail = ({ isAuthenticated, match, user }) => { } } + const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value}); + + const postLaporanFeedback = async () => { + if (localStorage.getItem('access')) { + console.log("inside if"); + var formDataToSend = new FormData(); + + const config = { + headers: { + 'Content-Type': 'multipart/form-data', + 'Authorization': `JWT ${localStorage.getItem('access')}`, + } + }; + formDataToSend.append('laporan', laporan.pk); + formDataToSend.append('deskripsi', laporanFeedback); + await axios.post(`${process.env.REACT_APP_BACKEND_API_URL}/api/feedback/`, formDataToSend, config) + .then((response) => { + console.log(response); + alert('Feedback telah ditambahkan'); + }, (error) => { + if (error.response) { + + console.log("error.response") + console.log(error.response) + + } else if (error.request) { + + console.log("error.request") + console.log(error.request) + + } else if (error.message) { + + console.log("error.message") + console.log(error.message) + + } + console.log(error); + alert("Silahkan isi feedback terlebih dahulu") + }); + } else { + console.log('missing token'); + alert('Terdapat kesalahan pada autentikasi akun anda. Anda dapat melakukan refresh pada halaman ini') + } + } + useEffect(() => { fetchDetail(); }, []); @@ -110,8 +168,41 @@ const LaporanDetail = ({ isAuthenticated, match, user }) => { } {laporan.status === "ASG" && <> - <button data-testid="m-i-buat-1" className="lap-detail-button lap-blue-border-button mx-2" onClick={() => handleRefuse()}><span>Batalkan</span></button> - <button data-testid="m-i-buat-2" className="lap-detail-button lap-blue-button" onClick={() => handleResolve()}><span>Selesai</span></button> + <div> + <button className="lap-detail-button lap-blue-button" onClick={handleOpen} data-testid="btn-feedback">Feedback</button> + <Modal show={showModal} onHide={handleClose} data-testid="popup" centered> + <Modal.Header closeButton> + <p>Feedback <span className="jual-red">Laporan</span></p> + </Modal.Header> + <Modal.Body> + <form id="d-t-form" className="centered"> + <div className="justify-content-center"> + <div className="form-group row"> + <label htmlFor='laporanFeedback' className="col-sm-3 col-form-label"> <span className="required"> * </span> Laporan Feedback </label> + <div className="col-sm-9"> + <input + id='laporanFeedback' + className='form-control' + type='text' + placeholder='Tuliskan hal yang sudah dikerjakan' + name='laporanFeedback' + value={laporanFeedback} + onChange={e => onChange(e)} + required + /> + </div> + </div> + </div> + </form> + </Modal.Body> + <Modal.Footer> + <button className="lap-detail-button lap-blue-button" onClick={() => postLaporanFeedback()}><span>Simpan</span></button> + </Modal.Footer> + </Modal> + </div> + <br></br> + <button data-testid="m-i-buat-1" className="lap-detail-button lap-blue-border-button mx-2" onClick={() => handleRefuse()}><span>Batalkan</span></button> + <button data-testid="m-i-buat-2" className="lap-detail-button lap-blue-button" onClick={() => handleResolve()}><span>Selesai</span></button> </> } diff --git a/src/page/UpdatePengadaan/UpdatePengadaan.js b/src/page/UpdatePengadaan/UpdatePengadaan.js index 195ba1f92e15f5f2c3a71cf163da7a6baf0fb9de..2b8384c0639aa5c6fe0cd80409deeee1b234a748 100644 --- a/src/page/UpdatePengadaan/UpdatePengadaan.js +++ b/src/page/UpdatePengadaan/UpdatePengadaan.js @@ -16,10 +16,8 @@ const UpdatePengadaan = ({ isAuthenticated, userData, match}) => { const [toko, setToko] = useState({'namaToko': ''}); const [filesPengadaanAwal, setFilesPengadaanAwal] = useState([]); const [selectedImages, setSelectedImages] = useState([]); - const [startDateInput, setStartDateInput] = useState(null); - const [endDateInput, setEndDateInput] = useState(null); - const [periodePengadaanMulai, setPeriodePengadaanMulai] = useState(''); - const [periodePengadaanAkhir, setPeriodePengadaanAkhir] = useState(''); + const [periodePengadaanMulai, setPeriodePengadaanMulai] = useState(null); + const [periodePengadaanAkhir, setPeriodePengadaanAkhir] = useState(null); const [estimasiKeuangan, setEstimasiKeuangan] = useState(''); const [focusedInput, setFocusedInput] = useState(null); @@ -42,12 +40,10 @@ const UpdatePengadaan = ({ isAuthenticated, userData, match}) => { setPengadaan(pengadaanObj.data); fetchFilePengadaan(pengadaanObj.data.files); setToko(pengadaanObj.data.toko); - var start = moment(new Date(pengadaanObj.data.periodePengadaanMulai)); - var end = moment(new Date(pengadaanObj.data.periodePengadaanAkhir)); - setStartDateInput(start); - setEndDateInput(end); - setPeriodePengadaanMulai(start.format("YYYY-MM-DD")); - setPeriodePengadaanAkhir(end.format("YYYY-MM-DD")); + var startDate = moment(new Date(pengadaanObj.data.periodePengadaanMulai)); + var endDate = moment(new Date(pengadaanObj.data.periodePengadaanAkhir)); + setPeriodePengadaanMulai(startDate); + setPeriodePengadaanAkhir(endDate); setEstimasiKeuangan(pengadaanObj.data.estimasiKeuangan); } catch (err) { @@ -82,14 +78,8 @@ const UpdatePengadaan = ({ isAuthenticated, userData, match}) => { }, []); const onDatesChange = ({startDate, endDate}) => { - setStartDateInput(startDate); - setEndDateInput(endDate); - if (startDate !== null && endDate !== null) { - setPeriodePengadaanMulai(startDate.format("YYYY-MM-DD")); - setPeriodePengadaanAkhir(endDate.format("YYYY-MM-DD")); - } else { - alert("Periksa kembali input tanggal Anda"); - } + setPeriodePengadaanMulai(startDate); + setPeriodePengadaanAkhir(endDate); }; const onSelectFile = e => { @@ -124,7 +114,7 @@ const UpdatePengadaan = ({ isAuthenticated, userData, match}) => { } const inputIsValid = () => { - return !(selectedImages.length === 0 || estimasiKeuangan === ''); + return !(selectedImages.length === 0 || estimasiKeuangan === '' || periodePengadaanMulai === null || periodePengadaanAkhir === null); } const updatePengadaan = () => { @@ -147,8 +137,8 @@ const UpdatePengadaan = ({ isAuthenticated, userData, match}) => { console.log(filesAwalArray); formDataToSend.append('totalBiaya', pengadaan.totalBiaya); - formDataToSend.append('periodePengadaanMulai', periodePengadaanMulai); - formDataToSend.append('periodePengadaanAkhir', periodePengadaanAkhir); + formDataToSend.append('periodePengadaanMulai', periodePengadaanMulai.format("YYYY-MM-DD")); + formDataToSend.append('periodePengadaanAkhir', periodePengadaanAkhir.format("YYYY-MM-DD")); formDataToSend.append('estimasiKeuangan', estimasiKeuangan); for (var pair of formDataToSend.entries()) { @@ -245,9 +235,9 @@ const UpdatePengadaan = ({ isAuthenticated, userData, match}) => { <label>Periode Pengadaan</label> <div className="col-sm-9"> <DateRangePicker - startDate={startDateInput} + startDate={periodePengadaanMulai} startDateId="tata-start-date" - endDate={endDateInput} + endDate={periodePengadaanAkhir} endDateId="tata-end-date" onDatesChange={onDatesChange} focusedInput={focusedInput} @@ -265,7 +255,7 @@ const UpdatePengadaan = ({ isAuthenticated, userData, match}) => { data-testid="estimasiKeuangan" type="text" defaultValue={pengadaan.estimasiKeuangan} - onChange={e => onEstimasiChange(e)} + onKeyUp={e => onEstimasiChange(e)} required /> <div id="space"></div> diff --git a/src/page/UpdatePengadaan/UpdatePengadaan.test.js b/src/page/UpdatePengadaan/UpdatePengadaan.test.js index ab1dbdf6dc944a5485bc1d3b575248a474400657..f0318db1684689f01e6a024480371e598493c4ad 100644 --- a/src/page/UpdatePengadaan/UpdatePengadaan.test.js +++ b/src/page/UpdatePengadaan/UpdatePengadaan.test.js @@ -1,13 +1,14 @@ import UpdatePengadaan from './UpdatePengadaan'; import '@testing-library/jest-dom'; import { BrowserRouter, Route } from 'react-router-dom' -import { getByTestId, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import configureStore from 'redux-mock-store' import thunk from 'redux-thunk' import axios from 'axios' import { Provider } from 'react-redux' import MockAdapter from 'axios-mock-adapter'; +import moment from 'moment'; const middlewares = [thunk] const mockStore = configureStore(middlewares) @@ -75,7 +76,7 @@ describe('<UpdatePengadaan />', () => { expect(screen.getByText(/Kembali/)).toBeInTheDocument(); }); - it('should be able to submit success', () => { + it('should be able to submit success', async () => { const mockUser = jest.fn() const initialState = { auth: { @@ -89,8 +90,7 @@ describe('<UpdatePengadaan />', () => { const store = mockStore(initialState) localStorage.setItem('access', 'token') - const alert = jest.spyOn(window, 'alert'); - alert.mockImplementation(() => {}); + const put = jest.spyOn(axios, 'put'); render( <Provider store={store}> @@ -104,10 +104,12 @@ describe('<UpdatePengadaan />', () => { userEvent.upload(FileInput, testFile); const startDateInput = screen.getByRole('textbox', { name: 'Start Date' }); - userEvent.type(startDateInput, '02/06/2021'); + userEvent.clear(startDateInput); + userEvent.type(startDateInput, (moment().add(5,'days')).format('DD/MM/YYYY')); const endDateInput = screen.getByRole('textbox', { name: 'End Date' }); - userEvent.type(endDateInput, '02/06/2021'); + userEvent.clear(endDateInput); + userEvent.type(endDateInput, (moment().add(20,'days')).format('DD/MM/YYYY')); const estimasiKeuanganInput = screen.getByTestId(/estimasiKeuangan/); userEvent.type(estimasiKeuanganInput, 'Lorem Ipsum estimasi'); @@ -118,8 +120,8 @@ describe('<UpdatePengadaan />', () => { mock.onPut(`${process.env.REACT_APP_BACKEND_API_URL}/api/pengadaan/ubah/1`).reply(200); mock.onDelete().reply(204); - expect(alert).toHaveBeenCalled(); - alert.mockRestore(); + expect(put).toHaveBeenCalled(); + put.mockRestore(); }); it('should be able to handle put error when submit', () => { @@ -136,8 +138,7 @@ describe('<UpdatePengadaan />', () => { const store = mockStore(initialState) localStorage.setItem('access', 'token') - const alert = jest.spyOn(window, 'alert'); - alert.mockImplementation(() => {}); + const put = jest.spyOn(axios, 'put'); render( <Provider store={store}> <BrowserRouter> @@ -150,10 +151,12 @@ describe('<UpdatePengadaan />', () => { userEvent.upload(FileInput, testFile); const startDateInput = screen.getByRole('textbox', { name: 'Start Date' }); - userEvent.type(startDateInput, '02/06/2021'); + userEvent.clear(startDateInput); + userEvent.type(startDateInput, (moment().add(5,'days')).format('DD/MM/YYYY')); const endDateInput = screen.getByRole('textbox', { name: 'End Date' }); - userEvent.type(endDateInput, '02/06/2021'); + userEvent.clear(endDateInput); + userEvent.type(endDateInput, (moment().add(20,'days')).format('DD/MM/YYYY')); const estimasiKeuanganInput = screen.getByTestId(/estimasiKeuangan/); userEvent.type(estimasiKeuanganInput, 'Lorem Ipsum estimasi'); @@ -162,8 +165,8 @@ describe('<UpdatePengadaan />', () => { userEvent.click(tombolSimpan); mock.onPut(`${process.env.REACT_APP_BACKEND_API_URL}/api/pengadaan/ubah/1`).reply(500); - expect(alert).toHaveBeenCalled(); - alert.mockRestore(); + expect(put).toHaveBeenCalled(); + put.mockRestore(); }); it('should be able to handle auth error when submit', () => { @@ -194,10 +197,12 @@ describe('<UpdatePengadaan />', () => { userEvent.upload(FileInput, testFile); const startDateInput = screen.getByRole('textbox', { name: 'Start Date' }); - userEvent.type(startDateInput, '02/06/2021'); + userEvent.clear(startDateInput); + userEvent.type(startDateInput, (moment().add(5,'days')).format('DD/MM/YYYY')); const endDateInput = screen.getByRole('textbox', { name: 'End Date' }); - userEvent.type(endDateInput, '02/06/2021'); + userEvent.clear(endDateInput); + userEvent.type(endDateInput, (moment().add(20,'days')).format('DD/MM/YYYY')); const estimasiKeuanganInput = screen.getByTestId(/estimasiKeuangan/); userEvent.type(estimasiKeuanganInput, 'Lorem Ipsum estimasi'); @@ -246,10 +251,12 @@ describe('<UpdatePengadaan />', () => { userEvent.click(tombolHapusFile); const startDateInput = screen.getByRole('textbox', { name: 'Start Date' }); - userEvent.type(startDateInput, '02/06/2021'); + userEvent.clear(startDateInput); + userEvent.type(startDateInput, (moment().add(5,'days')).format('DD/MM/YYYY')); const endDateInput = screen.getByRole('textbox', { name: 'End Date' }); - userEvent.type(endDateInput, '02/06/2021'); + userEvent.clear(endDateInput); + userEvent.type(endDateInput, (moment().add(20,'days')).format('DD/MM/YYYY')); const estimasiKeuanganInput = screen.getByTestId(/estimasiKeuangan/); userEvent.type(estimasiKeuanganInput, '');