diff --git a/app/javascript/src/common/CutomDatePicker/index.tsx b/app/javascript/src/common/CutomDatePicker/index.tsx new file mode 100644 index 0000000000..f8bf3d55dd --- /dev/null +++ b/app/javascript/src/common/CutomDatePicker/index.tsx @@ -0,0 +1,87 @@ +import React from "react"; +import DatePicker from "react-datepicker"; +import { getMonth, getYear } from "date-fns"; +import { CaretCircleLeft, CaretCircleRight } from "phosphor-react"; + +import "react-datepicker/dist/react-datepicker.css"; + +const CustomDatePicker = ({ handleChange }) => { + const range = (start, end) => { + const ans = []; + for (let i = start; i <= end; i++) { + ans.push(i); + } + return ans; + }; + + const years = range(1990, getYear(new Date()) + 1); + const months = [ + "Jan", + "Febr", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + ]; + return ( + ( +
+ +
+ + + +
+ +
+ )} + onChange={(date) => handleChange(date)} + /> + ); +}; + +export default CustomDatePicker; diff --git a/app/javascript/src/components/Invoices/Generate/Container.tsx b/app/javascript/src/components/Invoices/Generate/Container.tsx index fc587d0a95..27d3b716d5 100644 --- a/app/javascript/src/components/Invoices/Generate/Container.tsx +++ b/app/javascript/src/components/Invoices/Generate/Container.tsx @@ -12,8 +12,8 @@ const Container = ({ invoiceNumber, setInvoiceNumber, amount, setAmount, reference, setReference, - issueDate, - dueDate, + issueDate, setIssueDate, + dueDate, setDueDate, outstandingAmount, setOutstandingAmount, amountDue, setAmountDue, amountPaid, setAmountPaid, @@ -22,14 +22,16 @@ const Container = ({ selectedOption, setSelectedOption }) => (
- +
(false); + const getIssuedDate = dayjs(issueDate).format("DD.MM.YYYY"); + const getDueDate = dayjs(dueDate).format("DD.MM.YYYY"); const saveInvoice = () => { invoicesApi.post({ client_id: client.value, invoice_number: invoiceNumber, reference: reference, - issue_date: issueDate, - due_date: dueDate, + issue_date: getIssuedDate, + due_date: getDueDate, amount_due: amountDue, amount_paid: amountPaid, amount: amount, @@ -43,14 +46,14 @@ const Header = ({ timesheet_entry_id: ilt.timesheet_entry_id })) }) - .then(()=>navigate("/invoices")) + .then(() => navigate("/invoices")) .catch(); }; return ( - +

Generate Invoice

diff --git a/app/javascript/src/components/Invoices/Generate/InvoiceDetails.tsx b/app/javascript/src/components/Invoices/Generate/InvoiceDetails.tsx index 6850a178a4..990c82cf09 100644 --- a/app/javascript/src/components/Invoices/Generate/InvoiceDetails.tsx +++ b/app/javascript/src/components/Invoices/Generate/InvoiceDetails.tsx @@ -1,4 +1,6 @@ import React, { useState, useRef } from "react"; +import CustomDatePicker from "common/CutomDatePicker"; +import dayjs from "dayjs"; import { currencyFormat } from "helpers/currency"; import { PencilSimple } from "phosphor-react"; import ClientSelection from "./ClientSelection"; @@ -9,33 +11,60 @@ const InvoiceDetails = ({ clientList, selectedClient, setSelectedClient, amount, - issueDate, - dueDate, + issueDate, setIssueDate, + dueDate, setDueDate, invoiceNumber, setInvoiceNumber, reference }) => { + const [isEditing, setIsEditing] = useState(false); + const [showDateOfIssuePicker, setShowDateOfIssuePicker] = useState(false); + const [showDueDatePicker, setShowDueDatePicker] = useState(false); const wrapperRef = useRef(null); useOutsideClick(wrapperRef, () => setIsEditing(false), isEditing); + const getIssuedDate = dayjs(issueDate).format("DD.MM.YYYY"); + const getDueDate = dayjs(dueDate).format("DD.MM.YYYY"); + const handleDatePickerChange = (date) => { + setIssueDate(date); + setShowDateOfIssuePicker(false); + }; + + const handleDueDatePicker = (date) => { + setDueDate(date); + setShowDueDatePicker(false); + }; + return (
- +
-

- Date of Issue -

-

- {issueDate} -

-

- Due Date -

-

- {dueDate} -

+
+

+ Date of Issue + +

+ {showDateOfIssuePicker && } +

+ {getIssuedDate} +

+
+
+

+ Due Date + +

+ {showDueDatePicker && } +

+ {getDueDate} +

+
@@ -48,7 +77,7 @@ const InvoiceDetails = ({
- setInvoiceNumber(e.target.value) }/> + setInvoiceNumber(e.target.value)} />

Reference diff --git a/app/javascript/src/components/Invoices/Generate/index.tsx b/app/javascript/src/components/Invoices/Generate/index.tsx index 371b5c5039..4c446dc17a 100644 --- a/app/javascript/src/components/Invoices/Generate/index.tsx +++ b/app/javascript/src/components/Invoices/Generate/index.tsx @@ -3,7 +3,6 @@ import React, { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import generateInvoice from "apis/generateInvoice"; -import dayjs from "dayjs"; import Container from "./Container"; import Header from "./Header"; @@ -34,8 +33,8 @@ const GenerateInvoices = () => { const [amountPaid, setAmountPaid] = useState(0); const [discount, setDiscount] = useState(0); const [tax, setTax] = useState(0); - const [issueDate] = useState(dayjs().format("DD.MM.YYYY")); - const [dueDate] = useState(dayjs().add(1, "month").format("DD.MM.YYYY")); + const [issueDate, setIssueDate] = useState(new Date()); + const [dueDate, setDueDate] = useState(new Date()); const [selectedOption, setSelectedOption] = useState([]); useEffect(() => { @@ -72,7 +71,9 @@ const GenerateInvoices = () => { reference={reference} setReference={setReference} issueDate={issueDate} + setIssueDate={setIssueDate} dueDate={dueDate} + setDueDate={setDueDate} amount={amount} setAmount={setAmount} outstandingAmount={outstandingAmount} diff --git a/app/javascript/stylesheets/application.scss b/app/javascript/stylesheets/application.scss index a99749d015..938f19cb2c 100644 --- a/app/javascript/stylesheets/application.scss +++ b/app/javascript/stylesheets/application.scss @@ -5,6 +5,7 @@ @import "toastr/toastr"; @import "team"; @import "time-tracking"; +@import "react-calendar"; @layer base { @font-face { @@ -369,3 +370,17 @@ body { .box-shadow { box-shadow: 0px 0px 40px rgba(0, 0, 0, 0.1); } + +.hoverPencil:hover button { + @apply visible +} + + + +.hoverPencil .react-datepicker-wrapper { + width: 20px; + display: flex; + justify-content: center; + align-items: center; + margin-top: 2px; +} diff --git a/app/javascript/stylesheets/react-calendar.scss b/app/javascript/stylesheets/react-calendar.scss new file mode 100644 index 0000000000..ba2627498d --- /dev/null +++ b/app/javascript/stylesheets/react-calendar.scss @@ -0,0 +1,67 @@ +.miru-calendar { + padding: 28px; + position: absolute !important; + z-index: 15; + border: none !important; + box-shadow: 0px 0px 40px rgba(0, 0, 0, 0.1); + border-radius: 8px; + .headerWrapper { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 14px; + margin-bottom: 12px; + select { + color: #5b34ea; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 19px; + outline: none; + margin-right: 5px; + } + } + .react-datepicker__day-name { + color: #A5A3AD; + font-weight: 700; + } + .react-datepicker__day--keyboard-selected { + color: #5B34EA; + background-color: transparent; + } + .react-datepicker__day { + font-size: 12px; + line-height: 16px; + letter-spacing: 2px; + padding: 8px; + font-weight: 700; + &:hover { + color: #5B34EA; + background-color: #F5F7F9; + border-radius: 4px; + } + } + .react-datepicker__day--today { + color: #5B34EA; + background-color: transparent; + &:hover { + background-color: #F5F7F9; + border-radius: 4px; + } + } + .react-datepicker__current-month--hasMonthDropdown { + display: none; + } + .react-datepicker__navigation--previous { + top: 35px; + left: 27px; + } + .react-datepicker__navigation--next { + top: 35px; + right: 27px; + } + .react-datepicker__header { + background-color: transparent; + border-bottom: none; + } +} diff --git a/config/routes.rb b/config/routes.rb index 92d4dc1c38..04b6cb73c1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -38,6 +38,7 @@ def draw(routes_name) resources :team, only: [:index, :update, :destroy, :edit] resources :reports, only: [:index] + resources :workspaces, only: [:update] get "clients/*path", to: "clients#index", via: :all diff --git a/package.json b/package.json index 6b8b8d14ec..686baad525 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "babel-plugin-js-logger": "^1.0.17", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "classnames": "^2.3.1", + "date-fns": "^2.28.0", "dayjs": "^1.11.0", "fork-ts-checker-webpack-plugin": "^6.5.0", "formik": "^2.2.9", @@ -32,6 +33,7 @@ "postcss": "~7", "prop-types": "^15.7.2", "react": "^17.0.2", + "react-datepicker": "^4.7.0", "react-dom": "^17.0.2", "react-infinite-scroll-component": "^6.1.0", "react-router-dom": "^6.2.2", diff --git a/yarn.lock b/yarn.lock index 529323df6a..668e5ac6db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1209,6 +1209,11 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@popperjs/core@^2.9.2": + version "2.11.5" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64" + integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw== + "@rails/actioncable@^7.0.1": version "7.0.2" resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-7.0.2.tgz#69a6d999f4087e0537dd38fe0963db1f4305d650" @@ -2541,7 +2546,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.3.1: +classnames@^2.2.6, classnames@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== @@ -3189,6 +3194,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +date-fns@^2.24.0, date-fns@^2.28.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" + integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== + dayjs@^1.10.4, dayjs@^1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805" @@ -7604,6 +7614,18 @@ raw-body@2.4.3: iconv-lite "0.4.24" unpipe "1.0.0" +react-datepicker@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.7.0.tgz#75e03b0a6718b97b84287933307faf2ed5f03cf4" + integrity sha512-FS8KgbwqpxmJBv/bUdA42MYqYZa+fEYcpc746DZiHvVE2nhjrW/dg7c5B5fIUuI8gZET6FOzuDgezNcj568Czw== + dependencies: + "@popperjs/core" "^2.9.2" + classnames "^2.2.6" + date-fns "^2.24.0" + prop-types "^15.7.2" + react-onclickoutside "^6.12.0" + react-popper "^2.2.5" + react-dom@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" @@ -7618,6 +7640,11 @@ react-fast-compare@^2.0.1: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== +react-fast-compare@^3.0.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + react-infinite-scroll-component@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz#7e511e7aa0f728ac3e51f64a38a6079ac522407f" @@ -7630,6 +7657,19 @@ react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-onclickoutside@^6.12.0: + version "6.12.1" + resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz#92dddd28f55e483a1838c5c2930e051168c1e96b" + integrity sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q== + +react-popper@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.5.tgz#1214ef3cec86330a171671a4fbcbeeb65ee58e96" + integrity sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw== + dependencies: + react-fast-compare "^3.0.1" + warning "^4.0.2" + react-router-dom@^6.2.2: version "6.3.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d" @@ -9298,6 +9338,13 @@ warning@^3.0.0: dependencies: loose-envify "^1.0.0" +warning@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + watchpack-chokidar2@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"